Hybrid cloud task 4, providing NAT gateway to private subnet

In this article, we are going to host a WordPress application on AWS in a secure manner. A WordPress application requires us to run WordPress and MySQL. We are going to create a VPC and two subnets in it. Out of which, one subnet can access the internet through an internet gateway and another subnet is a private subnet that accesses the internet through a NAT gateway and a bastion host. We are going to be running three EC2 instances one running WordPress, another one running MySQL, and one which acts as a bastion host. The EC2 instance running WordPress is publically available through our public subnet. However, our MySQL instance has access only to our private subnet. Our WordPress instance can access our MySQL instance through our private subnet. The bastion host has access to our public subnet and NAT gateway. Our MySQL instance can be remotely accessed through our bastion host. This enhances the security of our application. The use of bastion host enables us to access our MySQL instance but it is going to be extremely difficult to exploit due to the presence of our bastion host. I am going to assume you have already installed AWS CLI and created a profile. We are going to be using terraform to describe, manage, and deploy our infrastructure. First, create a directory for our project and create a file called main.tf in it. The code to be written in main.tf is described in the following steps:-

  • First, configure provider details.
provider "aws" {
region="ap-south-1"
profile="adminuser_profile"
}
  • Then create a VPC.
# Main vpcresource "aws_vpc" "main_vpc" {
cidr_block="192.168.0.0/16"
instance_tenancy="default"
enable_dns_hostnames="true"
tags = {
Name="main_vpc"
}
}
  • Next, create a private and public subnet.
# Private subnet
resource "aws_subnet" "private_subnet" {
vpc_id=aws_vpc.main_vpc.id
cidr_block="192.168.1.0/24"
availability_zone="ap-south-1b"
tags = {
Name="private_subnet"
}
}
# Public subnet
resource "aws_subnet" "public_subnet" {
vpc_id=aws_vpc.main_vpc.id
cidr_block="192.168.0.0/24"
map_public_ip_on_launch="true"
availability_zone="ap-south-1a"
tags = {
Name="public_subnet"
}
}
  • Then create an internet gateway.
# Internet gatewayresource "aws_internet_gateway" "main_ig" {
vpc_id=aws_vpc.main_vpc.id
tags = {
Name="main_ig"
}
}
  • Then create a routeing table with a route and associate it with our public subnet.
# Routing tableresource "aws_route_table" "main_rt" {
vpc_id=aws_vpc.main_vpc.id
route {
cidr_block="0.0.0.0/0"
gateway_id=aws_internet_gateway.main_ig.id
}
tags = {
Name="main_rt"
}
}
# Route table associationresource "aws_route_table_association" "pub_rt_assoc" {
subnet_id=aws_subnet.public_subnet.id
route_table_id=aws_route_table.main_rt.id
}
  • Next, create an RSA key pair for ssh using terraform.
# RSA keypair for sshresource "tls_private_key" "main_key" {
algorithm="RSA"
}
module "key_pair" {
source="terraform-aws-modules/key-pair/aws"
key_name="main_key"
public_key=tls_private_key.main_key.public_key_openssh
}
  • Then create three security groups for our public subnet, bastion host, private subnet.
# Security groups# Security group for public subnetresource "aws_security_group" "pub_sg" {
name="pub_sg"
vpc_id=aws_vpc.main_vpc.id

ingress {
description="Allow ssh on port 22"
from_port=22
to_port=22
protocol="tcp"
cidr_blocks=["0.0.0.0/0"]
}
ingress {
description="Allow http on port 80"
from_port=80
to_port=80
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="pub_sg"
}
}
# Security group for bastion hostresource "aws_security_group" "bastion_sg" {
name="bastion_sg"
vpc_id=aws_vpc.main_vpc.id

ingress {
description = "ssh on port 22"
from_port=22
to_port=22
protocol="tcp"
cidr_blocks=["0.0.0.0/0"]
}
ingress {
from_port=0
to_port=0
protocol="-1"
cidr_blocks=["192.168.1.0/24"]
}
egress {
from_port=0
to_port=0
protocol="-1"
cidr_blocks=["0.0.0.0/0"]
}
tags = {
Name="bastion_sg"
}
}
# Security group for private subnetresource "aws_security_group" "priv_sg" {
name="priv_sg"
vpc_id=aws_vpc.main_vpc.id

