Build a secure web application on AWS “the hard way”
The purpose of this post is to show you how to create a very secure web application on AWS “the hard way”. Why the hard way? This is because you can just use AWS default settings but you won’t understand and/or utilizing all of AWS core components.
VPC
- Name: manual-vpc
- Region: US-East-1
- CIDR: 192.168.0.0/16
Private Subnets
- Name: private-a-manual | private-b-manual
- CIDR: 192.168.1.0/24 | 192.168.1.0/24
- Availability zone: us-east-1a | us-east-2b
Public Subnets
- Name: public-a-manual | public-b-manual
- CIDR: 192.168.10.0/24 | 192.168.20.0/24
- Availability zone: us-east-1a | us-east-2b
Internet Gateway
- Name: ig-manual
- Attach to manual-vpc
Route Table
- Name: public-rt-manual
- Destination: 0.0.0.0/0
- Target: ig-manual
- Subnet associations: public-a-manual, pubic-b-manual
Bastion Host — ec2 instance
- Linux Distro: Amazon Linux
- Instance Type: t2.micro
- Network: manual-vpc
- Subnet: public-a-manual
- Name: Bastion Host
- Auto-assign Public IP: use subnet setting(disable)
- Volume type’s root: 8GB
- Tags: Bastion Host
- Security Group: Create a new security group
- Security Group Name: bastion-host
- Security Group Type: SSH
- Security Group Port: 22
- Source: Your home/office ip-address
Elastic IP
- Instance: Bastion Host
At this point, you should be able to ssh into bastion host instance with ssh -K -i ~/.ssh/YOURKEY.pem ec2-user@ELASTIC-IP-ADDRESS (-K is for forwarding ssh-agent). Once you are in run sudo yum -y update to confirm that your instance can download and install update from the internet which means we have configured our public subnet correctly.
Next, let’s spin up two more ec2-instance that live in a private subnet, then we will try to ssh into those instances through our bastion host.
App1/App2 — ec2 instances
- Linux Distro: Amazon Linux
- Instance Type: t2.micro
- Network: manual-vpc
- Subnet: private-a-manual/private-b-manual
- Name: app1/app2
- Auto-assign Public IP: use subnet setting(disable)
- Volume type’s root: 8GB
- Tags: apps
- Security Group: Create a new security group
- Security Group Name: private-web
- Security Group Type: SSH,HTTP
- Security Group Port: 22,80
- Source: 0.0.0.0/0
From bastion host, you should be able to run ssh ec2-user@APP1_PRIVATE_IP to login to app1 instance! Next, try to run sudo yum -y update , this won’t work because our instance has no way to route traffic to the internet since it sits in a private subnet that doesn’t have internet gateway attach to it. To solve this problem, we have to use Nat Gateway(Nat Instance).
Nat Gateway
- Subnet: public-a-manual
- Elastic IP Allocation ID: create a new one
Route Table
- Name: private-rt-manual
- Destination: 0.0.0.0/0
- Target: nat-manual
- Subnet associations: private-a-manual, private-b-manual
Now back to app1/app2 instance, try sudo yum -y update again and it should work!
Install Nginx(web server) on app1/app2
- Run
sudo yum -y install nginx && sudo service nginx start - Run
curl -IL localhost, it should return http 200.
Next problem for us to solve is how to serve these two instances to the internet. If you noticed, there is no Public IP attached to the instance itself. To solve this, we can use a load balancer.
Load Balancer
- Type: Classic Load Balance
- Load Balance Name: lb-manual
- Create LB Inside: manual-vpc
- Load Balancer Protocol: HTTP
- Load Balancer Port: 80
- Instance Protocol: HTTP
- Instance Port: 80
- Subnets: public-a-manual, public-b-manual
- Security group protocol: http/https
- Security group port: 80/443
- Security group source: 0.0.0.0/0
- Health Check: leave it as default settings
- Add EC2 Instances: app1, app2
AWS creates a DNS for us to use such as http://lb-manual-783608569.us-east-1.elb.amazonaws.com.
Conclusion
We’ve created a very secure web application that utilizes all of the core components of AWS such as VPC, Subnets, Network ACLs, Route Tables, Security Groups, Load Balancer, and EC2 instances!
I hope you like it. Please share, comment, like. I’d love to hear feedback from you. Lastly, don’t forget to be awesome!
