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(ortrue): Mounts the/usrand/bootdirectories as read-only for the service. This prevents a service from modifying system binaries, libraries, or boot files.ProtectSystem=full: Extends the protection ofyesby also mounting the/etcconfiguration 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 usingstrict, any writable directories required by the service must be explicitly allowed using directives likeReadWritePaths=.
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 toyesmakes these paths completely inaccessible (InaccessiblePaths), whileread-onlylimits write operations, andtmpfsmounts an empty temporary filesystem over them.PrivateTmp=yes: Allocates a dedicated, isolated/tmpand/var/tmpdirectory inside the unit’s namespace, preventing services from reading temporary files created by other users or processes.ReadWritePaths=andReadOnlyPaths=: Provides granular control to override broad restrictions, enabling read-write access to specific operational folders like/var/log/servicenameor/var/lib/servicename.ProtectKernelTunables=yesandProtectControlGroups=yes: Mounts/proc/sys,/sys, and/sys/fs/cgroupas 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-serviceOnce 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.