Network Security: Security Groups and NACLs

Two firewall layers in a VPC: stateful security groups on resources and stateless network ACLs on subnets.

What is it?

AWS gives you two built-in packet filters inside a VPC:

  • Security group (SG): attached to an instance's network interface. Stateful: if you allow a request in, the reply is automatically allowed out, and vice versa. Only allow rules exist; anything not allowed is denied.
  • Network ACL (NACL): attached to a subnet. Stateless: inbound and outbound are judged separately, so you must also allow return traffic (usually ephemeral ports). It supports allow and deny rules evaluated in number order.

Default behavior: a new security group denies all inbound and allows all outbound. The default NACL of a default VPC allows all traffic in and out; a NACL you create yourself starts by denying everything.

Security groups can reference other security groups as sources, which lets you say 'the app tier may talk to the database tier' without hard-coding IPs.

Explain like I'm 10

A subnet's NACL is the guard at an office building's front desk who checks every person in and every person out against a written list, and treats the two directions as separate decisions. A security group is the receptionist at each company's door who remembers 'I invited this visitor' - so when the visitor leaves, no re-check is needed.

Examples

A web tier security group

Security group: web-sg   (stateful)
Direction  Protocol  Port   Source / Destination     Purpose
---------  --------  -----  -----------------------  -------------------
Inbound    TCP       443    0.0.0.0/0                public HTTPS
Inbound    TCP       22     203.0.113.10/32          admin SSH from office
Outbound   All       All    0.0.0.0/0                default egress

Security group: db-sg
Inbound    TCP       5432   source = web-sg          only app tier may connect
(No other inbound rules -> everything else denied)

A NACL pair (stateless: both directions needed)

Rule  Dir      Protocol  Ports        Source/Dest      Action
----  -------  --------  -----------  ---------------  ------
100   Inbound  TCP       443          0.0.0.0/0        ALLOW
110   Inbound  TCP       1024-65535   0.0.0.0/0        ALLOW   <- replies to our outbound calls
120   Inbound  All       All          198.51.100.0/24  DENY    (blocked bad range; lower number wins)
100   Outbound TCP       1024-65535   0.0.0.0/0        ALLOW   <- replies to clients
110   Outbound TCP       443          0.0.0.0/0        ALLOW
*     Both     All       All          0.0.0.0/0        DENY    (implicit final rule)

Rule 120 would never match traffic already allowed by rule 100/110 - NACL rules are processed by number. Put specific denies at lower numbers than broad allows.

Create and open a security group

SG=$(aws ec2 create-security-group --group-name web-sg \
  --description "web tier" --vpc-id vpc-0abc1234 --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" \
  --protocol tcp --port 443 --cidr 0.0.0.0/0

How it works

Walk a request through: a client sends HTTPS to a web server in a subnet. Traffic first meets the subnet's NACL inbound rules; if allowed, it reaches the instance's security group, which checks inbound rules. The response skips the security group's outbound evaluation (it is tracked as part of the connection), but it must pass the NACL outbound rules because NACLs do not track connections.

Because NACLs are blunt (per subnet, IP/port based), most day-to-day control uses security groups; NACLs are an extra layer, handy for blocking a specific IP range at the subnet edge.

  client --> [ NACL in ] --> [ SG in ] --> instance
  client <-- [ NACL out ] <-- (reply auto-allowed by SG)
              ^ stateless: needs its own rule

Why does it exist?

Defense in depth: a mistake in one control should not expose everything. Instance-level and subnet-level filters give layered protection and different tools - SGs for precise allow-lists, NACLs for coarse allow/deny at the boundary.

When to use it

Use security groups on every resource, with tight sources. Use NACLs when you need explicit deny rules or a coarse subnet-level guard.

When not to use it

Do not rely on a NACL alone to protect instances, and do not open 0.0.0.0/0 on administrative ports like SSH or RDP; use Systems Manager Session Manager or a bastion with restricted sources.

Common mistakes

  • Forgetting ephemeral-port return rules in a NACL and wondering why connections hang.

  • Expecting a security group to support deny rules.

  • Opening port 22 or 3389 to the world.

  • Misordering NACL rule numbers so a broad allow shadows a specific deny.

  • Hard-coding IPs where a security group reference would follow the instances automatically.

Practice exercises

  1. Easy:

    State two differences between security groups and NACLs.

  2. Medium:

    Write SG rules for a three-tier app (ALB, app, database) where each tier only accepts traffic from the tier before it.

  3. Medium:

    Use the CIDR calculator to find the range for a /27 and write a NACL rule that blocks it.

  4. Hard:

    A client times out on HTTPS although the SG allows 443 and the NACL has an inbound 443 allow. Find the most likely missing rule and explain why.

Interview questions

What does stateful mean for a security group?

Return traffic for an allowed connection is automatically permitted without a separate rule.

Can a security group deny traffic explicitly?

No, it only has allow rules; unmatched traffic is denied by default. Use a NACL for explicit denies.

What are the default rules of a new security group?

All inbound denied, all outbound allowed.

Exam-style: Which acts at the subnet level and is stateless?

Network ACL.

Exam-style: Which acts at the instance level and is stateful?

Security group.