Terraforming Bastion Architecture: Secure AWS Access for Private Subnets


Introduction

In cloud-native environments, private subnets are essential for securely hosting backend services like databases and internal APIs. However, connecting to instances in these private subnets requires a secure and controlled access method. This is where a bastion host (a jump server) becomes essential. In this guide, we'll explore how to deploy a bastion host architecture using Terraform on AWS to enable secure SSH access to private EC2 instances, without compromising security.


What is a Bastion Host?

A bastion host is a secure EC2 instance configured as the only entry point to your private subnets. It resides in a public subnet and provides SSH access to resources in private subnets.

Key Benefits:

  • Centralized access control

  • Improved audit logging and compliance

  • Minimized attack surface

  • Enhanced security when combined with SSH key rotation or Session Manager


Terraform Architecture Overview

Using Terraform, we’ll define the following key components:

  1. VPC and Subnets

    • One public subnet (for bastion host)

    • One or more private subnets (for application/data servers)

  2. Security Groups

    • Bastion host SG allows inbound SSH access (restricted to your IP)

    • Private instances SG allows SSH only from the bastion's SG

  3. EC2 Instances

    • Bastion EC2 instance with a public IP

    • Private EC2 instances without public IPs

  4. IAM Roles (Optional)

    • For integrating AWS Systems Manager (SSM) as an alternative access method

  5. Key Pair Management

    • Define SSH key pair for secure instance access.


Terraform Configuration Snippets

Here’s a high-level look at the main components in Terraform:

1. VPC and Subnet


resource "aws_vpc" "main" {

  cidr_block = "10.0.0.0/16"

}


resource "aws_subnet" "public" {

  vpc_id     = aws_vpc.main.id

  cidr_block = "10.0.1.0/24"

  map_public_ip_on_launch = true

}


resource "aws_subnet" "private" {

  vpc_id     = aws_vpc.main.id

  cidr_block = "10.0.2.0/24"

}


2. Security Groups


resource "aws_security_group" "bastion_sg" {

  name        = "bastion-sg"

  description = "Allow SSH from known IP"

  vpc_id      = aws_vpc.main.id


  ingress {

    from_port   = 22

    to_port     = 22

    protocol    = "tcp"

    cidr_blocks = ["<Your-IP>/32"]

  }

}


resource "aws_security_group" "private_sg" {

  name        = "private-sg"

  description = "Allow SSH from Bastion"

  vpc_id      = aws_vpc.main.id


  ingress {

    from_port       = 22

    to_port         = 22

    protocol        = "tcp"

    security_groups = [aws_security_group.bastion_sg.id]

  }

}


3. Bastion EC2 Instance


resource "aws_instance" "bastion" {

  ami           = "<Amazon-Linux-AMI>"

  instance_type = "t3.micro"

  subnet_id     = aws_subnet.public.id

  key_name      = "your-key"

  security_groups = [aws_security_group.bastion_sg.name]

  associate_public_ip_address = true

}



Best Practices for Bastion Hosts

  • Use SSH agent forwarding or AWS Session Manager to avoid direct key storage.

  • Enable CloudTrail and VPC Flow Logs for auditing.

  • Auto-terminate or schedule shutdown for idle bastions

  • Limit user permissions via IAM.

  • Consider placing bastions behind a NAT Gateway if outbound internet access is required for private instances.


Advanced: Replacing SSH with AWS SSM

To avoid managing SSH keys altogether, integrate AWS Systems Manager (SSM) for secure and auditable access to private instances. Attach the appropriate IAM role and SSM agent, and connect using:


aws ssm start-session --target <instance-id>



Conclusion

Terraforming your bastion architecture empowers you with automated, secure, and repeatable deployment of access patterns for AWS private subnets. It ensures your private resources remain isolated while being reachable under controlled, auditable conditions.

By combining Infrastructure as Code (IaC) with security best practices, you create a scalable cloud environment that aligns with DevSecOps principles.

Comments

Popular posts from this blog

ECS Deployment Best Practices: Blue/Green with CodePipeline and CodeDeploy

Creating BI Solutions: AI/BI Genie Space Authoring Best Practices in Databricks

AWS Console Not Loading? Here’s How to Fix It Fast

YouTube Channel