Security Groups vs NACLs

Security groups are stateful firewalls around instances; network ACLs are stateless firewalls around subnets.

AWS gives you two layers of network firewall. Using both is defense in depth.

Security Groups

  • Operate at the instance / ENI level.
  • Stateful — return traffic is automatically allowed.
  • Support allow rules only.
  • All rules are evaluated together.

Network ACLs (NACLs)

  • Operate at the subnet level.
  • Stateless — you must allow both inbound and outbound explicitly.
  • Support both allow and deny rules.
  • Rules are evaluated in numbered order, lowest first.
Security GroupNACL
Applies toInstanceSubnet
StateStatefulStateless
RulesAllow onlyAllow & deny

Example

Example · bash
# Security group: allow HTTPS inbound
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 443 --cidr 0.0.0.0/0

# NACL: explicitly DENY a bad IP range on the subnet
aws ec2 create-network-acl-entry \
  --network-acl-id acl-0123456789abcdef0 \
  --rule-number 90 --protocol -1 --rule-action deny \
  --ingress --cidr-block 198.51.100.0/24

When to use it

  • A security group allows port 3306 only from the application server's security group ID, creating an automatic whitelist as new app instances launch.
  • A network ACL explicitly denies traffic from a known malicious IP range at the subnet boundary before it ever reaches any instance.
  • A compliance team uses NACLs as a coarse subnet-level block and security groups for fine-grained instance-level rules, applying both layers.

More examples

Security Group Allow App to DB

Configures a stateful security group rule that allows PostgreSQL traffic only from instances in the app security group.

Example · bash
# Allow only the app security group to reach the DB on port 5432
aws ec2 authorize-security-group-ingress \
  --group-id sg-db-0abc123 \
  --protocol tcp \
  --port 5432 \
  --source-group sg-app-0abc123

Create Subnet-Level NACL Rule

Adds a stateless NACL deny rule at subnet level blocking all TCP traffic from a specific CIDR, evaluated before security groups.

Example · bash
# Deny inbound traffic from a blocked IP range at rule 100
aws ec2 create-network-acl-entry \
  --network-acl-id acl-0abc123 \
  --ingress \
  --rule-number 100 \
  --protocol tcp \
  --port-range From=0,To=65535 \
  --cidr-block 198.51.100.0/24 \
  --rule-action deny

Compare Effective Rules for Instance

Shows how to inspect both the security groups and NACL rules that apply to an EC2 instance for traffic troubleshooting.

Example · bash
# Check security groups on an instance
aws ec2 describe-instances \
  --instance-ids i-0abc123 \
  --query 'Reservations[].Instances[].SecurityGroups' \
  --output table

# Check NACLs on the instance's subnet
aws ec2 describe-network-acls \
  --filters Name=association.subnet-id,Values=subnet-0abc123 \
  --query 'NetworkAcls[].Entries[].{Rule:RuleNumber,Proto:Protocol,Action:RuleAction}' \
  --output table

Discussion

  • Be the first to comment on this lesson.