A pair of bash scripts that configure a two-VM network topology: VM1 acts as a NAT gateway and SSL-terminating nginx reverse proxy; VM2 runs an Apache web server accessible only through a VLAN subinterface. Internet access from VM2 is routed through VM1.
VM1 has three network interfaces:
| Interface | Type | Purpose |
|---|---|---|
| External | NAT / Bridged | Internet access |
| Internal | Host-only | Communication with VM2 |
| MGMT | Bridged | Remote management from host |
VM2 has two network interfaces:
| Interface | Type | Purpose |
|---|---|---|
| Internal | Host-only | Communication with VM1 |
| MGMT | Bridged | Remote management from host |
Internet traffic from VM2 is NATed through VM1's External interface.
| File | Description |
|---|---|
vm1.sh |
Configures VM1 networking, NAT, SSL certificates, and nginx |
vm1.config |
Network parameters for VM1 |
vm2.sh |
Configures VM2 networking and Apache |
vm2.config |
Network parameters for VM2 |
EXTERNAL_IF="ens3" # External interface name
INTERNAL_IF="ens4" # Internal (host-only) interface name
MANAGEMENT_IF="ens5" # Management interface name (not configured by script)
VLAN=278 # VLAN ID for the internal subinterface
EXT_IP="DHCP" # or a static pair: EXT_IP=172.16.1.1/24 + EXT_GW=172.16.1.254
INT_IP=10.0.0.1/24 # IP for the internal interface
VLAN_IP=10.1.0.1/24 # IP for the VLAN subinterface (e.g. ens4.278)
NGINX_PORT=443 # Port nginx listens on
APACHE_VLAN_IP=10.1.0.2 # VM2 VLAN IP — nginx proxies requests here
INTERNAL_IF="ens3" # Internal (host-only) interface name
MANAGEMENT_IF="ens4" # Management interface name (not configured by script)
VLAN=278 # VLAN ID for the internal subinterface
APACHE_VLAN_IP=10.1.0.2/24 # IP for the VLAN subinterface (e.g. ens3.278)
INT_IP=10.0.0.2/24 # IP for the internal interface
GW_IP=10.0.0.1 # Default gateway — VM1's internal interface IP
The VLAN subinterface name is derived from the template <INTERNAL_IF>.<VLAN>. For example, with INTERNAL_IF="ens4" and VLAN=278, the subinterface is named ens4.278.
- Sources variables from
vm1.config. - Writes
/etc/network/interfaces:- External interface — DHCP if
EXT_IP=DHCP, otherwise static withEXT_IP/EXT_GW. - Internal interface — static
INT_IP. - VLAN subinterface
<INTERNAL_IF>.<VLAN>— staticVLAN_IP.
- External interface — DHCP if
- Installs the
vlanpackage and creates the VLAN subinterface withvconfig. - Enables IPv4 forwarding and configures iptables NAT so that VM2 can reach the internet through VM1's External interface.
- Generates SSL certificates:
- Root CA →
/etc/ssl/certs/root-ca.crt - Web certificate signed by the root CA, for hostname
vm1, with the External IP insubjectAltName→/etc/ssl/certs/web.crt(full chain: web cert + root CA appended)
- Root CA →
- Installs
nginxand configures it as an HTTPS-only SSL termination proxy:- Listens exclusively on
<EXT_IP>:<NGINX_PORT>; HTTP connections on port 80 are dropped. - Proxies all requests to
<APACHE_VLAN_IP>:80on VM2 over the VLAN subinterface.
- Listens exclusively on
- Sources variables from
vm2.config. - Writes
/etc/network/interfaces:- Internal interface — static
INT_IPwithGW_IPas the default gateway. - VLAN subinterface
<INTERNAL_IF>.<VLAN>— staticAPACHE_VLAN_IP.
- Internal interface — static
- Installs the
vlanpackage and creates the VLAN subinterface withvconfig. - Sets the default route via
GW_IPand configures DNS resolvers. - Installs
apache2and configures it to listen only onAPACHE_VLAN_IP:80— not on all interfaces.
- Internet access from VM2 must be routed through VM1. The gateway is defined by
GW_IPinvm2.config. - MGMT interfaces are configured automatically during verification — the scripts must not touch
MANAGEMENT_IF. - All variables in
vm*.configmust be sourced (exported) by the correspondingvm*.shscripts. - The repository must be named
task6_7. - The repository root must contain exactly four files:
vm1.sh,vm1.config,vm2.sh,vm2.config.
This section describes how to spin up a local Ubuntu 16.04 environment that matches the grader's setup so you can verify the solution before submission.
Prerequisites: Docker Desktop installed and running.
# 1. Pull the Ubuntu 16.04 image
docker pull ubuntu:16.04
# 2. Start a privileged container (required for network configuration)
docker run -it --privileged --name topo-vm1 ubuntu:16.04 /bin/bashInside the container:
# 3. Install prerequisites
apt-get update && apt-get install -y git net-tools iproute2
# 4. Clone the repository (replace with your actual URL)
git clone https://github.com/<your-username>/task6_7 /root/task6_7
cd /root/task6_7
# 5. Edit vm1.config to match the container's interface names
# Use `ip link` to list available interfaces
# 6. Run the VM1 configuration script
bash vm1.sh
# 7. Verify nginx is running and the certificate chain is valid
systemctl status nginx
curl -k https://localhost:<NGINX_PORT>
openssl verify -CAfile /etc/ssl/certs/root-ca.crt /etc/ssl/certs/web.crtTo re-use the container later:
docker start -ai topo-vm1To start fresh:
docker rm topo-vm1Repeat the same steps in a second container (--name topo-vm2) running vm2.sh for VM2 testing.
Prerequisites: VirtualBox and Vagrant installed.
Create a Vagrantfile in any working directory:
Vagrant.configure("2") do |config|
config.vm.define "vm1" do |vm1|
vm1.vm.box = "ubuntu/xenial64"
vm1.vm.network "private_network", type: "dhcp"
vm1.vm.provision "shell", inline: <<-SHELL
apt-get update && apt-get install -y git
git clone https://github.com/<your-username>/task6_7 /root/task6_7
cd /root/task6_7 && bash vm1.sh
SHELL
end
config.vm.define "vm2" do |vm2|
vm2.vm.box = "ubuntu/xenial64"
vm2.vm.network "private_network", type: "dhcp"
vm2.vm.provision "shell", inline: <<-SHELL
apt-get update && apt-get install -y git
git clone https://github.com/<your-username>/task6_7 /root/task6_7
cd /root/task6_7 && bash vm2.sh
SHELL
end
end# Start both VMs
vagrant up
# SSH into VM1
vagrant ssh vm1
# SSH into VM2
vagrant ssh vm2| Check | Command | Expected result |
|---|---|---|
| nginx is running | systemctl status nginx |
active (running) |
| nginx listens on correct address | ss -tlnp | grep nginx |
Shows <EXT_IP>:<NGINX_PORT> |
| HTTPS request returns Apache page | curl -k https://<EXT_IP>:<NGINX_PORT> |
Apache default page |
| Root CA validates web certificate | openssl verify -CAfile /etc/ssl/certs/root-ca.crt /etc/ssl/certs/web.crt |
web.crt: OK |
| SAN contains External IP | openssl x509 -in /etc/ssl/certs/web.crt -text -noout | grep -A1 "Subject Alternative" |
Shows External IP |
| Full certificate chain is served | openssl x509 -in /etc/ssl/certs/web.crt -text -noout | grep Issuer |
Matches root CA CN |
| HTTP is blocked | curl http://<EXT_IP>:80 |
Connection refused / no response |
| VM1 VLAN subinterface is up | ip addr show <INTERNAL_IF>.<VLAN> |
Shows VLAN_IP |
| apache2 is running | systemctl status apache2 |
active (running) |
| Apache listens only on VLAN IP | ss -tlnp | grep apache2 |
Shows only <APACHE_VLAN_IP>:80 |
| VM2 VLAN subinterface is up | ip addr show <INTERNAL_IF>.<VLAN> |
Shows APACHE_VLAN_IP |
| VM2 has internet access via VM1 | curl -s http://example.com (on VM2) |
HTTP response body |
- OS: Ubuntu Xenial 16.04 Server (
xenial-server-cloudimg-amd64-disk1.img) - User:
root - VM1: internet access through External interface
- VM2: internet access only if correctly routed through VM1
- The assignment repository is cloned onto both VMs from
https://github.com/<user>/task6_7. vm1.shis executed on VM1.vm2.shis executed on VM2.- A request is made from the host to nginx over HTTPS. The response must be the Apache default page served by VM2. Certificate validity is checked against
/etc/ssl/certs/root-ca.crton VM1.
A repository name other than
task6_7results in automatic failure.
- VM1's External interface is reachable from the host.
- nginx accepts HTTPS connections on
EXT_IP:NGINX_PORTand presents a certificate signed by the root CA at/etc/ssl/certs/root-ca.crt. - nginx proxies all requests to
APACHE_VLAN_IP:80on VM2 over the VLAN subinterface. - Apache on VM2 responds with its default page.
- VM2 can reach the internet via VM1 (NATed through the External interface).
- All HTTP connections to VM1 on port 80 are dropped — only HTTPS is served.
- Both VMs create a tagged subinterface named
<INTERNAL_IF>.<VLAN>(e.g.ens4.278) usingvconfig. - VM1's VLAN IP is
VLAN_IP; VM2's VLAN IP isAPACHE_VLAN_IP. - Apache listens exclusively on VM2's VLAN subinterface IP — not on all interfaces.
