vagrant-libvirt and Bridged Networking

Published on

Once I had a way to install virtual machines on my Ubuntu home lab server, my next goal was to be able to directly SSH into them from my laptop. By default, the Vagrant-installed libvirt virtual machines all connect to a virtual subnet. In order to be able to connect from other computers in my home, I needed to set up Bridged Networking.

Updating Netplan Configuration

In order to set up Bridged Networking, I first had to update how my server configures its network. Ubuntu 20.04 Server uses Netplan, so I had to update /etc/netplan/00-installer-config.yaml. While I was at it, I converted from using DHCP (with a static lease defined by my router) to using a static IP instead.

Old config:


# This is the network config written by 'subiquity'
network:
  ethernets:
    enp5s0:
      dhcp4: true
  version: 2

Updated config:


network:
  ethernets:
    enp5s0:
      dhcp4: false
      dhcp6: false

  bridges:
    br0:
      interfaces: [ enp5s0 ]
      addresses: [ 192.168.1.22/24 ]
      gateway4: 192.168.1.1
      mtu: 1500
      nameservers:
        addresses: [ 192.168.1.1 ]
      parameters: 
        stp: true
        forward-delay: 4
      dhcp4: no
      dhcp6: no

  version: 2

The updates were applied using netplan appl, and my ethernet card was bridged to the br0 bridge device.

Updating ufw firewall rules

I spent many hours trying many things to get vagrant-libvirt, and also VMs created directly using virt-install, to be able to access the internet via the bridge network. It turned out that the first thing I needed to do was update my firewall settings. I have a firewall set up using ufw to only allow SSH and a couple of specific application ports, and to deny everything else. What I hadn't realized was that the settings also prevent the bridge from properly forwarding between the bridge interface and the physical one.

I didn't want to completely disable the firewall, even though that was the simplest solution to allow the VMs to be able to access the internet via the bridge. What I found worked best for my configuration was to add a rule to /etc/ufw/before.rules to allow the bridge interface to talk with the physical one:


-I FORWARD -m physdev --physdev-is-bridged -j ACCEPT

  • -I FORWARD means to insert a rule into the FORWARD chain
  • -m physdev means to match with the physdev extension1
  • --physdev-is-bridged means “Matches if the packet is being bridged and therefore is not being routed.”
  • -j ACCEPT means to accept (allow) the packet

Bridging vagrant-libvert

According to the vagrant-libvirt README.md documentation, one should be able to set up a public (bridge) connection by defining the network as public in the Vagrantfile:


  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  config.vm.network "public_network",
      :dev => "br0",
      :mode => "bridge",
      :type => "bridge"

Note, that while this does set up a second virtual network device within the VM, that device will not be bound to an IPv4 address. Thanks to an example in this GitHub issue, I realized that I could assign a static IP address simply by adding in an :ip parameter:


  config.vm.network "public_network",
      :dev => "br0",
      :mode => "bridge",
      :type => "bridge",
      :ip => "192.168.1.200"

References


  1. https://ipset.netfilter.org/iptables-extensions.man.html ↩︎