When setting up cloud infrastructure in Amazon Web Services (AWS) as a beginner, the "VPC and more" console wizard gives a fast way to generate a complete networking setup including a VPC, subnets, route tables, NAT Gateway, Endpoints and Internet Gateway all in one go.
In this post, I will walk through how I used the "VPC and more" wizard to build a secure two-tier network, adjusted it to meet my project requirements (free-tier instances, single Availability Zone, and no NAT Gateway), created my SSH key pair, chained my Security Groups, and tested secure SSH access.
1. What a Bastion Host (Jump Box) Is and Why It Is Used
A bastion host (also called a jump box) is a hardened server placed in a public subnet that acts as the single entry point into an isolated private network.
In real-world environments, backend servers and databases should never be given public IP addresses or exposed directly to the internet. Doing so increases security risks and invites unauthorized traffic.
By putting our private servers behind a bastion host, we achieve better security:
- Fewer Entry Points: Instead of exposing multiple servers to the public internet, we only allow access through one controlled bastion host.
- Controlled Access: We configure our private servers to accept SSH connections only from the bastion host rather than from the open internet.
2. How I Built the Two-Tier Network Using "VPC and more"
Instead of building every network component manually from scratch, I used the automated "VPC and more" wizard in the AWS Console and customized it for my project.
Step 1: Automated Provisioning via "VPC and more"
- Open the VPC Dashboard in your AWS Console and click Create VPC.
- Select the VPC and more option.
- Fill in the settings:
-
Name tag prefix:
Hye-T-vpc -
IPv4 CIDR block:
10.0.0.0/20 -
Number of Availability Zones (AZs):
1 AZ -
Number of public subnets:
1 -
Number of private subnets:
1 - NAT gateways: Select None (This ensures we do not create a NAT Gateway)
-
VPC endpoints:
None -
DNS options:
Enable DNS hostnamesEnable DNS resolution
-
Name tag prefix:
- Click Create VPC. The wizard automatically generates your VPC, subnets, route tables, and Internet Gateway.
Step 2: Perimeter & Internal Security Group Chaining (Security Layer)
Next, I set up my firewall rules:
-
Create the Bastion Security Group (
bastion-sg):- Navigate to EC2 Dashboard -> Security Groups -> Create security group.
-
Security group name:
bastion-sg -
Description:
Allow SSH only from my public IP -
VPC: Select
Hye-T-vpc. - Under Inbound rules, click Add rule:
-
Type:
SSH(TCP, Port 22) -
Source: Select My IP (verify it displays
[INSERT_YOUR_PUBLIC_IP]/32).
-
Type:
- Click Create security group.
-
Create the Private Instance Security Group (
private-instance-sg):- Click Create security group.
-
Security group name:
private-instance-sg -
Description:
Allow SSH only from Bastion Security Group -
VPC: Select
Hye-T-vpc. - Under Inbound rules, click Add rule:
-
Type:
SSH(TCP, Port 22) -
Source: Select Custom -> Click the search field, type
sg-, and selectbastion-sg(sg-xxxxxxxx). Do not enter an IP or CIDR block.
-
Type:
- Click Create security group.
-
"Why": AWS Security Groups operate as stateful virtual firewalls bound to an instance's Network Interface. Configuring
bastion-sgto accept TCP port 22 only from your workstation's/32public IP blocks all unauthorized internet scanners. Configuringprivate-instance-sgto accept TCP port 22 only frombastion-sg(selecting the Security Group ID as the source rather than an IP address) guarantees that only packets originating from an instance attached tobastion-sgare permitted to reach the private VM.
Step 3: Compute Tier Provisioning
A. Creating an AWS Key Pair
Before launching our virtual machines, we need an SSH key pair to authenticate securely:
- Navigate to the EC2 Dashboard in the AWS Console.
- In the left navigation menu, click on Key Pairs under Network & Security.
- Click the Create key pair button in the top right.
- Configure the settings:
-
Name:
bastion-host-kp -
Key pair type:
RSA -
Private key file format:
.pem
-
Name:
- Click Create key pair. Your browser will automatically download the private key file (
bastion-host-kp.pem). Move this file to your local~/.ssh/directory.
B. Launching the EC2 Instances
Using the key pair we just created, I launched two free-tier instances (t3.micro):
-
Hye-Bastion: Placed in the public subnet with Auto-assign public IP enabled and attached to
bastion-sg, selectingbastion-host-kp. -
Navigate to EC2 -> Instances -> Launch instances.
-
Name:
Hye-Bastion - AMI: Amazon Linux 2023
-
Instance type:
t2.microort3.micro -
Key pair:
bastion-host-kp. - Under Network settings -> Click Edit:
-
VPC:
Hye-T-vpc -
Subnet:
Hye-T-subnet-public1-eu-south-1a - Auto-assign public IP: Enable
-
Firewall (security groups): Select existing security group ->
bastion-sg
-
VPC:
- Click Launch instance.
-
Name:
Hye-Private-Instance: Placed in the private subnet with Auto-assign public IP disabled and attached to
private-instance-sg, selectingbastion-host-kp.
-
Navigate to EC2 -> Instances -> Launch instances.
-
Name:
Hye-Private-Instance - AMI: Amazon Linux 2023
-
Instance type:
t2.microort3.micro -
Key pair:
bastion-host-kp. - Under Network settings -> Click Edit:
-
VPC:
Hye-T-vpc -
Subnet:
Hye-T-subnet-private1-eu-south-1a - Auto-assign public IP: Disable
-
Firewall (security groups): Select existing security group ->
private-instance-sg
-
VPC:
- Click Launch instance.
-
Name:
"Why": The Bastion VM is placed in the public subnet with a Public IPv4 address so it can receive administrative SSH connections via the Internet Gateway. The Private VM is placed in the private subnet with Auto-assign public IP disabled, ensuring it possesses only a private IPv4 address (
10.0.8.x/24) and cannot be reached directly from outside the VPC.
Step 4: Connect to the private VM through the bastion host using ProxyJump/Agent Forwarding
Connecting via ProxyJump
To avoid copying my private .pem key onto the bastion host, I connected from my local computer using SSH agent forwarding or ProxyJump:
# 1. Set key permissions and add to your local ssh-agent
chmod 400 ~/.ssh/bastion-host-kp.pem
# 2. SSH ProxyJump from your Local PC into the Bastion Host with
ssh -o "ProxyCommand=ssh -i ~/.ssh/bastion-host-kp.pem -W %h:%p ec2-user@<BASTION_IP>" -i ~/.ssh/bastion-host-kp.pem ec2-user@<PRIVATE_IP>
-
"Why": I didn't copy my private SSH key (
.pem) onto a Bastion host because that will violates cloud security best practices because any compromise of the perimeter Bastion host would expose the private key on disk or in memory, granting an attacker unrestricted access to all internal instances.- Using SSH Agent Forwarding (
ssh -A) keeps the private key strictly inside your local PC'sssh-agentmemory and forwards only cryptographic signature challenges over a secure Unix domain socket ($SSH_AUTH_SOCK). - Alternatively, ProxyJump establishes an end-to-end encrypted SSH session directly from your PC to the Private VM multiplexed over the Bastion connection.
- Using SSH Agent Forwarding (
3. Testing, Validation & Security Verification Matrix
| Validation Test | Verification Command / Action | Expected Result | Observed Status |
|---|---|---|---|
| Bastion SSH Access | ssh -A ec2-user@<BASTION_PUBLIC_IP> |
Authenticates into 10.0.0.x public subnet shell |
[PASS] |
| No Private Key on Bastion |
ls -la ~/.ssh on Bastion Host |
Only authorized_keys present; no .pem key file on disk |
[PASS] |
| Private Instance SSH Hop | ssh ec2-user@<PRIVATE_VM_IP> |
Authenticates into 10.0.8.x private subnet shell via agent socket |
[PASS] |
| Private Subnet Isolation |
ping -c 3 8.8.8.8 on Private Instance |
100% packet loss (confirms complete absence of NAT Gateway / Internet route) | [PASS] |
4. Troubleshooting & Post-Assessment Teardown
Common Operational Failure Modes
-
Issue 1 —
Permission denied (publickey)on First Hop (Bastion) with-J:-
Cause: By default, the top-level
-i <key>flag in OpenSSH is passed to the destination target, not the jump host. If the key is not loaded into your local session, the bastion hop rejects the connection. -
Fix (One-Liner): Explicitly supply the key for both hops via
ProxyCommand:
ssh -o "ProxyCommand=ssh -i ~/.ssh/bastion-host-kp.pem -W %h:%p ec2-user@<BASTION_IP>" -i ~/.ssh/bastion-host-kp.pem ec2-user@<PRIVATE_IP>-
Fix (Persistent): Map the keys cleanly in
~/.ssh/config:
Host bastion HostName <BASTION_IP> User ec2-user IdentityFile ~/.ssh/bastion-host-kp.pem Host private-instance HostName <PRIVATE_IP> User ec2-user IdentityFile ~/.ssh/bastion-host-kp.pem ProxyJump bastion -
Cause: By default, the top-level
```bash
Then simply run `ssh private-instance`.
```
-
Issue 2 —
Permission denied (publickey)on Second Hop (when using SSH Agent Forwarding):-
Cause: The private key was not added to your local
ssh-agentbefore initiating agent forwarding (ssh -A). -
Fix: Run
eval "$(ssh-agent -s)"andssh-add ~/.ssh/bastion-host-kp.pemon your local machine before connecting.
-
Cause: The private key was not added to your local
Issue 3 —
Connection timed outto Bastion Public IP: Confirm that the public subnet is explicitly associated with your public route table, that the0.0.0.0/0 -> igw-xxxxroute is active, and verify that your home ISP has not rotated your workstation's/32public IPv4 address.





Top comments (0)