Create NAT Gateway To Provide Internet Connectivity In Private Subnet

Sumit Rasal
10 min readJul 19, 2020

Create NAT gateway to provide internet connectivity to private subnet from bastion host for management purpose.

Following are the high level information to know what concepts we are using in this project :-

AWS VPC : -

It enables you to launch AWS resources into a 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.

Subnets : -

A subnet is a range of IP addresses in your VPC. You can launch AWS resources into a specified subnet. Use a public subnet for resources that must be connected to the internet, and a private subnet for resources that won’t be connected to the internet .

To protect the AWS resources in each subnet, you can use multiple layers of security, including security groups and network access control lists (ACL).

You can optionally associate an IPv6 CIDR block with your VPC, and assign IPv6 addresses to the instances in your VPC.

Gateway in networking :-

A gateway is a hardware device that acts as a “gate” between two networks. It may be a router, firewall, server, or other device that enables traffic to flow in and out of the network. While a gateway protects the nodes within network, it also a node itself.

Routing Table :-

A routing table contains the information necessary to forward a packet along the best path toward its destination. Each packet contains information about its origin and destination. Routing Table provides the device with instructions for sending the packet to the next hop on its route across the network.

NAT Gateway :-

NAT Gateway is a highly available AWS managed service that makes it easy to connect to the Internet from instances within a private subnet in an Amazon Virtual Private Cloud (Amazon VPC). Previously, you needed to launch a NAT instance to enable NAT for instances in a private subnet.

Bastion Host :-

Bastion host is a server whose purpose is to provide access to a private network from an external network, such as the Internet. Because of its exposure to potential attacks, a bastion host must minimize the chances of penetration.

Task Objective :-

Statement: We have to create a web portal for our company with all the security as much as possible.

Perform task with an additional feature to be added that is NAT Gateway to provide the internet access to instances running in the private subnet.

NOTE :-

you can see my previous task to get detailed information.

Customized VPC and Subnets For Secure Deployment Using Terraform

Setup WordPress with MySQL using private and public subnets by terraform

I am also doing all code again in this task you can refer this also.

Performing steps:

1. Write an Infrastructure as code using terraform, which automatically create a VPC.

2. In that VPC we have to create 2 subnets:

a) public subnet [ Accessible for Public World! ]

b) private subnet [ Restricted for Public World! ]

3. Create a public facing internet gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC.

4. Create a routing table for Internet gateway so that instance can connect to outside world, update and associate it with public subnet.

5. Create a NAT gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC in the public network

6. Update the routing table of the private subnet, so that to access the internet it uses the nat gateway created in the public subnet

7. Launch an ec2 instance which has Wordpress setup already having the security group allowing port 80 so that our client can connect to our wordpress site. Also attach the key to instance for further login into it.

8. Launch an ec2 instance which has MYSQL setup already with security group allowing port 3306 in private subnet so that our wordpress vm can connect with the same. Also attach the key with the same.

Note: WordPress instance has to be part of public subnet so that our client can connect our site.

mysql instance has to be part of private subnet so that outside world can’t connect to it.

Don’t forgot to add auto ip assign and auto dns name assignment option to be enabled.

we need provider to know where we going to launch our infrastructure.

# cloud providerprovider "aws" {
region = "ap-south-1"
profile ="sumit"
}

Step 1: Write a Infrastructure as code using terraform, which automatically create a VPC.

# vpcresource "aws_vpc" "myvpc" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames = true
tags = {
Name = "Task3-VPC"
}
}
output "vpc_id" {
value = aws_vpc.myvpc.id
}

Step 2: In that VPC we have to create 2 subnets:

a) public subnet [ Accessible for Public World! ]

b) private subnet [ Restricted for Public World! ]

