This page is about making services run by systemd more secure by restricting what they can do. This is sometimes called "hardening" or "sandboxing".
A significant benefit of systemd is the ability to restrict the access that services are granted to the system.
Contents
Overview of hardening in systemd
Because most services need to use some features only available to root, a single compromised service can often allow an attacker to take over the whole system. Systemd gives you several options that can be used to limit what a service can do. This does not mitigate all issues: proper security still needs people to design services securely and implement the design correctly.
These hardening features include:
- systemd manages the daemon startup process
systemd-ask-password and associated programs allow passwords to be read without giving services access to the sysadmin terminal
several security settings (sometimes called hardening directives) allow services to be run as non-root users, in a SELinux security context, or without restrict access to parts of the filesystem or to system calls. See systemd.exec(5) for full details.
Showing the hardening settings in use
The following command shows a list of all units and rates them based on the hardening directives used (a lower score is better):
systemd-analyze security
The following command shows the details of the ssh service:
systemd-analyze security ssh.service
Generally you want to decrease the numbers as much as possible, without breaking functionality.
Hardening standard services
If the service is controlling a traditional daemon (for example, a long-running program that runs in the background and which does not need to access files other than its configuration in /etc) you can usually use several hardening features. If it is a 'oneshot' program that runs on-demand and then exits there are some unfortunate issues with sending email that limit what can be used (see below).
Restricting Filesystem access
You can block services from accessing specific directories.
The following is recommended for every service that doesn't directly interact with user files. For examples, most services do not need to access any files in user home directories by default:
ProtectHome=true
The following setting means each service sees its own version of /tmp, and cannot see files in /tmp created by other services or users. This can prevent a service being attacked by a user or another service via /tmp race conditions. It's unlikely to cause any problems:
PrivateTmp=true
General settings
The following settings are good for protecting the overall OS installation. You can use usually use these:
ProtectKernelLogs=true ProtectControlGroups=true ProtectKernelModules=true MemoryDenyWriteExecute=true ProtectHostname=true LockPersonality=true RestrictRealtime=true DevicePolicy=closed ProtectClock=true RestrictSUIDSGID=true ProtectKernelTunables=true PrivateDevices=true
The following is good for daemons which need no networking. This allows Unix domain sockets by filesystem (EG /run/$DAEMON/whatever.sock) but not the abstract socket namespace. The risk of this is PAM services that use networking or other indirect network access. This is a good option for the sysadmin who is locking down their own system but risky for the package maintainer.
PrivateNetwork=true
The NoNewPrivileges option prevents a daemon from running a SETUID program to gain privileges that are undesired. But there are some corner cases for this such as running a program that has a SE Linux automatic domain transition to LESS privileges can be denied. This cannot be used with services that want to send email via exim.
NoNewPrivileges=true
See systemd.directives(7) for a full list.
Restricting Capabilities
The CapabilityBoundingSet controls the capabilities that a service can use. There is a full list of capabilities with their descriptions in the file /usr/include/linux/capability.h in the linux-libc-dev package, and some limited documentation in capabilities(7). Unfortunately, working out which capabilities a service needs is difficult: if something doesnt work, you blocked too much, but usually the logs don't indicate what was missing or even what capability was missing.
You can use an allowlist (space separated on a single line) or blocklist (prefix with a ~). Usually it's best to just list the capabilities that are to be permitted as it's a smaller set.
Here is an example of allowing a daemon to use DAC_OVERRIDE (allows a UID 0 process to read files even if the filesystem permissions would deny access), SETUID and SETGID (for dropping privileges), PTRACE, and memory locking. All other capabilities will be denied:
CapabilityBoundingSet=CAP_DAC_OVERRIDE CAP_SETGID CAP_SETUID CAP_SYS_PTRACE CAP_IPC_LOCK
Blocking individual system calls
Systemd can restrict access to individual system calls. Filtering system calls has some overlap with restricting capabilities but it's good to restrict things in two ways. It usually isn't difficult to map error messages logged by a daemon to which groups of syscalls were filtered and then do a binary search for the most restrictive options.
The following example blocks access to some system calls that are known to be risky (such as the @clock group and the @mount group), some system calls that aren't usually needed (such as @cpu-emulation and @raw-io).
SystemCallFilter=~@mount @cpu-emulation @debug @raw-io @reboot @resources @swap @module @obsolete @clock
Security hardening and oneshot services
If you have a Type=?OneShot service that wants to send email via sendmail or similar then only a few hardening settings can be used.
The following works with all debian's mta in their default configuration - but setting the capabilities is perhaps too risky to be in debian!
Type=oneshot ExecStart=/usr/bin/myprog # you _need_ to add this to allow the mail to be delivered by exim4 - # otherwise it sits in the queue ExecStart=sleep 10s # Hardening options for this unit are limited because anything setting # NoNewPrivilages will break exim's ability to deliver the email: exim # wants to change user to transmit and deliver the email :( # (If User=root is set then some of the options marked 'cannot set' # do become settable - but may not have any effect) #User=root # cannot set: DynamicUser=yes # cannot set: PrivateUsers=true ProtectSystem=strict StateDirectory=whatever # exim needs to spool and log the email ReadWritePaths=-/var/spool -/var/mail -/var/log ## exim allows you to disable these next two ReadWritePaths # but other mta may want to write other dirs (eg: courier needs /var/lib/courier) ReadWritePaths=-/var/lib # some mta deliver to /home/<user>/Maildir (may be needed for bounce messages) # (for exim, ProtectHome=read-only is enough) ProtectHome=false PrivateTmp=true PrivateMounts=true # cannot set: PrivateDevices=true (unless we also set User=root) # even with PrivateDevices, some of these four are needed or exim's log does not contain the '<=' and '=>' lines # DevicePolicy=closed DevicePolicy=strict DeviceAllow=/dev/stdout w DeviceAllow=/dev/stdin r DeviceAllow=/dev/stderr w DeviceAllow=/dev/null rw ProtectProc=invisible ProcSubset=pid RemoveIPC=true ProtectControlGroups=true # cannot set: RestrictNamespaces=true # cannot set: LockPersonality=true # cannot set: ProtectKernelTunables=true # cannot set: ProtectKernelModules=true # cannot set: ProtectKernelLogs=true # cannot set: ProtectHostname=true # cannot set: ProtectClock=true # cannot set: RestrictRealtime=true # cannot set: MemoryDenyWriteExecute=true # do not want: IPAddressDeny=any # do not want: PrivateNetwork=true # do not want, and cannot set: RestrictAddressFamilies=none # cannot set: NoNewPrivileges=true # cannot set: RestrictSUIDSGID=true # cannot set: NoNewPrivileges=true # cannot set: RestrictSUIDSGID=true AmbientCapabilities= # exim (and most mta) works with just these: needs to change ownership of mail-related files CapabilityBoundingSet=CAP_SETGID CapabilityBoundingSet=CAP_SETUID CapabilityBoundingSet=CAP_FSETID CapabilityBoundingSet=CAP_CHOWN CapabilityBoundingSet=CAP_DAC_OVERRIDE CapabilityBoundingSet=CAP_FOWNER # cannot set: SystemCallArchitectures=native # cannot set: SystemCallFilter=@system-service # cannot set (mail is frozen): SystemCallFilter=~@privileged # cannot set: SystemCallFilter=~@resources
CategorySystemSecurity | CategorySystemAdministration | CategoryPackaging
