Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Harden systemd services for freshclam and clamd #859

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
28 changes: 28 additions & 0 deletions clamd/clamav-daemon.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,35 @@ ExecStart=@prefix@/sbin/clamd --foreground=true
# Reload the database
ExecReload=/bin/kill -USR2 $MAINPID
TimeoutStartSec=420
LogsDirectory=clamav
ConfigurationDirectory=clamav

##
## Security Hardening Options
##

# Remove `ProtectSystem`, `ProtectHome`, and `ReadWritePaths`
# if you want ClamAV to be able to remove infected files.
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/log/clamav /run /var/run

NoExecPaths=/
eternaltyro marked this conversation as resolved.
Show resolved Hide resolved
# If you want to run commands or execute binaries on event,
# append the full path of the binary or executable to `ExecPaths`
# Commonly, this is used for `VirusEvent` in clamd.conf or `VirusAction`
# in clamav-milter.conf.The binaries must be space separated like so:
;ExecPaths=@prefix@/sbin/clamd /bin/kill /usr/local/bin/send_sms /usr/local/bin/my_infected_message_handler
ExecPaths=@prefix@/sbin/clamd @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill

ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target
Also=clamav-daemon.socket
Alias=clamd.service
26 changes: 24 additions & 2 deletions clamonacc/clamav-clamonacc.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,30 @@ After=clamav-daemon.service syslog.target network.target
[Service]
Type=simple
User=root
ExecStartPre=/bin/bash -c "while [ ! -S /run/clamav/clamd.ctl ]; do sleep 1; done"
ExecStart=@prefix@/sbin/clamonacc -F --log=/var/log/clamav/clamonacc.log --move=/root/quarantine
ExecStartPre=/usr/bin/install --owner=root --group=root --directory /var/local/quarantine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find that this fails if /var/local/quarantine does not exist.

I created it and did sudo chown clamav /var/local/quarantine and then tried again. This time it failed with `Failed to execute /usr/bin/install: Permission denied", which seems similar to the failure I'm seeing with trying to start cthe clamd service.

I'm at a loss as the ExecPaths in both service files look correct to me.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, let me investigate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me more information about your setup so I can attempt to replicate the issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm testing on an ubuntu 22.04 VM where I've installed systemd and libsystemd-dev before building clamav with these instructions and installing to the default prefix. Like:
mkdir build && cd build && cmake .. && make -j12 && sudo make install

I had to set up:

  • /usr/local/etc/freshclam.conf and /usr/local/etc/clamd.conf with the basics
  • sudo groupadd clamav && sudo useradd -g clamav -s /bin/false -c "Clam Antivirus" clamav
  • sudo mkdir /usr/local/share/clamav && sudo chown clamav /usr/local/share/clamav
  • sudo mkdir /var/log/clamav && sudo chown clamav /var/log/clamav

I think that's it. Then after reloading with sudo systemctl daemon-reload, I tried starting the various services.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me more information about your setup so I can attempt to replicate the issue?

You did a lot of work on this. I don't want to close/cancel this PR unless you've lost interest in solving the remaining issues.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@micahsnyder Sorry, this completely slipped my mind. I'll work on this over the weekend. I already have a VM that I can test this in.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To begin with, I tested the default installation on Debian 12. I found one issue where if /usr/lib/x86_64-linux-gnu had to be in the ExecPaths or clamav-daemon won't start. I also found /dev/null in the list of open files by the daemon - preferably for writing. I don't know if /dev/null and other char-files need to be in the read-only exception in the unit file.

ExecStart=@prefix@/sbin/clamonacc --foreground --log=/var/log/clamav/clamonacc.log --move=/var/local/quarantine --ping 120 --wait
ExecReload=/bin/kill -SIGHUP $MAINPID
ExecStop=/bin/kill -SIGTERM $MAINPID
LogsDirectory=clamav
ConfigurationDirectory=clamav

##
## Security Hardening Options
##
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
NoExecPaths=/
ExecPaths=@prefix@/sbin/clamonacc @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill /usr/bin/install

# Remove `ProtectSystem`, `ProtectHome`, and `ReadWritePaths` if you
# want ClamAV to be able to quarantine or remove infected files.
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/log/clamav /var/local/quarantine

[Install]
WantedBy=multi-user.target
Alias=clamonacc.service
3 changes: 3 additions & 0 deletions etc/clamav-milter.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ Example
# Note #2: the process is invoked in the context of clamav-milter
# Note #3: clamav-milter will wait for the process to exit. Be quick or fork to
# avoid unnecessary delays in email delivery
# Note #4: When using systemd to manage ClamAv daemon, ensure that the full path to
# the VirusAction target binary / executable is listed in ExecPaths in the service
# file clamav-daemon.service in order for the process to be able to execute it.
# Default: disabled
#VirusAction /usr/local/bin/my_infected_message_handler

Expand Down
3 changes: 3 additions & 0 deletions etc/clamd.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ Example
# be replaced with the virus name and %f will be replaced with the file name.
# Additionally, two environment variables will be defined: $CLAM_VIRUSEVENT_FILENAME
# and $CLAM_VIRUSEVENT_VIRUSNAME.
# Note: When using systemd to manage ClamAV daemon, ensure that the full path to
# the VirusEvent target binary / executable is listed in ExecPaths in the service
# file clamav-daemon.service in order for the process to be able to execute it.
# Default: no
#VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %v in %f"

Expand Down
12 changes: 12 additions & 0 deletions etc/freshclam.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,27 @@ DatabaseMirror database.clamav.net

# Run command after successful database update.
# Use EXIT_1 to return 1 after successful database update.
# Note: When using systemd to manage FreshClam service, append the
# full path to the OnUpdateExecute target command to `ExecPaths` in the
# service file clamav-freshclam.service in order for the process to
# be able to execute it.
# Default: disabled
#OnUpdateExecute command

# Run command when database update process fails.
# Note: When using systemd to manage FreshClam service, append the
# full path to the OnErrorExecute target command to `ExecPaths` in the
# service file clamav-freshclam.service in order for the process to
# be able to execute it.
# Default: disabled
#OnErrorExecute command

# Run command when freshclam reports outdated version.
# In the command string %v will be replaced by the new version number.
# Note: When using systemd to manage FreshClam service, append the
# full path to the OnOutdatedExecute target command to `ExecPaths`
# in the service file clamav-freshclam.service in order for the process to
# be able to execute it.
# Default: disabled
#OnOutdatedExecute command

Expand Down
22 changes: 22 additions & 0 deletions freshclam/clamav-freshclam.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ After=network-online.target

[Service]
ExecStart=@prefix@/bin/freshclam -d --foreground=true
LogsDirectory=clamav
ConfigurationDirectory=clamav

##
## Security Hardening Options
##
ProtectSystem=full
ProtectHome=tmpfs
ProtectClock=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
eternaltyro marked this conversation as resolved.
Show resolved Hide resolved

NoExecPaths=/
eternaltyro marked this conversation as resolved.
Show resolved Hide resolved
# If you want to run commands on event, append the full path of the command or
# executable to `ExecPaths`. Commonly, this is used for `OnUpdateExecute`,
# `OnErrorExecute`, and `OnOutdatedExecute` options in freshclam.conf. Make sure
# there is only one `ExecPaths` option. The binaries must be space separated like so:
;ExecPaths=@prefix@/sbin/freshclam /usr/local/bin/OnOutdated.sh /usr/local/bin/OnErrorExecute.sh
ExecPaths=@prefix@/bin/freshclam
ReadWritePaths=@DATADIR@ /var/log/clamav

[Install]
WantedBy=multi-user.target