# aws Public subnets In the VPCresource "aws_subnet" "public" {depends_on = [ aws_vpc.myvpc ]vpc_id     = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-south-1a"
tags = {
Name = "subnet-1"
}
}
output "aws_subnet_public" {
value = aws_subnet.public.id
# aws private subnets IN the vpcresource "aws_subnet" "private" {depends_on = [ aws_vpc.myvpc ]vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.2.0/24"
map_public_ip_on_launch = true
availability_zone = "ap-south-1b"
tags = {
Name = "subnet-2"
}
}
output "aws_subnet_private" {
value = aws_subnet.private.id
}

Step 3: Create a public facing internet gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC.

# internet gateway to connect public worldresource "aws_internet_gateway" "gw" {depends_on = [ aws_vpc.myvpc ]vpc_id = aws_vpc.myvpc.idtags = {
Name = "my-internet-gateway"
}
}
output "internet_gateway_id" {
value = aws_internet_gateway.gw.id
}

Step 4: Create a routing table for Internet gateway so that instance can connect to outside world, update and associate it with public subnet.

# route tableresource "aws_route_table" "route-table" {depends_on = [ aws_internet_gateway.gw ]vpc_id = aws_vpc.myvpc.idroute {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id

}
tags = {
Name = "route-table"
}
}
output "route_table_id" {
value = aws_route_table.route-table.id
}
# route table associationresource "aws_route_table_association" "a" {depends_on = [ aws_subnet.public ]subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.route-table.id
}
output "route_table_association_id" {
value = aws_route_table_association.a.id
}

Step 5: Create a NAT gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC in the public network

Here we have to create Elastic ip as NAT gateway uses static ips to work

resource "aws_eip" "eip"{depends_on = [aws_vpc.myvpc,aws_internet_gateway.gw]vpc = true
}
# NAT gatewayresource "aws_nat_gateway" "natgw"{depends_on = [aws_vpc.myvpc,aws_eip.eip,aws_subnet.public]allocation_id = aws_eip.eip.id
subnet_id= aws_subnet.public.id
tags = {
Name = "NAT gateway"
}
}

Step 6: Update the new routing table of the private subnet, so that to access the internet it uses the nat gateway created in the public subnet

# route table for internet gatewayresource "aws_route_table" "route-table" {depends_on = [ aws_internet_gateway.gw ]vpc_id = aws_vpc.myvpc.idroute {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id

}
tags = {
Name = "route-table"
}
}
output "route_table_id" {
value = aws_route_table.route-table.id
}
# route table for NAT gatewayresource "aws_route_table" "route-table-2" {depends_on = [ aws_internet_gateway.gw,aws_nat_gateway.natgw ]vpc_id = aws_vpc.myvpc.idroute {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.natgw.id

}
tags = {
Name = "route-table-2"
}
}
output "route_table_id-2" {
value = aws_route_table.route-table-2.id
}

Step 7: Create 2 security groups in which both have ssh and one have MYSQL port (3306) and another have HTTP port (80) enabled

# WordPress security group for our instanceresource "aws_security_group" "wordpress-sg" {depends_on = [ aws_vpc.myvpc ]name        = "wordpress-sg"
description = "security group for myvpc(wordpress)"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [aws_vpc.myvpc.cidr_block]
}
ingress {
description = "TLS from VPC"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [ "0.0.0.0/0" ]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "wordpress-sg"
}
}
output "aws_security_group_wordpress-sg" {
value = aws_security_group.wordpress-sg.id
}
# MYSQL security group for our instanceresource "aws_security_group" "mysql-sg" {

depends_on = [ aws_vpc.myvpc ]
name = "mysql-sg"
description = "security group for myvpc(mysql)"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "MYSQL from VPC"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = [ "0.0.0.0/0" ]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "wordpress-sg"
}
}
output "aws_security_group_mysql-sg" {
value = aws_security_group.mysql-sg.id
}

Step 8: Create 2 security groups for bastion host and mysql to enhance settings.

  1. First group is for bastion host that allows SSH to connect with him.
  2. second group is for MYSQL to allow bastion host to connect MYSQL through the bastion host and perform various task.
