Engineering Notes
Home/DevOps & Cloud/AWS/Security Groups
DevOps & Cloud · AWS

Security Groups

Security groups are stateful, allow-only firewalls attached to network interfaces. Design a three-tier chain where each tier only accepts traffic from the security group in front of it, and see why SG references beat IP ranges.

☁️ DevOps & Cloud● Beginner⏱ 4 min read▶ 2 interactive🛠️ 1 exercise🎤 5 interview Q&As

🎯 What you'll learn

  • What security groups are: stateful, allow-only, per ENI
  • Inbound/outbound rules and default behaviour
  • CIDR sources vs security-group references
  • Tiered designs, SGs vs NACLs, connection tracking

📚 Before you start

1What a Security Group Is

A security group (SG) is a stateful virtual firewall attached to an elastic network interface (ENI) — so to EC2 instances, ECS tasks (awsvpc), RDS instances, Lambda functions in a VPC, load balancers, VPC endpoints. It decides which traffic may reach (ingress) and leave (egress) that interface.

text
                 ┌──────────── sg-app ────────────┐
alb (sg-alb) ──► │ ingress: tcp 3000 from sg-alb  │──► rds (sg-db: tcp 5432 from sg-app)
                 │ egress:  all (default)          │
                 └─────────────────────────────────┘

2The Rules of the Game

  • Allow rules only — there are no deny rules. Anything not allowed is denied.
  • Stateful: if inbound traffic is allowed, the response is automatically allowed out (and vice versa). You never write rules for ephemeral return ports (unlike NACLs — 02-vpc_subnets_route_tables §6).
  • All rules are evaluated together (no order); the most permissive match wins.
  • Default SG for a new group: no inbound, all outbound allowed.
  • An ENI can have up to 5 SGs (adjustable); rules from all of them are unioned.
  • Changes apply immediately to existing and new connections' evaluation (tracked connections keep flowing).

3Sources: CIDR vs Security Group References

bash
# CIDR source — anyone in that address range
aws ec2 authorize-security-group-ingress --group-id sg-alb --protocol tcp --port 443 --cidr 0.0.0.0/0

# SG reference — any ENI that has sg-alb attached, whatever its IP
aws ec2 authorize-security-group-ingress --group-id sg-app --protocol tcp --port 3000 --source-group sg-alb

# managed prefix list — e.g. CloudFront's origin-facing IP ranges
aws ec2 authorize-security-group-ingress --group-id sg-alb \
  --ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds=[{PrefixListId=pl-xxxxxxxx}]'

Referencing security groups is the key design tool: rules describe roles ("the app tier may talk to the DB tier") instead of IP addresses, so autoscaling, redeploys and IP changes never require rule updates.

4A Tiered Design

SGIngressEgress
sg-alb443 from 0.0.0.0/0 (or CloudFront prefix list)3000 to sg-app
sg-app3000 from sg-alb5432 to sg-db, 6379 to sg-cache, 443 to the internet (via NAT) for third-party APIs
sg-db5432 from sg-app (and sg-migrations)none needed (stateful replies)
sg-cache6379 from sg-appnone
sg-bastion → replace with SSM Session Manager: no inbound 22 at all

Tightening egress (instead of the default allow-all) limits what a compromised instance can reach — data exfiltration and command-and-control traffic.

5Security Groups vs NACLs

Security groupNetwork ACL
attached toENI (instance-level)subnet
statestatefulstateless (return traffic needs explicit rules)
rulesallow onlyallow and deny, numbered, evaluated in order
typical useprimary, fine-grained controlcoarse subnet guardrails, explicit denies (block an IP range)

Use SGs for nearly everything; NACLs as an extra layer or for explicit deny lists.

Under the HoodConnection Tracking▾

