PROJECT GUIDE · 04

Build a Secure Enterprise Cloud Network

Design and implement a VPC-style network with public and private subnets, routing rules, firewall policies, NAT simulation, and a load balancer — all locally using Linux bridge networking in your VirtualBox lab.

Environment
2× Ubuntu VMs in VirtualBox
Linux bridge networking
Difficulty
⭐⭐⭐ Intermediate
First networking project
Course Module
Chapter 5
Cloud Networking & Delivery
Duration
3–4 Days
VPC + Subnets + LB + Firewall

1. Network Architecture – VPC with Public and Private Subnets

In real cloud environments, networks are organized into Virtual Private Clouds (VPCs) — isolated network environments where you control all routing and access. A typical enterprise VPC has public subnets (accessible from the internet) and private subnets (isolated from the internet). You will simulate this exact architecture locally using VirtualBox network adapters and Linux iptables firewall rules.

🌐 INTERNET Public Access ⚡ Internet Gateway 10.0.0.1 (iptables MASQUERADE) VPC: 10.0.0.0/16 — abc-retail-vpc PUBLIC SUBNET · 10.0.1.0/24 Route: 0.0.0.0/0 → Internet Gateway ⚖ HAProxy Load Balancer 10.0.1.10 · Port 80 → Web Servers 🌐 Web Server 1 10.0.1.20 Nginx + Docker ABC Retail Site 🌐 Web Server 2 10.0.1.21 Nginx + Docker Redundancy PRIVATE SUBNET · 10.0.2.0/24 Route: 0.0.0.0/0 → NAT Gateway (no direct internet) 🔄 NAT Gateway Outbound only 🗄 MariaDB 10.0.2.10 No public access Port 3306 only 🔒 Security Groups sg-web: ALLOW 80, 443, 22 sg-db: ALLOW 3306 (web only) sg-lb: ALLOW 80 INBOUND Default: DENY ALL = iptables rules in local lab DB calls
⚠ About This Lab Cloud providers like AWS and Azure manage VPC networking at the data center level using hardware-level routing. In this lab, we simulate the same concepts using Linux's built-in networking tools: ip, iptables, and bridge-utils. The commands you learn here are the exact same tools running inside cloud providers' own infrastructure.

2. Step-by-Step Action Items

PHASE 1 · STEP 1 Understand Cloud Networking Concepts Before Configuring

Before touching any configuration, you must understand the components. In this step, you will create a "Network Design Document" — a professional artifact that describes what you are going to build and why.

1
Open your strategy document (or a new Word/Google Doc). Create a "Network Design Document" section. Define the following terms in your own words:
Cloud TermWhat It IsLocal Lab Equivalent
VPC (Virtual Private Cloud)An isolated virtual network environment with its own IP range, routing tables, and security rules — your own private network in the cloudLinux bridge network (172.168.100.0/24)
SubnetA subdivision of the VPC's IP range, typically one per availability zone and separated by public/private access requirementsDifferent IP ranges on same bridge (10.0.1.x / 10.0.2.x)
Internet GatewayThe component that allows a VPC to send/receive traffic to/from the internetiptables MASQUERADE + NAT on Ubuntu VM
Route TableA set of rules that determines where network traffic is directed based on destination IPLinux kernel routing table (ip route)
Security GroupA virtual firewall that controls what traffic can reach each cloud resource (stateful)iptables INPUT/OUTPUT rules per service
Network ACLSubnet-level stateless firewall rules (applied before Security Groups)iptables FORWARD rules on the bridge
NAT GatewayAllows private subnet resources to initiate connections to the internet without being reachable from the internetiptables MASQUERADE on a specific interface
Load BalancerDistributes incoming network traffic across multiple servers to ensure no single server is overwhelmedHAProxy (an open-source load balancer)
Understanding the mapping between cloud concepts and their Linux equivalents is critical. When you use AWS, Azure, or GCP, you are using polished UIs for the same underlying network operations. Security Groups are iptables rules. Route Tables are Linux routing tables. NAT Gateways are Linux NAT rules. Cloud providers abstract these details — but when things go wrong in production, engineers who understand the underlying mechanisms can diagnose issues much faster.
PHASE 1 · STEP 2 Create a Second Ubuntu VM for Private Subnet Simulation

To simulate a two-subnet network, you need two VMs — one representing the public subnet (the web server from Project 2) and one representing the private subnet (a database server).

