Custom VPC + Bastion Host + Apache Web Server (AWS)
Modern cloud networks need secure entry points and clear traffic flow. In this hands-on lab, you will design a custom VPC, deploy a bastion host for secure SSH access, run an Apache Web Server in the public subnet, optionally add a private EC2, and validate NAT connectivity — then clean everything up.
Before You Start: Key Concepts
What is a VPC (Virtual Private Cloud)?
A VPC is your logically isolated network in AWS. You choose the IP range (CIDR), create subnets (public/private), attach gateways, define route tables, and control traffic with security groups and NACLs.
Internet Gateway (IGW)
An Internet Gateway is a horizontally scaled, highly available AWS component that allows resources in public subnets to reach the internet. A subnet becomes “public” when its route table has a default route 0.0.0.0/0 pointing to the IGW.
NAT Gateway
A NAT Gateway lets instances in private subnets initiate outbound internet connections (e.g., to download packages) without exposing those instances to inbound traffic from the internet. Private subnet route tables point 0.0.0.0/0 to the NAT (which lives in a public subnet).
Bastion Host (Jump Host)
A bastion host is a hardened EC2 instance in a public subnet used as a single, controlled SSH entry point to reach private resources. Instead of opening SSH to the world on every server, you lock SSH to the bastion and then jump (ProxyJump) from there into private hosts.
🎯 Goal
- Create a custom VPC with public and private subnets
- Attach an Internet Gateway and deploy a NAT Gateway
- Configure route tables for correct egress
- Create security groups for bastion / web / private instances
- Launch a Bastion Host (public) and a Web Server (public)
- (Optional) Launch a Private EC2 and test outbound via NAT
- Validate access paths and clean up resources
1️⃣ Create VPC and Subnets
AWS Console → VPC → “Create VPC”
- Vpc Settings: VPC only
- Name tag: Workshop-VPC
- IPv4 CIDR block: 10.0.0.0/16
Subnets
Public Subnet
Subnets→Create Subnet
- VPC ID: Workshop-VPC
- Subnet Name: Public Subnet A
- Availability Zone: us-east-1a
- IPv4 subnet CIDR block: 10.0.1.0/24
Private Subnet
Subnets→Create Subnet
- VPC ID: Workshop-VPC
- Subnet Name: Private Subnet A
- Availability Zone: us-east-1b
- IPv4 subnet CIDR block: 10.0.2.0/24
2️⃣ Internet Gateway (IGW) & NAT Gateway
Internet Gateway
VPC→Internet gateways→Create internet gateway
Internet gateway settings:
Name: Workshop-IGW
Attach the Internet Gateway to the VPC (Workshop-VPC)
Select internet gateway →Actions →Attach to VPC
NAT Gateway
VPC→NAT gateways→Create NAT gateway
- Name: Workshop-NAT
- Subnet: Public Subnet A
- Connectivity type: Public
- Elastic IP allocation ID: Allocate Elastic IP
Result
- Public subnet egress → IGW
- Private subnet egress → NAT Gateway
3️⃣ Route Tables
Public Route Table
VPC →Route tables→Create route table
- Name:
Public-RT - VPC:
Workshop-VPC
Add Route
Select Public-RT→Routes→Edit routes–Add route
- Destination: 0.0.0.0/0
- Target: internet Gateway(Workshop-IGW)
Associate with Public Subnet A
Public-RT→Subnet associations→Edit subnet associations→Select Public subnet A
- Available subnets: Public subnet A
Private Route Table:
VPC →Route tables→Create route table
- Name: Private-RT
- VPC: Workshop-VPC
Route Add
Select Private-RT→Routes→Edit routes–Add route
- Destination: 0.0.0.0/0
- Target: NAT Gateway(Workshop-NAT)
Associate with Private Subnet A
Select Private-RT→Subnet associations→Edit subnet associations→Select private subnet A
- Available subnets: Private subnet A
4️⃣ Security Groups
VPC→Security→Security groups→Create Security Group
SG-Bastion
- Security group name: SG-Bastion
- Description: SG-Bastion
- VPC: Workshop-VPC
Inbound rules:
- Type: SSH
- Protocol: TCP
- Port range: 22
- Source:0.0.0.0/0
SG-Web
- Security group name: SG-Web
- Description: SG-Web
- VPC: Workshop-VPC
- Inbound: 80/tcp → 0.0.0.0/0
- Inbound: 22/tcp → SG-Bastion
5️⃣ Bastion Host (Public Subnet)
EC2→Instances→Launch an instance
- Name and tags: Bastion
- AMI→ Amazon Linux 2023, t3.micro
- Key Pair (login):
- Create new key pair → Key pair name: ec2_ssh_key
- Type: RSA, File format: .pem → Create
Network settings → Edit:
- VPC: Workshop-VPC
- Subnet: Public subnet A
- Auto-assign public IP: Enable
- Firewall (SG): Select security group
- Security group name: SG-Bastion
SSH Test
Use the bastion’s public IP to connect over SSH, for example: ssh -i ec2_ssh_key.pem ec2-user@<BASTION_PUBLIC_IP>
chmod 400 ec2_ssh_key.pem
ssh -i ec2_ssh_key.pem ec2-user@44.212.21.1066️⃣ Web Server (Public Subnet)
EC2→Instances→Launch an instance
- Name and tags: Web
- AMI→ Amazon Linux 2023, t3.micro
Network settings → Edit:
- VPC: Wokshop-VPC
- Subnet: Public subnet A
- Auto-assign public IP: Enable
- Firewall (SG): Select security group
- Security group name: SG-Web
Advanced details:
User data
#!/bin/bash
sudo dnf update -y
sudo dnf install httpd -y
systemctl start httpd
systemctl enable httpd
echo "<html><h1>Welcome to Tech Istanbul: Apache Web Server</h1></html>" > /var/www/html/index.htmlClick on Launch instance button.
Validate in browser
http://<WEB_PUBLIC_IP>SSH Test Connection
ssh -i ec2_ssh_key.pem ec2-user@3.82.122.163
SSH directly to Web from the internet should fail by design (22 allowed only from SG-Bastion).
We’ll SSH to the web server through the bastion host, targeting its private IP address (not the public IP)
ssh -i ec2_ssh_key.pem \
-o 'ProxyCommand=ssh -i ec2_ssh_key.pem -W %h:%p ec2-user@44.212.21.106' \
ec2-user@10.0.1.157️⃣ Private EC2
EC2→Instances→Launch an instance
- Name and tags: private-EC2
- AMI→ Amazon Linux 2023, t3.micro
Network settings → Edit:
- VPC: Wokshop-VPC
- Subnet: Private subnet A
- Auto-assign public IP: disable
- Firewall (SG): Select security group
- Security group name: SG-private
- Inbound: 22/tcp → SG-Bastion
Click on Launch instance button
🔐 To connect Private-EC2 SSH via Bastion
ssh -i ec2_ssh_key.pem \
-o 'ProxyCommand=ssh -i ec2_ssh_key.pem -W %h:%p ec2-user@44.212.21.106' \
ec2-user@10.0.2.190NAT Gateway Validation (Private EC2)
- On Private-EC2, install a package:
sudo yum install nginxShould succeed (egress via NAT). nginx installed.
Now remove the 0.0.0.0/0 → NAT route from Private-RT and try again:
VPC →Route tables→PrivateRT→Edit routes
Now install a package
sudo dnf install httpdThis should fail (no internet). Restore the NAT route afterwards.
🧹 Cleanup
- Terminate EC2 instances (Bastion, Web, Private if created)
- Delete NAT Gateway
- Release Elastic IP
- Delete the VPC
