Audio qube

An audio qube acts as a secure handler for potentially malicious audio devices, preventing them from coming into contact with dom0
(which could otherwise be fatal to the security of the whole system).
It thereby mitigates some of the security risks of using USB
devices
and Bluetooth devices.
Nonetheless, we strongly recommend carefully reading the security warning on USB input
devices
devices before proceeding.

With an audio qube, many kinds of devices can be used (that previously were either unsafe or impossible to use in dom0):

  • Jack
  • HDMI/DisplayPort/…
  • USB audio devices
  • Bluetooth

Manual setup

In this section, we will explain how to manually set up an audio qube.
A lot of alternatives configurations are also possible.

Creating the qubes

  1. Create a template named ‘audio-template’ – You can clone ‘fedora-XX’ template (or a fedora minimal template) for that –
  2. Then create an appvm named ‘audio-app’ and mark it as a disposablevm template
  3. Finally, create a disposablevm named ‘sys-audio’

Note: By making “sys-audio” disposable, volume configuration will not be
persistent.

See below, some screenshot of this configuration

Result of `qvm-prefs audio-app`
audiovm               D  sys-audio
autostart             D  False
backup_timestamp      -  1689304025
debug                 D  False
default_dispvm        D  fedora-dvm
default_user          D  user
dns                   D  
gateway               D  
gateway6              D  
guivm                 D  dom0
icon                  D  templatevm-gray
include_in_backups    D  True
installed_by_rpm      D  False
ip                    D  
ip6                   D  
kernel                D  6.4.8-1.fc37
kernelopts            D  swiotlb=2048
keyboard_layout       D  fr+oss+
klass                 D  AppVM
label                 -  gray
mac                   D  00:16:3e:5e:6c:00
management_dispvm     D  default-mgmt-dvm
maxmem                D  4000
memory                D  400
name                  -  audio-app
netvm                 -  None
provides_network      -  False
qid                   -  50
qrexec_timeout        D  60
shutdown_timeout      D  60
start_time            D  
stubdom_mem           U
stubdom_xid           D  -1
template              -  audio-template
template_for_dispvms  -  True
updateable            D  False
uuid                  -  62ad295e-cfdc-4cbd-a5a7-09284dff87d9
vcpus                 D  2
virt_mode             D  pvh
visible_gateway       D  
visible_gateway6      D  
visible_ip            D  
visible_ip6           D  
visible_netmask       D  
xid                   D  -1
Result of `qvm-prefs audio-template`
audiovm             D  sys-audio
autostart           D  False
backup_timestamp    -  1689304024
debug               D  False
default_dispvm      D  fedora-dvm
default_user        D  user
dns                 D  
gateway             D  
gateway6            D  
guivm               D  dom0
icon                D  templatevm-black
include_in_backups  D  True
installed_by_rpm    D  False
ip                  D  
ip6                 D  
kernel              D  6.4.8-1.fc37
kernelopts          D  swiotlb=2048
keyboard_layout     D  fr+oss+
klass               D  TemplateVM
label               -  black
mac                 D  00:16:3e:5e:6c:00
management_dispvm   D  default-mgmt-dvm
maxmem              D  4000
memory              D  400
name                -  audio-template
netvm               D  None
provides_network    D  False
qid                 -  49
qrexec_timeout      D  60
shutdown_timeout    D  60
start_time          D  
stubdom_mem         U
stubdom_xid         D  -1
updateable          D  True
uuid                -  43dfeca9-41b6-4b85-b462-2c566cc5da1f
vcpus               D  2
virt_mode           D  pvh
visible_gateway     D  
visible_gateway6    D  
visible_ip          D  
visible_ip6         D  
visible_netmask     D  
xid                 D  -1
Result of `qvm-prefs sys-audio`
audiovm             D  sys-audio
auto_cleanup        D  False
autostart           -  True
backup_timestamp    -  1689304025
debug               D  False
default_dispvm      D  audio-app
default_user        D  user
dispid              -  2229
dns                 D  
gateway             D  
gateway6            D  
guivm               D  dom0
icon                D  dispvm-gray
include_in_backups  D  True
installed_by_rpm    D  False
ip                  D  
ip6                 D  
kernel              D  6.4.8-1.fc37
kernelopts          D  
keyboard_layout     D  fr+oss+
klass               D  DispVM
label               -  gray
mac                 D  00:16:3e:5e:6c:00
management_dispvm   D  default-mgmt-dvm
maxmem              -  0
memory              D  400
name                -  sys-audio
netvm               -  None
provides_network    -  False
qid                 -  51
qrexec_timeout      D  60
shutdown_timeout    D  60
start_time          D  1693413928.06
stubdom_mem         U
stubdom_xid         D  2
template            -  audio-app
updateable          D  False
uuid                -  9422a856-7b81-464d-9fd5-987ef2cf99f7
vcpus               D  2
virt_mode           -  hvm
visible_gateway     D  
visible_gateway6    D  
visible_ip          D  
visible_ip6         D  
visible_netmask     D  
xid                 D  1

