Fully Secured Web App on AWS EC2 Automated using Terraform Part 2
This task is almost same like my last-task with an additional feature to be added that is NAT Gateway to provide the internet access to instances running in the private subnet. Here I am using Terraform for launching this whole setup on the top of AWS.
Task Overview:
Performing the following steps:
- Write an Infrastructure as code using terraform, which automatically create a VPC.
- In that VPC we have to create 2 subnets:
1. public subnet [ Accessible for Public World! ]
2. private subnet [ Restricted for Public World! ]
- Create a public-facing internet gateway to connect our VPC/Network to the internet world and attach this gateway to our VPC.
- Create a routing table for Internet gateway so that instance can connect to the outside world, update and associate it with the public subnet.
- Create a NAT gateway to connect our VPC/Network to the internet world and attach this gateway to our VPC in the public network
- Update the routing table of the private subnet, so that to access the internet it uses the nat gateway created in the public subnet
- Launch an ec2 instance that 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 the instance for further login into it.
- Launch an ec2 instance that has MYSQL setup already with security group allowing port 3306 in a 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 the public subnet so that our client can connect our site.
- MySQL instance has to be part of a private subnet so that the outside world can't connect to it.
- Don't forget to add auto IP assign and auto DNS name assignment option to be enabled.
----------------------------------------------------------------------------------------------------------
What is 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).
What is Bastion Host?
A 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 attack, a bastion host must minimize the chances of penetration.
Task Description:
Create a file having .tf extension with all the code.
0. Providing user
provider "aws" { region = "ap-south-1" profile = "udit"
}
1. Creating a VPC:
For creating VPC, Code is:
resource "aws_vpc" "udit_VPC" { cidr_block = "192.168.0.0/16" instance_tenancy = "default" enable_dns_hostnames = true tags = { Name = "udit_VPC" }
}
This create one VPC with enabling DNS host name.
2. Create one Public Subnet:
For creating a Public Subnet, you need to create one Internet gateway and also one routing table.
resource "aws_subnet" "udit_public_subnet" { vpc_id = aws_vpc.udit_VPC.id cidr_block = "192.168.0.0/24" availability_zone = "ap-south-1a" tags = { Name = "udit_public_subnet" }
}
resource "aws_internet_gateway" "my_internet_gateway" { // depends_on = [ aws_route_table.my_route_table_private ] vpc_id = aws_vpc.udit_VPC.id tags = { Name = "my_internet_gateway" }
}
resource "aws_route_table" "my_route_table_public" { vpc_id = aws_vpc.udit_VPC.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_internet_gateway.id } tags = { Name = "my_route_table" }
}
Now your Public Subnet is launched and one routing Table also. But for attaching the routing table to Subnet, we need to write some more code...
resource "aws_route_table_association" "assign_route_table_to_public" { subnet_id = aws_subnet.udit_public_subnet.id route_table_id = aws_route_table.my_route_table_public.id }
Now my Public Subnet is created for connecting with the Internet.
3. Create one Private Subnet:
This part is also same as the previous step, but here we don't want to connect with the outside world as we make it private so don't create any Internet Gateway.
resource "aws_subnet" "udit_private_subnet" { vpc_id = aws_vpc.udit_VPC.id cidr_block = "192.168.1.0/24" availability_zone = "ap-south-1b" tags = { Name = "udit_private_subnet" }
}
Now we need, one more thing that our private subnet can connect to internet for some important update but no vice-versa for security reason. For this, we have one concept of Source Network Address Translation(SNAT) in Networking. AWS has one sub-service inside VPC called NAT Gateway for this.
Code for launch NAT gateway:
resource "aws_eip" "udit_elastic_ip" { vpc = true } resource "aws_nat_gateway" "udit_nat_gw" { depends_on = [ aws_eip.udit_elastic_ip,aws_subnet.udit_public_subnet ] allocation_id = aws_eip.udit_elastic_ip.id subnet_id = aws_subnet.udit_public_subnet.id tags = { Name = "gw NAT" } }
We need to create one Elastic IP for creating NAT Gateway. Also we need to make it in our Public Subnet either you don't connect to Internet.
Now for connecting this to our private subnet, we need to create one routing Table and associate it with our Private Subnet.
resource "aws_route_table" "my_route_table_private" { vpc_id = aws_vpc.udit_VPC.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.my_internet_gateway.id } tags = { Name = "my_route_table" } }
resource "aws_route_table_association" "assign_route_table_to_private" { subnet_id = aws_subnet.udit_private_subnet.id route_table_id = aws_route_table.my_route_table_private.id }
Now my Private Subnet is launched and also we can access Internet from this.
4. Launch one EC2 instance for WordPress with allowing http port on security Group:
For doing SSH, we need to provide one Key to it, for creating a Key...
resource "tls_private_key" "key_private" { algorithm = "RSA" } resource "aws_key_pair" "task_key" { key_name = "task_key" public_key = "${tls_private_key.key_private.public_key_openssh}" }
Now next step is creating one Security Group allowing SSH, HTTPS and HTTP port...
resource "aws_security_group" "webserver_security_group" { name = "webserver_security_group" description = "Allow ssh and http" vpc_id = aws_vpc.udit_VPC.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" 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 = "webserver_security_group" } }
For launching your wordpress instance, code is...
resource "aws_instance" "wordpress" { depends_on=[ aws_subnet.udit_public_subnet, aws_route_table.my_route_table_public] ami = "ami-000cbce3e1b899ebd" instance_type = "t2.micro" associate_public_ip_address = true subnet_id = aws_subnet.udit_public_subnet.id vpc_security_group_ids = [aws_security_group.webserver_security_group.id] key_name = "myarthos1" tags = { Name = "wordpress" }
}
Now my WordPress is launched.
Why we require a NAT Gateway?
As MySQL instance is a part of our private subnet so that no one from outside world connect/hack to our database.But we also need to do update the software. For this use case, we need a NAT Gateway.
So for going inside the instance, we need to attach a key and allow SSH to the security group but it is not a good way to provide permission to everyone.
Here comes the concept of Bastion Host.... Using this OS, you can do SSH only to go inside the MySQL instance.
Now come to the next step...
6. Launch one EC2 instance for MySQL Database:
For this, we need to provide a key for login inside the instance but also we make one security group to restrict the access through SSH by giving security group as source we create for Bastion Host and also allow MYSQL port.
resource "aws_security_group" "database_security_group" { name = "database_security_group" description = "Allow MYSQL" vpc_id = aws_vpc.udit_VPC.id ingress { description = "MYSQL" security_groups = [aws_security_group.webserver_security_group.id] from_port = 3306 to_port = 3306 protocol = "tcp" } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "database_security_group" }
}
resource "aws_instance" "MySQL" { depends_on=[ aws_subnet.udit_private_subnet, aws_route_table.my_route_table_private] ami = "ami-0019ac6129392a0f2" instance_type = "t2.micro" subnet_id = aws_subnet.udit_private_subnet.id vpc_security_group_ids = [aws_security_group.database_security_group.id] key_name = "myarthos1" tags = { Name = "MySQL" }
}
Now, MySQL database is launched.
For run the file, open your command prompt and run these two commands:
- terraform init
- terraform apply
Thank You for reading :)
MERN Stack Developer || React.js || Node.js || MongoDB || JavaScript | Full Stack Developer
4yGreat