AWS VPC with public, private subnets, internet gateway and NAT gateway
Ridham Dogra
Aspiring Data Science and Artificial Intelligence Enthusiast | Post Graduate Student in Artificial Intelligence and Data Science | Generative AI Extern
VPC(Virtual Private Network)
Amazon Virtual Private Cloud (Amazon VPC) lets you provision a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. You have complete control over your virtual networking environment, including selection of your own IP address range, creation of subnets, and configuration of route tables and network gateways. You can use both IPv4 and IPv6 in your VPC for secure and easy access to resources and applications.
Steps:
1. Write an Infrastructure as code using terraform, which automatically creates 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 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.
Pre-requisites:
- You should have an AWS account,
- Create an IAM user and download the credentials file in which they have provided the Access and secret key which we use for logging in through CLI.
- Download AWS CLI and give the path for the same by editing the environment variables.
- Use the credentials file for logging into AWS using aws configure command.
- Download terraform and give the path for the same.
- Create a profile using aws configure –profile ‘profilename’ command. We create a profile because if we want to share our code using some SCM(Source Code Management) Tool we don’t want to reveal the access and secret keys so it’s good practice to create a profile.
So Let's Begin:
Firstly we have to tell terraform about the provider and we can do so by using the following code->
provider "aws" { region = "ap-south-1" profile = "task_4" }
Now we have to create our vpc and for doing so we have to provide a range of IP addresses which is also known as CIDR. Here I am providing cidr_block=”192.168.0.0/16”.
We can check form the AWS console that it has been created.
resource "aws_vpc" "tvpc" { cidr_block = "192.168.0.0/16" enable_dns_support="true" enable_dns_hostnames="true" instance_tenancy = "default" tags = { Name = "t4vpc" } }
Next we have to create two subnets in that VPC one is private and another one is public.
Subnets are a logical partition of an IP network into multiple, smaller network segments. They are like labs in which we can launch AWS resources in any of the specified subnet.
Public Subnet-It is the subnet which is connected to the internet. I have created this subnet in ap-south-1a region. We will launch our WordPress in this subnet and for connectivity we have to assign public IP to the instance and for doing so I have enabled auto assigning of public IP.
resource "aws_subnet" "pubsubnet" { vpc_id = aws_vpc.tvpc.id cidr_block = "192.168.0.0/24" availability_zone = "ap-south-1a" map_public_ip_on_launch = true tags = { Name = "public_subnet" } }
Private subnet-This subnet cannot connect to the internet. I have created this subnet in ap-south-1b region. We will launch our MySQL database in this subnet.
resource "aws_subnet" "prisubnet" { vpc_id = aws_vpc.tvpc.id cidr_block = "192.168.1.0/24" availability_zone = "ap-south-1b" tags = { Name = "private_subnet" } }
Now After creating the subnets we will create an Elastip IP for NAT Gateway.
Elastic IP: An Elastic IP address is a static IPv4 address designed for dynamic cloud computing. An Elastic IP address is associated with your AWS account. With an Elastic IP address, you can mask the failure of an instance or software by rapidly remapping the address to another instance in your account.
resource "aws_eip" "elasticip"{ vpc = true }
Now after creating elastic IP we will create the NAT gateway.
NAT Gateway: also known as Network Address Translation Gateway, is used to enable instances present in a private subnet to help connect to the internet or AWS services. In addition to this, the gateway makes sure that the internet doesn't initiate a connection with the instances.
resource "aws_nat_gateway" "nat" { allocation_id = aws_eip.elasticip.id subnet_id = aws_subnet.pubsubnet.id tags = { Name = "Natgw" } }
Next we will create an internet gateway.
Internet gateway is a VPC component that helps in establishing a connection between VPC and internet.
resource "aws_internet_gateway" "gw" { vpc_id = aws_vpc.tvpc.id tags = { Name = "In_gateway" } }
Now we will create two route tables one for public subnet and another one for private subnet.
Route Table- A route table contains a set of rules, called routes, that are used to determine where network traffic from your subnet or gateway is directed. To put it simply, a route table tells network packets which way they need to go to get to their destination. We have to associate the route tables with the subnets for controlling the routing of the subnets.
resource "aws_route_table" "igwrt" { vpc_id = aws_vpc.tvpc.id tags = { Name = "internet_gw_rt" } } resource "aws_route" "r1" { route_table_id = aws_route_table.igwrt.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.gw.id } resource "aws_route_table_association" "rta1" { subnet_id = aws_subnet.pubsubnet.id route_table_id = aws_route_table.igwrt.id } resource "aws_route_table" "ngwrt" { vpc_id = aws_vpc.tvpc.id tags = { Name = "nat_gw_rt" } } resource "aws_route" "r2" { route_table_id = aws_route_table.ngwrt.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_nat_gateway.nat.id } resource "aws_route_table_association" "rta2" { subnet_id = aws_subnet.prisubnet.id route_table_id = aws_route_table.ngwrt.id }
Next we have to launch two instances of WordPress and MySQL . For launching these instances we have to first create a key-pair and also we have to create different security groups for these instances.
Key-pair-A key pair is a combination of a public key that is used to encrypt data and a private key that is used to decrypt data. We need a key-pair to connect to our instances.
resource "aws_key_pair" "key" { key_name = "mykeyy" public_key="ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAryh7wbLe3IvfHCLmrc1fbXw1d1dwM7VQN029wAphsKi/gzzWdTLlafUi+Teuo1Ze84sPAb3IxUw5ewwED/N0hTy/7YgvBEX08FTU8X1eH06AtD8Zyf6kAbwXrjO2SGkz/TJ3gebhqfrDu3iYEG1Uo1JKgg284ce8cAd9G3/U5FD/LKdajGmLTAHLIoxp3WHBpRW9ciOK9+JQL9SGnYYF62+++h4fMCc/lyX4A/Sy7UJ7pCFP+ZjsRZ8V6SOXTpy+4PrrdqoDC/NMqs/5pBdBn8ORRk43WjUP8LsvTBEw3AvkMSMgazWl/Ov68tVN3UiwUE9vEQbB0mExsZrixvNYJw== rsa-key-20200713" }
Security group-A security group acts as a virtual firewall for your instance to control incoming and outgoing traffic. Inbound rules control the incoming traffic to your instance, and outbound rules control the outgoing traffic from your instance.
Security group for WordPress instance- In this security group I have added two inbound rules, one for port 22(ssh) so that we can connect to our instance through ssh and another of port 80(HTTP) so that we can connect to WordPress through internet. I have allowed all the outbound traffic.
resource "aws_security_group" "wpsg" { name = "wordpress" description = "Allow TLS inbound traffic" vpc_id = aws_vpc.tvpc.id ingress { description = "ssh" from_port = 0 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } ingress { description = "http" from_port = 0 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 = "wordpress" } }
Security group for MySQL instance- In this security group I have added a port 3306 which is a default port for MySQL protocol. I have also added security group which we use for wordpress instance so we can connect our wordpress instance with mysql instance.
resource "aws_security_group" "mysg" { name = "mysql" description = "Allow MYSQL" vpc_id = aws_vpc.tvpc.id ingress { description = "MYSQL/Aurora" from_port = 0 to_port = 3306 protocol = "tcp" security_groups = [ aws_security_group.wpsg.id ] } ingress { description = "ssh" from_port = 22 to_port = 22 protocol = "tcp" security_groups = [ aws_security_group.wpsg.id ] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] }
Now we will create our instances->
For launching an instance I have used the ami(image) of WordPress so that we can directly connect to it. I have attached the security group and the key-pair that I have created and I have launched this instance in public subnet.
resource "aws_instance" "wordpress" { ami = "ami-000cbce3e1b899ebd" instance_type = "t2.micro" key_name = "mykeyy" security_groups = [aws_security_group.wpsg.id] subnet_id = aws_subnet.pubsubnet.id tags = { Name = "wordpress-os" } }
Next I have launched an instance with MySQL/Aurora image. I have launched this instance in private subnet and attached the security group and key pair that I have created.
resource "aws_instance" "mysql" { ami = "ami-08706cb5f68222d09" instance_type = "t2.micro" key_name = "mykeyy" security_groups = [aws_security_group.mysg.id] subnet_id = aws_subnet.prisubnet.id tags = { Name = "mysql-os" }
We can download the required plugins using the following command:
terraform init
We can also validate our code using the following command:
terrafrom validate
We can create the whole infrastructure using the following command:
terraform apply --auto-approve
Now after creating the whole infrastructure we will go inside our wordpress instance and then through this instance we can go inside our MySql instance and check whether there is an internet connectivity or not. In this case our wordpress instance would be our Bastion Host/Jump Box.
Bastion Host: A bastion host is a server whose purpose is to provide access to a private network from an external network. Because of its exposure to potential attack, a bastion host must minimize the chances of penetration.
For connecting to our mysql instance through wordpress instance we have to first transfer our key to that instance and for doing so I have used WinSCP.
After transferring the key we can connect to our instance but before connecting we have to give the permission to our key to execute and we can do so using the following command:
chmod 400 "key_name"
Now we can login to our mysql instance which is in private subnet and can check the outside connectivity.
We can successfully connect to internet through our mysql instance due to nat gateway but the outside world cannot connect to our instance.
We can destroy the complete infrastructure using the following command:
terraform destroy --auto-approve
Github Repository for the code:
Thank You