Routing decides whether a packet can find your instance. A security group decides whether it's let in. It's a firewall that wraps each network interface — not the subnet, the individual instance (or ALB, or ECS task, or RDS database).
Three properties define how SGs think:
① Allow-only. Rules can only permit traffic. Anything not explicitly allowed is denied. There is no "deny" rule to write, ever.
② Stateful. If a connection is allowed in one direction, the reply traffic is automatically allowed back. You never write rules for responses.
③ Rules can reference other security groups. Instead of "allow port 8080 from these IPs," you say "allow port 8080 from anything wearing the ALB's security group." This is the single most useful trick in cloud networking — identities instead of addresses.
Build the firewall yourself
Fire packets at an instance
Toggle inbound rules on the instance's security group, pick a packet, and hit send. Try to predict allow/deny before each shot.
SG-APP · INBOUND RULESunchecked = rule deleted
Did you try it? Send ALB → 8080 with the sg-alb rule off, then on. That rule — "allow the app port only from the load balancer's SG" — is exactly how production services are wired. No IP addresses to maintain; if the ALB scales to new IPs tomorrow, the rule still holds.
Statefulness: the rule you never write
Your instance calls an external API. The response comes back on some high random port — you never wrote an inbound rule for it. Why does it work? Because SGs track connections, not packets. Once the outbound request is allowed, its return traffic is recognized and waved through.
NACLs (network ACLs) — the subnet-level firewall — are the opposite: stateless. Every packet is judged alone, so return traffic needs its own explicit rule. Feel the difference:
Request out, response back
The instance calls api.stripe.com:443. Watch the response leg. Then switch the firewall mode and run it again.
SG vs NACL, side by side
Security group
Attached to the instance (its network interface)
Stateful — replies auto-allowed
Allow rules only
All rules evaluated together; any match admits
Can reference other SGs
Your day-to-day tool — 95% of the work
Network ACL
Attached to the subnet — a moat, not a bodyguard
Stateless — replies need explicit rules
Allow and deny rules
Rules numbered, evaluated in order, first match wins
IP/CIDR based only
Mostly left at default; used for blunt blocks (e.g. banning an IP range)
Where identity products care: security groups are themselves permission objects — "who can talk to what" is an access-control graph, just at the network layer instead of the API layer. An overly broad 0.0.0.0/0 : 22 rule is the networking twin of a wildcard IAM policy.
Check yourself
1. Traffic reaches an instance but you want to block one abusive IP. What can do it?
SGs have no deny rules — they can only fail to allow. For an explicit block, the NACL is the tool: it supports numbered deny rules at the subnet edge.
2. Your web server's SG allows inbound 443 from 0.0.0.0/0 and has no outbound rules for high ports. Can it respond to browsers?
Statefulness means the connection, once admitted, is tracked — replies ride back without their own rules. (The NACL point is technically real but the default NACL allows everything, and the question is about the SG.)
3. Best way to ensure only the load balancer can reach your app tasks on port 8080?
SG-to-SG references follow the identity, not the address. ALB IPs change as it scales; the SG reference never goes stale. Option A breaks silently; option C leaves the port open to the world.