#  Security group for bashion hostresource "aws_security_group" "bashion_host" {
depends_on = [aws_vpc.myvpc]
name = "bashion-host"
description = "allow ssh login to bashion host"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "allow ssh"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# creating security rules for ssh to my sql via basion host serverresource "aws_security_group" "sg" {depends_on = [aws_vpc.myvpc,aws_security_group.bashion_host]name = "allowing_bashion_host_to-ssh"
description = "bashion_host_allow_ssh"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "allow ssh"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups =[ aws_security_group.bashion_host.id ]
}
ingress {
description = "ping"
from_port = 0
to_port = 0
protocol = "tcp"
cidr_blocks = [aws_security_group.bashion_host.id]
}
egress {
from_port= 0
to_port= 0
protocol = "-1"
cidr_blocks= ["0.0.0.0/0"]
}
}

Step 9: Launch an ec2 instance which has Wordpress setup already having the security group allowing port 80 so that our client can connect to our wordpress site. Also attach the key to instance for further login into it.

#  EC2 Wordpress instance

resource "aws_instance" "wordpress" {
depends_on = [ aws_internet_gateway.gw ]ami = "ami-000cbce3e1b899ebd"
instance_type = "t2.micro"
key_name = "newKey"
subnet_id = aws_subnet.public.id
security_groups = [ aws_security_group.wordpress-sg.id ]
tags = {
Name = "Wordpress"
}
}
output "aws_instance_wordpress" {
value = aws_instance.wordpress.id
}

Step 10: Launch an ec2 instance which has MYSQL setup already with security group allowing port 3306 in private subnet so that our wordpress vm can connect with the same . Also attach the key with the same.

i am also adding new Security group here so that mysql will allow SSH through bastion host and we can do our required task.

#  EC2 MYSQL instanceresource "aws_instance" "mysql" {ami           = "ami-08706cb5f68222d09"
instance_type = "t2.micro"
key_name = "newKey"
subnet_id = aws_subnet.private.id
vpc_security_group_ids = [ aws_security_group.mysql-sg.id,aws_security_group.sg.id ]
tags = {
Name = "MYSQL"
}
}
output "aws_instance_mysql" {
value = aws_instance.mysql.id
}

Step 11: Launch an ec2 instance which is bastion host and having the security group allowing SSH at port 22 so that we can connect to our bastion host and do our task. Also attach the key to instance for further login into it.

#  EC2 WordPress bashion hostresource "aws_instance" "bashion_host" {depends_on = [aws_vpc.myvpc,aws_security_group.wordpress-sg,aws_internet_gateway.gw,
aws_route_table.route-table,aws_route_table.route-table-2]
ami = "ami-000cbce3e1b899ebd"
instance_type = "t2.micro"
key_name = "newKey"
security_groups = [ aws_security_group.bashion_host.id ]
subnet_id = aws_subnet.public.id
associate_public_ipE_address = "true"
tags = {
Name = "Bashion Host-Server"
}
}

then type following command to run our Deployment:-

# validate your code for error correction 
terraform validat
# to start your program 
terraform apply

To connect MYSQL server through bastion host you need to copy private key of mysql to our bastion host and then with the help of SSH we can connect to mysql

You can copy this private key file using WinSCP

NOTE :-

I used WordPress image to create bastion host you can use any image.

Following is the MYSQL server we have connected through bastion Host.

then type following command to destroy our code :-

# for destroy your deployment terraform destroy

Conclusion:-

As, our whole setup is complete and now we can do whatever we want in the WordPress which is internally connected with MySQL instance in another subnet (private subnet ) .

Also we can update our MYSQL Database server using bastion host

Thanku For checking Blog

--

--

Sumit Rasal

Technology I Know — MLops | Devops | Docker | RHCSA | CEH |