AWS VPC: Security Groups, NACLs & Flow Logs
Security Groups
Security groups are stateful firewalls attached to EC2 instances, RDS, etc. Rules are evaluated as a whole — if any rule allows the traffic, it's permitted. Deny rules are not possible.
# Create security group
aws ec2 create-security-group --group-name web-sg --description "Web server security group" --vpc-id vpc-0abc123
# Add inbound rules
aws ec2 authorize-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 443 --cidr 0.0.0.0/0
# Allow SSH from specific IP
aws ec2 authorize-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 203.0.113.0/32
# Reference another SG (allow app-sg to connect to db-sg on 5432)
aws ec2 authorize-security-group-ingress --group-id sg-db123 --protocol tcp --port 5432 --source-group sg-app123
# Describe rules
aws ec2 describe-security-group-rules --filter Name=group-id,Values=sg-0abc123
# Revoke rule
aws ec2 revoke-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 0.0.0.0/0Network ACLs (NACLs)
NACLs are stateless subnet-level firewalls. Both inbound AND outbound rules must explicitly allow traffic. Rules are evaluated in order by rule number (lowest first).
Security Groups vs NACLs:
Security Group NACL
Level: Instance (ENI) Subnet
State: Stateful (return Stateless (must allow
traffic auto-allowed) return traffic too)
Allow/Deny: Allow only Allow AND Deny
Rule evaluation: All rules together In order (100, 200, ...)
Default: Deny all in, all out Allow all in, all out
NACL use cases:
- Block specific IP addresses (can't do this with SG)
- Add extra defense-in-depth at subnet boundary
- Emergency "shut off" — deny all ingress on subnet# Add inbound deny rule (rule 50 evaluated before allow rules)
aws ec2 create-network-acl-entry --network-acl-id acl-0abc123 --rule-number 50 --protocol tcp --rule-action deny --ingress --cidr-block 192.168.1.0/24 --port-range From=0,To=65535
# Ephemeral ports must be allowed for return traffic (1024-65535)
aws ec2 create-network-acl-entry --network-acl-id acl-0abc123 --rule-number 200 --protocol tcp --rule-action allow --egress --cidr-block 0.0.0.0/0 --port-range From=1024,To=65535VPC Flow Logs
Flow logs capture IP traffic information for network interfaces, subnets, or VPCs. Essential for security analysis and troubleshooting.
# Create flow log (to CloudWatch Logs)
aws ec2 create-flow-logs --resource-type VPC --resource-ids vpc-0abc123 --traffic-type ALL --log-destination-type cloud-watch-logs --log-destination arn:aws:logs:us-east-1:123456789:log-group:/vpc/flowlogs --deliver-logs-permission-arn arn:aws:iam::123456789:role/FlowLogsRole
# Or to S3 (cheaper for long retention)
aws ec2 create-flow-logs --resource-type VPC --resource-ids vpc-0abc123 --traffic-type REJECT --log-destination-type s3 --log-destination arn:aws:s3:::my-flow-logs-bucket/vpc/
# Flow log record format:
# version accountid interface-id srcaddr dstaddr srcport dstport protocol
# packets bytes windowstart windowend action flowdirection log-status
# Example: REJECT record
# 2 123456789012 eni-abc123 203.0.113.1 10.0.1.5 45678 22 6 1 40 ... REJECT INGRESS OK
# Query with CloudWatch Logs Insights
# fields @timestamp, srcAddr, dstAddr, dstPort, action
# | filter action = "REJECT"
# | stats count() by srcAddr
# | sort count descKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free