From 0bec1ceb856b9043a9e7b3035aad57afed693210 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Tue, 14 Mar 2023 17:10:21 +0000 Subject: [PATCH 01/10] Harden systemd services for freshclam and clamd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default systemd service files `clamav-freshclam` and `clamav-daemon` (at least as installed in Arch Linux) do not contain any Sandboxing options configured. Therefore, the service units are marked as `UNSAFE` when `systemd-analyze security`[1] is run. ``` ➜ ~ systemd-analyze security UNIT EXPOSURE PREDICATE HAPPY .. clamav-daemon.service 9.6 UNSAFE 😨 clamav-freshclam.service 9.6 UNSAFE 😨 .. ``` By including some basic security options[2], we can increase the security level of the services - even if only a little bit - from systemd's perspective. For the daemon, I have used `ProtectSystem` and `ProtectHome` directives to make the filesystem read-only - which should be okay when scanning files. However, we have to make an exception for the log path using `ReadWritePaths`. Additionally, this configuration would interfere with the `--remove` option if the user chooses to auto-remove infected files. Other options protect sysconfig, kernel modules, and the hardware clock. Adverse effects are less pronounced if we apply similar configuration to freshclam service - which ostensibly only updates a specific set of files on disk. I referred to the Installation manual[3] to ascertain the default file path to which fresh databases are downloaded. With all these changes applied, the result of `systemd-analyze security` looks like this: ``` ➜ ~ systemd-analyze security UNIT EXPOSURE PREDICATE HAPPY .. clamav-daemon.service 7.8 EXPOSED 🙁 clamav-freshclam.service 7.8 EXPOSED 🙁 .. ``` While this seems like a definite improvement, we need to take user experience, distribution-specific defaults, and best-practices (as determined by ClamAV itself) to ensure that this changeset is valid. [1] https://www.freedesktop.org/software/systemd/man/systemd-analyze.html [2] https://www.freedesktop.org/software/systemd/man/systemd.exec.html [3] https://docs.clamav.net/manual/Installing/Packages.html --- clamd/clamav-daemon.service.in | 13 +++++++++++++ freshclam/clamav-freshclam.service.in | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index 579a51286b..f7e76eda68 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -11,6 +11,19 @@ ExecStart=@prefix@/sbin/clamd --foreground=true # Reload the database ExecReload=/bin/kill -USR2 $MAINPID TimeoutStartSec=420 +# Security 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/ +NoExecPaths=/ +ExecPaths=@prefix@/sbin/clamd /bin/kill +ProtectClock=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes [Install] WantedBy=multi-user.target diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index 8a69ce854c..874e79c500 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -8,6 +8,16 @@ After=network-online.target [Service] ExecStart=@prefix@/bin/freshclam -d --foreground=true +# Security Options +ProtectSystem=full +ProtectHome=tmpfs +ProtectClock=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +NoExecPaths=/ +ExecPaths=@prefix@/bin/freshclam [Install] WantedBy=multi-user.target From 661e7a2ada9cdef8a1b2df4d71d5c153a4177b12 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Tue, 4 Apr 2023 18:09:38 +0100 Subject: [PATCH 02/10] Add documentation for ExecPaths in systemd units In systemd unit files, I had missed some paths to commands that are potentially executed in response to events. These commands are arbitrary and configurable in clamd.conf and freshclam.conf. Each of these options invoke an appropriate path to a configured executable when - for example - a scan is complete or signature update fails. In order for these executables to run, systemd should allow it. It is necessary to add these paths to `ExecPaths` in systemd service unit files. This change adds comments instructing users and administrators how to do that and generally helps make sense of the defaults. - Plus some formatting changes --- clamd/clamav-daemon.service.in | 17 ++++++++++++++--- etc/clamav-milter.conf.sample | 3 +++ etc/clamd.conf.sample | 3 +++ etc/freshclam.conf.sample | 12 ++++++++++++ freshclam/clamav-freshclam.service.in | 11 ++++++++++- 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index f7e76eda68..c639767d39 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -11,14 +11,25 @@ ExecStart=@prefix@/sbin/clamd --foreground=true # Reload the database ExecReload=/bin/kill -USR2 $MAINPID TimeoutStartSec=420 -# Security options -# Remove ProtectSystem, ProtectHome, and ReadWritePaths.. -# if you want clamav to be able to remove infected files. + +## +## 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/ + NoExecPaths=/ +# 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 /bin/kill + ProtectClock=yes ProtectKernelTunables=yes ProtectKernelModules=yes diff --git a/etc/clamav-milter.conf.sample b/etc/clamav-milter.conf.sample index 7ca0e6eda3..96a51ad895 100644 --- a/etc/clamav-milter.conf.sample +++ b/etc/clamav-milter.conf.sample @@ -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 diff --git a/etc/clamd.conf.sample b/etc/clamd.conf.sample index 8cf370c600..21907d1180 100644 --- a/etc/clamd.conf.sample +++ b/etc/clamd.conf.sample @@ -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" diff --git a/etc/freshclam.conf.sample b/etc/freshclam.conf.sample index 26c4e7a63f..b669d3f0e9 100644 --- a/etc/freshclam.conf.sample +++ b/etc/freshclam.conf.sample @@ -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 diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index 874e79c500..8802421ca1 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -8,7 +8,10 @@ After=network-online.target [Service] ExecStart=@prefix@/bin/freshclam -d --foreground=true -# Security Options + +## +## Security Hardening Options +## ProtectSystem=full ProtectHome=tmpfs ProtectClock=yes @@ -16,7 +19,13 @@ ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes + NoExecPaths=/ +# 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 [Install] From 68fc5e96c57e86e5e3df0740197ab25c6daa7a11 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Tue, 4 Apr 2023 18:42:12 +0100 Subject: [PATCH 03/10] Harden service file for clamav-clamonacc.service Harden ClamAV OnAccess service systemd unit file. - Removed default move options to be consistent with the behaviour of the rest of the service files - Added hardening parameters for service - Added Reload and Stop signals for graceful reload and stop --- clamonacc/clamav-clamonacc.service.in | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index f103d86658..7014fc31df 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -11,7 +11,26 @@ After=clamav-daemon.service syslog.target network.target 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 +ExecStart=@prefix@/sbin/clamonacc --foreground --log=/var/log/clamav/clamonacc.log +ExecReload=/bin/kill -SIGHUP $MAINPID +ExecStop=/bin/kill -SIGTERM $MAINPID + +## +## Security Hardening Options +## +ProtectClock=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +NoExecPaths=/ +ExecPaths=@prefix@/sbin/clamonacc /bin/kill + +# 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 [Install] WantedBy=multi-user.target From e69b6114899725cae8519cdb7811e7a57f9c1e05 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Sun, 30 Apr 2023 21:42:17 +0100 Subject: [PATCH 04/10] Fix ClamOnAcc ExecPaths and ExecStart directives This commit includes four changes: 1. Wait for clamd process using `--wait` and `--ping` switches instead of using a bash test for the presence of clamd.ctl socket 2. Use the PreStart directive to create log and quarantine directories 3. Add shared library path to ExecPaths allow-list 4. Add quarantine directory path to ReadWritePaths allow-list --- clamonacc/clamav-clamonacc.service.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index 7014fc31df..d82dbe7fbf 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -10,8 +10,8 @@ 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 --foreground --log=/var/log/clamav/clamonacc.log +ExecStartPre=/usr/bin/install --owner=root --group=root --directory /var/log/clamav /var/local/quarantine +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 @@ -24,13 +24,13 @@ ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes NoExecPaths=/ -ExecPaths=@prefix@/sbin/clamonacc /bin/kill +ExecPaths=@prefix@/sbin/clamonacc @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill # 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 +ReadWritePaths=/var/log /var/local/quarantine [Install] WantedBy=multi-user.target From 7ef6ef3b8aae4c39f6a7638598a53259235ecefb Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 1 May 2023 18:28:43 +0100 Subject: [PATCH 05/10] Improve & fix ReadWritePaths and other directives - Add LogsDirectory= and ConfigurationDirectory= directives to the service files. This creates the log directory under /var/log and the configuration directory under /etc as specified in the unit file. - Add LogsDirectory path to ReadWritePaths= - Add /run and /var/run to ReadWritePaths= - Add shared library path to ReadWritePaths= - Add Alias to all three services Known Issues: - Terminating ClamOnAcc service is really slow and times out. SIGKILL takes over where SIGTERM fails to stop the process - For ClamOnAcc to function effectively on files in $HOME, it needs the --fdpass switch - The PIDFILE and Lockfile paths for services are not managed by Systemd yet. --- clamd/clamav-daemon.service.in | 8 ++++++-- clamonacc/clamav-clamonacc.service.in | 9 ++++++--- freshclam/clamav-freshclam.service.in | 3 +++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index c639767d39..d4391a1cf9 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -11,6 +11,8 @@ ExecStart=@prefix@/sbin/clamd --foreground=true # Reload the database ExecReload=/bin/kill -USR2 $MAINPID TimeoutStartSec=420 +LogsDirectory=clamav +ConfigurationDirectory=clamav ## ## Security Hardening Options @@ -20,7 +22,7 @@ TimeoutStartSec=420 # if you want ClamAV to be able to remove infected files. ProtectSystem=strict ProtectHome=read-only -ReadWritePaths=/var/log/ +ReadWritePaths=/var/log/clamav /run /var/run NoExecPaths=/ # If you want to run commands or execute binaries on event, @@ -28,14 +30,16 @@ NoExecPaths=/ # 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 /bin/kill +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 diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index d82dbe7fbf..e0d4b73de5 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -10,10 +10,12 @@ After=clamav-daemon.service syslog.target network.target [Service] Type=simple User=root -ExecStartPre=/usr/bin/install --owner=root --group=root --directory /var/log/clamav /var/local/quarantine +ExecStartPre=/usr/bin/install --owner=root --group=root --directory /var/local/quarantine 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 @@ -24,13 +26,14 @@ ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes NoExecPaths=/ -ExecPaths=@prefix@/sbin/clamonacc @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill +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 /var/local/quarantine +ReadWritePaths=/var/log/clamav /var/local/quarantine [Install] WantedBy=multi-user.target +Alias=clamonacc.service diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index 8802421ca1..1b353ffd4e 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -8,6 +8,8 @@ After=network-online.target [Service] ExecStart=@prefix@/bin/freshclam -d --foreground=true +LogsDirectory=clamav +ConfigurationDirectory=clamav ## ## Security Hardening Options @@ -27,6 +29,7 @@ NoExecPaths=/ # 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 From a694172d130682f1dc46f6e53ccb295d6ed11304 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 29 Jan 2024 06:35:20 +0000 Subject: [PATCH 06/10] Improve SystemD service file settings Service user: Added explicit user and group settings for service files. Clamd and Freshclam now run as clamav user. Per documentation, the service forks off process that runs as user specfied in the config. This change makes that explicit. Clamonacc still runs as root since it needs to access files that are not owned by the default clamav user. An alternative approach is to use the --fdpass to pass the file descriptor perms to Clamd instead of having to stream the entire file. Other changes: - [gen] Added explicit service types. Forking type for ClamD failed even without foreground switch. So the service runs as a simple daemon. - [doc] Updated comment strings to make them concise and unambiguous. - [sec] Added a safer permission mode for quarantine directory. - [sec] Added several new security settings to protect the system. Since unknown settings are safely ignored by older versions of SystemD, the settings should automatically apply when SystemD version is bumped. - [sec] Added new network security settings. Clamd and OnAcc are not allowed to use the networkr; only Freshclam is. - [sec] Added restrictions to several system calls and Linux capabilities TODO / Known issues: - PreStart tasks need root user access - for example, to create the quarantine directory. The current approach is unreliable. - Some settings such as PrivateUsers=yes break the service file. - We need CMAKE/Automake substitution strings to add (multi-arch and arch dependent) LIBDIR to certain settings. - ExecPaths and ReadWritePaths need locking down. - Parameterize DATADIR - e.g. /var/log/quarantine --- clamd/clamav-daemon.service.in | 52 ++++++++++++++++++++------ clamonacc/clamav-clamonacc.service.in | 54 +++++++++++++++++++++------ freshclam/clamav-freshclam.service.in | 48 ++++++++++++++++++------ 3 files changed, 120 insertions(+), 34 deletions(-) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index d4391a1cf9..dcc937e885 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -7,6 +7,12 @@ ConditionPathExistsGlob=@DATADIR@/main.{c[vl]d,inc} ConditionPathExistsGlob=@DATADIR@/daily.{c[vl]d,inc} [Service] +Type=simple +# WARNING: Must match clamd.conf directive +User=clamav +Group=clamav +;DynamicUser=yes +;ExecStartPre=/usr/bin/install --owner=root --group=root --mode=640 --directory /var/local/quarantine ExecStart=@prefix@/sbin/clamd --foreground=true # Reload the database ExecReload=/bin/kill -USR2 $MAINPID @@ -14,30 +20,52 @@ TimeoutStartSec=420 LogsDirectory=clamav ConfigurationDirectory=clamav -## -## Security Hardening Options -## +#============================# +# Security Hardening Options # +#============================# +NoNewPrivileges=yes +;PrivateUsers=no # Remove `ProtectSystem`, `ProtectHome`, and `ReadWritePaths` -# if you want ClamAV to be able to remove infected files. +# if you want ClamAV to be able to quarantine (`--move`) infected files. ProtectSystem=strict ProtectHome=read-only -ReadWritePaths=/var/log/clamav /run /var/run +PrivateTmp=yes +PrivateDevices=yes +ReadWritePaths=/var/log/clamav /run/clamav /var/run/clamav /var/local/quarantine NoExecPaths=/ -# 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 +# WARNING: To execute targets of `VirusEvent` and `VirusAction` directives +# in the configuration, whitelist them in ExecPaths. +;ExecPaths=@prefix@/sbin/clamd /usr/local/bin/send_sms /usr/local/bin/my_infected_message_handler +ExecPaths=@prefix@/sbin/clamd @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill /usr/bin/install ProtectClock=yes +ProtectHostname=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes -PrivateTmp=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes + +PrivateNetwork=yes +IPAddressDeny=any +RestrictAddressFamilies=none + +# ProtectProc is only effective if: +# (1) Service is a system service and not a user service. +# (2) CAP_SYS_PTRACE is restricted and +# (3) Service user is non-root. +ProtectProc=invisible +ProcSubset=pid + +CapabilityBoundingSet=CAP_CHOWN CAP_SETGID CAP_SETUID CAP_DAC_OVERRIDE CAP_MKNOD +RestrictNamespaces=yes +SystemCallFilter=~@clock @cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @resources @swap [Install] WantedBy=multi-user.target diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index e0d4b73de5..519758f732 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -9,31 +9,63 @@ After=clamav-daemon.service syslog.target network.target [Service] Type=simple +# WARNING: Must match clamd.conf directive User=root -ExecStartPre=/usr/bin/install --owner=root --group=root --directory /var/local/quarantine +Group=root +;DynamicUser=yes +ExecStartPre=/usr/bin/install --owner=root --group=root --mode=640 --directory /var/local/quarantine 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 +#============================# +# Security Hardening Options # +#============================# +NoNewPrivileges=yes +;PrivateUsers=no # Remove `ProtectSystem`, `ProtectHome`, and `ReadWritePaths` if you # want ClamAV to be able to quarantine or remove infected files. ProtectSystem=strict ProtectHome=read-only +PrivateTmp=yes +PrivateDevices=yes ReadWritePaths=/var/log/clamav /var/local/quarantine +NoExecPaths=/ +# WARNING: To execute targets of `VirusEvent` and `VirusAction` directives +# in the cofiguration, whitelist them in ExecPaths. +ExecPaths=@prefix@/sbin/clamonacc @CMAKE_INSTALL_FULL_LIBDIR@ /bin/kill /usr/bin/install + +ProtectClock=yes +ProtectHostname=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes + +PrivateNetwork=yes +IPAddressDeny=any +RestrictAddressFamilies=none + +# ProtectProc is only effective if: +# (1) Service is a system service and not a user service. +# (2) CAP_SYS_PTRACE is restricted and +# (3) Service user is non-root. +ProtectProc=invisible +ProcSubset=pid + +CapabilityBoundingset=CAP_CHOWN CAP_SETGID CAP_SETUID CAP_DAC_OVERRIDE CAP_MKNOD +RestrictNamespaces=yes +SystemCallFilter=~@clock @cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @resources @swap + [Install] WantedBy=multi-user.target Alias=clamonacc.service diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index 1b353ffd4e..a52ae16fbb 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -7,29 +7,55 @@ Wants=network-online.target After=network-online.target [Service] +Type=simple +# WARNING: Must match clamd.conf directive +User=clamav +Group=clamav +;DynamicUser=yes ExecStart=@prefix@/bin/freshclam -d --foreground=true LogsDirectory=clamav ConfigurationDirectory=clamav -## -## Security Hardening Options -## +#============================# +# Security Hardening Options # +#============================# +NoNewPrivileges=yes +;PrivateUsers=no + ProtectSystem=full ProtectHome=tmpfs +PrivateTmp=yes +PrivateDevices=yes +ReadWritePaths=@DATADIR@ /var/log/clamav + +NoExecPaths=/ +# WARNING: To execute targets of `OnUpdateExecute`, `OnErrorExecute` and `OnOutdatedExecute` +# directives in freshclam.conf, whitelist them in ExecPaths. +;ExecPaths=@prefix@/sbin/freshclam /usr/local/bin/OnOutdated.sh /usr/local/bin/OnErrorExecute.sh +ExecPaths=@prefix@/bin/freshclam + ProtectClock=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes -NoExecPaths=/ -# 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 +PrivateNetwork=no +IPAddressAllow=any +RestrictAddressFamilies=AF_INET AF_INET6 + +ProtectProc=noaccess +ProcSubset=pid + +CapabilityBoundingSet=CAP_CHOWN CAP_SETGID CAP_SETUID CAP_DAC_OVERRIDE CAP_MKNOD +RestrictNamespaces=yes +SystemCallFilter=~@clock @cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @resources @swap [Install] WantedBy=multi-user.target +Alias=freshclam.service From 3c28acda696ca4e5316c95b45948e3db6b3641d4 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 29 Jan 2024 13:01:13 +0000 Subject: [PATCH 07/10] Secure oneshot freshclam service --- freshclam/clamav-freshclam-once.service.in | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/freshclam/clamav-freshclam-once.service.in b/freshclam/clamav-freshclam-once.service.in index 5aad7a17d8..2620b32c87 100644 --- a/freshclam/clamav-freshclam-once.service.in +++ b/freshclam/clamav-freshclam-once.service.in @@ -1,12 +1,59 @@ [Unit] Description=Update ClamAV virus database once Documentation=man:freshclam(1) man:freshclam.conf(5) https://docs.clamav.net/ +;ConditionPathExists=!/etc/cron.d/clamav-freshclam Wants=network-online.target After=network-online.target [Service] Type=oneshot +# WARNING: Must match clamd.conf directive +User=clamav +Group=clamav +;DynamicUser=yes ExecStart=@prefix@/bin/freshclam +;LogsDirectory=clamav +;ConfigurationDirectory=clamav + +#============================# +# Security Hardening Options # +#============================# +NoNewPrivileges=yes +;PrivateUsers=no + +ProtectSystem=full +ProtectHome=tmpfs +PrivateTmp=yes +PrivateDevices=yes +ReadWritePaths=@DATADIR@ /var/log/clamav + +NoExecPaths=/ +# WARNING: To execute targets of `OnUpdateExecute`, `OnErrorExecute` and `OnOutdatedExecute` +# directives in freshclam.conf, whitelist them in ExecPaths. +;ExecPaths=@prefix@/sbin/freshclam /usr/local/bin/OnOutdated.sh /usr/local/bin/OnErrorExecute.sh +ExecPaths=@prefix@/bin/freshclam + +ProtectClock=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectKernelLogs=yes +ProtectControlGroups=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +LockPersonality=yes +MemoryDenyWriteExecute=yes +RestrictRealtime=yes + +PrivateNetwork=no +IPAddressAllow=any +RestrictAddressFamilies=AF_INET AF_INET6 + +ProtectProc=noaccess +ProcSubset=pid + +CapabilityBoundingSet=CAP_CHOWN CAP_SETGID CAP_SETUID CAP_DAC_OVERRIDE CAP_MKNOD +RestrictNamespaces=yes +SystemCallFilter=~@clock @cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @resources @swap [Install] WantedBy=multi-user.target From 9a9aefc9c1be72631d4877188d2f05d1dccbcd6e Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 29 Jan 2024 13:46:03 +0000 Subject: [PATCH 08/10] Make freshclam oneshot and daemon exclusive --- freshclam/clamav-freshclam-once.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/freshclam/clamav-freshclam-once.service.in b/freshclam/clamav-freshclam-once.service.in index 2620b32c87..decf5352dc 100644 --- a/freshclam/clamav-freshclam-once.service.in +++ b/freshclam/clamav-freshclam-once.service.in @@ -4,6 +4,7 @@ Documentation=man:freshclam(1) man:freshclam.conf(5) https://docs.clamav.net/ ;ConditionPathExists=!/etc/cron.d/clamav-freshclam Wants=network-online.target After=network-online.target +Conflicts=clamav-freshclam.service [Service] Type=oneshot From 722d141f666147683532afc1b9d7e204ae8aa843 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 5 Feb 2024 14:34:40 +0000 Subject: [PATCH 09/10] Remove LogsDir from ReadWritePaths in SystemD LogsDirectory specified explicitly as such is automatically configured to be writable by systemD. So it need not be explicitly specified under ReadWritePaths. --- clamd/clamav-daemon.service.in | 2 +- clamonacc/clamav-clamonacc.service.in | 2 +- freshclam/clamav-freshclam-once.service.in | 4 ++-- freshclam/clamav-freshclam.service.in | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index dcc937e885..337aea2c9c 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -32,7 +32,7 @@ ProtectSystem=strict ProtectHome=read-only PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=/var/log/clamav /run/clamav /var/run/clamav /var/local/quarantine +ReadWritePaths=/run/clamav /var/run/clamav /var/local/quarantine NoExecPaths=/ # WARNING: To execute targets of `VirusEvent` and `VirusAction` directives diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index 519758f732..9c686b2bd1 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -32,7 +32,7 @@ ProtectSystem=strict ProtectHome=read-only PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=/var/log/clamav /var/local/quarantine +ReadWritePaths=/var/local/quarantine NoExecPaths=/ # WARNING: To execute targets of `VirusEvent` and `VirusAction` directives diff --git a/freshclam/clamav-freshclam-once.service.in b/freshclam/clamav-freshclam-once.service.in index decf5352dc..dfd372584d 100644 --- a/freshclam/clamav-freshclam-once.service.in +++ b/freshclam/clamav-freshclam-once.service.in @@ -13,7 +13,7 @@ User=clamav Group=clamav ;DynamicUser=yes ExecStart=@prefix@/bin/freshclam -;LogsDirectory=clamav +LogsDirectory=clamav ;ConfigurationDirectory=clamav #============================# @@ -26,7 +26,7 @@ ProtectSystem=full ProtectHome=tmpfs PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=@DATADIR@ /var/log/clamav +ReadWritePaths=@DATADIR@ NoExecPaths=/ # WARNING: To execute targets of `OnUpdateExecute`, `OnErrorExecute` and `OnOutdatedExecute` diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index a52ae16fbb..cba75a9f1d 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -26,7 +26,7 @@ ProtectSystem=full ProtectHome=tmpfs PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=@DATADIR@ /var/log/clamav +ReadWritePaths=@DATADIR@ NoExecPaths=/ # WARNING: To execute targets of `OnUpdateExecute`, `OnErrorExecute` and `OnOutdatedExecute` From 77a125be1ce087f95b689685812ed5e828934324 Mon Sep 17 00:00:00 2001 From: eternaltyro Date: Mon, 5 Feb 2024 15:02:36 +0000 Subject: [PATCH 10/10] Specify RuntimeDirectory for services Specify RuntimeDirectory for clamav services `/run/clamav` to make PIDFiles writeable. The RuntimeDirectory ownership is changed by SystemD to match the `User` and `Group` specified in the service unit files. ClamOnAccess runs as root and therefore would clobber the ownership of these directories set by other services in the family. For this reason, until a better approach is available, RuntimeDirectory and LogsDirectory are not managed by SystemD for ClomOnAccess service. --- clamd/clamav-daemon.service.in | 3 ++- clamonacc/clamav-clamonacc.service.in | 5 +++-- freshclam/clamav-freshclam-once.service.in | 3 ++- freshclam/clamav-freshclam.service.in | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/clamd/clamav-daemon.service.in b/clamd/clamav-daemon.service.in index 337aea2c9c..f7e522759d 100644 --- a/clamd/clamav-daemon.service.in +++ b/clamd/clamav-daemon.service.in @@ -19,6 +19,7 @@ ExecReload=/bin/kill -USR2 $MAINPID TimeoutStartSec=420 LogsDirectory=clamav ConfigurationDirectory=clamav +RuntimeDirectory=clamav #============================# # Security Hardening Options # @@ -32,7 +33,7 @@ ProtectSystem=strict ProtectHome=read-only PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=/run/clamav /var/run/clamav /var/local/quarantine +ReadWritePaths=/var/local/quarantine NoExecPaths=/ # WARNING: To execute targets of `VirusEvent` and `VirusAction` directives diff --git a/clamonacc/clamav-clamonacc.service.in b/clamonacc/clamav-clamonacc.service.in index 9c686b2bd1..b76e40cbb9 100644 --- a/clamonacc/clamav-clamonacc.service.in +++ b/clamonacc/clamav-clamonacc.service.in @@ -17,8 +17,9 @@ ExecStartPre=/usr/bin/install --owner=root --group=root --mode=640 --directory / 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 +;LogsDirectory=clamav ConfigurationDirectory=clamav +;RuntimeDirectory=clamav #============================# # Security Hardening Options # @@ -32,7 +33,7 @@ ProtectSystem=strict ProtectHome=read-only PrivateTmp=yes PrivateDevices=yes -ReadWritePaths=/var/local/quarantine +ReadWritePaths=/var/local/quarantine /var/log/clamav NoExecPaths=/ # WARNING: To execute targets of `VirusEvent` and `VirusAction` directives diff --git a/freshclam/clamav-freshclam-once.service.in b/freshclam/clamav-freshclam-once.service.in index dfd372584d..3f915949e4 100644 --- a/freshclam/clamav-freshclam-once.service.in +++ b/freshclam/clamav-freshclam-once.service.in @@ -14,7 +14,8 @@ Group=clamav ;DynamicUser=yes ExecStart=@prefix@/bin/freshclam LogsDirectory=clamav -;ConfigurationDirectory=clamav +ConfigurationDirectory=clamav +RuntimeDirectory=clamav #============================# # Security Hardening Options # diff --git a/freshclam/clamav-freshclam.service.in b/freshclam/clamav-freshclam.service.in index cba75a9f1d..20f95918cb 100644 --- a/freshclam/clamav-freshclam.service.in +++ b/freshclam/clamav-freshclam.service.in @@ -15,6 +15,7 @@ Group=clamav ExecStart=@prefix@/bin/freshclam -d --foreground=true LogsDirectory=clamav ConfigurationDirectory=clamav +RuntimeDirectory=clamav #============================# # Security Hardening Options #