1
In VirtualBox Manager, right-click your existing ABC-Retail-WebServer VM and select "Clone...".
2
In the Clone dialog:
• Name: ABC-Retail-DBServer
• MAC Address Policy: Select "Generate new MAC addresses for all network adapters" (IMPORTANT — otherwise both VMs will have the same MAC address and conflict)
• Click "Clone"
Cloning a VM creates an identical copy of all configuration and disk contents. We need to generate new MAC addresses because MAC addresses must be unique on any network — two devices with the same MAC address would cause network communication to fail unpredictably. This is similar to how cloud providers assign unique MAC addresses to each virtual network interface card (vNIC) they provision.
3
Start the cloned DB VM and log in with the same credentials you set during Ubuntu installation (cloudadmin / your password).
4
Change the hostname of the DB VM to distinguish it from the web VM:
sudo hostnamectl set-hostname abc-retail-db bash
The hostnamectl set-hostname command permanently changes the server's hostname. The bash command starts a new shell session that picks up the new hostname (you'll see the prompt change to cloudadmin@abc-retail-db:~$). In cloud environments, hostnames help identify servers in monitoring dashboards, log files, and SSH connections — a server named "abc-retail-db" is instantly recognizable as the database server.
PHASE 1 · STEP 3 Install MariaDB on the Private Subnet Server

The database server represents resources in the private subnet. Install MariaDB — an enterprise-grade MySQL-compatible database — and configure it to be accessible only from the web server (not from the internet).

1
On the DB VM terminal, install MariaDB:
sudo apt update sudo apt install -y mariadb-server mariadb-client
MariaDB is a community-developed fork of MySQL, widely used in production cloud environments. The server package (mariadb-server) contains the database engine, and the client package (mariadb-client) provides command-line tools to interact with it. After installation, MariaDB automatically starts and creates default databases.
2
Secure the MariaDB installation:
sudo mysql_secure_installation
The mysql_secure_installation script runs an interactive security wizard that: removes anonymous users (who could connect without a password), disables remote root login (root should only be accessible locally), removes the test database (a security risk), and reloads privilege tables. Always run this on any new database installation — these defaults exist for development convenience, not production security.
3
When prompted by the wizard:
• Enter current password for root: press Enter (blank initially)
• Set root password? → Y, then set a strong password like RootDB@Secure2024!
• Remove anonymous users? → Y
• Disallow root login remotely? → Y
• Remove test database? → Y
• Reload privilege tables? → Y
4
Create the ABC Retail database and user:
sudo mysql -u root -p # Enter the root password you just set # Inside the MySQL prompt, run: CREATE DATABASE abc_retail CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'retailapp'@'10.0.1.%' IDENTIFIED BY 'AppDB@Pass2024!'; GRANT ALL PRIVILEGES ON abc_retail.* TO 'retailapp'@'10.0.1.%'; FLUSH PRIVILEGES; SHOW DATABASES; EXIT;
Breaking down the SQL commands: CREATE DATABASE creates the database with UTF-8 encoding (supports all international characters). CREATE USER 'retailapp'@'10.0.1.%' creates a user named retailapp that can ONLY connect from the 10.0.1.x subnet (the public subnet where web servers live) — this is the database equivalent of a Security Group. GRANT ALL PRIVILEGES ON abc_retail.* gives this user full control over only the abc_retail database. FLUSH PRIVILEGES applies the changes immediately. This principle of least privilege — giving each component only the minimum access it needs — is a core security principle.
5
Configure MariaDB to listen on the network interface (not just localhost):
sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
6
Find the line that says bind-address = 127.0.0.1 and change it to:
bind-address = 0.0.0.0
By default, MariaDB only listens for connections on the loopback interface (127.0.0.1 = "localhost") — meaning only processes on the same machine can connect. Changing it to 0.0.0.0 means "listen on all network interfaces", which is necessary for the web server (on a different VM) to connect. However, we will use firewall rules to ensure ONLY the web server can actually reach port 3306 — the firewall is the security layer, not the bind address alone.
7
Save and restart MariaDB:
sudo systemctl restart mariadb sudo systemctl status mariadb
PHASE 2 · STEP 1 Configure Security Group Rules (UFW Firewall Policies)

Security Groups in cloud platforms are stateful firewalls — they track connection state, so if you allow incoming port 80, the return traffic is automatically allowed. In Linux, UFW (and the underlying iptables) provides the same stateful firewall functionality. You will configure separate firewall policies for the web server and database server.

