Introduction: Why Build a VPC from Scratch?
Amazon Web Services revolutionized cloud computing with Virtual Private Clouds (VPCs), allowing users to create isolated network environments in the cloud. But have you ever wondered how VPCs actually work under the hood?
In this project, I recreated the core AWS VPC functionality on a single Linux machine using native networking primitives. No Docker, no Kubernetes—just pure Linux networking: network namespaces, bridges, veth pairs, and iptables.
What You’ll Learn:
- How network isolation works at the kernel level.
- Linux network namespaces as lightweight containers.
- Bridging and routing fundamentals.
- NAT implementation with iptables.
- Building infrastructure automation tools.
- Real-World Applications:
- Understandin…
Introduction: Why Build a VPC from Scratch?
Amazon Web Services revolutionized cloud computing with Virtual Private Clouds (VPCs), allowing users to create isolated network environments in the cloud. But have you ever wondered how VPCs actually work under the hood?
In this project, I recreated the core AWS VPC functionality on a single Linux machine using native networking primitives. No Docker, no Kubernetes—just pure Linux networking: network namespaces, bridges, veth pairs, and iptables.
What You’ll Learn:
- How network isolation works at the kernel level.
- Linux network namespaces as lightweight containers.
- Bridging and routing fundamentals.
- NAT implementation with iptables.
- Building infrastructure automation tools.
- Real-World Applications:
- Understanding cloud provider networking internals.
- Building custom network isolation for multi-tenant systems.
- DevOps and infrastructure automation skills.
- Debugging complex network issues.
Architecture Overview
A VPC provides: isolated network space (CIDR), subnets, Internet Gateway, NAT Gateway, Security Groups, and VPC Peering.
My implementation uses Linux primitives to map these concepts:
- AWS VPC Concept Linux Implementation
- VPC Linux Bridge (br-vpc1)
- Subnet Network Namespace
- Subnet Connection veth pair
- Internet Gateway iptables NAT (MASQUERADE)
- Route Table ip route commands
- Security Group iptables INPUT rules
- VPC Peering veth pair between bridges
Conceptual Diagram
Part 1: Understanding Network Namespaces
Network namespaces are Linux’s way of creating isolated network stacks. Each namespace has its own interfaces, IP addresses, routing tables, and iptables rules.
This is the same technology Docker uses for container networking.
Creating Isolation:
# In vpcctl.py - SubnetManager.add()
run_command(f"ip netns add {ns_name}") # Create isolated namespace
When created, the namespace gets a completely separate network stack. Processes inside can't see the host's network—achieving perfect isolation.
Test Isolation
# Create namespace
sudo ip netns add test-ns
# List interfaces in host
ip link
# List interfaces in namespace (only loopback!)
sudo ip netns exec test-ns ip link
Part 2: Connecting Namespaces - The veth Pair Magic
Isolated namespaces need connection. veth pairs are virtual ethernet cables with two ends: one plugs into the namespace, the other into the host or a bridge.
Creating the Connection
# Creating the connection
run_command(f"ip link add {veth_host} type veth peer name {veth_ns}")
run_command(f"ip link set {veth_host} master {bridge}") # Connect to bridge
run_command(f"ip link set {veth_ns} netns {ns_name}") # Move to namespace
Key Insight: Packets entering one end exit the other. We assign the first usable IP (.2) to the namespace and the bridge gets the gateway IP (.1), mirroring AWS subnet assignments.
Part 3: The Bridge - Your VPC Router
A Linux bridge is a virtual network switch that forwards packets between connected interfaces at Layer 2.
Bridge Creation and Gateway
bridge_name = f"br-{name}"
run_command(f"ip link add {bridge_name} type bridge")
run_command(f"ip addr add {net_info['gateway']}/{net_info['prefix']} dev {bridge_name}")
run_command(f"ip link set {bridge_name} up")
Critical Configuration:
We disable bridge netfilter to allow direct Layer 2 forwarding for performance, making it act like a real switch.
# Disable bridge netfilter - allows direct L2 forwarding
run_command("sysctl -w net.bridge.bridge-nf-call-iptables=0")
Routing Setup
The bridge acts as the default gateway.
# Inside namespace: route everything through bridge gateway
run_command(f"ip netns exec {ns_name} ip route add default via {gateway_ip}")
Part 4: NAT Gateway - Internet Access
Private IP addresses (RFC 1918) aren’t routable on the internet. NAT (Network Address Translation) solves this by rewriting the source IP.
NAT Implementation with iptables
# Enable IP forwarding (routing between interfaces)
run_command("sysctl -w net.ipv4.ip_forward=1")
# MASQUERADE: Replace source IP with host's public IP
run_command(f"iptables -t nat -A POSTROUTING -s {cidr} -o {interface} -j MASQUERADE")
The MASQUERADE rule rewrites the source IP of packets leaving the VPC (e.g., 10.0.1.2) to the host’s public IP, and then automatically tracks and rewrites the destination for return traffic.
Key Insight: We only NAT for public subnets. Private subnets have no NAT rule and remain isolated from the internet.
Part 5: VPC Isolation & Peering
By default, VPCs can’t communicate because they have separate bridges and no routes exist between them.
VPC Peering Implementation
We create a veth pair between the two VPC bridges and add explicit routes inside each namespace to the other VPC’s CIDR, pointing to their own gateway (bridge).
# Create veth pair between bridges
run_command(f"ip link add {veth1} type veth peer name {veth2}")
run_command(f"ip link set {veth1} master {vpc1['bridge']}")
run_command(f"ip link set {veth2} master {vpc2['bridge']}")
# CRITICAL: Add routes in EACH namespace
run_command(f"ip netns exec {ns_name} ip route add {vpc2['cidr']} via {vpc1['gateway']}")
Common Mistake: Adding routes to the host’s routing table is ineffective because the traffic originates from the namespaces, which have their own routing tables.
Part 6: Firewall Rules (Security Groups)
We simulate AWS’s stateful Security Groups using iptables rules inside the network namespaces.
Implementation Details
# Essential: Allow established connections (stateful behavior)
run_command(f"ip netns exec {ns_name} iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT")
# Apply custom rules
# For example, to allow/deny specific ports
run_command(f"ip netns exec {ns_name} iptables -A INPUT -p {protocol} --dport {port} -j {action}"
)
The ESTABLISHED,RELATED rule is vital, as it automatically permits return traffic for outbound connections, creating the stateful firewall behavior.
Testing & Validation
This process confirms core functionality:
- Intra-VPC Communication: Successful ping between two subnets (10.0.1.2 to 10.0.2.2) confirms the bridge is routing correctly.
- NAT Gateway: Public subnet pings 8.8.8.8 successfully, private subnet fails, confirming selective NAT.
- VPC Isolation: Ping between two VPCs (e.g., 10.x.x.x to 172.16.x.x) fails initially.
- VPC Peering: The same ping succeeds after peering, and route tables are verified inside the namespaces.
- Firewall Rules: Testing with curl or telnet confirms that only explicitly allowed ports pass through the iptables INPUT chain inside the namespace.
Key Takeaway
- Network Namespaces provide true isolation—the foundation of containers.
- veth Pairs are the virtual cables connecting isolated environments.
- Bridges act as virtual switches for Layer 2 forwarding.
- iptables is powerful for NAT, routing, and firewalling.
This project provides a deep, hands-on understanding of how complex cloud networks are built from simple, powerful Linux primitives.
Resources GitHub Repository: github.com/cypher682/hng-4-vpc