ingress {
description="TLS on port 3306"
from_port=3306
to_port=3306
protocol="tcp"
security_groups=[aws_security_group.pub_sg.id]
}
ingress {
from_port=0
to_port=0
protocol="-1"
security_groups=[aws_security_group.bastion_sg.id]
}
egress {
from_port=0
to_port=0
protocol="-1"
cidr_blocks=["0.0.0.0/0"]
}
tags = {
Name="priv_sg"
}
}
  • Then create three EC2 instances that are our WordPress instance, MySQL instance, and bastion host instance.
# EC2 instances# EC2 instance for worpressresource "aws_instance" "wp_instance" {
ami="ami-004a955bfb611bf13"
instance_type="t2.micro"
subnet_id=aws_subnet.public_subnet.id
vpc_security_group_ids=[aws_security_group.pub_sg.id]
key_name="main_key"
tags = {
Name="wp_instance"
}
}
# EC2 instance for bastion hostresource "aws_instance" "bastion_instance" {
ami="ami-0732b62d310b80e97"
instance_type="t2.micro"
subnet_id=aws_subnet.public_subnet.id
key_name="main_key"
vpc_security_group_ids=[aws_security_group.bastion_sg.id]

tags = {
Name="bastion_instance"
}
}
# EC2 instance for mysqlresource "aws_instance" "mysql_instance" {
ami="ami-08706cb5f68222d09"
instance_type="t2.micro"
subnet_id=aws_subnet.private_subnet.id
vpc_security_group_ids=[aws_security_group.priv_sg.id]
key_name="main_key"
tags = {
Name="mysql_instance"
}
}
  • Next, create an IP and a NAT gateway.
# EIPresource "aws_eip" "nat" {
vpc=true
}
# NAT gatewayresource "aws_nat_gateway" "nat_gw" {
depends_on=[
aws_instance.mysql_instance,
]
allocation_id=aws_eip.nat.id
subnet_id=aws_subnet.public_subnet.id

tags = {
Name="nat_gw"
}
}
  • Then create a private route table and associate it with our private subnet.
# Private route tableresource "aws_route_table" "private_rt" {
depends_on=[
aws_nat_gateway.nat_gw,
]
vpc_id=aws_vpc.main_vpc.id

route {
cidr_block="0.0.0.0/0"
gateway_id=aws_nat_gateway.nat_gw.id
}
tags = {
Name = "private_rt"
}
}
# Private route table associationresource "aws_route_table_association" "priv_rt_assoc" {
depends_on = [
aws_route_table.private_rt
]
subnet_id=aws_subnet.private_subnet.id
route_table_id=aws_route_table.private_rt.id
}
  • Next, create a local-exec for writing the ssh key to a local pem file.
resource "null_resource" "write_key" {
depends_on = [
tls_private_key.main_key
]
provisioner "local-exec" {
command="echo '${tls_private_key.main_key.private_key_pem}' > main_key.pem"
}
}
  • Then we run “terraform init” command.
  • Then we run the “terraform plan” command.
  • Then run “terraform apply -auto-approve” command. This command may take a few minutes to run.
  • We can see our application and resources utilized.
  • Run “chmod 600 main_key.pem” to change the permission of the ssh key. Follow it by SCP-ing the key into the bastion instance. Then ssh into the bastion instance and follow the steps shown to connect to the MySQL instance (if you are unable to find the IP of MySQL instance consider using a tool like Nmap). We can see that this lets us access our MySQL instance. Now ping google.com to check if the internet is working. Type exit to end ssh session (We must do it twice as we are in a nested ssh session).
  • Now type “terraform destroy -auto-approve” to destroy our infrastructure.

Thank you for reading this article.

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

JupyterHub, Dask, and XArray on the Cloud

How to Enable Hardware GL and RealView in Solidworks on a Virtual Machine for a MacBook Pro

The shiniest red cube imaginable!

From Confused to Proficient: Analysis of Failure to Delete a Kubernetes Cluster Namespace

Double Equality in Kotlin

KERnano : The No-Install Pen Testing Tool (for Windows & Linux)

How to build a RSS Parser in Python?

Local developer CI/CD with Tilt

HTTP/1.1, HTTP/2, and HTTP/3: A Comparison

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Hiteshkoolwal

Hiteshkoolwal

More from Medium

Live Streaming — Turning Gears at Godspeed

African AgriTech: The challenge of hacking digital adoption

Korea’s 1st pitch tournament attracts a global audience

Marketing ⬆️🔥💰🌠