Bastion Hosts

Architecture

Deploy a bastion host (jump server) for secure access to private resources:

  • Public subnet placement for internet accessibility
  • Minimal security group allowing only SSH from approved IPs
  • Session logging via CloudWatch or S3
  • Auto-recovery for high availability
  • Systems Manager as a more secure alternative
┌─────────────────────────────────────────────────────────────┐
│                     Admin Workstation                        │
│                    (Approved IP Address)                     │
└─────────────────────────────────────────────────────────────┘
                              │ SSH (port 22)
┌─────────────────────────────────────────────────────────────┐
│                      Public Subnet                           │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                    Bastion Host                       │   │
│  │                    (EC2 Instance)                     │   │
│  └──────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │ SSH (port 22)
┌─────────────────────────────────────────────────────────────┐
│                     Private Subnet                           │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │   App Server    │  │   Database      │                   │
│  │   (Private)     │  │   (Private)     │                   │
│  └─────────────────┘  └─────────────────┘                   │
└─────────────────────────────────────────────────────────────┘

When to Use

Bastion hosts are appropriate when:

  • Accessing private EC2 instances that have no public IP
  • Maintaining compliance requiring controlled access points
  • Auditing SSH access through a single entry point
  • Using legacy applications that require SSH access
  • Temporary access is needed for troubleshooting

Example Configuration

module "bastion" {
  source  = "registry.patterneddesigns.ca/patterneddesigns/ec2-instance/aws"
  version = "1.5.0"

  instance_name      = "bastion"
  instance_type      = "t3.micro"
  ami_id             = data.aws_ami.amazon_linux.id
  subnet_id          = module.vpc.public_subnets[0]
  security_group_ids = [aws_security_group.bastion.id]
  key_name           = aws_key_pair.bastion.key_name

  assign_public_ip     = true
  enable_auto_recovery = true
  monitoring           = true

  iam_instance_profile = aws_iam_instance_profile.bastion.name

  user_data = <<-EOF
    #!/bin/bash
    yum update -y

    # Enable SSH session logging
    echo 'ForceCommand /usr/bin/script -q -f /var/log/ssh-sessions/$USER-$(date +%Y%m%d%H%M%S).log' >> /etc/ssh/sshd_config
    mkdir -p /var/log/ssh-sessions
    chmod 733 /var/log/ssh-sessions
    systemctl restart sshd
  EOF

  tags = {
    Environment = "production"
    Role        = "bastion"
  }
}

resource "aws_security_group" "bastion" {
  name        = "bastion-sg"
  description = "Security group for bastion host"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description = "SSH from approved IPs"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.allowed_ssh_cidrs # e.g., ["203.0.113.0/24"]
  }

  egress {
    description = "SSH to private instances"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [module.vpc.vpc_cidr]
  }

  tags = {
    Name = "bastion-sg"
  }
}

# Allow SSH from bastion to private instances
resource "aws_security_group" "private_ssh" {
  name        = "private-ssh-sg"
  description = "Allow SSH from bastion"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description     = "SSH from bastion"
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion.id]
  }
}

Considerations

AspectConsideration
Access ControlUse IP whitelisting and strong key management
LoggingEnable SSH session logging and send to CloudWatch
HardeningDisable root login, use fail2ban, keep updated
High AvailabilityUse auto-recovery or deploy in multiple AZs
Key ManagementRotate SSH keys regularly, consider EC2 Instance Connect

Alternatives

Consider more modern alternatives:

  • AWS Systems Manager Session Manager - No SSH keys, IAM-based access, full audit logging
  • EC2 Instance Connect - Temporary SSH keys via IAM
  • AWS Client VPN - Direct VPN access to VPC
  • PrivateLink - Service-specific private connectivity