Installing the required packages

In ‘audio-template’, install the required packages:

sudo dnf install -y pipewire-qubes qubes-audio-daemon pavucontrol qubes-core-admin-client qubes-usb-proxy alsa-utils

Optional package, software to configure sound effect, like noise cancellation:

sudo dnf install easyeffects

If you want to be able to use Bluetooth devices, you also need to install the
following packages:

sudo dnf install -y blueman
Note for `debian-12-minimal` templates

Debian Minimal requires the libspa-0.2-bluetooth for bluetooth to work with Pipewire.

And the required drivers for your Bluetooth hardware, for example, some hardware
will need this package:

sudo dnf install -y linux-firmware

If you want to have a systray to control the sound, you can install this package

sudo dnf install -y pasystray

Configuring PCI passthrough

Then we need to passthrough the PCI devices we want to ‘sys-audio’.
It needs to be configured as HVM, without memory balancing.

Configuring sys-audio as the default audiovm

In dom0 execute the following command:

qubes-prefs default_audiovm sys-audio

Testing sound

Note: qubes that were running before the moment the audiovm has started will not
have sound. See Troubleshooting section.

Now you can check that sound can be played in ‘sys-audio’ by executing the
following command in ‘sys-audio’.

aplay /usr/share/sounds/alsa/Noise.wav

In the screenshot below, I passed my audio jack device to
‘sys-audio’. The name of these devices are different on every hardware.

audiovm-devices

Note: You could permanently assign a usb device to a qube using the dom0 command qvm-device. Example : qvm-device usb attach --persistent sys-audio sys-usb-1:2-6

You can check what hardware is available using ‘pavucontrol’:

See the ‘Troubleshooting’ part if you have trouble with USB audio devices.

Bluetooth configuration

Nothing specific to QubesOS to configure your Bluetooth device, so the
explanation will be minimal.

  1. In ‘sys-audio’ launch ‘blueman-manager’
  2. Pair with your Bluetooth hardware
  3. Activate the audio profiles

When you configure a Bluetooth device, the configuration files are stored as root in /var/lib/bluetooth. If you want the device to be permanently known by the audiovm (even after reboot), you need to either copy this folder to the template qube, or have any other means of deploying back the configuration that have been saved to /var/lib/bluetooth

Configuring services

Add the custom service ‘audiovm’ using QubesManager for your audio qube.

Adding this service will launch at startup the following command:

qvm-start-daemon --all --watch

Bluetooth service

Additionally, if you intend to use bluetooth services, you probably want to also enable “blueman” service

Patch the source code | HELP WANTED

In the audio template, you need to patch the source code.
Modify the file “/usr/lib/python3.12/site-packages/qubesadmin/tools/qvm_start_daemon.py”

Change the line

events = qubesadmin.events.EventsDispatcher(args.app)

to

events = qubesadmin.events.EventsDispatcher(args.app, enable_cache=False)

HELP WANTED
This should not be necessary, either there is an issue in this guide, or there is a kind of issue in how the caching mecanisme work for Qubes OS.
If you have technical skills and time, this point need to be fixed properly.

Configuring policy

Finally, we need to create a policy file to allow the required communications between
‘sys-audio’ and the other qubes.
In dom0, create the file ‘/etc/qubes/policy.d/50-sys-audio.policy’ with the
following content:

admin.Events          *   sys-audio     @adminvm                allow   target=dom0

