You can use a bridge without enslaving any of your physical Ethernet interfaces on the VM host to it.
Say we stick with the choice of subnet 10.0.2.0/24
(which is NOT necessary):
# ip l add natbr0 type bridge
# ip a add 10.0.2.1/24 dev natbr0
Then create the following file:
$ echo 'allow natbr0' | sudo tee /etc/qemu/bridge.conf
allow natbr0
Then start qemu with the e.g. -nic bridge,br=natbr0
or -netdev bridge,br=natbr0,id=nb0 -device virtio-net,netdev=nb0
, which will tap
your VM to the bridge in a dynamic manner (i.e. the tap
interface will be removed once the the VM is shut down).
You'll need to configure static IP on the VM as well:
# ip a add 10.0.2.2/24 dev ens3
# ip r add default via 10.0.2.1
Unless you also set up a DHCP server (with e.g. dnsmasq) on the host. Don't forget to configure the DNS server to use inside the VM as well.
Note that VMs that make use of the same bridge can communicate with each other unless you block such communication by some means (e.g. ebtables).
The default
route (and DNS server to use) are only necessary if you want the VM to be able to reach the "outside". If you only need it to be able to communicate with the VM host, you should skip the second command and can stop reading. (Well, read the P.S.
)
It would be probably be best to configure e.g. dnsmasq on the host to be a DNS forwarder if you do not want to use a specific "public" DNS server in the VM, although using DNAT to forward DNS requests to e.g. 192.168.1.1
should work for basic ones.
Then you'll need to enable IP forwarding:
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
If you want to avoid IP forwarding from/to certain network interface (e.g. tun0
) for security reasons, you'll need to set up a firewall. For example:
# iptables -A FORWARD -i tun0 -j DROP
# iptables -A FORWARD -o tun0 -j DROP
Since you have (VPN) tunnel routes that practically overrides the default
route, the traffics from the VM to the Internet will go into the tunnel as well (unless you added the example rules above). If you want the traffics to go e.g. via your router, you'll need policy routing. For example:
# ip ru add iif natbr0 lookup table 123
# ip r add 192.168.1.1 dev wlan0 table 123 # probably optional
# ip r add default via 192.168.1.1 table 123
You can also prevent your VMs from being able to reach your LAN hosts:
# iptables -A FORWARD -i natbr0 -d 192.168.1.0/24 -j DROP
Make exceptions (note the -I
) if you are going to redirect DNS requests to your router:
# iptables -I FORWARD -i natbr0 -d 192.168.1.1 -p tcp --dport 53 -j ACCEPT
# iptables -I FORWARD -i natbr0 -d 192.168.1.1 -p udp --dport 53 -j ACCEPT
Finally, configure iptables to perform SNAT dynamically (as per the outbound interface) for your VM subnet:
# iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -j MASQUERADE
Note that this is NOT intended to and will not exactly prevent certain traffics from the "outside" (your physical LAN hosts or the Internet; the VM host does not count) to be able to reach your VMs. It merely breaks the communication as a side effect when the source address of replying traffics from the VMs are changed before their forwarded out. For proper isolation, you will need (additional) appropriate rules in the FORWARD
chain. Consider having a "stateful" setup there if you have such need.
Additionally, you can redirect DNS requests to the host from the VMs to your router:
iptables -t nat -A PREROUTING -d 10.0.2.1 -p udp --dport 53 -j DNAT --to-destination 192.168.1.1
iptables -t nat -A PREROUTING -d 10.0.2.1 -p tcp --dport 53 -j DNAT --to-destination 192.168.1.1
Which will more or less allow you to use 10.0.2.1
as the DNS server in the VM.
P.S. All the manipulations above (except the creation of / write to /etc/qemu/bridge.conf
) are volatile, i.e. they will be gone once you reboot (unless your distro does something silly). I'm not going to dive into how you can make them persistent, since there are different ways/approaches and it can be distro-specific.