In a previous article I showed how to use a bridged network to give a KVM Guest access to the same network as the Host.
Now we will go over NAT networks which allow the KVM Guest to reach outside the network, but hosts from the outside cannot reach the guest directly. This opens up the amount of network space available and provides application isolation, and could also offer a level of security if a firewall was introduced.
Ensure IP forwarding is enabled
Before going forward, make sure IP forwarding is enabled on the host.
# this needs to be "1" cat /proc/sys/net/ipv4/ip_forward
If it is not enabled, then set it on the fly using:
sudo sysctl -w net.ipv4.ip_forward=1
And then enable it permanently by editing the “/etc/sysctl.conf” file with the following key:
net.ipv4.ip_forward=1
Create libvirt NAT network
First check the currently defined networks recognized by libvirt. Use the following command:
virsh net-list --all
To create a new NAT network, create an xml file “nat223.xml”. Use either the physical network or bridge name connected to the physical network for the “dev” attribute of the <forward> element.
cat <<EOF > nat223.xml <network> <name>nat223</name> <forward mode='nat' dev='br0'/> <bridge name='virbr223' stp='on' delay='2'/> <ip address='192.168.223.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.223.141' end='192.168.223.254'/> <host name='myclone3' ip='192.168.223.143'/> </dhcp> </ip> </network> EOF
If you do not set the “dev” attribute to either the physical network device or bridge, then you will get unexpected behavior because it may take shortcuts and forward traffic directly from one KVM network to another using internal interfaces. By using the physical interface or bridge connected to the physical interface, traffic will be redirected by the outermost network level on the host and ensure the iptables rules are applied correctly
Then create the network using these commands:
# create libvirt NAT network (net-create is for transient) virsh net-define nat223.xml virsh net-start nat223 virsh net-autostart nat223 # NAT state should be active, autostart, and persistent virsh net-list --all
As described in the libvirt firwall documentation, rules are created in iptables to support this new network.
# virtual bridge virbr223 created ip a | grep 223 # iptables FORWARD rules for nat223 sudo iptables -L FORWARD -nv --line-number | grep virbr223 # iptables NAT POSTROUTING chain sudo iptables -t nat -L -n -v --line-number | grep 223
Create VM using NAT network
If you are creating guest OS from the console using virt-install, then simply replace the default network specifier with the name of the “nat223” network like below.
--network network=nat223
If using the virt-manager GUI, the network can be selected from the network source pulldown.
Once the guest VM is up and configured, you can run the command “ip a” and see that the new host now has an IP address randomly assigned from the DHCP pool defined in nat223.xml
This new guest VM should be able to reach out the public network, but no one will be able to initiate connections back to you (not even the from the Host VM). Guest VM in the same 192.168.223.0/24 NAT can communicate freely with one another.
Create VM, assigned specific IP from DHCP Pool
Notice the nat223.xml has a snippet that looks like:
<dhcp> <range start='192.168.223.141' end='192.168.223.254'/> <host name='myclone3' ip='192.168.223.143'/> </dhcp>
If you create a guest VM with the name “myclone3”, then instead of getting a random IP address from the DHCP pool, it will instead receive the one specified “192.168.223.143”. Using virt-install, the parameter used would be:
--name=myclone3
You can also specify by MAC address using the attribute “mac” in the <host> element, but then you must start up the guest first so the MAC can be randomly generated. So it is easier to just use the name.
DHCP Leases
To view the current DHCP leases on a network, use this command:
virsh net-dhcp-leases nat223
Isolation from other NAT
Although the Host OS and public networks cannot initiate connections back to guests in the NAT network, take note that KVM guests in other NAT do have the ability to reach it.
REFERENCES
libvirt docs, network definition
libvirt docs, network filters and rules, IPtables differences for isolated/nat/routed
NAT KVM guest, forward ports with iptables for external access
libvirt wiki, VirtualNetworking docs
NAT bridge with DCHP and also set of static IP
libvirt xml definition for network section
libvirt, use nat, routed, isolated, pre-existing host bridge, macvtap
Disable hardware checksums when using pfsense with virtio
DMZ virtualization with vmware infra, PDF
KVM hook for port forwarding to NAT guest
NOTES
If you wanted one NAT network to have access to other NAT, that would require adding an extra iptables nat rule.
# get rule numbers sudo iptables -t nat -L -n -v --line-number # delete specific rule sudo iptables -t nat -D POSTROUTING <number>
Watching packet stats on FORWARD or POSTROUTING chain
watch -n 1 "sudo iptables -L -v -n --line-number | grep FORWARD -A20" watch -n 1 "sudo iptables -t nat -L -n -v --line-number"
Making sure each virtual bridge is associated with only single vnet
sudo apt-get install bridge-utils -y brctl show
Check arp table
sudo apt-get install arping -y arp -n
Show traffic on vnet from KVM host
brctl show tcpdump -i vnet<N> -n