Vagrant, Libvirt, and nftables

Monday, August 17, 2020

Vagrant and libvirt work just fine together when using the standard iptables firewalling, but I wanted to move to the simpler (IMO) nftables. However, getting everything working properly was no simple task, so here is the process of how I got it all to work (finally).

In Arch Linux first you need to install both nftables and iptables-nft. The iptables-nft package allows libvirt to write rules using both the iptables and ebtables syntax. Without this nothing will work at all.

Next, get your base set of firewall rules for nftables in place. On my machine I only want SSH open to the world. The caveat here is that you need a rule for the virbr0 interface to accept traffic as well. Your interface might be named differently, but this is the interface that libvirt created for your NAT network. Here is my current nftables.conf:

#!/usr/bin/nft -f
# ipv4/ipv6 Simple & Safe Firewall
# you can find examples in /usr/share/nftables/

table inet filter {
  chain input {
    type filter hook input priority 0;

    # allow established/related connections
    ct state {established, related} accept

    # early drop of invalid connections
    ct state invalid drop

    # allow from loopback
    iifname lo accept

    # allow icmp
    ip protocol icmp accept
    meta l4proto ipv6-icmp accept

    # allow ssh
    tcp dport ssh accept

    # allow libvirt nat
    iif virbr0 accept

    # everything else
    reject with icmpx type port-unreachable
  }
}

# vim:set ts=2 sw=2 et:

Next, start the nftables systemd service. Check that your rules have been applied with nft list ruleset. You should see a very small set of rules that matches your basic ruleset:

table inet filter {
	chain input {
		type filter hook input priority filter; policy accept;
		ct state { established, related } accept
		ct state invalid drop
		iifname "lo" accept
		ip protocol icmp accept
		meta l4proto ipv6-icmp accept
		tcp dport 22 accept
		iif "virbr0" accept
		reject
	}
}

Now, start the libvirtd systemd service. When checking nft list ruleset you should see a much more robust set of rules, because libvirt has now been able to write to your firewall for the NAT forwarding. I’m not going to show the output here, but it will be very noticable that there are changes to your ruleset.

For spinning up machines just using virsh or virt-manager you are now done and they will get IP addresses and work as you would expect them to. However, for Vagrant your machines will hang when trying to get an IP address. This is because Vagrant wants to create it’s own “management” network which has not been added to the firewall ruleset. The easiest way around this is to add a section to your Vagrant configuration telling it to use your normal network as the “management” network:

  config.vm.provider "libvirt" do |libvirt|
    libvirt.cpus = 4
    libvirt.memory = 4096

    libvirt.management_network_name = "default"
    libvirt.management_network_address = "192.168.100.0/24"
  end

In the example above my libvirt network name is “default”. If you don’t know your network name you can find it by running virsh net-list --all:

 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   no          yes

At this point everything should work as intended.

devopslibvirtvagrantlinuxnftables

CentOS and Newer Versions of Python

CentOS 8 Software RAID Monitoring