AWS Networking with Terraform: Bastion Hosts to restrict access to Private Hosts
Tags: aws, awscommuniybuilders, terraform, upskilling
Published: 2024-02-12 16:46:53

In our previous post, we explored how to enhance storage resiliency by attaching EFS volumes to multiple EC2 instances. 

Now, let's delve into AWS networking concepts, starting with the pivotal role of Bastion hosts. We'll employ Terraform to create modular components, ensuring a seamless and reproducible setup.

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. For example, you can use a bastion host to mitigate the risk of allowing SSH connections from an external network to the Linux instances launched in a private subnet of your Amazon Virtual Private Cloud (VPC).

## Architecture Overview:

Before diving into implementation, let's understand the architecture we'll be building:


![Bastion Host Architecture Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yopf72fosthdzqce75zy.png)

## Step 1: Creating the VPC and Network Components
Create VPC with IGW, 1 Public and 1 private subnet with route table associations. Please refer to my github repo in resources section below.

## Step 2: Deploying a Bastion Host in the Public Subnet and a Private Host in the Private Subnet
Deploy Linux EC2 instances one in each subnet. 
```terraform
####################################################
# Create EC2 Server Instances
####################################################

module "vpc_a_bastion_host" {
  source           = "./modules/web"
  instance_type    = var.instance_type
  instance_key     = var.instance_key
  subnet_id        = module.vpc_a.public_subnets[0]
  vpc_id           = module.vpc_a.vpc_id
  ec2_name         = "Bastion Host A"
  sg_ingress_ports = var.sg_ingress_public
  common_tags      = local.common_tags
  naming_prefix    = local.naming_prefix
}

module "vpc_a_private_host" {
  source           = "./modules/web"
  instance_type    = var.instance_type
  instance_key     = var.instance_key
  subnet_id        = module.vpc_a.private_subnets[0]
  vpc_id           = module.vpc_a.vpc_id
  ec2_name         = "Private Host A"
  sg_ingress_ports = var.sg_ingress_private
  common_tags      = local.common_tags
  naming_prefix    = local.naming_prefix
}
```

## Step 3: Implementing Access Restriction using Security Groups
Amend private subnet security group to allow traffic only from public subnet.
```terraform
####################################################
# Amend Private Host SG to allow traffic from Bastion Host SG
####################################################
resource "aws_security_group_rule" "public_in_ssh" {
  type                     = "ingress"
  from_port                = 22
  to_port                  = 22
  protocol                 = "tcp"
  security_group_id        = module.vpc_a_private_host.security_group_id
  source_security_group_id = module.vpc_a_bastion_host.security_group_id
}
```

## Steps to Run Terraform

Follow these steps to execute the Terraform configuration:
```terraform
terraform init
terraform plan 
terraform apply -auto-approve
```

Upon successful completion, Terraform will provide relevant outputs.
```terraform
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
```

## Testing the outcome
Bastion Host with Public IP in Public Subnet:
![Bastion Host](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgggq8a91tvcd2i5wn1z.png)

Private Host without Public IP in Private Subnet:
![Private Host](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14i4shb8qphyn7ox5hxz.png)

Private Host Security group with only inbound from Bastion Host Security Group:
![Private Host Security Group](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ii08g3f3czoys1l9bi62.png)

Connecting to Private Host from Bastion Host. 
You will need to create key pair with correct permissions on bastion host before connecting to Private Host
```terraform
chmod 0400 WorkshopKeyPair.pem
ssh ec2-user@10.1.2.71 -i WorkshopKeyPair.pem
```

![Connecting Private Host via Bastion Host](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/assobbp6viyxnbttqc2u.png)

## Cleanup:
Remember to stop AWS components to avoid large bills.
```terraform
terraform destroy -auto-approve
```


With the Bastion host setup accomplished, our next module will delve deeper into AWS networking. We'll explore setting up peer-to-peer VPC connections to further enhance our network architecture.

## Resources:
Github Link: https://github.com/chinmayto/terraform-aws-networking-bastion-host
Bastion Host Concept: https://www.knowledgehut.com/tutorials/aws/aws-bastion-host