Defense in depth is a layered approach to vulnerability management that reduces risk.
Introduction
AWS offers a variety of security services that can be utilized in different ways and for different types of defense. When it comes to security, we have multiple approaches to effectively defend our infrastructure. We understand that security is the most crucial aspect of our infrastructure. In this demo, I will implement the Defense in Depth approach by utilizing different AWS services. I will set up an infrastructure with a basic application running on an EC2 instance. To implement the Defense in Depth approach, I will make a custom VPC to use custom subnets and ACL, and I will also use EC2, Application Load Balancer, Web Application Firewall, Route 53, and Amazon Certificate Manager.
Cf. Liebmann (1996): “The concept of defense in depth is not only a guide for the review of a particular technical solution as, for example, a set of singular barriers, but a method of reasoning and a general frame- work to examine more fully the entire facility, both for its design and for its analysis”
Prerequisites
- Create AWS Account. I already have an AWS Account and I won’t be creating a new one.
- Install Terraform to your Local Machine.
- This is a WARNING ALERT. This demo can make small charges on your account. You need to have some money in your account.
Create custom VPC
According to AWS Docs, VPC is a logically isolated virtual network that you’ve defined. This virtual network closely resembles a traditional network that you’d operate in your own data center, with the benefits of using the scalable infrastructure of AWS.
If you subscribed to my Medium profile, you may have seen my story “How to build an AWS VPC using the Console or Terraform”. I will be creating another one using Terraform. If you’re not familiar with Terraform, I also covered how to create a VPC in the AWS Console. I should mention that in the previous story, I used “eu-central-1” and now I will be changing it to “us-east-1”.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "ahmed-srebrenica-vpc"
}
}
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "us-east-1a"
tags = {
Name = "ahmed-srebrenica-public-subnet-a"
}
}
resource "aws_subnet" "public_b" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.3.0/24"
map_public_ip_on_launch = true
availability_zone = "us-east-1b"
tags = {
Name = "ahmed-srebrenica-public-subnet-b"
}
}
resource "aws_subnet" "private_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1a"
tags = {
Name = "ahmed-srebrenica-private-subnet-a"
}
}
resource "aws_subnet" "private_b" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.4.0/24"
availability_zone = "us-east-1b"
tags = {
Name = "ahmed-srebrenica-private-subnet-b"
}
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "ahmed-srebrenica-igw"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
tags = {
Name = "ahmed-srebrenica-public-rt"
}
}
resource "aws_route_table_association" "public_association_a" {
subnet_id = aws_subnet.public_a.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_association_b" {
subnet_id = aws_subnet.public_b.id
route_table_id = aws_route_table.public.id
}
resource "aws_route" "public_route" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
resource "aws_nat_gateway" "nat" {
allocation_id = ""
subnet_id = aws_subnet.public_a.id
connectivity_type = "private"
tags = {
Name = "ahmed-srebrenica-nat-gateway"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "ahmed-srebrenica-private-rt"
}
}
resource "aws_route_table_association" "private_association_a" {
subnet_id = aws_subnet.private_a.id
route_table_id = aws_route_table.private.id
}
resource "aws_route_table_association" "private_association_b" {
subnet_id = aws_subnet.private_b.id
route_table_id = aws_route_table.private.id
}
resource "aws_route" "private_route" {
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
In the picture below, you can see that I have successfully created my VPC, and I named it “ahmed-srebrenica-vpc”. I have 4 subnets (2 public and 2 private subnets), Public and Private route tables, an Internet Gateway, and a NAT Gateway. For this demo, we don’t need Private subnets, but if you want to add a database you must have private subnets.
Let's summarize: Every time you start a new project, create a custom VPC. NEVER use the Default VPC. We have created a custom VPC so far with two public and private subnets. Our custom VPC has Network Access Control list, we will change some rules for it later.
Create two EC2 Instances and a Security Group for it
EC2 Instance is like a Virtual Machine but in the Cloud. Security Group is stateful and acts as a virtual firewall for your EC2 instances to control incoming and outgoing traffic.
Our next move will be to create a Security group. To do that, go to the EC2 Dashboard, and from the left-hand side choose “Security Groups” and then click the “Create Security Group “ button.
You have to name your security group and don’t forget to select the custom VPC that you created in the first step. Open ports 80, and 443 from Inbound rules.
Click the “Create Security Group” button.
Our next step is to create an EC2 instance and launch Nginx. Go to the EC2 dashboard and from the left-hand side choose “Instances”, then click the “Launch Instances” button.
Our first step is to give the name to the instance and select Amazon Linux 2023 AMI.
The instance type should be t2.micro, and the Key pair is optional.
This is the most important step. Under the “Network Settings”, select your custom VPC, and public subnet, enable Auto-assign public IP, and select the Security Group you previously created.
Go with 8 GiB of storage, and click “Advanced details”
Go to the bottom of the page and in “User data” paste this code:
#!/bin/bash
sudo yum install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
echo "Hello Friend from $(hostname -f)" > /usr/share/nginx/html/index.html
Click the “Launch Instance” button.
Launch another instance with the same configuration, and change only the subnet. This time select subnet-b.
Wait 2–3 minutes and you will see that the EC2 instances are successfully created.
Let’s summarize: We have created two EC2 instances. To achieve high availability, we launched one EC2 instance in public-subnet-a using us-east-1a, and another EC2 instance in public-subnet-b using us-east-1b. We have created Security Groups for the instances, allowing ports 80 and 443. The Security Group will act as a firewall for our EC2 instances.
Create an Application Load Balancer
According to AWS Docs, Elastic Load Balancing automatically distributes your incoming traffic across multiple targets, such as EC2 instances, containers, and IP addresses, in one or more Availability Zones. It monitors the health of its registered targets, and routes traffic only to the healthy targets. Elastic Load Balancing scales your load balancer as your incoming traffic changes over time. It can automatically scale to the vast majority of workloads. Application load balancer serves as the single point of contact for clients. The Application load balancer distributes incoming application traffic across multiple targets, such as EC2 instances, in multiple Availability Zones. This increases the availability of your application.
Our next step is to create a Target Group for Application Load Balancer. Go to EC2 Dashboard and from the left-hand side, choose Target Groups, and then the “Create Target Group” button.
Under Basic Configuration select Instances and name your Target Group.
The protocol should be HTTP and don’t forget to select your custom VPC.
Leave everything as default and click the “Next” button.
Now we should register targets. Select both instances and click “Include as pending below” button. After that, click the “Create Target Group” button.
Great, we successfully created the target group.
We have to create an Application Load Balancer now. From the left-hand side, choose Load Balancers and then the “Create Load Balancer” button.
Name your ALB, the Scheme should be Internet-facing, the Address type should be IPv4.
Select your custom VPC and both Public subnets.
Great, our next step is to create another Security Group, but this time, the group should be associated with the Application Load Balancer. Click “create a new security group”.
Create a Security Group with 443 opened port.
Go back to Load Balancer and select the new Security Group.
Under the Listeners and Routing, select these Listeners and Configuration.
We must enable WAF protection.
Leave everything as default and click the “Create load balancer” button.
Wait a few minutes for the message “Successfully created load balancer”. Copy the DNS name and paste it into your browser to see if everything works correctly.
Application Load Balancer is successfully created and working correctly.
Btw. this is the WAF traffic overview after a few hours.
Let’s summarize: In these steps, we successfully created an Application Load Balancer in front of two EC2 instances. The Application Load Balancer acts as a server and distributes traffic to the EC2 instances. There are numerous security benefits associated with the Application Load Balancer. It provides SSL/TLS and protects us from DDoS attacks. We also add another layer of security with security group attached to Application Load Balancer.
We attached Web Application Firewall, it helps to protect your web applications from common application-layer exploits that can affect availability or consume excessive resources. We will use WAF default setup.
Create Route 53 records
What is Route 53? According to AWS Docs, Route 53 is a highly available and scalable Domain Name System (DNS) web service. You can use Route 53 to perform three main functions in any combination: domain registration, DNS routing, and health checking.
Our next step would be to create new records in Hosted Zones. Go to Route 53 in your Console and from the left-hand side choose “Hosted Zones”. I already have Domain ahmedsrebrenica.com.
Go to your Domain name and click the “Create record” button.
Name your record, select A record type and TTL should be 60 seconds. Enable Alias and route traffic to Application Load Balancer. Select the us-east-1 region and select the DNS of the Load Balancer. Click the “Create records” button.
We successfully created a new record. We have to add a new record, this time with www.
Let’s summarize: We have successfully created domain names for our demo. However, these domain names do not have SSL certificates.
Create SSL/TLS certificates using AWS Certificate Manager
“AWS Certificate Manager (ACM) handles the complexity of creating, storing, and renewing public and private SSL/TLS X.509 certificates and keys that protect your AWS websites and applications.”
In your Console, search for Certificate Manager service. You should see this. Click the “Request a certificate” button.
The names must be the same as you created in the Route 53 service. Leave everything else to default and click the “Request” button.
We successfully created certificates. Our next step would be to create another Route 53 record, but this time with CNAME Names and CNAME Values from our certificates.
Go to Route 53 and again click the “Create record” button. Paste the CNAME names from Certificate Manager to Record name, select CNAME record type, and paste CNAME Values from Certificate Manager to Value box. Click the “Create Record” button.
That’s it, we are done with Route 53.
Let’s summarize: To secure our domain names, we added certificates using the ACM service.
Add a new listener (HTTPS:443) and edit existing (HTTP:80)
Our final step is to add 443 listener to our existing Application Load Balancer. Go to the Load Balancer section and select the existing Load Balancer. Click the “Add listener” button.
Just follow the configuration from the screenshot. The protocol must be HTTPS, Port 443, and Forward to the target group you created.
Select Certificate from ACM and click the “Add” button.
Check your domain in the browser and you will see the website.
The last step is to edit the existing HTTP:80 Listener. Select HTTP:80 and click Edit Listener.
Switch from Forward to target groups to Redirect to URL and the port must be 443. Click the “Save changes” button.
Okay, we set the routing action redirect to HTTPS on port 443.
Let’s summarize: We have added an HTTPS:443 listener to monitor HTTPS traffic on port 443. Additionally, we have updated the HTTPS listener to redirect from HTTP port 80 to HTTPS port 443. We have also added a certificate from ACM to the HTTPS:443 listener, allowing us to properly use our domain name with SSL.
Change Network ACLs
A network access control list (ACL) allows or denies specific inbound or outbound traffic at the subnet level. To provide better security, we have to change the Access Control List Rules. Go to your custom VPC and from the left-hand side choose Network ACLs. Click the “Edit inbound rules” button.
Change Type from All Trafic to HTTPS (443), because we want to allow only HTTPS traffic, nothing else. Click the “Save changes” button.
ACLs are stateless and compared to security groups, we have to edit outbound rules and open ephemeral ports (port range 1024–65535).
Let’s see if everything works correctly. Check your website.
Let’s summarize: Network Access Control List (NACL) is an additional layer of security that protects our subnets. We have only allowed 443 traffic, and because NACL is stateless, we have opened ephemeral ports in our Outbound section.
Optionally you can set up CloudFront
You can enhance security by using CloudFront alongside the Application Load Balancer. CloudFront automatically defends against DDoS attacks at both the network and application levels. This is achieved through a variety of methods, including traffic filtering, traffic throttling, and traffic redirection. Additionally, CloudFront leverages Amazon’s global network infrastructure to provide an extra layer of protection at the network edge. It also delivers content through a global network of data centers known as edge locations. When a user requests content served with CloudFront, the request is directed to the edge location with the lowest latency, ensuring optimal performance. However, it’s important to note that using CloudFront is optional for this demo.
Conclusion
In this demonstration, we utilized various AWS services and implemented multiple layers of security to follow the Defense in Depth Approach. This approach is commonly used to protect Cloud infrastructure. If attackers attempt to compromise our infrastructure, they will encounter numerous security measures. We have employed security groups for our EC2 instances and an additional security group for the Application Load Balancer. Our Application Load Balancer is equipped with DDoS protection and a Web Application Firewall to safeguard against web-based attacks. Furthermore, we have secured our domain with an SSL certificate and have restricted traffic to only HTTPS in our NACL.
If you like this story, clap and follow me.
Check out my website, you will gain basic information about me: ahmedsrebrenica.com.