AWS Network Security — Security Groups & NACLs¶
1. Foundation — Ports & Protocols¶
Port Range¶
| Range | Category | Examples |
|---|---|---|
| 0 – 1023 | Well-known / System | 22 (SSH), 80 (HTTP), 443 (HTTPS), 3306 (MySQL) |
| 1024 – 49151 | Registered | 3389 (RDP), 8080 (alt HTTP), 5432 (PostgreSQL) |
| 49152 – 65535 | Ephemeral / Dynamic | OS-assigned for response traffic |
Ephemeral Ports — Why They Matter for NACL ⭐¶
When a client connects to a server, the OS assigns a random high port for the return traffic. This is called an ephemeral port.
Client (your laptop) Server (EC2)
Port: 54231 (ephemeral) ←─────────── Port: 443
HTTP Response
SG (stateful): No problem — return traffic is automatically tracked.
NACL (stateless): You must explicitly allow the ephemeral port range in your outbound rules on the server side, and inbound rules on the client side.
NACL Inbound rule (server subnet): allow TCP 443 inbound (incoming request)
NACL Outbound rule (server subnet): allow TCP 1024-65535 outbound (response to client)
If you forget ephemeral ports in NACL outbound rules, responses will be blocked even though the request was allowed. This is the #1 NACL debugging trap.
To learn more about ephemeral ports, click here.
OSI Layer¶
Both SG and NACL operate at Layer 3 (Network) and Layer 4 (Transport):
- Layer 3: IP address-based rules
- Layer 4: Port + Protocol (TCP/UDP) rules
2. Security Groups¶
Definition: A stateful virtual firewall attached to an ENI — not to EC2 directly.
Internet → ENI → Security Group evaluates → EC2
2.1 Stateful — What It Means ⭐¶
Stateful = SG tracks connection state. Return traffic is automatically allowed.
Client → SG (inbound rule: allow TCP 443) → EC2
EC2 → response automatically allowed ← Client
(No outbound rule needed for the response)
But new outbound is different:
EC2 → tries to call external API (new connection, not a response)
→ needs explicit outbound rule ✅
Stateful = only NEW traffic is evaluated against rules. Established/return traffic bypasses evaluation entirely.
2.2 Rule Evaluation — All Rules Simultaneously¶
SG evaluates ALL rules at once — most permissive rule wins. This is NOT first-match.
Rule 1: Allow TCP 80 from 10.0.0.5
Rule 2: Allow TCP 80 from 0.0.0.0/0
Traffic from 10.0.0.5 on port 80:
→ BOTH rules match → traffic is ALLOWED (most permissive)
Because SG is allow-only, all rules are just "add more allows." There is no ordering — it's a union of all allow rules.
2.3 Default vs Custom SG Behavior¶
| Default SG (auto-created with VPC) | Custom SG (you create) | |
|---|---|---|
| Inbound | Allow from same SG only | ❌ Block all |
| Outbound | Allow all (0.0.0.0/0) | Allow all (0.0.0.0/0) |
| Deletable | ❌ Cannot delete | ✅ Yes |
Custom SG outbound allows all by default — you can restrict it. Default SG inbound self-reference = instances in the same SG can talk to each other.
2.4 SG as Source/Destination (SG Chaining) ⭐¶
Instead of specifying an IP range, you can reference another SG ID as the source or destination of a rule.
Example — 3-tier architecture:
ALB-SG → allows all HTTP/HTTPS from internet (0.0.0.0/0)
AppServer-SG → allows port 8080 from ALB-SG only
DB-SG → allows port 3306 from AppServer-SG only
Configuration:
AppServer-SG inbound rule:
Protocol: TCP
Port: 8080
Source: ALB-SG (sg-xxxxxxxx) ← references SG ID, not IP
DB-SG inbound rule:
Protocol: TCP
Port: 3306
Source: AppServer-SG (sg-yyyyyyyy)
Why this is better than IP-based rules:
- Auto-scales — when new app server launches and joins AppServer-SG, it can immediately talk to DB without updating DB-SG rules
- No hardcoded IPs — works even when instances are replaced
- Uses private IPs of instances in the referenced SG (not public IPs)
SG referencing is the standard way to architect secure multi-tier applications in AWS.
2.5 SG Limits¶
| Limit | Default Value |
|---|---|
| Inbound rules per SG | 60 |
| Outbound rules per SG | 60 |
| SGs per ENI | 5 |
| SGs per VPC | 2,500 |
2.6 Key SG Properties¶
| Property | Detail |
|---|---|
| Attachment point | ENI (one or more SGs per ENI) |
| Rules type | Allow only — no deny |
| Rule evaluation | All rules simultaneously — most permissive wins |
| Updates | Real-time — no restart needed |
| Scope | Applies within VPC (same or peered VPC with referencing) |
| Default outbound | Allow all (you can restrict) |
3. Network ACL (NACL)¶
Definition: A stateless firewall at the subnet boundary — controls all traffic entering and leaving a subnet.
Internet
↓
NACL evaluated (subnet entry)
↓
Inside subnet → Security Group evaluated (ENI)
↓
EC2
3.1 Stateless — What It Means ⭐¶
Stateless = NACL has no memory of connections. Every packet is evaluated independently against the rules — both directions independently.
Client → EC2 (inbound port 443)
NACL evaluates: is inbound TCP 443 allowed? YES → packet passes
EC2 → Client (response on ephemeral port 54231)
NACL evaluates: is outbound TCP 54231 allowed? SEPARATE evaluation
→ If no rule allows 1024-65535 outbound → BLOCKED ❌
You must configure both inbound and outbound rules for every interaction.
3.2 Default vs Custom NACL¶
| Default NACL | Custom NACL | |
|---|---|---|
| Inbound | ✅ Allow ALL | ❌ Deny ALL (only * deny rule exists) |
| Outbound | ✅ Allow ALL | ❌ Deny ALL |
| Assigned to | Every new subnet | Must manually associate |
| Deletable | ❌ Cannot delete | ✅ Yes |
3.3 Rule Numbering and Evaluation (First Match) ⭐¶
Rules evaluated in ascending number order — first match wins, lower rules are ignored.
| Rule # | Type | Protocol | Port | Action |
|---|---|---|---|---|
| 100 | Inbound | TCP | 22 | ALLOW |
| 200 | Inbound | TCP | 80 | ALLOW |
| 300 | Inbound | TCP | 443 | ALLOW |
| 400 | Inbound | TCP | 0-65535 | ALLOW |
| * | Inbound | All | All | DENY |
Rule
*is the implicit deny — always present, always last, cannot be modified. Every NACL ends with*DENY ALL — you cannot remove it.
Numbering best practice: - Use increments of 100 (100, 200, 300...) for room to insert later (150, 250) - Valid range: 1 – 32766
3.4 NACL Allows DENY Rules ⭐¶
Unlike SG, NACL supports explicit DENY rules. This is critical for blocking specific IPs or ranges.
Rule 90: DENY TCP from 203.0.113.0/24 (block known bad IP range)
Rule 100: ALLOW TCP 443 from 0.0.0.0/0
Rule *: DENY ALL
Rule 90 blocks the bad range before Rule 100 can allow it. Lower number = higher priority.
3.5 Subnet Association¶
| Property | Detail |
|---|---|
| One NACL per subnet | Each subnet can only be associated with ONE NACL |
| One NACL → many subnets | One NACL can be applied to multiple subnets |
| Default | Every subnet starts with the default NACL (allow all) |
| Change | You can swap NACL association at any time |
3.6 NACL Rule Limits¶
| Limit | Value |
|---|---|
| Rules per NACL (inbound) | 20 (can request increase to 40) |
| Rules per NACL (outbound) | 20 (can request increase to 40) |
| NACLs per VPC | 200 |
4. SG vs NACL — Complete Comparison ⭐¶
| Dimension | Security Group | NACL |
|---|---|---|
| Level | ENI (instance-level) | Subnet (boundary-level) |
| State | Stateful — tracks connections | Stateless — evaluates each packet independently |
| Rules | Allow only (no deny) | Allow + Deny |
| Evaluation | All rules simultaneously | First match wins (ordered by rule number) |
| Implicit behavior | Implicit deny all inbound | Implicit deny all (the * rule) |
| Return traffic | Automatically allowed | Must explicitly allow (both directions) |
| Ephemeral ports | Not needed (stateful) | Must allow 1024-65535 for responses |
| Multiple per resource | ✅ Multiple SGs per ENI | ❌ One NACL per subnet |
| Default (inbound) | Block all (custom SG) | Allow all (default NACL) |
| Scope | One instance at a time | All instances in the subnet |
| Updates | Real-time | Real-time |
| Source/Dest | IP, CIDR, or SG reference | IP / CIDR only |
5. Full Traffic Flow (Request + Response)¶
Request: Client → EC2 (inbound)¶
Client (54.x.x.x) → EC2 (port 443)
1. NACL inbound rule evaluated at subnet boundary
→ Rule 100: Allow TCP 443 from 0.0.0.0/0 ✅
2. Security Group inbound rule evaluated at ENI
→ Allow TCP 443 from 0.0.0.0/0 ✅
3. EC2 receives request ✅
Response: EC2 → Client (outbound)¶
EC2 (port 443) → Client (ephemeral port 54231)
1. Security Group: STATEFUL → response automatically allowed ✅
2. NACL outbound rule evaluated at subnet boundary: STATELESS
→ Must have rule: Allow TCP 1024-65535 outbound ✅ or ❌
The most common NACL debugging scenario: Request works (inbound allowed) but response fails (outbound ephemeral not allowed).
6. Real Architecture — 3-Tier App¶
Internet
│
↓ TCP 80/443
[Public Subnet NACL] ← Allow: inbound 80/443, outbound 1024-65535
│
↓
[ALB] ← SG: inbound 80/443 from 0.0.0.0/0
│
↓ TCP 8080
[Private Subnet NACL] ← Allow: inbound 8080, outbound 1024-65535
│
↓
[App Servers] ← SG: inbound 8080 from ALB-SG (SG reference)
│
↓ TCP 3306
[DB Subnet NACL] ← Allow: inbound 3306, outbound 1024-65535
│
↓
[RDS] ← SG: inbound 3306 from AppServer-SG (SG reference)
NACL usage here: Coarse boundary control between subnet tiers. SG usage here: Fine-grained instance-level control using SG references.
7. When to Use What¶
| Use This | When |
|---|---|
| Security Group | Controlling access to a specific instance / ENI |
| Security Group | Microservices — referencing SG ID instead of IPs |
| Security Group | Auto-scaling — no IP hardcoding needed |
| NACL | Explicitly blocking a known malicious IP range |
| NACL | Adding an extra layer of protection at subnet boundary |
| NACL | Compliance requirement for subnet-level firewall |
| Both | Defense in depth — always recommended for production |
8. Troubleshooting Checklist¶
When a connection fails, check in this order:
1. Route Table → Is there a route to the destination?
2. NACL (inbound) → Is the inbound traffic allowed at subnet boundary?
3. NACL (outbound) → Is response traffic allowed out (ephemeral ports)?
4. Security Group → Is the port allowed at the ENI level?
5. OS Firewall → Is iptables/Windows Firewall blocking?
6. Application → Is the service listening on the correct port?
Ping (ICMP) fails? → Check both NACL and SG for ICMP protocol rules — neither allows ICMP by default in custom configs.
9. Common Mistakes ✅¶
| ❌ Wrong | ✅ Correct |
|---|---|
| SG evaluates rules top to bottom | SG evaluates ALL rules simultaneously — most permissive wins |
| NACL evaluates all rules | NACL is first-match — rule order matters |
| Return traffic needs outbound SG rule | SG is stateful — return traffic automatically allowed |
| NACL allows all traffic by default (custom) | Custom NACL denies ALL by default — only default NACL allows all |
| Only need inbound NACL rule | NACL stateless — must add both inbound AND outbound rules |
| Forget ephemeral ports in NACL | Must allow 1024-65535 outbound for responses to reach clients |
| SG attached to EC2 | SG attached to ENI — EC2 has no direct SG |
| One subnet can have multiple NACLs | One subnet → one NACL at a time |
| SG can only use IP ranges as source | SG can reference another SG ID as source/destination |
| Deleting NACL rules = allow | Deleting rule = falls through to * implicit deny |
10. Interview Questions Checklist ✅¶
- What is a Security Group? What level does it operate at?
- What does "stateful" mean in Security Groups?
- SG rule evaluation — all at once or first match?
- Can Security Groups have DENY rules? (No)
- What is the default inbound/outbound for a custom SG?
- What is the default SG in a VPC? How is it different?
- What is SG chaining / SG referencing? Why use it?
- What is a NACL? What level does it operate at?
- What does "stateless" mean in NACLs?
- NACL rule evaluation order — how does first-match work?
- Can NACLs have DENY rules? (Yes — only NACLs can explicitly deny)
- What is the
*rule in a NACL? - Default NACL vs Custom NACL — inbound/outbound defaults?
- How many NACLs can one subnet have? (One)
- What are ephemeral ports? Why do they matter for NACLs?
- You added an inbound NACL rule but responses are blocked — why?
- Walk through the full request + response flow through NACL + SG
- SG vs NACL — 5 key differences
- When would you use NACL over SG?
- A connection is failing — what is your debugging order?