By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(For AWS Solutions Architect – Associate & Real-World Cloud Engineers)
You’re a cloud engineer at a fintech startup. Your team just migrated a monolithic app to AWS, and now latency spikes are causing payment processing failures. The DevOps lead blames "network security misconfigurations," but no one can pinpoint the issue. Meanwhile, your CISO demands zero public exposure of internal databases.
Here’s the problem: Security Groups (SGs) and Network ACLs (NACLs) are both firewalls, but they work differently—and mixing them up in production can break your app, expose data, or kill performance.
Why this matters in production:- If you misconfigure a Security Group, your EC2 instance won’t respond to SSH/RDP, or worse, it’ll accept traffic from the entire internet.- If you misconfigure a Network ACL, your entire subnet (e.g., all databases in us-east-1a) could lose connectivity to the internet or other VPCs.- Exam trap: AWS loves asking, "Which firewall is stateful?" (Answer: Security Groups. NACLs are stateless—you must explicitly allow both inbound and outbound rules.)
us-east-1a
Real-world scenario:You inherit a legacy VPC where: - A Security Group allows 0.0.0.0/0 on port 22 (SSH) for "debugging." - A Network ACL denies all inbound traffic to the subnet.Result? Your EC2 instances are publicly exposed but unreachable—a compliance nightmare.
0.0.0.0/0
Production insight:- Security Groups = "Who can talk to my instance?" - Network ACLs = "What traffic is allowed in/out of my subnet?" - Always use both for defense in depth (e.g., NACL blocks malicious IPs, SG restricts instance access).
ec2:*
vpc:*
aws --version
Deploy a public-facing web server (EC2 in a public subnet) and a private database (EC2 in a private subnet), secured with: - Security Groups (allow HTTP to web server, MySQL only from web server).- Network ACLs (block all SSH to private subnet, allow only ephemeral ports for responses).
# Create VPC VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query 'Vpc.VpcId' --output text) aws ec2 create-tags --resources $VPC_ID --tags Key=Name,Value=SecureWebAppVPC # Create public subnet (us-east-1a) PUB_SUBNET_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text) aws ec2 create-tags --resources $PUB_SUBNET_ID --tags Key=Name,Value=PublicSubnet # Create private subnet (us-east-1b) PRIV_SUBNET_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text) aws ec2 create-tags --resources $PRIV_SUBNET_ID --tags Key=Name,Value=PrivateSubnet # Create Internet Gateway + attach to VPC IGW_ID=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text) aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID # Create route table for public subnet PUB_RT_ID=$(aws ec2 create-route-table --vpc-id $VPC_ID --query 'RouteTable.RouteTableId' --output text) aws ec2 create-route --route-table-id $PUB_RT_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID aws ec2 associate-route-table --subnet-id $PUB_SUBNET_ID --route-table-id $PUB_RT_ID
WEB_SG_ID=$(aws ec2 create-security-group \ --group-name WebServerSG \ --description "Allow HTTP and SSH" \ --vpc-id $VPC_ID \ --query 'GroupId' --output text) # Allow HTTP (port 80) from anywhere aws ec2 authorize-security-group-ingress \ --group-id $WEB_SG_ID \ --protocol tcp \ --port 80 \ --cidr 0.0.0.0/0 # Allow SSH (port 22) from your IP only (replace X.X.X.X with your public IP) aws ec2 authorize-security-group-ingress \ --group-id $WEB_SG_ID \ --protocol tcp \ --port 22 \ --cidr X.X.X.X/32
DB_SG_ID=$(aws ec2 create-security-group \ --group-name DatabaseSG \ --description "Allow MySQL from Web Server" \ --vpc-id $VPC_ID \ --query 'GroupId' --output text) # Allow MySQL (port 3306) only from Web Server SG aws ec2 authorize-security-group-ingress \ --group-id $DB_SG_ID \ --protocol tcp \ --port 3306 \ --source-group $WEB_SG_ID
# Get default NACL for public subnet (or create a new one) PUB_NACL_ID=$(aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=$PUB_SUBNET_ID" \ --query 'NetworkAcls[0].NetworkAclId' --output text) # Allow HTTP (inbound) aws ec2 create-network-acl-entry \ --network-acl-id $PUB_NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=80,To=80 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --ingress # Allow SSH (inbound, from your IP) aws ec2 create-network-acl-entry \ --network-acl-id $PUB_NACL_ID \ --rule-number 200 \ --protocol tcp \ --port-range From=22,To=22 \ --cidr-block X.X.X.X/32 \ --rule-action allow \ --ingress # Allow ephemeral ports (outbound, for responses) aws ec2 create-network-acl-entry \ --network-acl-id $PUB_NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=1024,To=65535 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --egress
PRIV_NACL_ID=$(aws ec2 describe-network-acls \ --filters "Name=association.subnet-id,Values=$PRIV_SUBNET_ID" \ --query 'NetworkAcls[0].NetworkAclId' --output text) # Deny SSH (inbound) aws ec2 create-network-acl-entry \ --network-acl-id $PRIV_NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=22,To=22 \ --cidr-block 0.0.0.0/0 \ --rule-action deny \ --ingress # Allow MySQL (inbound, from public subnet) aws ec2 create-network-acl-entry \ --network-acl-id $PRIV_NACL_ID \ --rule-number 200 \ --protocol tcp \ --port-range From=3306,To=3306 \ --cidr-block 10.0.1.0/24 \ --rule-action allow \ --ingress # Allow ephemeral ports (outbound) aws ec2 create-network-acl-entry \ --network-acl-id $PRIV_NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=1024,To=65535 \ --cidr-block 0.0.0.0/0 \ --rule-action allow \ --egress
WEB_INSTANCE_ID=$(aws ec2 run-instances \ --image-id ami-0c55b159cbfafe1f0 \ # Amazon Linux 2 (us-east-1) --instance-type t2.micro \ --key-name YourKeyPair \ # Replace with your key pair --security-group-ids $WEB_SG_ID \ --subnet-id $PUB_SUBNET_ID \ --associate-public-ip-address \ --query 'Instances[0].InstanceId' --output text) aws ec2 create-tags --resources $WEB_INSTANCE_ID --tags Key=Name,Value=WebServer
DB_INSTANCE_ID=$(aws ec2 run-instances \ --image-id ami-0c55b159cbfafe1f0 \ --instance-type t2.micro \ --key-name YourKeyPair \ --security-group-ids $DB_SG_ID \ --subnet-id $PRIV_SUBNET_ID \ --no-associate-public-ip-address \ --query 'Instances[0].InstanceId' --output text) aws ec2 create-tags --resources $DB_INSTANCE_ID --tags Key=Name,Value=DatabaseServer
YourKeyPair.pem
bash ssh -i "YourKeyPair.pem" ec2-user@$(aws ec2 describe-instances --instance-ids $WEB_INSTANCE_ID --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
http://<WEB_SERVER_PUBLIC_IP>
bash mysql -h $(aws ec2 describe-instances --instance-ids $DB_INSTANCE_ID --query 'Reservations[0].Instances[0].PrivateIpAddress' --output text) -u root -p
Name
Environment
Owner
SecurityGroupRulesEvaluated
NetworkAclRulesEvaluated
❌ Network ACL (must explicitly allow inbound + outbound).
"You need to block all SSH traffic to a subnet. Which do you use?"
❌ Security Group (instance-level, can’t block entire subnet).
"A web server can’t receive HTTP responses. What’s the issue?"
❌ Security Group (stateful, doesn’t need outbound rules for responses).
"Which is evaluated first: SG or NACL?"
Scenario:You’re troubleshooting a database connection issue. The web server (in a public subnet) can’t connect to the database (in a private subnet). The Security Group allows MySQL (port 3306) from the web server, but the connection times out.
Your task:1. Identify the issue (hint: check NACLs).2. Fix it using the AWS CLI.
Solution:
# Check NACL rules for the private subnet aws ec2 describe-network-acls --filters "Name=association.subnet-id,Values=$PRIV_SUBNET_ID" # Issue: Outbound ephemeral ports (1024-65535) are not allowed.# Fix: Add outbound rule for ephemeral ports.aws ec2 create-network-acl-entry \ --network-acl-id $PRIV_NACL_ID \ --rule-number 100 \ --protocol tcp \ --port-range From=1024,To=65535 \ --cidr-block 10.0.1.0/24 \ # Public subnet CIDR --rule-action allow \ --egress
Why it works:NACLs are stateless—you must allow both inbound (MySQL) and outbound (ephemeral ports) for responses.
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.