systemd Service Sandboxing

PrivateTmp, PrivateNetwork, ProtectSystem, NoNewPrivileges, and systemd-analyze security.

Overview

systemd provides a rich set of sandboxing directives that restrict what a service can access — without writing custom seccomp profiles or AppArmor rules. These directives leverage the same Linux kernel primitives (namespaces, seccomp, cgroups, capabilities) used by containers, but are configured declaratively in unit files.

Effective sandboxing follows least privilege: each service should have exactly the access it needs and nothing more. A compromised web server that cannot access /home/, /root, or the network (if it only serves static files) has severely limited blast radius.

Core Sandboxing Directives

Filesystem isolation: - PrivateTmp=yes — the service gets its own private /tmp and /var/tmp. Other services cannot access its temp files. - ProtectSystem=strict — mounts /usr, /boot, and /etc read-only. The service cannot modify system files. - ProtectHome=yes — makes /home, /root, and /run/user inaccessible. - ReadOnlyPaths= — make specific paths read-only. - ReadWritePaths= — re-grant write access to specific paths (used together with ProtectSystem=strict).

Privilege isolation: - NoNewPrivileges=yes — the service cannot gain new privileges via setuid binaries or file capabilities. This single directive blocks a large class of privilege escalation attacks. - User= / Group= — run the service as a non-root user. - CapabilityBoundingSet= — restrict which capabilities the service can ever hold.

Network isolation: - PrivateNetwork=yes — the service gets its own network namespace with only a loopback interface. Cannot make outbound connections or listen on ports.

Auditing Security Score

systemd-analyze security prints a security score and lists which sandboxing directives are missing. A score of 10 (worst) means no sandboxing at all; 0.0 (best) is fully sandboxed.

Key concepts

PrivateTmp=yes
Service gets its own private /tmp and /var/tmp via a mount namespace. Other services cannot read its temp files. Prevents /tmp race conditions and information leaks.
ProtectSystem=strict
Mounts /usr, /boot, and /etc read-only. The service cannot write to system directories. Combine with ReadWritePaths= to re-grant write access to specific paths.
ProtectHome=yes
Makes /home, /root, and /run/user inaccessible (tmpfs overlay). Service cannot read user home directories or SSH keys.
NoNewPrivileges=yes
Sets the PR_SET_NO_NEW_PRIVS bit. The process and all its children cannot gain new privileges via setuid executables or file capabilities. Blocks most local privilege escalation.
PrivateNetwork=yes
Gives the service its own network namespace with only loopback. Cannot connect to the network, listen on external ports, or interact with other services over the network.
systemd-analyze security
Security auditing command that scores a service from 0 (best) to 10 (worst) based on which sandboxing directives are applied. Lists missing directives with their risk contribution.

When to use it

  • Hardening a third-party service unit — add PrivateTmp, NoNewPrivileges, ProtectSystem=strict as a baseline
  • Auditing service security posture — systemd-analyze security nginx to see score and missing directives
  • Preventing a compromised service from accessing other services tmp files — PrivateTmp=yes
  • Blocking a service from making outbound network connections (e.g., a local metrics exporter) — PrivateNetwork=yes
  • Running a service as non-root with filesystem write access only to its data directory — User=appuser + ReadWritePaths=/var/lib/myapp

Interview questions

  • How do you harden a systemd service unit for a web application? Show the unit file directives.
  • What does systemd-analyze security show and how do you use it to improve a unit?

Sign up free to read the full topic

More Linux topics

Start free on Cariara · Coding problems · System design studio