Create VPC,Subnet,Internet Gateway,Router Table,NAT Gateway,Bastion Host,EC2 Instance

How can we provide internet access to instances in private subnet(NAT Gateway,Bastion host)

*Task 4*

Gauri Raskar
8 min readJul 26, 2020

--

What we want to achieve?

Create NAT Gateway to provide the internet access to instances running in the private subnet. As we have seen in Task 3 we have created VPC, Internet Gateway, subnets and two instances that is “wordpress” and “MySQL” for our web portal but while doing this we face some challenges we have launch MySQL instance in private subnet so we are not able to go to outside world, sometimes suppose we want to update some software in this case our MySQL instance can not access internet because it is in private subnet. This challenge can be solved using NAT Gateway and Bastion Host Let’s see how….

How can we achieve our goal?

Steps:

Performing the following steps:
1. Write an Infrastructure as code using terraform, which automatically create a VPC.
2. In that VPC we have to create 2 subnets:
1. public subnet [ Accessible for Public World! ]
2. 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 sothat 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.

Step 1 : First we have to configure AWS , for that we will provide an AWS provider

provider "aws" {
region = "ap-south-1"
profile = "gauri"
}

Step 2 : We will create VPC using terraform

First let’s understant what is VPC

Amazon Virtual Private Cloud (Amazon vpc) 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.

Here we will create our own VPC which is lwvpc with cidr block here the prefix is “192.168.0.0” with The total number of bits in this address is 16 by doing this we provide a network address that is used for the identification of our network

resource "aws_vpc" "myVpc" {
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames= "true"
tags = {
Name = "lwvpc"
}
}

Step 3: Create public and private subnet in our VPC

Now we will create two sunbets in this VPC we created in above step, we will create one private and one public VPC

Public subnet: For Wordpress instance so that client will be able to visit our web app

Private subnet: For MySQL instance so that no one can outside world connect to our private database

resource "aws_subnet" "public" {
vpc_id = "${aws_vpc.myVpc.id}"
cidr_block = "192.168.0.0/24"
availability_zone= "ap-south-1a"
map_public_ip_on_launch = "true"
tags = {
Name = "PUBLIC subnet"
}
}
resource "aws_subnet" "private" {
vpc_id = "${aws_vpc.myVpc.id }"
cidr_block = "192.168.1.0/24"
availability_zone= "ap-south-1b"
tags = {
Name = "PRIVATE subnet"
}
}

Step 4 : Create Internet Gateway

Now let’s create Internet Gateway

The gateway supports connections initiated from within the egress and connections initiated from the internet (ingress).

Resources (Wordpress Instance) that need to use the gateway for internet access must be in a public subnet and have public IP addresses.

resource "aws_internet_gateway" "internet_gw" {
vpc_id = "${aws_vpc.myVpc.id }"
tags = {
Name = "gw"
}
}

Step 5 :Create Routing Table

Each public subnet that needs to use the internet gateway must have a Router Table rule that specifies the gateway as the target.

resource "aws_route_table" "r" {
vpc_id = "${aws_vpc.myVpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.internet_gw.id}"
}
tags = {
Name = "r"
}
}

Step 6 :Associate Routing Table

Here we associate out public subnet from VPC to the router table which we have created for our VPC to allow internet access

resource "aws_route_table_association" "a" {
subnet_id = "${aws_subnet.public.id}"
route_table_id = "${aws_route_table.r.id}"
}

Step 7 : Create NAT Gateway

What if private instance wants to visit outside world for a say software update(e.g. yum)
-Here we cannot add Internet Gateway
-Instead, we will use NAT Gateway, so that private instance can to outside world

-that makes it easy to connect to the Internet from instances within a private subnet

Step 1 : Create Elastic IP for NAT Gateway

We need Elastic IP to create NAT Gateway

resource "aws_eip" "nat_eip" {
vpc = true
}

Step 2: Create NAT Gateway

resource "aws_nat_gateway" "nat_gw" {
allocation_id = "${aws_eip.nat_eip.id}"
subnet_id = "${aws_subnet.public.id}"
tags = {
Name = "gw NAT"
}
depends_on = [aws_internet_gateway.internet_gw]
}

Step 8 :Create Route Table for private subnet and NAT gateway

We need to have internet connectivity for our instance in private subnet for that we will have to add one more route table that will enable internet access, we will associate this route table to NAT gateway

resource "aws_route_table" "r_nat" {
vpc_id = "${aws_vpc.myVpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_nat_gateway.nat_gw.id}"
}
tags = {
Name = "route table private_nat"
}
}
resource "aws_route_table_association" "nat_private" {
subnet_id = "${aws_subnet.private.id}"
route_table_id = "${aws_route_table.r_nat.id}"
}

