Home / AWS

AWSHow-toBeginner

Security groups vs network ACLs: AWS VPC firewall basics and how to debug a blocked connection

How security groups and network ACLs differ (stateful vs stateless, allow-only vs allow and deny), sensible defaults for web and database tiers, and a step-by-step check when traffic does not get through.

Deepak Bhardwaj4 min read

Applies toAmazon VPC security groups and network ACLs, EC2, RDS, AWS CLI v2

Two firewalls filter traffic inside a VPC. Most people only need to change one of them. Knowing which one, and how each behaves, saves hours of guessing.

The difference in one table

Security group (SG) Network ACL (NACL)
Attached to Network interface (instance, RDS, load balancer, Lambda in VPC) Subnet
State Stateful: replies are allowed automatically Stateless: you must allow return traffic too
Rules Allow only Allow and deny, evaluated in rule-number order
Default Inbound: nothing; outbound: all Default NACL allows all in and out
Can reference Other security groups CIDR blocks only

Rule of thumb: do day-to-day filtering with security groups. Leave NACLs at the default allow-all unless you need a subnet-wide deny (for example blocking a known bad range).

A sensible three-tier layout

# load balancer: HTTPS from the internet
aws ec2 authorize-security-group-ingress --group-id sg-alb --protocol tcp --port 443 --cidr 0.0.0.0/0

# app servers: only from the load balancer's SG
aws ec2 authorize-security-group-ingress --group-id sg-app --protocol tcp --port 8080 --source-group sg-alb

# database: only from the app SG
aws ec2 authorize-security-group-ingress --group-id sg-db --protocol tcp --port 5432 --source-group sg-app

Referencing a security group instead of an IP range means new app instances get access automatically and nothing else does.

Warning

Never open SSH (22) or RDP (3389) to 0.0.0.0/0. Use Systems Manager Session Manager or EC2 Instance Connect Endpoint instead and keep those ports closed.

Look at what is there

aws ec2 describe-security-groups --group-ids sg-app \
  --query "SecurityGroups[].IpPermissions[].{From:FromPort,To:ToPort,Proto:IpProtocol,CIDR:IpRanges[].CidrIp,SG:UserIdGroupPairs[].GroupId}" --output table

aws ec2 describe-network-acls --filters "Name=association.subnet-id,Values=subnet-0abc" \
  --query "NetworkAcls[].Entries[].[RuleNumber,Egress,RuleAction,Protocol,CidrBlock,PortRange.From,PortRange.To]" --output table

NACLs and the ephemeral port trap

Because NACLs are stateless, a rule allowing inbound 443 is not enough. The reply goes back to the client on a random high port, so outbound must allow the ephemeral range (commonly 1024-65535):

aws ec2 create-network-acl-entry --network-acl-id acl-0abc --egress \
  --rule-number 120 --protocol tcp --port-range From=1024,To=65535 --cidr-block 0.0.0.0/0 --rule-action allow

Forgetting this is the most common NACL mistake: connections open but replies never arrive.

Debug a blocked connection, step by step

  1. Is the service listening? On the server: ss -tlnp | grep 5432 (Linux) or Get-NetTCPConnection -LocalPort 5432 -State Listen (Windows).
  2. OS firewall? firewalld, ufw or Windows Defender Firewall can block before AWS is involved.
  3. Destination SG inbound allows the source IP or SG on that port.
  4. Source SG outbound allows the destination (default allows all).
  5. NACLs on both subnets: inbound and outbound, including ephemeral ports.
  6. Route tables: is there a route between the subnets / to the internet or NAT gateway?
  7. Let AWS check the path for you:
aws ec2 create-network-insights-path --source i-0app123 --destination i-0db456 --protocol tcp --destination-port 5432
aws ec2 start-network-insights-analysis --network-insights-path-id nip-0abc
aws ec2 describe-network-insights-analyses --network-insights-analysis-ids nia-0abc \
  --query "NetworkInsightsAnalyses[].{Reachable:NetworkPathFound,Why:Explanations[].ExplanationCode}"

Reachability Analyzer tells you which component blocks the path.

  1. Flow logs show REJECT entries with source, destination and port when traffic is dropped by an SG or NACL.

Quick symptom map

Symptom Usually
Connection times out SG or NACL blocks, or no route
Connection refused immediately Reached the host; nothing listening on that port
Works from one subnet, not another NACL on one subnet, or SG references a CIDR not an SG
Works on IPv4, not IPv6 Rules only for 0.0.0.0/0, none for ::/0

Prevent it next time

  • Name SGs after their role (sg-app-web, sg-db-orders) and reference SGs rather than IPs.
  • Enable VPC flow logs on production VPCs.
  • Alert on SG changes that open 22/3389 to the world with AWS Config managed rules such as restricted-ssh.