Debian Service Isolation with Systemd ProtectSystem

Debian uses systemd’s built-in sandboxing features to isolate background services from the rest of the operating system, significantly reducing the attack surface of potential vulnerabilities. By leveraging Linux kernel namespaces, control groups, and capabilities, directives like ProtectSystem enforce filesystem restrictions directly at the unit level. This article explains how Debian implements these restrictions, how the ProtectSystem directive operates internally, and how it pairs with related systemd options to secure daemon execution without the overhead of complete containerization.

The Underlying Mechanism: Mount Namespaces

When systemd starts a service configured with sandboxing directives, it does not run the daemon directly in the host's global environment. Instead, it creates a dedicated mount namespace (CLONE_NEWNS) for the service process.

Within this private namespace, systemd modifies mount points, changing permissions or remounting specific directories. Because these modifications occur inside the process's unique namespace, they are completely invisible to other processes and leave the underlying Debian host filesystem untouched. Once the service terminates, its mount namespace is automatically torn down.

Understanding the ProtectSystem Directive

The ProtectSystem= directive protects critical operating system directories from accidental modification or deliberate tampering by a compromised service. It provides three primary levels of protection:

  • ProtectSystem=yes (or true): Mounts the /usr and /boot directories as read-only for the service. This prevents a service from modifying system binaries, libraries, or boot files.
  • ProtectSystem=full: Extends the protection of yes by also mounting the /etc configuration directory as read-only. This prevents daemons from altering configuration files for other services or the system itself.
  • ProtectSystem=strict: Mounts the entire filesystem hierarchy as read-only for the service, with the exception of API file systems such as /dev, /proc, and /sys. When using strict, any writable directories required by the service must be explicitly allowed using directives like ReadWritePaths=.

Complementary Sandboxing Directives

ProtectSystem is usually combined with other systemd directives to form a comprehensive sandbox on a Debian system:

  • ProtectHome=: Restricts access to /home, /root, and /run/user. Setting this to yes makes these paths completely inaccessible (InaccessiblePaths), while read-only limits write operations, and tmpfs mounts an empty temporary filesystem over them.
  • PrivateTmp=yes: Allocates a dedicated, isolated /tmp and /var/tmp directory inside the unit’s namespace, preventing services from reading temporary files created by other users or processes.
  • ReadWritePaths= and ReadOnlyPaths=: Provides granular control to override broad restrictions, enabling read-write access to specific operational folders like /var/log/servicename or /var/lib/servicename.
  • ProtectKernelTunables=yes and ProtectControlGroups=yes: Mounts /proc/sys, /sys, and /sys/fs/cgroup as read-only, preventing the daemon from altering kernel parameters or cgroup configurations.

Implementation in Debian

Debian utilizes systemd as its default init system, storing vendor-provided service units in /lib/systemd/system/. Administrators and package maintainers apply sandboxing configurations through drop-in override files to ensure modifications persist across package updates.

To apply ProtectSystem to an existing service on Debian, administrators run:

systemctl edit <service-name>

This creates an override file in /etc/systemd/system/<service-name>.service.d/override.conf, where sandboxing directives are defined:

[Service]
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/my-service /var/log/my-service

Once saved, reloading the systemd daemon (systemctl daemon-reload) and restarting the unit applies the namespace changes immediately, confining the service strictly to its designated boundaries.