Ravindra BagaleCourses & study guides मराठी Track your progress

Chapter 15: Amazon VPC — Subnets, Route Tables, NACL, NAT Gateway and Bastion Host

15.12 Project: test every hop, troubleshoot and clean up

Let's see the result! Open the web server's public IP in the browser – if you see the students list, then the three tiers, three subnets, route tables, NAT and the security group chain are all correct. If not, don't panic, we will test hop by hop. So the very first job – the browser.

Request flow in the 3-tier project: Nginx, PHP-FPM and MySQL The browser sends a request through the internet gateway to Nginx in the public web subnet on port 80. Nginx forwards the PHP request with fastcgi_pass to PHP-FPM on the app server 10.0.2.10 port 9000 in a private subnet. PHP connects to MySQL (MariaDB) on 10.0.3.10 port 3306 in the private DB subnet, gets the students, and the finished HTML page travels back to the browser. Each security group allows traffic only from the tier before it. Browser society-vpc 10.0.0.0/16 IGW public 10.0.1.0/24 Nginxweb-sg :80 private 10.0.2.0/24 PHP-FPMapp-sg :9000 private 10.0.3.0/24 MySQLdb-sg :3306 from 0.0.0.0/0 only from web-sg only from app-sg Studentslist ✓ GET / HTML Nginx → fastcgi_pass :9000 → PHP-FPM → PDO :3306 → MySQL

The browser reaches Nginx in the public subnet on port 80. Nginx passes the PHP request with fastcgi_pass to PHP-FPM on port 9000, PHP reads the students from MySQL on port 3306, and the HTML page goes back. Each security group allows only the tier before it.

Open http://<WEB_PUBLIC_IP>/ in your browser. You should see the Students list table with three rows and the line "PHP ran on ip-10-0-2-10…". That single page proves the full path: internet → IGW → public subnet → Nginx → (port 9000) → PHP-FPM in the private app subnet → (port 3306) → MariaDB in the private DB subnet.

Verify each hop on its own

When something fails, test hop by hop from the server before it. Bash can test a TCP port with /dev/tcp, so no extra tools are needed:

Hop Run on Command Good result
You → web (HTTP) Your laptop curl -​I http://<WEB_​PUBLIC_​IP>/ HTTP/1.1 200 OK, Server: nginx
You → bastion (SSH) Your laptop ssh ec2-​user@<BASTION_​PUBLIC_​IP> Shell prompt
Web → app (9000) web timeout 3 bash -​c 'echo > /​dev/​tcp/​10.​0.​2.​10/​9000' && echo OPEN OPEN
App → db (3306) app timeout 3 bash -​c 'echo > /​dev/​tcp/​10.​0.​3.​10/​3306' && echo OPEN OPEN
App → MySQL login app mysql -​h 10.​0.​3.​10 -​u app -​p -​e "SELECT COUNT(*) FROM appdb.​students;" 3
Private → internet (NAT) app or db curl -​s https://​checkip.​amazonaws.​com The NAT gateway's Elastic IP
Nginx → PHP locally web curl -​s localhost \| grep -​c "<tr>" 4 (header + 3 students)

Prove the blocks work too

A secure design is not only "what works" but also "what is refused". These must fail (time out):

Test Run on Expected Why
timeout 3 bash -​c 'echo > /​dev/​tcp/​10.​0.​3.​10/​3306' web times out db-sg allows 3306 only from app-sg
ssh ec2-​user@10.​0.​2.​10 directly from your laptop laptop no route Private IP, and no public IP on app
curl http://<APP_​PRIVATE_​IP>:​9000 from the internet laptop impossible Private subnet has no route from the IGW
Add NACL deny rule 90 for 198.51.100.7/32 on web-public-1a console your browser stops loading NACL denies before the allow rule 100 (remove it afterwards!)

Troubleshooting table

Symptom Where to look Cause and fix
502 Bad Gateway + connect() failed (111: Connection refused) in /​var/​log/​nginx/​error.​log app PHP-FPM not running or still on the socket. Check grep ^listen /​etc/​php-​fpm.​d/​www.​conf, sudo service php-​fpm restart
502 / 504 + (110: Connection timed out) Security group app-sg has no rule for TCP 9000 from web-sg
502 + (104: Connection reset by peer) app listen.​allowed_​clients does not contain 10.0.1.10
File not found. and Primary script unknown in the log app index.php is not at /var/www/html/ on the app server, or SCRIPT_FILENAME path is wrong
Nginx welcome page instead of the students list web Missing placeholder /​usr/​share/​nginx/​html/​index.​php or php-app.conf not in /​etc/​nginx/​default.​d/. Run sudo nginx -t and reload
yum install hangs, Curl error (28): Timeout was reached Private route table No 0.​0.​0.​0/​0 → nat-… route, or NAT gateway not Available
SQLSTATE[HY000] [2002] Connection timed out db-sg No 3306 rule from app-sg
SQLSTATE[HY000] [2002] Connection refused db MariaDB still bound to 127.0.0.1: check bind-address and sudo service mariadb restart
SQLSTATE[HY000] [1045] Access denied for user 'app'@'10.​0.​2.​10' db Wrong password or user created for another host; recreate 'app'@'10.0.2.%'
ssh -J: Permission denied (publickey) on the private server Laptop Key not in the agent: run ssh-​add society-​key.​pem and retry

Clean up (in this order)

The NAT gateway and the Elastic IP are billed every hour, even when you are sleeping. Delete everything once you have finished:

  1. EC2 → Instances: terminate web, bastion, app and db.
  2. VPC → NAT gateways: select society-nat → Actions → Delete NAT gateway. Wait until the state is Deleted.
  3. EC2 → Elastic IPs: select the NAT's address → Actions → Release Elastic IP addresses. Deleting the NAT gateway does not release it.
  4. VPC → Your VPCs: select society-vpc → Actions → Delete VPC. This also deletes its subnets, route tables, internet gateway, network ACLs and security groups.
  5. Next day, open Billing and Cost Management and confirm that no NAT gateway or public IPv4 charges are still growing.

Ravindra Bagale's Tip

Clean up right after the project – that is the rule. My students' biggest bills come from the NAT gateway and a forgotten Elastic IP. Note: even after you delete the NAT, the Elastic IP has to be released. And while troubleshooting, test hop by hop – from web to app, from app to db. No guessing; get proof with /dev/tcp.

Try it at home

Build the whole project again from an empty account, this time without looking at the notes for the security group chain. Then add a second page add.php on the app server with a small form that inserts a student (the app user already has INSERT). Finally, block your own IP with NACL rule 90 for five minutes, watch the site stop, remove the rule, and clean up everything.