1
On the Web Server VM — configure the Security Group rules for a public-facing web server:
# Reset UFW to a clean state sudo ufw --force reset # Allow SSH (ALWAYS configure this first to avoid locking yourself out) sudo ufw allow 22/tcp comment 'SSH - Admin access' # Allow HTTP and HTTPS for the website sudo ufw allow 80/tcp comment 'HTTP - Website traffic' sudo ufw allow 443/tcp comment 'HTTPS - Secure website traffic' # Deny everything else sudo ufw default deny incoming sudo ufw default allow outgoing # Enable the firewall sudo ufw --force enable # Verify the rules sudo ufw status verbose
This firewall configuration mirrors an AWS Security Group for a web server: Allow SSH (port 22) for administrative access, Allow HTTP (port 80) and HTTPS (port 443) for web traffic, Deny all other inbound traffic. The comment option adds descriptive labels to each rule — a best practice that makes audits much easier. In 6 months, you want to know WHY a rule exists, not just what it does.
2
On the Database Server VM — configure the much more restrictive private subnet firewall:
# Reset and configure from scratch sudo ufw --force reset # Allow SSH only from the web server's IP # (Replace X.X.X.X with your web server's Host-only IP) sudo ufw allow from 192.168.56.101 to any port 22 comment 'SSH - From web server only' # Allow MariaDB ONLY from the web server sudo ufw allow from 192.168.56.101 to any port 3306 comment 'MariaDB - From web server only' # Block everything else sudo ufw default deny incoming sudo ufw default allow outgoing # Enable sudo ufw --force enable sudo ufw status verbose
Notice the critical difference: the database server's SSH rule only allows connections FROM the web server's IP address (from 192.168.56.101), not from anywhere on the internet. The MariaDB port (3306) is similarly restricted. This simulates a cloud private subnet where the database has no public IP address and can only be reached through the web server. This "bastion host" or "jump server" pattern is a fundamental cloud security architecture principle — the database is completely inaccessible from the internet.
3
Test connectivity from the web server to the database:
# Run on the WEB SERVER VM nc -zv 192.168.56.102 3306 # Replace with DB server IP
The nc -zv command (netcat with verbose output) tests whether a TCP connection can be established to port 3306 on the database server. The -z flag scans for open ports without sending data, and -v shows verbose output. If you see "Connection to 192.168.56.102 3306 port [tcp/mysql] succeeded!" — the web server can reach the database port, and the firewall rules are working correctly. If it fails, check your DB server's UFW rules and that MariaDB is running.
PHASE 2 · STEP 2 Install and Configure HAProxy Load Balancer

A load balancer distributes incoming traffic across multiple servers. If one server fails, the load balancer automatically routes traffic to the healthy servers. HAProxy is an open-source load balancer used by major companies and is functionally equivalent to AWS Application Load Balancer or Azure Application Gateway.

1
On the Web Server VM, install HAProxy:
sudo apt install haproxy -y
HAProxy stands for High Availability Proxy. It operates as a TCP/HTTP proxy that sits in front of your backend servers and distributes traffic between them. It also handles health checks — if a backend server stops responding, HAProxy automatically stops sending it traffic until it recovers. This is identical behavior to cloud load balancers.
2
Configure HAProxy to load balance between two web servers:
sudo nano /etc/haproxy/haproxy.cfg
3
Replace the entire contents with this configuration:
global log /dev/log local0 log /dev/log local1 notice maxconn 4096 user haproxy group haproxy daemon defaults log global mode http option httplog option dontlognull timeout connect 5000ms timeout client 50000ms timeout server 50000ms # ====== LOAD BALANCER FRONTEND ====== frontend abc_retail_frontend bind *:80 default_backend abc_retail_backend # ====== BACKEND WEB SERVERS ====== backend abc_retail_backend balance roundrobin # Distribute requests evenly option httpchk GET / # Health check: GET the homepage http-check expect status 200 # Healthy = returns HTTP 200 server web1 127.0.0.1:8080 check # Nginx container on local port 8080 server web2 127.0.0.1:9090 check # Second instance (or simulate failure) # ====== STATISTICS PAGE ====== listen stats bind *:8404 stats enable stats uri /stats stats refresh 10s stats admin if TRUE
Breaking down the HAProxy configuration: frontend defines where HAProxy listens for incoming connections — port 80 in this case. backend defines the real servers to forward requests to, using round-robin load balancing (web1 gets request 1, web2 gets request 2, web1 gets request 3, etc.). The check keyword enables health checks — HAProxy sends HTTP GET requests every few seconds; if a server stops responding with HTTP 200, it's removed from rotation. The stats section enables a web-based statistics dashboard on port 8404 — this is similar to the monitoring dashboards provided by cloud load balancers.
4
Save and exit nano. Start a second Nginx container on port 9090 so HAProxy has two backends:
docker run -d \ --name abc-retail-web2 \ -p 9090:80 \ --restart unless-stopped \ abc-retail:v1.0
This starts a second instance of the ABC Retail web container on port 9090. HAProxy's configuration references 127.0.0.1:8080 and 127.0.0.1:9090 as its two backend servers. In a production environment, these would be on different VMs or even different availability zones for true high availability — but on a single machine they simulate the load balancing behavior accurately.
5
Test HAProxy configuration syntax and start it:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg sudo systemctl restart haproxy sudo systemctl status haproxy
The haproxy -c -f command validates the configuration file for syntax errors (similar to nginx -t). Never restart a production load balancer without testing the configuration first — an invalid config could take down the load balancer and make ALL backend servers unreachable. After confirming syntax is valid, we restart HAProxy to apply the new configuration.
6
Access the HAProxy statistics page from your host browser: http://[VM-IP]:8404/stats. Take a screenshot — you should see both web servers listed as GREEN (healthy). This is a required deliverable.
7
Test load balancing by watching traffic spread across both servers. Run this from your VM terminal:
for i in {1..10}; do curl -s http://localhost:80 | grep -o "Server [0-9]*" || echo "Request $i sent" done
This loop sends 10 HTTP requests to the load balancer. HAProxy distributes them in round-robin order — alternating between web server 1 and web server 2. Each request is routed to a different backend. In a real scenario, this means if one server is handling a slow request, new requests still get serviced by the other server — preventing one slow request from blocking all users.
PHASE 3 · DOCUMENT Document the Network Architecture

