SystemD is loved by some, hated by others. It offers a wide range of features, beyond that of a simple init system. And, today I want to talk about service sandboxing. Now, I know what you’re going to say, why not use containers. Containers aren’t always an option, such as trying to improve security in an existing application deployment. With SystemD, sandboxing is a relatively simple process, just a simple text file defining what capabilities, system calls, and file system paths the service can write to. You can even go further, and run the service under a dynamically generated user account.
Below, is an example of a sandbox configuration for Apache on Ubuntu 18.04. You’ll notice several entries under SystemCallFilter. If you are running a newer distro, check your systemd man pages. It may offer more system call groups, making this configuration a bit simpler.
[Service] CapabilityBoundingSet=~CAP_SYS_ADMIN ProtectSystem=strict ProtectHome=yes ReadWritePaths=/run /var/log /var/www /var/cache /tmp /var/tmp PrivateDevices=yes ProtectClock=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes LockPersonality=yes [email protected] @cpu-emulation @debug @obsolete @module @mount @raw-io @reboot @swap SystemCallErrorNumber=EPERM
How do we go from sandbox config, to actual sandboxed service? Easy, just drop the config file into /etc/systemd/system/SOMESERVICE.service.d/sandbox.conf. You could use a different file name, but that seems most logical. Then, just use systemctl to reload, and then restart the service.
Disclaimer: The above is not the most secure configuration you could use. Or, it may be too secure for your environment. Adjust as needed.