:bulb: How to reach a VM running on a host server from outside, when the VM uses the default virbr0 (NAT) network. (Topology: external server A ↔ host server B ↔ VM A inside host B)

[01] Create Ubuntu VM Image

[02] Install (and Optionally Configure) Nginx Inside the VM

Install Package

1
sudo apt-get -y install nginx net-tools vim

Edit Nginx index.html

  • Useful to confirm the request actually reaches the VM
  • File: /var/www/html/index.nginx-debian.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<!-- Edited: -VM -->
<h1>Welcome to nginx!-VM</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
</body>
</html>

Restart:

1
service nginx restart

[03] Configure External Access Forwarding (iptables)

Forward Packets Between Host Server and VM

  • Requests arriving at host server B on a specific port should be forwarded to the internal VM.
  • Uses iptables nat and filter tables
    • Without -t, the filter table is used by default.
1
2
iptables -t nat -A PREROUTING -i enp0s31f6 -p tcp -m tcp --dport 9999 -j DNAT --to-destination 192.168.122.131:80
iptables -I FORWARD -o virbr0 -d 192.168.122.131 -p tcp --dport 80 -j ACCEPT
  • Rule (1)
    • enp0s31f6 is host server B’s NIC name
    • --dport 9999 matches requests arriving at host B’s NIC on port 9999
    • --to-destination 192.168.122.131:80 forwards them to the VM at 192.168.122.131 port 80
    • Net effect: all traffic to host B port 9999 → VM port 80
  • Rule (2)
    • -o virbr0 matches packets leaving host B via virbr0
    • -d 192.168.122.131 -p tcp --dport 80 matches that VM IP/port via TCP
    • -j ACCEPT allows the packet
    • Net effect: opens the firewall so the forwarded packets can pass through

Persist Rules Across Server Restarts

  • Automatic (with package)
1
2
3
4
5
6
7
8
9
10
11
# Install and start
sudo apt-get update
sudo apt-get install iptables-persistent

# Stop
sudo systemctl stop netfilter-persistent
sudo systemctl disable netfilter-persistent

# Restart
sudo systemctl enable netfilter-persistent
sudo systemctl start netfilter-persistent
  • Manual (save files)
1
2
3
4
5
6
7
8
9
10
# Save
sudo iptables-save > /etc/iptables/rules.v4

# Rename/backup
sudo mv /etc/iptables/rules.v4 /etc/iptables/rules.v4.backup
sudo mv /etc/iptables/rules.v6 /etc/iptables/rules.v6.backup

# Remove
sudo rm /etc/iptables/rules.v4
sudo rm /etc/iptables/rules.v6

(Reference) iptables Tables

Logical groups for filtering and manipulating packets

  • Filter
    • Packet filtering / firewall policy
    • Decides allow/deny
    • Chains: INPUT, OUTPUT, FORWARD
  • NAT
    • Network Address Translation and port forwarding
    • Chains: PREROUTING, POSTROUTING
  • Mangle
    • Modify specific packet attributes (path, priority)
  • Raw
    • Handle exceptions to connection tracking

(Reference) iptables Chains

Sets of rules applied at specific stages of packet flow

  • INPUT: packets entering the local system
  • OUTPUT: packets generated by the local system
  • FORWARD: packets routed through the system (router role)
  • PREROUTING: applied before routing — used for port forwarding, source address rewriting
  • POSTROUTING: applied after routing — typically for NAT (source/destination rewriting)

(Reference) iptables Commands

1
2
3
4
5
6
7
8
9
10
11
12
13
# Inspect rules
sudo iptables -t nat --line-numbers -L PREROUTING
sudo iptables -t filter --line-numbers -L FORWARD

# List with addresses
sudo iptables -t filter -L -n
sudo iptables -t nat -L -n
sudo iptables -t mangle -L -n
sudo iptables -t raw -L -n

# Delete a specific rule
sudo iptables -t nat -D PREROUTING ${line-number}
sudo iptables -t filter -D FORWARD ${line-number}

(Reference) virsh Commands

1
2
3
4
5
6
7
8
9
10
# List VMs
virsh list

# IP address of a specific VM
virsh domifaddr ${id}
virsh domifaddr ${name}

# All DHCP addresses
virsh net-dhcp-leases ${interface_name}
# e.g.: virsh net-dhcp-leases default

[04] Verify Access

  • ServerIP + Forwarding (iptables) Port
    • e.g., 111.111.111.111:9999
    • Uses port 9999 from [03]
    • Confirm <h1>Welcome to nginx!-VM</h1> from [02] is displayed