Professional cloud engineers always document their network architecture. This document is used for onboarding new team members, security audits, disaster recovery planning, and troubleshooting.

1
Run this command to generate a network status report:
echo "=== ABC RETAIL NETWORK AUDIT REPORT ===" && \ echo "Generated: $(date)" && \ echo "" && \ echo "--- Active Network Interfaces ---" && \ ip addr show | grep -E "(^[0-9]|inet )" && \ echo "" && \ echo "--- Routing Table ---" && \ ip route show && \ echo "" && \ echo "--- Active Firewall Rules ---" && \ sudo ufw status verbose && \ echo "" && \ echo "--- Listening Services ---" && \ sudo ss -tlnp && \ echo "" && \ echo "--- Docker Network Info ---" && \ docker network ls && docker inspect bridge --format='{{.IPAM.Config}}'
This compound command generates a complete network audit report. ip addr show lists all interfaces and their IPs. ip route show shows the routing table (which interface handles traffic for which IP ranges). ss -tlnp (socket statistics) lists all listening TCP services with the process names that own them. Copy this output into your documentation as the "Network State Baseline" — a snapshot of the network configuration that can be used to verify the network was correctly configured or to restore it if something goes wrong.
2
Create the Network Design Document including:
• The SVG network diagram above (copy it into your document as an embedded image)
• IP address allocation table
• Security Group rules for each server
• Load balancer configuration explanation
• Firewall audit report output

3. Network Traffic Flow Pipeline

🌐 USER Request 🌐 IGW / NAT Route Table 🔒 Security Group ALLOW/DENY LOAD BALANCER HAProxy Port 80 🖥 Web 1 :8080 🖥 Web 2 :9090 🗄 DATABASE Private Subnet

4. Configuration Files & Templates

haproxy.cfg — Production Load Balancer Configuration
global log /dev/log local0 maxconn 4096 user haproxy; group haproxy; daemon defaults log global; mode http timeout connect 5s timeout client 50s timeout server 50s # Where traffic comes in frontend abc_frontend bind *:80 default_backend abc_backend # Round-robin to two web containers backend abc_backend balance roundrobin option httpchk GET / server web1 127.0.0.1:8080 check server web2 127.0.0.1:9090 check # Statistics dashboard listen stats bind *:8404 stats enable; stats uri /stats

5. Deliverables Summary

📄 Files to Submit

  • Network Design Document (with diagram)
  • /etc/haproxy/haproxy.cfg
  • Screenshot: HAProxy stats dashboard (green servers)
  • Screenshot: Website accessible through load balancer port 80
  • Screenshot: sudo ufw status verbose for both VMs
  • Screenshot: Database connectivity test from web VM
  • Network audit report (generated output)
  • Security Group rule documentation table

✅ Verification Checklist

  • Second VM (DB server) cloned and running
  • MariaDB installed and secured
  • ABC Retail database and user created
  • Web server firewall: ports 22, 80, 443 open
  • DB server firewall: only from web server IP
  • Web server can connect to DB on port 3306
  • HAProxy installed and configuration valid
  • Both web containers running (ports 8080 + 9090)
  • HAProxy stats page shows both servers GREEN
  • Website accessible via load balancer port 80
  • Network architecture document complete

6. Why We Did This & What It Accomplishes

Strategic Intent & Operational Impact

Why We Did This

What This Accomplishes