Step 9: Create keys for instance

resource "tls_private_key" "webserver_private_key" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "local_file" "private_key" {
content = tls_private_key.webserver_private_key.private_key_pem
filename = "webserver_key.pem"
file_permission = 0400
}
resource "aws_key_pair" "webserver_key" {
key_name = "webserver"
public_key = tls_private_key.webserver_private_key.public_key_openssh
}

Step 10 : Create Security Group for Wordpress instance

Our Security Group will allow HTTP and SSH for our Wordpress Instance

resource "aws_security_group" "wp_http" {
name = "wp_http"
description = "Allow http inbound traffic"
vpc_id = "${aws_vpc.myVpc.id}"
ingress {
description = "http"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]

}
ingress {
description = "ssh"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups =[aws_security_group.bastion_ssh.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "allow_http"
}
}

Step 11 : Create Wordpress instance

We will create our Wordpress instance in public subnet to which we have associated our routing table which allows all internet traffic we will enable “associate_public_ip_address” to true so that we will get auto IP assinged

resource "aws_instance" "wordpress" {
ami = "ami-0732b62d310b80e97"
instance_type = "t2.micro"
subnet_id = "${aws_subnet.public.id}"
key_name = aws_key_pair.webserver_key.key_name
vpc_security_group_ids = [aws_security_group.wp_http.id]
associate_public_ip_address ="true"
tags = {
Name = "wordpress"
}
}

Step 12 : Create Bastion Host or Jump Server

As we have mentioned above our MySQL instance will be in private subnet so what if someone from outside world wants to connect to our instance let’s say for management purpose, To achieve this use-case we will add one more instance in public subnet and that instance we will use to connect to our MySQL instance , This instance will be known as “Bastion Host Instance or Jump Server” by doing this we are not risking security of our infrastructure because the only one who has “Bastion Host IP” will be able to connect to MySQL instance

Let’s see how can we create Bastion Host Instance

Step 1 : Create a Security Group for Bastion Host or Jump Server

resource "aws_security_group" "bastion_ssh" {
name = "bastion_ssh"
description = "Allow ssh"
vpc_id = "${aws_vpc.myVpc.id}"
ingress {
description = "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"]
}
tags = {
Name = "allow_ssh"
}
}

Step 2 : Create Bastion Host in public subnet

resource "aws_instance" "bostion_host" {
ami = "ami-0732b62d310b80e97"
instance_type = "t2.micro"
subnet_id = "${aws_subnet.public.id}"
key_name = aws_key_pair.webserver_key.key_name
vpc_security_group_ids = [aws_security_group.bastion_ssh.id]
associate_public_ip_address ="true"
tags = {
Name = "bostion_host"
}
}

Step 13 : Create a Security Group for MySQL

Here we will add SSH for Bastion Host while MySQL 3306 port for our MySQL instance, Here we are giving dependencies this security will only get created when the security group for Bastion Host and Wordpress will get created because by only doing this we confirms that we have all the requirements to finally create our MySQL instance in Private Subnet now

resource "aws_security_group" "sg_mysql" {
name = "sg_mysql"
description = "Allow mysql inbound traffic"
vpc_id = "${aws_vpc.myVpc.id}"
ingress {
description = "mysql"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups =[aws_security_group.wp_http.id]

}
ingress {
description = "ssh"
from_port = 22
to_port = 22
protocol = "tcp"
security_groups =[aws_security_group.bastion_ssh.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "sg_mysql"
}
depends_on= [aws_security_group.wp_http,aws_security_group.bastion_ssh]
}

Step 14 : Create MYSQL Server in a private subnet

resource "aws_instance" "mysql-server" {
ami = "ami-08706cb5f68222d09" //amzaon linux with
instance_type = "t2.micro"
subnet_id = "${aws_subnet.private.id}"
key_name = aws_key_pair.webserver_key.key_name
vpc_security_group_ids = [aws_security_group.sg_mysql.id]
tags = {
Name = "mysql-task2"
}
}

Created Instances are

After creating our Infrastructure we will check if we have done it accurately

Now we have to check do we have internet connection in instance in private subnet or not. for that, we will transfer the key to the bastion host and connect to MySQL instance.

For transferring key I am using the WinSCP tool.

using WinSCP to transfer key
Checking Successful Transfer

Now let’s see if we can ping to the outside world

We will connect to MySQL instance from Bastion Host and try to connect to internet

Ping google from our private instance

As mentioned above we have created VPC and accessed instance from private subnet using bastion host and NAT Gateway

--

--