How to Set Up ClamAV on Qubes
If you have read the warnings about the potential security risks of running ClamAV on Qubes OS, and (like me) you want to do it anyway, here’s how you can accomplish that.
These instructions have been developed and tested on Qubes OS 4.2.3, with Fedora 40 and Debian 12 templates. Other versions will probably work as well.
-
Clone your fedora-40-xfce template. Call the new template something like ‘fedora-40-clamd’. This is the template for the dedicated qube in which you will run clamd, aka clamav-daemon, which keeps a copy of the virus definition database in memory, making virus scanning light-years faster.
-
Launch a terminal window in the new template. Run
sudo dnf update
, thensudo dnf install thunar-sendto-clamtk clamd clamav-freshclam clamav-unofficial-sigs clamav-data
. -
Once these packages are installed, shut down the template by running
poweroff
. -
Create an AppVM based on the above template. Call it something like ‘fedora-40-clamd-1’. This is the AppVM where you will run clamd.
-
Open the qube settings for your new AppVM. Allocate more private storage space for it. 20-30 GB is probably enough. Then click OK to apply the changes and close the settings dialog box.
-
Reopen the settings dialog and switch to the Advanced Settings tab. Allocate more RAM for this qube. I have mine set to an initial size of 900 MB, up to a max of 8192 MB. So roughly twice the defaults. You may also want to increase the number of VCPUs allocated, to 3- 5 or so. Perhaps more if you have CPU cores to spare and want to improve scanning speed.
Finally, click OK to apply the settings changes and close the settings dialog once more.
- Next we need to make the necessary files and directories persist between AppVM reboots. Start the AppVM and launch a Terminal window. Run the following commands:
sudo mkdir -p /rw/config/qubes-bind-dirs.d
sudo touch /rw/config/qubes-bind-dirs.d/50_user.conf
sudoedit /rw/config/qubes-bind-dirs.d/50_user.conf
Set the contents of the 50_user.conf
file as follows:
binds+=( ‘/var/lib/clamav’ )
binds+=( ‘/etc/clamd.d’ )
binds+=( ‘/etc/freshclam.conf’ )
binds+=( ‘/var/log/clamav’ )
Save and close the file.
- Now let’s create the three directories referenced by the above file. (The remaining entry, freshclam.conf, is an individual config file.) Run the following commands:
sudo mkdir -p /rw/bind-dirs/var/lib/clamav
sudo mkdir -p /rw/bind-dirs/var/log/clamav
sudo mkdir -p /rw/bind-dirs/etc/clamd.d
sudo touch /rw/bind-dirs/etc/freshclam.conf
poweroff
- The AppVM should shut down at that last command. Restart it, and launch another Terminal window. Run
sudoedit /etc/freshclam.conf
. Set the following settings in the freshclam.conf file:
DatabaseDirectory /var/lib/clamav
UpdateLogFile /var/log/freshclam.log
LogSyslog yes
PidFile /var/run/freshclam.pid
DatabaseOwner root
DatabaseMirror database.clamav.net
NotifyClamd /etc/clamd.d/scan.conf
Foreground yes
Leave the remaining settings in this file commented, to use their default values.
- Now to configure clamd. Run
sudoedit /etc/clamd.d/scan.conf
. Set the following settings as specified. You can configure the remaining settings according to your preferences, but the defaults will likely work.
LogSyslog yes
ExtendedDetectionInfo yes
PidFile /run/clamd.scan/clamd.pid
TemporaryDirectory /var/tmp
DatabaseDirectory /var/lib/clamav
OfficialDatabaseOnly no
LocalSocket /run/clamd.scan/clamd.sock
LocalSocketGroup virusgroup
LocalSocketMode 666
FixStaleSocket yes
TCPSocket 3310
TCPAddr localhost
MaxThreads 4 # up to the number of vCPUs you allocated for this AppVM
ExcludePath ^/proc/
ExcludePath ^/sys/
ExcludePath ^/dev/
MaxDirectoryRecursion 20 # optional
CrossFilesystems yes
SelfCheck 600
ConcurrentDatabaseReload yes
User clamscan
ExitOnOOM yes
Foreground yes
DetectPUA yes # optional, but recommended, to scan for PUAs (Potentially Unwanted Applications)
OnAccessMaxThreads 3 # up to MaxThreads - 1
OnAccessExcludeRootUID yes
OnAccessExcludeUname clamav
HeuristicAlerts no
ScanHTML no
ScanHWP3 no
ScanXMLDOCS no
ScanSWF no
ScanPDF no
ScanOLE2 no
Save and close the file.
- Shut down the fedora-40-clamd-1 qube, then restart it. Open Terminal, and run the following command to download and initialize the virus signature database for the first time:
sudo freshclam
There will likely be a warning that clamd could not be notified. This is OK for this first run of freshclam.
- Open a new Terminal tab or window. Run the following command:
sudo clamd
. This will start the clamav in-meory daemon. It will likely take a few seconds to start up. When it finishes, you should see a few dozen lines of output, ending with:
Self checking every 600 seconds.
-
Open another new Terminal tab or window. Run the command:
sudo clamdtop
. This launches the ClamdTop application, which gives you statistics about clamd and its performance in realtime. -
Open a Terminal window in Dom0. Edit the following file:
/etc/qubes/policy.d/30-user-networking.policy
. Add the following line to this file:
qubes.ConnectTCP +3310 * @default allow target=fedora-40-clamd-1
Save and close the file. Now your other qubes will be able to stream files to the dedicated ClamD qube for scanning, using the clamdscan
tool. This has to be installed first on the respective templates.
Configuring clamdscan Client-Side
To install clamdscan, open a Terminal window in your Fedora-40-based or Debian-12-based template. (These instructions will probably work with other Linux distributions and versions as well, but I have not tested them yet.)
Fedora 40
For Fedora 40:
sudo dnf update
sudo dnf install thunar-sendto-clamtk
sudoedit /etc/clamd.d/scan.conf
In scan.conf, only the following settings really matter:
TCPSocket 3310
TCPAddr localhost
Everything else in the file can be commented out or deleted.
These settings just tell clamdscan, clamdtop, etc. where to find clamd. To them, it will look like clamd is running on the localhost. But it really isn’t. clamd traffic will be redirected to the fedora-40-clamd-1 VM behind the scenes, using a cool trick that we’ll explore next.
sudoedit /usr/lib/systemd/system/clamd.socket
Set the contents of this file as follows:
[Unit]
Description=clamd
[Socket]
ListenStream=127.0.0.1:3310
Accept=true
[Install]
WantedBy=sockets.target
Save and close the file. Next, run:
sudoedit /usr/lib/systemd/system/clamd@.service
Fill out this file with the following:
[Unit]
Description=clamd
[Service]
ExecStart=qrexec-client-vm ‘’ qubes.ConnectTCP+3310
StandardInput=socket
StandardOutput=inherit
Now save and close this file. Close the Terminal window, and shut down the Fedora 40 template qube.
These settings cause port 3310 on localhost to be forwarded to port 3310 on the clamd qube. This is done transparently, so that processes running on the local qube will hardly notice the difference.
Now to test it! To test your newfound scanning ability, launch an AppVM based on the Fedora 40 template, then launch a Terminal window within it. Run sudo systemctl enable --now clamd.socket
to start the clamd socket listening on the client-vm side. Then run clamdscan --multiscan --stream .
to scan the files in the current directory and all subdirectories, via streaming them to fedora-40-clamd-1. You will likely see a single eror message at the start about being unable to connect, but then after that, the scanning should proceed normally.
Neat, huh?
Debian 12
The instructions for setting up Debian-12-based templates are pretty nearly the same as the instructions for Fedora 40, except that in place of the dnf commands, you will run:
sudo apt update
sudo apt install clamdscan
Also, the clamd configuration file should be named /etc/clamav/clamd.conf instead of scan.conf. Then set up the clamd.socket and clamd@.service files under /usr/lib/systemd/system the same as for Fedora.
Optional: Set up your own local mirror of the ClamAV virus definitions
You can, if desired, set up your own local mirror of the ClamAV virus definitions, and run freshclam against your private mirror. Then the dedicated clamd qube won’t need to talk to the Internet directly at all.
To do this, make another clone of the fedora-40-xfce template. I suggest calling this template fedora-40-cvd
.
Launch a terminal window in this new template. Run sudo dnf update
, followed by sudo dnf install python3-cvdupdate
.
Once python3-cvdupdate and its dependencies are installed, shut down the template. Create an AppVM based on it:
qvm-create --template fedora-40-cvd --label green fedora-40-cvd-1
Launch the new AppVM, and launch a Terminal window running inside it. Run the following commands:
cvdupdate config set
cvdupdate config show
cvdupdate update
cvdupdate serve
Back in Dom0, edit the /etc/qubes/policy.d/30-user-networking.policy
file again. Add another line:
qubes.ConnectTCP +8000 fedora-40-clamd-1 @default allow target=fedora-40-cvd-1
Save and close the file.
Next, shut down fedora-40-clamd-1, and launch a terminal window in the template it’s based on, fedora-40-clamd. Create two more files under /usr/lib/systemd/system/
: cvd.socket (sudoedit cvd.socket
) and cvd@.service (sudoedit cvd\@.service
). Fill out cvd.socket as follows:
[Unit]
Description=cvd
[Socket]
ListenStream=127.0.0.1:8000
Accept=true
[Install]
WantedBy=sockets.target
And cvd@.service like this:
[Unit]
Description=cvd
[Service]
ExecStart=qrexec-client-vm ‘’ qubes.ConnectTCP+8000
StandardInput=socket
StandardOutput=inherit
Save and close both files. Shut down the template qube. Change the settings for fedora-40-clamd-1 so that it has no direct network access (n/a
).
Now launch fedora-40-clamd-1. Add the following to /rw/config/rc.local:
systemctl start cvd.socket
Save and close the file. Next, edit /etc/freshclam.conf. Uncomment one of the PrivateMirror lines, and change it to the following:
PrivateMirror localhost:8000
Save and close that file.
Now to test it out! Run sudo systemctl enable --now cvd.socket
. Start clamd using the sudo clamd
command, then in another Terminal window, run sudo freshclam
. You will likely see some warnings, but the update process should succeed. And if there are new updates, clamd should be notified to reload the database.
That’s pretty much it. Note that for now, at least, you will have to launch clamd, freshclam, and cvdupdate manually. I haven’t figured out yet how to get these to run automatically, as services, for example.
Whenever you want to scan a file or directory for viruses, use that clamdscan --multiscan --stream <path>
command, after making sure that clamd.socket is active and listening on the local VM. (You can check this by running sudo systemctl status clamd.socket
.)
Let me know if you have any questions, or if you run into any issues following these steps. Thanks!