admin.Events          +domain-stopped    sys-audio     @tag:audiovm-sys-audio                allow   target=dom0
admin.Events          +domain-shutdown   sys-audio     @tag:audiovm-sys-audio                allow   target=dom0
admin.Events          +domain-start	 sys-audio     @tag:audiovm-sys-audio                allow   target=dom0
admin.Events          +connection-established      sys-audio     @tag:audiovm-sys-audio                allow   target=dom0

admin.vm.CurrentState *   sys-audio     @tag:audiovm-sys-audio                allow   target=dom0
admin.vm.List         *   sys-audio     @tag:audiovm-sys-audio                allow   target=dom0
admin.vm.CurrentState *   sys-audio     @adminvm                allow   target=dom0
admin.vm.List         *   sys-audio     @adminvm                allow   target=dom0

admin.vm.property.Get               +audiovm      sys-audio     @tag:audiovm-sys-audio  allow   target=dom0
admin.vm.property.Get               +xid          sys-audio     @tag:audiovm-sys-audio  allow   target=dom0
admin.vm.feature.CheckWithTemplate  +audio        sys-audio     @tag:audiovm-sys-audio  allow   target=dom0
admin.vm.feature.CheckWithTemplate  +audio-model  sys-audio     @tag:audiovm-sys-audio  allow   target=dom0
admin.vm.feature.CheckWithTemplate  +supported-service.pipewire sys-audio @tag:audiovm-sys-audio allow target=dom0
admin.vm.feature.CheckWithTemplate  +audio-low-latency sys-audio @tag:audiovm-sys-audio allow target=dom0
admin.vm.property.Get               +stubdom_xid  sys-audio	@tag:audiovm-sys-audio	allow	target=dom0

# Once the caching issue mentionned in the "Patch the source code | HELP WANTED", the line below could be reactivated
#admin.vm.property.GetAll * sys-audio   @tag:audiovm-sys-audio  deny   notify=no

Once this is done, you can restart your ‘sys-audio’ qube.
If everything has been configured correctly, you should see the active qube
configured to use ‘sys-audio’ in the ‘pavucontrol’ interface of ‘sys-audio’.

Disabling audio for qubes that doesn’t need it

You should disable the audiovm for qubes that doesn’t need it.
For example “sys-net” “sys-firewall” and others qubes that will never need audio.

Case 1: Most of your qubes need to have audio

By default, configure all qubes to use ‘sys-audio’ as the audiovm

qubes-prefs default_audiovm sys-audio

Then remove the audiovm from the qubes that doesn’t need audio

qvm-prefs sys-net audiovm ''
...

Case 2: Most of your qubes doesn’t need to have audio

By default, configure all qubes to use nothing as the audiovm

qubes-prefs default_audiovm ''

Then add the audiovm for the qubes that need audio

qvm-prefs sys-net audiovm 'sys-audio'
...

Optional step: Noise suppression

In “audio-app” you can configure noise suppression: GitHub - werman/noise-suppression-for-voice: Noise suppression plugin based on Xiph's RNNoise
Audio recording is much better, at the cost of a bit of cpu consumption

Important note:

Most user will never encounter such a case, but for people that have a LOT of qubes running, it is important to properly configure the audiovm property, it need to be empty for qubes that will never use audio

