LibVirt - hpaluch/hpaluch.github.io GitHub Wiki

Common tips for LibVirt

LibVirt is universal virtualization layer - it supports several hypervisors (QEMU+KVM, Xen, ...) and even containers (called LXC however it uses different toolset for these).

Note: because libvirt started as wrapper on Xen it uses (sometimes confusing) Xen terms - for example 'destroy' normally means "stop" or "shutdown" but Not delete... Similarly create means start (fortunately there is now also start command)

Installation under Fedora 40:

$ sudo dnf install libvirt-daemon-kvm libvirt-daemon-config-network libvirt-client-qemu

$ sudo dnf install virt-manager  # pouplar GUI for LibVirt

# ensure that creepyware geoclue2 is masked:
$ sudo systemctl mask --now geoclue
$ sudo rm -f /etc/xdg/autostart/geoclue-demo-agent.desktop

If you plan to use virt-manager remotely via SSH X11 Forwarding you have to install these packages on Server (with virt-manager):

# required
$ sudo dnf install xorg-x11-xauth

# optional - for testing
$ sudo dnf install xclock xterm

Also on your SSHd server enable X11 forwarding, for example, create /etc/ssh/sshd_config.d/99-local.conf with contents:

X11Forwarding yes

And restart SSHd with systemctl restart sshd

Also ensure that your SSH client requests X11 forwarding, here is excerpt from my ~/.ssh/config

Host f40lvm-s350
        HostName 192.168.0.X
        User USERNAME
        IdentityFile ~/.ssh/MY_SSH_KEY
        IdentitiesOnly yes
        HostKeyAlias f40lvm-s350
        ForwardX11 yes

Now try ssh connection from your client using alias, in my example: ssh f40lvm-s350 And try some X11 application, for example xclock (use Ctrl-C in terminal to quit)

But before running virt-manager please read following chapter:

Connect to system

By default when you run any libvirt command it will use your private User connection (called "session"). So all networks and disks and VMs will be available to you only (and data stored under $HOME/.local)

To use always shared global (called system) connection you have to:

  1. Add yourself to libvirt group using:
    sudo /usr/sbin/usermod -G libvirt -a $USER
  2. Create file ~/.config/libvirt/libvirt.conf with following content:
    # see https://listman.redhat.com/archives/libvirt-users/2018-October/msg00067.html
    uri_default = "qemu:///system"

On Fedora I have to enable and start "libvirtd legacy service" with:

$ sudo systemctl enable --now libvirtd

To avoid virsh client error:

Failed to connect socket to '/var/run/libvirt/virtqemud-sock': No such file or directory

Remember that you should logout and login to ensure that you are member of group libvirt. Then you can for example try listing networks or pools:

$ virsh net-list

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

$ virsh pool-list

 Name   State   Autostart
---------------------------

If there is no pool you can follow guide from Ubuntu MAAS KVM. Run as you (non-privileged user)

virsh pool-define-as default dir - - - - "/var/lib/libvirt/images"
virsh pool-autostart default
virsh pool-start default
virsh pool-list

   Name      State    Autostart
  -------------------------------
   default   active   yes

virsh pool-dumpxml default

   ...

Logging DNS queries

At least when using NAT network we can pass option log-queries to dnsmasq that is used to provide both DHCP and DNS server for NAT network.

I followed various sources including: https://serverfault.com/a/1017645

Here is diff of default network - using virsh net-edit default or using virsh net-dumpxml default:

diff -u default.xml default-log-queries.xml
--- default.xml	2024-07-01 19:15:27.965334382 +0200
+++ default-log-queries.xml	2024-07-01 19:28:14.214143882 +0200
@@ -1,4 +1,4 @@
-<network connections='1'>
+<network xmlns:dnsmasq='http://libvirt.org/schemas/network/dnsmasq/1.0'>
   <name>default</name>
   <uuid>2c9b8477-9b7c-4ca5-8c20-f1fbcc7df3c3</uuid>
   <forward mode='nat'>
@@ -14,5 +14,8 @@
       <range start='192.168.100.128' end='192.168.100.254'/>
     </dhcp>
   </ip>
+  <dnsmasq:options>
+    <dnsmasq:option value='log-queries'/>
+  </dnsmasq:options>
 </network>

And then you have to restart network using (scary) destroy and start:

virsh net-destroy default
virsh net-start default

You can also peek content of /var/lib/libvirt/dnsmasq/default.conf if there is your option (log-queries in our case).

After restart you can try on Host:

journalctl -u libvirtd -f

And boot any VM that uses NAT network under LibVirt.

Create private IPv4 Network

I often use private isolated network in LibVirt to avoid mixing copy of production workload (in test VM) with real production (for example, GitLab CE with active push mirrors would screw mirror targets!). To create private IPv4 network we can mostly follow: https://libvirt.org/formatnetwork.html#isolated-network-config

Examples below was tested on:

  • Host: openSUSE LEAP 15.6
  • Guest (VM): Ubuntu 24.04 LTS

