Virtualized Router-on-a-stick: Guest Firewall on Debian Hypervisor (and OPSEC hardening)
Virtualized Router-on-a-stick: Guest Firewall on Debian Hypervisor (and OPSEC hardening)
In home-lab setups or security environments, the default approach of the amateur sysadmin is to install the firewall directly on the host hardware or, even worse, expose the hypervisor's management interface to every single available subnet. Nothing could be more wrong. If a service exposed in the DMZ or a compromised client on the trusted LAN manages to execute an exploit, they gain direct access to the hypervisor's keys (KVM, Proxmox, or whatever you are running). Game over.
The elegant and secure solution is to segregate roles: a minimal Debian Linux hypervisor that only handles virtual bridges (without putting its face on them, i.e., without assigning itself an IP) and a dedicated guest KVM virtual machine that acts as the actual centralized firewall/router.
Today, we will look at how to cleanly and anonymously configure a virtualized Router-on-a-stick setup under Debian, with a particular focus on OPSEC hardening.
The Scenario: Router-on-a-stick
The physical host has a single Ethernet network interface (eth0). This interface is connected to a trunk port of a managed switch, carrying several tagged VLANs:
- VLAN 10: External Network (WAN)
- VLAN 50: Demilitarized Zone (DMZ)
- VLAN 100: Trusted Network (Trust LAN)
- VLAN 150: Anonymized Network (Tor LAN)
Our goal is to direct all VLANs into the firewall virtual machine, which will handle routing, NAT, packet filtering (nftables), and transparent redirection to Tor.
Host Preparation (Packages to install)
First, we need to transform our minimal Debian installation into a KVM hypervisor and enable network bridging utilities.
Install the necessary packages using apt:
sudo apt install qemu-system qemu-utils libvirt-daemon-system libvirt-clients bridge-utils(Note for ARM64 architectures like Raspberry Pi: install qemu-system-aarch64 instead of the generic qemu-system to utilize native hardware virtualization).
Ensure that the virtualization services are active and enabled at boot:
sudo systemctl enable --now libvirtd1. Host Configuration (Debian Hypervisor)
On the Debian host, we configure the VLAN sub-interfaces and map them to their respective virtual bridges. The golden rule of hardening is: the host must not have an IP on networks it does not directly manage.
We edit /etc/network/interfaces on the host:
source /etc/network/interfaces.d/*
# Configure physical VLAN sub-interfaces (no logical IP)
iface eth0 inet manual
iface eth0.10 inet manual
iface eth0.50 inet manual
iface eth0.100 inet manual
iface eth0.150 inet manual
# Enable bridges at boot
auto br0
auto br1
auto br2
auto br3
# WAN BRIDGE (VLAN 10) - Host has NO IP here
iface br0 inet manual
bridge_ports eth0.10
bridge_stp off
# DMZ BRIDGE (VLAN 50) - Host has NO IP here
iface br1 inet manual
bridge_ports eth0.50
bridge_stp off
# TRUST BRIDGE (VLAN 100) - Host has management IP only here
iface br2 inet static
address 10.152.0.254
netmask 255.255.255.0
broadcast 10.152.0.255
bridge_ports eth0.100
bridge_stp off
bridge_waitport 0
# TOR BRIDGE (VLAN 150) - Host has NO IP here
iface br3 inet manual
bridge_ports eth0.150
bridge_stp off
bridge_waitport 0Why set "manual" on sensitive bridges?
Notice that br0 (WAN), br1 (DMZ), and br3 (Tor) are configured in manual mode (no logical IP address for the host). This is critical for security:
- The VMs' Ethernet packets still traverse the bridge (which acts as a pure Layer 2 virtual switch).
- The host hypervisor does not have an active IP stack on those subnets. If an attacker compromises a web server in the DMZ (
br1), they will find no hypervisor IP address to scan or attack on that segment. The hypervisor is invisible. - The host is only reachable on the Trust network (
br2at IP10.152.0.254), isolating the SSH management console from the rest of the world.
2. Guest Configuration (KVM Firewall VM)
In the virtualization manager (e.g., Libvirt / QEMU-KVM), we create the firewall virtual machine and associate virtual network cards with it, connected directly to the host's bridges:
- Virtual interface
virtio1 → connected to bridgebr0(WAN) - Virtual interface
virtio2 → connected to bridgebr1(DMZ) - Virtual interface
virtio3 → connected to bridgebr2(Trust) - Virtual interface
virtio4 → connected to bridgebr3(Tor)
Inside the firewall VM (ideally also a minimal Debian install), the interfaces will be detected as enp1s0, enp8s0, enp9s0, enp12s0 (names may vary depending on the PCI layout).
We configure /etc/network/interfaces inside the firewall VM:
auto lo
iface lo inet loopback
# External Interface (WAN)
auto enp1s0
iface enp1s0 inet static
address 10.255.0.5
netmask 255.255.255.0
broadcast 10.255.0.255
gateway 10.255.0.1
dns-nameservers 1.1.1.1
# DMZ Interface (Gateway for DMZ subnet)
auto enp8s0
iface enp8s0 inet static
address 10.153.0.1
netmask 255.255.255.0
broadcast 10.153.0.255
# Trust Interface (Gateway for trusted local subnet)
auto enp9s0
iface enp9s0 inet static
address 10.152.0.1
netmask 255.255.255.0
broadcast 10.152.0.255
# TOR Interface (Gateway for clients routed through Tor)
auto enp12s0
iface enp12s0 inet static
address 10.254.0.1
netmask 255.255.255.0
broadcast 10.254.0.255Operational Conclusion
With this setup, the firewall VM becomes the sole default gateway for machines physically connected to VLANs 50, 100, and 150. Any packet destined for the internet must pass through the virtual firewall, which decides whether to perform NAT, forward the traffic in the clear, or redirect it transparently through the Tor network.
The Debian hypervisor remains silent and safe from external scanning or local compromise. Hardened OPSEC, minimal effort.
[May the Talpone be with you]
Discussion and support channel. To discuss configurations or other topics, join us on fluxer.dreadful.work.