There is a maximum of ~19 qubes that can be running simultaneously without using audio (but with a audiovm configured). After that number, when a new qube is created, sys-audio will stop working. (xenstore quota issue, audiovm - Argument list too long - no sound for new qubes · Issue #8966 · QubesOS/qubes-issues · GitHub )

Microphone note:

You have to attach the device named “dom0:mic” to the qube you want to be able to record your microphone input

tata

Shortcuts

In your desktop environment, you can configure shortcuts.
Turn up the volume:

qvm-run sys-audio 'amixer sset Master 2%+'

Turn down the volume:

qvm-run sys-audio 'amixer sset Master 2%-'

Toggle the audio between muted and un-muted

qvm-run sys-audio 'amixer sset Master toggle'

Additional technical information

In ‘sys-audio’, the system needs to find all the qubes of the system (and that
is configured to use ‘sys-audio’ as an audiovm) and create
the communication channel to receive the audio information from those qubes.
The software responsible for that can be launched manually like that:

qvm-start-daemon --all --watch

In any qube, you can find the XID of your configured audio VM with this command:

qubesdb-read -w /qubes-audio-domain-xid

In any qube, pulseaudio is responsible for trying to send the audio stream to
the audiovm.
It is done with a specific pulseaudio module ‘vchan-sink’.
Depending on some specific configurations, you could create (or any other reason),
you could want to manually configure this module to define the audiovm to try to
use. This is done with the ‘domid’ parameter of the module.
For example, in the file ‘/etc/pulse/qubes-default.pa’:

load-module module-vchan-sink domid=XID_OF_THE_AUDIO_QUBE

For pulseaudio setup: In any qube, to start pulseaudio with the ‘vchan – *’ modules, run:

start-pulseaudio-with-vchan

For pipewire setup: In any qube, to start pipewire with the ‘vchan – *’ modules, run:

systemctl --user restart pipewire

You can have multiple audiovm. You can configure each qubes to use a specific
audiovm:

qvm-prefs QUBE_NAME audiovm
qvm-prefs QUBE_NAME audiovm MYAUDIOVM

Special cases of non-linux HVM.

Support have been added by the QubesOS team, so no additional configuration is needed. I haven’t yet personnally tested it. If it doesn’t work, the old way of doing that is below

Old way of doing that

Windows qubes don’t support audiovm at the moment, and are hardwired to dom0 for
audio processing.

Below, example to how to add audiovm for windows qubes.

First, you need to manually edit the xen template file in dom0
“/usr/share/qubes/templates/libvirt/xen.xml”

Above the line

<emulator 

Add the following content:

{% if vm.audiovm %}
 {% set audiovm_xid = vm.audiovm.xid %}
{% else %}
 {% set audiovm_xid = 0 %}
{% endif %}

In the rest of the file, you will find two occurrences of lines starting by “cmdline=”,
append the following content to these two lines (before the closing double-quote)

 -qubes-audio:audiovm_xid={{ audiovm_xid }}

Stubdom-linux-rootfs

Then, you need to modify to patch “stubdom-linux-rootfs”.
In dom0, in your home directory, execute the following bash command:

mkdir stubroot
cp /usr/libexec/xen/boot/qemu-stubdom-linux-rootfs stubroot/qemu-stubdom-linux-rootfs.gz
cd stubroot
gunzip qemu-stubdom-linux-rootfs.gz
cpio -i -d -H newc --no-absolute-filenames < qemu-stubdom-linux-rootfs
rm qemu-stubdom-linux-rootfs
nano init

After the line starting by “audio_model=”, add:

# Extract network parameters and remove them from dm_args
audio_args=$(echo "$dm_args" | sed -n '/^-qubes-audio:/p')
dm_args=$(echo "$dm_args" | sed '/^-qubes-audio:/d')

get_audio_arg() {
    echo "$audio_args" | sed -n 's/^.*[:,]'$1'=\([^,]\+\).*$/\1/p'
}

And in the rest of the file, replace

module-vchan-sink domid=0

by

module-vchan-sink domid=$(get_audio_arg 'audiovm_xid')

Save and close the file. Then run:

find . -print0 | cpio --null -ov \
--format=newc | gzip -9 > ../qemu-stubdom-linux-rootfs
sudo mv ../qemu-stubdom-linux-rootfs /usr/libexec/xen/boot/

Stubdom-linux-full-rootfs

Redo the same things but for the file “stubdom-linux-full-rootfs”

Known issues. HELP WANTED

  • Keyboard layout switching issue
  • Autoreconnect to audio vm when audio vm is restarted

Troubleshooting

USB devices

Some USB audio controller cannot be passed from a USB qube to an audio qube.
In those cases, the audio qube need to also be a USB qube to have direct
access to the PCI USB controller.
For example, this is the case for the Sennheiser GSX 1XXX.

No sound in qubes started before audiovm

This issue is that the vchan modules of the pulseaudio/pipewire daemon running in the already started qubes are
not using the correct audiovm xid (ID of the xen vm).

If you are using pulseaudio: In standard setup, restarting the pulseaudio daemon is enough. Kill the
pulseaudio process and run “start-pulseaudio-with-vchan”.

If you are using pipewire (pipewire will be the default soon if not already the case):
systemctl --user restart pipewire

In special configuration, you may need to manually set the “domid” parameter of
the vchan modules.

Salt

A community effort to have this configuration done by a salt script is available here: qusal/salt/sys-audio at main · ben-grande/qusal · GitHub

Links

Document sys-audio · Issue #8093 · QubesOS/qubes-issues · GitHub
qvm-start-daemon - audiovm - keyboard layout · Issue #8109 · QubesOS/qubes-issues · GitHub
qubes-doc/user/advanced-topics/audio-qubes.md at sys-audio · neowutran/qubes-doc · GitHub
qubes-attachment/doc at audiovm · neowutran/qubes-attachment · GitHub

35 Likes

Many thanks to this detailed and comprehensive guide!

One more question, is the part of “Special cases of non-linux HVM” upstreamable? That would benefit every user of audio vm.

1 Like

If some people try it and confirm that it work as expected, someone could send a pull request

1 Like

Thanks for this guide. I have 4.1. I got to this:
qvm-prefs default_audiovm sys-audio
and I got in response:

usage: qvm-prefs [--verbose] [--quiet] [--help] [--help-properties]
                 [--hide-default] [--get] [--set] [--default]
                 VMNAME [PROPERTY] [VALUE]
qvm-prefs: error: no such domain: 'default_audiovm'

What did I do wrong?

1 Like

wrong command, re-read the guide…

3 Likes

My bad. Thank you. I’ve never used qubes-prefs before.

Another question: is bluetooth a PCI device that needs to be attached to sys-audio?
I only have one audio device in the “Available devices” list in sys-audio Settings. And I attached connected it to sys-audio.

it is usually a USB device with a weird name. Forgot to add the screenshot

1 Like

These are my available devices. I have USB for USB-C and USB 3.2. All 3 USBs are assigned to sys-usb. Would you be able to recognize Bluetooth?

usb device showing up once you started your sys-usb, not pci device

Thanks. Do I need to connect bluetooth via the Devices Manager on every boot?

You could permanently assign it to the audiovm.
My example, in dom0:
qvm-devices usb attach --persistant sys-audio sys-usb-1:2-6

Thanks! I will test.
You may have had some typos. Here’s what I will try:
qvm-device usb attach --persistent sys-audio sys-usb-1:2-6

indeed, it is getting late :slight_smile:
ofcourse you need to adjust this part “sys-usb-1:2-6”

2 Likes

I think xen.xml will be overwritten on at least kernel update (I am absolutely not sure in which cases this happens, but it does happen), so maybe it would be a good idea to add this notice to OP.

xen.xml can be overwritten, Stubdom-linux-rootfs too, qvm_start_daemon too

I followed all the steps. Here’s a quick feedback.

I opted for the:

Because I don’t know how to patch source code.

It works! Bluetooth audio works. It has two options: high quality listening mode, and low quality headset mode.

But sys-audio does not boot on start despite having chosen this option. And I couldn’t figure out how to get the mic from the bluetooth device (headset mode) to be the main mic for the computer.

One more thing. Bluetooth device name changed from 4:10 to 2:10, so the persistent attached failed me once so far.

I don’t know how to easily do that (aka: without custom code).

You could have a script in dom0 that scan usb devices / hook the usb qubes event, and automatically attach them to sys-audio when they are detected.
Around 10 lines of bash should be enough to write a dumb implementation of that.

First of all, thank you for this great guide, @neowutran.

I wanted to know if anyone here had a similar issue. I have gone through all of the steps in the guide successfully up to the “Configuring policy” step with policy file created and audio vm rebooted. The only deifference from the guide is that I’ve applied the steps to my sys-usb qube instead of creating a new sys-audio qube. I have the sound working in my sys-usb qube and all of the other qubes use it as their audio vm, but when I try to play back any sound in any qube besides sys-usb I get the following:

$ aplay /usr/share/sounds/alsa/Noise.wav 
ALSA lib confmisc.c:855:(parse_card) cannot find card '0'
ALSA lib conf.c:5181:(_snd_config_evaluate) function snd_func_card_inum returned error: No such file or directory
ALSA lib confmisc.c:422:(snd_func_concat) error evaluating strings
ALSA lib conf.c:5181:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1334:(snd_func_refer) error evaluating name
ALSA lib conf.c:5181:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5704:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2666:(snd_pcm_open_noupdate) Unknown PCM default
aplay: main:834: audio open error: No such file or directory

Or if I try to play back some video, for example, it gets stuck until I mute it.