A bastion host is a server whose purpose is to provide access to a private network from an external network, such as the Internet. [source: How to Record SSH Sessions Established Through a Bastion Host]
Prerequisites
- Custom VPC in which we want to run EC2 instances
- Internet Gateway which connects VPC with Internet; depends on VPC (ID)
- (Internet) Route Table (for this VPC) which allows connection to Internet (routing to Internet Gateway); depends on VPC (ID) and CIDR chosen for public subnet
- (Local) Route Table (for this VPC) which allows only local traffic (within VPC); depends on VPC (ID) and CIDR chosen for private subnet
- A (public) subnet in chosen AZ in this VPC
- connected to route table which allows Internet connection => that makes this subnet public
- A (private) subnet in the same AZ in this VPC
- connected to route table which allows local traffic only => that makes this subnet private
- Security group in this VPC that allows SSH; depends on VPC (ID)
- Running (public) EC2 instance (bastion host)
- running in the same VPC
- running in the same AZ
- running in the created public subnet
- having attached security group mentioned above
- Running (private) EC2 instance
- running in the same VPC
- running in the same AZ
- running in the created private subnet
- having attached security group mentioned above
- SSH client available on a dev machine
Resource Details
- Name: test-vpc
- CIDR: 10.10.0.0/16
- Name: Allow public SSH
- VPC: test-vpc
- Inbound rules:
- IP version: IPv4
- Type: SSH
- Protocol: TCP
- Port range: 22
- Source: 0.0.0.0/0
- Description: SSH access
- Outbound rules:
- IP version: IPv4
- Type: All traffic
- Protocol: All
- Port range: All
- Destination: 0.0.0.0/0
- Description: -
- Subnet has Enable auto-assign public IPv4 address disabled
- When launching EC2 instance, this can be overridden with EC2 setting Auto-assign public IP
- Subnet uses a private IP range as its CIDR block
A private EC2 has a network interface which does not have a public IPv4 address but has a private IPv4 address e.g. 10.10.3.226 if it's in 10.10.3.0/24 subnet.
Private EC2 instance needs to have attached to it Allow public SSH security group (SSH Access Security Group in the diagram below).
Architecture diagram:
SSH Connection
We'll use SSH feature called agent forwarding. If we load a key into a local SSH agent, we can use ssh -A command to allow any remote ssh server to read this key as if it was added to the local SSH agent. We need to use -A only when establishing the first SSH connection.
From man ssh:
-A Enables forwarding of connections from an authentication agent such as ssh-agent(1). This can also be specified on a per-host basis in a configuration file.
Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's UNIX-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. A safer alternative may be to use a jump host (see -J).
To add a private key to the local ssh agent:
$ ssh-add MyTestMachine.pem
Identity added: MyTestMachine.pem (MyTestMachine.pem)
Note that in this setup we are using the same SSH key (MyTestMachine.pem) for secure connection to both bastion and private host.
$ ssh -A ec2-user@34.255.30.127
Last login: Wed Jun 15 16:38:34 2022 from r-123.45.67.195.ptr.example.com
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
16 package(s) needed for security, out of 26 available
Run "sudo yum update" to apply all updates.
If each host has its own SSH key we can load to SSH agent the one of the target private EC2 host and then later use -i option to specify the key for connecting to bastion host:
(These .pem files store private keys but in general, they can contain also public keys, or both, or certificates...security - Does .pem file contain both private and public keys? - Stack Overflow)
From inside the bastion host we can connect to the target private EC2:
[ec2-user@ip-10-10-0-85 ~]$ ssh ec2-user@10.10.3.226
__| __|_ )
_| ( / Amazon Linux 2 AMI
___|\___|___|
https://aws.amazon.com/amazon-linux-2/
[ec2-user@ip-10-10-3-226 ~]$
This is possible because Agent Forwarding added MyTestMachine.pem to bastion host's SSH agent:
ssh-rsa AAAA...E2XN MyTestMachine.pem
ssh-rsa AAAA...VeXQ== john.smith@example.com
---