In this lab, two instances are created, one is launched in a first subnet which is public, while the other is launched the second subnet which is private.
The private instance must access the internet using a Network Address Translation (NAT) gateway deployed into the first subnet.
No default security group, NACL, or route table is used in this demonstration. This means any non-default being used must be explicitly associated with its resource. For example, a non-default route table must be explicitly associated with a subnet, etc.
Also, in this lab, we will explore the stateful nature of a Security Group, at Algorithm no 12.
Algorithm of the Infrastructure
- Create a VPC
- Create an Internet Gateway
- Create a first subnet
- Create a first NACL for the first subnet
- Specify the first route table
- Add a route destined to all IP addresses, and a target to the Internet Gateway.
- Create a security group in the VPC for the first subnet
- Add an ingress rule for SSH
- Add an egress rule for SSH
- Create an instance for the first subnet
- Create an elastic IP
- Create a NAT Gateway in the first subnet and attach the elastic IP to it
- Create a second subnet
- Create a second NACL for the second subnet
- Create a second security group
- Add an ingress rule for SSH
- Add an egress rule for all ICMP
- Create a second route table
- Add a route destined for the NAT Gateway
- Create a second instance
Create a VPC
CIDR = 10.0.0.0/16
resource "aws_vpc" "VPC_A" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
tags = {
Name = "VPC_A"
}
}
Create an Internet Gateway
resource "aws_internet_gateway" "VPC_A_IGW" {
vpc_id = aws_vpc.VPC_A.id
tags = {
Name = "VPC_A_IGW"
}
}
Create a first subnet
resource "aws_subnet" "VPC_A_Public_Subnet_A" {
vpc_id = aws_vpc.VPC_A.id
cidr_block = "10.0.20.0/24"
availability_zone = "us-west-2a"
map_public_ip_on_launch = "true"
tags = {
Name = "VPC_A_Public_Subnet_A"
}
}
Create a first NACL for the first subnet
resource "aws_network_acl" "VPC_A_Public_NACL_A" {
vpc_id = aws_vpc.VPC_A.id
subnet_ids = [aws_subnet.VPC_A_Public_Subnet_A.id]
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = {
"name" = "VPC_A_Public_NACL_A"
}
}
resource "aws_network_acl_association" "VPC_A_Public_NACL_A_Association" {
network_acl_id = aws_network_acl.VPC_A_Public_NACL_A.id
subnet_id = aws_subnet.VPC_A_Public_Subnet_A.id
}
Specify the first route table
resource "aws_route_table" "VPC_A_Public_RT_A" {
vpc_id = aws_vpc.VPC_A.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.VPC_A_IGW.id
}
tags = {
Name = "VPC_A_Public_RT_A"
}
}
resource "aws_route_table_association" "VPC_A_Public_RT_A_Association_A" {
subnet_id = aws_subnet.VPC_A_Public_Subnet_A.id
route_table_id = aws_route_table.VPC_A_Public_RT_A.id
}
Create a security group in the VPC for the first subnet
resource "aws_security_group" "SG_bastion" {
vpc_id = aws_vpc.VPC_A.id
description = " SG for bastion host. SSH access only"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Create an instance for the first subnet
resource "aws_instance" "Bastion" {
ami = "ami-0b029b1931b347543"
instance_type = "t2.micro"
availability_zone = "us-west-2a"
key_name = ""
tenancy = "default"
subnet_id = aws_subnet.VPC_A_Public_Subnet_A.id
security_groups = ["${aws_security_group.SG_bastion.id}"]
}
Create an elastic IP
resource "aws_eip" "EIP_for_NAT_GW" {
vpc = true
}
Create a NAT Gateway in the second subnet and attach the elastic IP to it
resource "aws_nat_gateway" "NAT_GW" {
subnet_id = aws_subnet.VPC_A_Public_Subnet_A.id
connectivity_type = "public"
allocation_id = aws_eip.EIP_for_NAT_GW.id
tags = {
"Name" = "NAT_GW"
}
}
Create a second subnet
resource "aws_subnet" "VPC_A_Private_Subnet_A" {
vpc_id = aws_vpc.VPC_A.id
cidr_block = "10.0.10.0/24"
availability_zone = "us-west-2a"
map_public_ip_on_launch = "false"
tags = {
Name = "VPC_A_Private_Subnet_A"
}
}
Create a second NACL for the second subnet
resource "aws_network_acl" "VPC_A_Private_NACL_A" {
vpc_id = aws_vpc.VPC_A.id
subnet_ids = [aws_subnet.VPC_A_Private_Subnet_A.id]
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = {
"name" = "VPC_A_Private_NACL_A"
}
}
resource "aws_network_acl_association" "VPC_A_Private_NACL_A_Association" {
network_acl_id = aws_network_acl.VPC_A_Private_NACL_A.id
subnet_id = aws_subnet.VPC_A_Private_Subnet_A.id
}
Create a second security group
Here we will explore the stateful nature of a security group
resource "aws_security_group" "SG_Private" {
name = "SG_Private"
description = "Security group for private subnet instances."
vpc_id = aws_vpc.VPC_A.id
ingress {
description = "Incoming & Outgoing SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Outgoing & Incoming ICMP"
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
"name" = "SG_Private"
}
}
We can notice that the ingress
rule is for an SSH protocol, while the egress
rule is for the ICMP protocol.
On AWS, when a packet ingresses or is directed towards an instance, it can leave the instance even if the egress rule doesn't match. That is why I described it as “incoming & outgoing SSH”
The Bastion instance can SSH into the private instance. Since the Bastion security group has an egress SSH rule set. Also, since the Private security grphp has an ingress SSH rule set.
Also, the private instance can ping any address on the Internet.
ICMP uses an "echo request" and an "echo reply.”
Since the first outgoing “request” traffic was approved, its incoming “reply” traffic will be approved too. That is why I described it as an “outgoing & incoming ICMP”
Create a second route table
resource "aws_route_table" "VPC_A_Private_RT_A" {
vpc_id = aws_vpc.VPC_A.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.NAT_GW.id
}
tags = {
Name = "VPC_A_Private_RT_A"
}
}
resource "aws_route_table_association" "VPC_A_Private_RT_A_Association" {
subnet_id = aws_subnet.VPC_A_Private_Subnet_A.id
route_table_id = aws_route_table.VPC_A_Private_RT_A.id
}
Create a second instance
resource "aws_instance" "VPC_A_Private_Subnet_A_Instance_A" {
ami = "ami-0b029b1931b347543"
instance_type = "t2.micro"
availability_zone = "us-west-2a"
key_name = ""
tenancy = "default"
subnet_id = aws_subnet.VPC_A_Private_Subnet_A.id
security_groups = ["${aws_security_group.SG_Private.id}"]
tags = {
"name" = "VPC_A_Private_Subnet_A_Instance_A"
}
}
After deploying the configurations, configure SSH Agent forwarding on Putty, then SSH into your first instance, and then into the second instance. Lastly do a ping to any public URL from the second instance and make sure its successful.
Follow my AWS Advanced Networking Journey on Github
AWS Advanced Networking
https://github.com/Its-All-About-the-Journey/AWS-Advanced-Networking
Top comments (0)