A selection of miscellaneous door keys with the systemd logo in front.

systemd service sandboxing and security hardening 101

systemd enable services to run with a whole suite of hardening and sandboxing features from the Linux kernel. Here’s how to get a quick security review of the services running on your system and how to go about hardening their security.

The Linux kernel can filter and limit access to file systems, networks, devices, kernel capabilities and system calls (syscalls), and more. In this article, I’ll focus on sandboxing access to the file system as a simple introduction to systemd service security hardening.

The systemd service security review tool was added in version 240 (released in .) You’ll need to be running that or a later version to follow along with this article.

Security overview of systemd services

Run the systemd-analyze security command to get a security audit of all your systemd services. It will list each service unit and give them a security exposure score rating from 0–10. Lower scores are more secure.

UNIT             EXPOSURE PREDICATE
atd.service           9.6 UNSAFE
crond.service         9.6 UNSAFE
httpd.service         9.2 UNSAFE
mariadb.service       8.8 EXPOSED
sshd.service          9.6 UNSAFE

The exposure score is entirely based on a service’s utilization of security features provided by systemd. It doesn’t consider security features built-in to the program or enforced by access control policies like Security-Enhanced Linux (SELinux) or AppArmor. Nor does the score in any way evaluate the risk factors of a program or its configuration.

In the above example, you’ll notice that task scheduling daemons like atd and crond and the remote terminal access daemon for sshd are considered to be unsafe. That’s an accurate assessment as these services are designed to allow unrestricted execution of arbitrary commands. You may want to disable these services entirely unless you need them.

You can also see that the httpd (Apache HTTP Server) service has a slightly lower score. This is because Fedora Linux, which is the distribution I use, ships its httpd service unit with a few systemd security directives enabled by default. Let’s look at how to review a service’s security directives next.

Analyze a service’s security directives

You should start by analyzing any service that is exposed to the public internet or deal with untrusted/user-provided data. These services will benefit system security the most from being sandboxed. We looked at the Apache web server earlier, so let us stick with that example.

Run the systemd-analyze security httpd.service command to generate a report of the security directives applied to the httpd.service.

As of version 233, there are 77 tests in the security review. Below, I’ve included a small selection of test results that focus on file system access.

  NAME                   DESCRIPTION                                          EXPOSURE
 PrivateDevices         Service potentially has access to hardware devices        0.2
✓ PrivateTmp             Service has no access to other software's temporary files
 ProtectControlGroups   Service may modify to the control group file system       0.2
 ProtectHome            Service has full access to home directories               0.2
 ProtectKernelTunables  Service may alter kernel tunables                         0.2
 ProtectSystem          Service has full access to the OS file hierarchy          0.2
 RestrictSUIDSGID       Service may create SUID/SGID files                        0.2
 RootDirectory          Service runs within the host's root directory             0.1
…
→ Overall exposure level for httpd.service: 9.2 UNSAFE

We can see that Apache HTTP Server is restricted to its own sandboxed temp directory and that it doesn’t have access to the system /tmp directory. This is a default policy for httpd on Fedora Linux. You may get different results in your Linux distribution of choice.

The test results show us that our web server could definitively be configured to be more secure. There’s a lot of room for improvement, depending on your Apache HTTP Server configuration. Your web server probably doesn’t need write-access to most locations in your file hierarchy. It probably doesn’t even need read-access.

Let’s move on to tightening the security of the httpd.service.

Service unit security hardening

Run the systemctl edit httpd.service command to make changes to the httpd service unit. This will open a text editor where any change you make will override the service’s default directives. (It will use your default text editor as specified in the EDITOR environmental variable.)

The following is an example service unit override that addresses some of the points identified in the above security review. Please refer to the systemd.exec man page for extensive documentation on each directive.

[Service]
PrivateDevices=true
ProtectControlGroups=true
ProtectHome=true
ProtectKernelTunables=true
ProtectSystem=full
RestrictSUIDSGID=true

Save this to the override file, run the systemctl daemon-reload command to make systemd aware of the changes, and finally systemctl restart httpd.service to make the changes take effect. You should see about 2 points improvement if you re-run the service security directive testing.

You now need to do extensive testing to ensure that everything still works as expected. You’ve put in place restrictions that the program was never designed to run under. It or programs you run on top of it may misbehave under these conditions. You may need to grant it access to specific files or directories. Let’s look at how you do that.

If something breaks with the above configuration, it might suggest that you’ve tasked your web server to do something potentially risky.

Further locking down access

The ProtectSystem=full directive makes the /boot, /etc, and /usr directories read-only. This will prevent a compromised program from interfering with system configurations. However, a compromised program can still read and copy sensitive data such as your Let’s Encrypt certificates from the /etc/letsencrypt directory.

You can block access to specific directories by adding them to the InaccessiblePaths=-/etc/letsencrypt directive. You can specify multiple paths separated by space characters. Any file or directory under that path in the file hierarchy will be made inaccessible. The minus in front of the path tells systemd not to raise an error if the path doesn’t exist at runtime.

The InaccessiblePaths directive combined with the ones from the above example can be used to effectively prevent different processes from getting at each other’s data. You may not want to maintain a white-list per service. However, you should at least set up a block-list of sensitive locations and make them inaccessible to every other service.

You can take it one step further and manually specify ReadWritePaths, ReadOnlyPaths, and InaccessiblePaths. A utility like strace -e trace=%file program can help you identify what paths a program requires. Requirements will change over time so a setup like this requires ongoing monitoring.

You can go even further and change the root directory with the RootDirectory directive. This works like the traditional chroot command and lets you set up a mock file system and launch the service inside it.

Conclusions

systemd can offer extra protections for your custom services as well as services shipped by your Linux distributed. Your Linux distributions may ship services with little or no protections. Security needs differ from installation to installation so it’s up to the system administrator to enable security protections.

You can quickly strengthen your system security by deploying some of the standard sandboxing directives. It may just be the thing that saves your organization from having a compromised service turn into a security nightmare.

Read on to part two for a tutorial on more advanced systemd security directives.