Here is full definition of LibVirt network named private stored in net-private.xml with contents:

<network xmlns:dnsmasq='http://libvirt.org/schemas/network/dnsmasq/1.0'>
  <name>private</name>
  <bridge name="virbr9"/>
  <domain name='example.com' localOnly='yes'/>
  <dns>
    <host ip='10.99.99.20'>
      <hostname>vm1</hostname>
    </host>
  </dns>
  <ip address="10.99.99.1" netmask="255.255.255.0">
    <dhcp>
      <range start="10.99.99.100" end="10.99.99.200"/>
      <host mac='10:20:30:40:50:60' name='vm1.example.com' ip='10.99.99.20'/>
    </dhcp>
  </ip>
  <ip family="ipv6" address="fd00:0609:c0a8:0a00::1" prefix="64"/>
  <dnsmasq:options>
    <dnsmasq:option value='log-queries'/>
    <dnsmasq:option value='log-facility=/var/log/dnsmasq-private-net.log'/>
  </dnsmasq:options>
</network>

WARNING!

  • if you will use above log-facility to log to file you have to prepare log file that is compatible with AppArmor policy - in openSUSE it is in /etc/apparmor.d/usr.sbin.dnsmasq

    /var/log/dnsmasq*.log w,
    
  • so we must use log name matching /var/log/dnsmasq*.log

  • and set proper permissions:

    sudo chown dnsmasq:nogroup /var/log/dnsmasq-private-net.log
    sudo touch /var/log/dnsmasq-private-net.log
  • WARNING! AppArmor will NOT report denied access!

To create, start and autostart this private network at 10.99.99.1/24 you have to run:

xmllint --noout --relaxng /usr/share/libvirt/schemas/network.rng net-private.xml
virsh net-define net-private.xml
virsh net-start private
virsh net-autostart private

To be able to install packages I will also install Proxy under Host (openSUSE LEAP):

sudo zypper in tinyproxy

Next I changed its configuration /etc/tinyproxy/tinyproxy.conf - diff:

23c23
< Port 8888
---
> Port 3128
30c30
< #Listen 192.168.0.1
---
> Listen 10.99.99.1
96c96
< #LogFile "/var/log/tinyproxy/tinyproxy.log"
---
> LogFile "/var/log/tinyproxy/tinyproxy.log"
120c120,121
< LogLevel Info
---
> #LogLevel Info
> LogLevel Connect
190c191
< MaxClients 100
---
> MaxClients 20
201a203
> Allow 10.99.99.0/24
285c287
< #ConnectPort 443
---
> ConnectPort 443

Enable and start tinyproxy with:

sudo systemctl enable --now tinyproxy
# test
netstat -tln | fgrep 3128

  tcp        0      0 10.99.99.1:3128         0.0.0.0:*               LISTEN

# test proxy access - we must use Private network IP, because tinyproxy listens only there
curl -fsS -D - -o /dev/null -x 10.99.99.1:3128 http://www.linux.cz

  HTTP/1.1 200 OK
  Via: 1.1 tinyproxy (tinyproxy/1.11.2)
  Date: Wed, 04 Dec 2024 09:32:28 GMT
  Server: Apache

Now we can create sample VM (I will use Ubuntu 24.04 LTS) - remember to assign it network with name "private".

Once VM boots verify its IP address - must start with 10.99.99. otherwise you are using wrong network:

$ ip -br -4 a | fgrep -w UP

eth0             UP             10.99.99.193/24 metric 100

Step specific for openSUSE LEAP 15.6:

  • I have to allow access to port 3128/tcp to zone libvirt using:
firewall-cmd --zone=libvirt --add-port=3128/tcp --permanent
firewall-cmd --reload
  • WARNING! Even when I have set firewall-cmd --set-log-denied=unicast there is nothing logged by firewall!

Transparent Proxy access for APT:

  • WARNING! Do not set globally proxy variables - there is risk that some system component will misuse it.

  • from https://askubuntu.com/a/257296, to enable just APT proxy access, create file /etc/apt/apt.conf.d/99zz_proxy.conf with contents:

    Acquire::http::Proxy "http://10.99.99.1:3128";

Now inside Ubuntu guest you can run sudo apt-get update and ensure that there is NO single line with Ign prefix (Ignored). All should be Get: or Hit:

Now you can try to install some program, for example sudo apt-get install ncdu (neat tool to show directory usages interactively).

To use proxy (temporarily) with other commands I created script /usr/local/bin/http_proxy.sh with contents:

#!/bin/bash
set -euo pipefail
p=10.99.99.1:3128

[ $# -gt 0 ] || {
	echo "Usage: $0 command args ..." >&2
	exit 1
}
set -x
http_proxy=$p https_proxy=$p "$@"
exit 0

Example usage:

http_proxy.sh curl -fsS -D - -o /dev/null https://www.google.com
⚠️ **GitHub.com Fallback** ⚠️