Skip to main content

Command Palette

Search for a command to run...

Hands-On: Security Groups and NACLs in Action

Updated
3 min read
Hands-On: Security Groups and NACLs in Action
A
DevOps engineer focused on building, automating, and securing cloud infrastructure. I work with AWS, Docker, Terraform, and CI/CD pipelines to ship scalable and reliable systems. I write about cloud services & architecture, deployment strategies, and real-world DevOps practices.

Watching traffic get blocked and unblocked in real time is what actually makes Security Groups and NACLs click. This post walks through a practical setup - a custom VPC, an EC2 instance running a Python HTTP server, and live manipulation of Security Group and NACL rules.


The Setup

Custom VPC (10.0.0.0/16)
    -    Public Subnet
        -   EC2 Instance (Python HTTP server on port 8000)
            - Security Group (inbound rules)
            - NACL (subnet-level rules)

Next is an EC2 instance, we've to configure it's VPC to the one we've created.

SSH and fire the following commands to initiate a python HTTP server

sudo apt update
python3 -m http.server 8000

Case1: Default Security Group Blocks Everything

Hit http://65.2.191.13:8000 in the browser. Nothing.

Expected - the default Security Group AWS creates only allows port 22 (SSH). Port 8000 isn't in there. The NACL at this point is default allow-all, so the block is entirely coming from the Security Group.

Clean. One checkpoint, one rule, done.


Case 2: NACL Overrides the Security Group

This is where it gets interesting.

Security Group still allows port 8000. I went into the NACL and set up these inbound rules:

Rule # Type Port Source Action
100 Custom TCP 8000 0.0.0.0/0 DENY
200 All traffic All 0.0.0.0/0 ALLOW

Refreshed the browser. Application GONE! The Security Group hadn't changed. Port 8000 was still explicitly allowed at the instance level. Didn't matter - NACL rule 100 killed it at the subnet boundary.


Round 3: Flipped the Rule Order

Rule # Type Port Source Action
100 All traffic All 0.0.0.0/0 ALLOW
200 Custom TCP 8000 0.0.0.0/0 DENY

Application came back up immediately.

Rule 100 matched first (Allow All), evaluation stopped, rule 200 never ran.

This is a subtle thing that would be genuinely painful to debug if you didn't know how NACL evaluation works.


What Stuck With Me

The Security Group and NACL interaction is clean in theory but the stateless nature of NACLs is easy to forget in practice. If I were doing this in a real setup I'd also need outbound rules for responses to leave the subnet - something I skipped here since I was testing inbound blocking behavior, not response flow.

Worth breaking in a test VPC before it breaks something that matters.