Statefulness is implemented with connection tracking on the host's virtual network: when an allowed flow starts, the 5-tuple (protocol, src IP/port, dst IP/port) is recorded, and packets matching a tracked flow are allowed regardless of rules. Consequences:

  • Removing a rule doesn't cut an established tracked connection immediately — existing flows continue until they end or time out (unless the rule was an "untracked" all-open rule, a subtle edge case). To kill a compromised session, also use NACLs or stop the instance.
  • Tracked connections consume per-ENI conntrack capacity; very high connection rates (e.g. a UDP-heavy service) can hit allowance limits, visible as conntrack_allowance_exceeded in ENA metrics.
  • Some flows are untracked when rules allow all traffic both ways (0.0.0.0/0 all protocols in and out), which is why tightening rules can surprisingly increase conntrack usage.
An ALB security group admits 443 from the internet, the app security group admits port 3000 only from the ALB group, and the database group admits 5432 only from the app group; rules reference groups rather than IPs and replies are allowed by connection tracking
An ALB security group admits 443 from the internet, the app security group admits port 3000 only from the ALB group, and the database group admits 5432 only from the app group; rules reference groups rather than IPs and replies are allowed by connection tracking

⚠️ Common Pitfalls

  1. 0.0.0.0/0 on SSH/RDP/database ports.
  2. CIDR rules for internal tiers instead of SG references — break on IP changes, too broad.
  3. Allow-all egress everywhere — no exfiltration control.
  4. Thinking SGs can deny a specific IP — they can't; use NACLs/WAF.
  5. One shared "default" SG for everything — every resource can talk to every other.
  6. Forgetting that removing a rule doesn't drop established connections.

📋 Quick Reference

text
stateful · allow-only · attached to ENIs · rules unioned · default: no in, all out
reference SGs for internal tiers; prefix lists for AWS-managed ranges
ALB(443 public) → app(port from sg-alb) → db(5432 from sg-app)
tighten egress; replace bastions with SSM Session Manager
NACL = stateless subnet guardrail with deny rules

🧠 Key takeaways

  • Security groups are stateful, allow-only firewalls attached to ENIs.
  • Reference other security groups as sources instead of CIDRs: rules follow instances as IPs change.
  • Build tiered chains (ALB → app → DB); never open databases or SSH to 0.0.0.0/0 (use SSM Session Manager).
  • NACLs are stateless subnet-level filters with deny rules; use them sparingly as a coarse backstop.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1What is a security group?core▾

A stateful virtual firewall attached to ENIs (instances, load balancers, RDS, Lambda in VPC). It has only allow rules; return traffic is automatically allowed. Default: deny all inbound, allow all outbound.

Follow-ups interviewers ask
  • Can you write deny rules? No; use NACLs or network firewalls for explicit denies.
Q2Security groups vs network ACLs?core▾

Security groups: instance level, stateful, allow-only, all rules evaluated. NACLs: subnet level, stateless (configure both directions and ephemeral ports), allow and deny, rules evaluated in order.

Follow-ups interviewers ask
  • When use NACLs? Coarse subnet-wide blocks, e.g. denying a malicious IP range.
Q3How do you model a three-tier app with security groups?core▾

ALB SG allows 443 from the internet; app SG allows the app port only from the ALB SG; DB SG allows 5432 only from the app SG. Referencing SGs instead of IPs keeps rules correct as instances scale.

Follow-ups interviewers ask
  • Why reference SGs? IPs change with autoscaling; SG references don't.
Q4What are common security group mistakes?senior▾

0.0.0.0/0 on SSH/RDP or database ports, overly broad port ranges, unused rules left behind, and sharing one SG across unrelated services.

Follow-ups interviewers ask
  • How do you audit them? AWS Config rules, Security Hub, Trusted Advisor.
Q5Do security groups limit outbound traffic?core▾

Only if you replace the default allow-all outbound rule. Restricting egress limits data exfiltration and SSRF impact, though it requires knowing dependencies.

Follow-ups interviewers ask
  • Combine with? VPC endpoints and egress proxies.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. Do you need an outbound rule for responses to allowed inbound traffic?
Connection tracking.
2. Why reference sg-alb instead of the ALB's IPs?
Identity-based rules.
3. Can security groups have explicit deny rules?
Anything not allowed is dropped.
4. Safest way to get a shell on private instances?
Audited, no open ports.