← Start of tutorial

WIUNIX ACADEMY

systemd hardening: prove the sandbox, then protect the real service

Page 2 of 3. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Rocky Linux 10Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment3 editable values
06

LET’S BUILD

Check the host view after the transient process exits

The transient service has its own mount namespace; the host mount should still be writable. Check the allowed artifact, confirm no forbidden artifact exists and create a second host control file. Do not use an anonymous unit name that could collide with a concurrent task; the selected application-prefixed probe name must be unused before execution.

bash

Authorized Linux terminal

sudo test -s '/var/lib/academy-sandbox-lab/state/allowed-inside'
sudo test ! -e '/var/lib/academy-sandbox-lab/forbidden-inside'
sudo sh -c 'printf control > "$1/control-after"' sh '/var/lib/academy-sandbox-lab'
sudo stat -c '%U %G %a %n' '/var/lib/academy-sandbox-lab/control-after'
How do I know it worked?

Only the intended state artifact exists and the host remains writable outside the ended namespace.

Translate the probe into the application’s permission contract

A real service should use its existing dedicated account, which is deliberately not changed here. Choose the narrow state path /var/lib/example-app only after inventory. ReadWritePaths does not grant Unix ownership and cannot override a filesystem mounted read-only underneath. ProtectSystem does not block communication through an already accessible Unix socket. PrivateTmp can break applications that exchange temporary files with another service. Keep these compatibility constraints visible beside the change.

Path allow-lists can accumulate across drop-ins: adding this ReadWritePaths entry does not cancel a broader pre-existing exception. Inspect the merged unit and reconcile that exception with its owner. ProtectSystem also has documented API-filesystem and mount-propagation limits; PrivateTmp creates writable private temporary directories. The fixture demonstrates two selected write boundaries, not confinement of a malicious root process with retained capabilities.

07

LET’S BUILD

Reserve one new drop-in without overwriting existing policy

Only after the staging baseline and rollback window are agreed, create the selected unit’s drop-in directory and reserve the new file exclusively. Stop if that exact filename already exists; inspect the current owner instead of replacing it. Edit only this file. Do not change vendor unit files or use a broad revert operation that could erase someone else’s drop-ins.

bash

Authorized Linux terminal

sudo install -d -m 0755 '/etc/systemd/system/example-app.service.d'
sudo sh -c 'set -C; : > "$1"' sh '/etc/systemd/system/example-app.service.d/90-academy-hardening.conf' || exit 1
sudoedit '/etc/systemd/system/example-app.service.d/90-academy-hardening.conf'
How do I know it worked?

One new empty dedicated drop-in is open for editing; an existing same-name file causes refusal.

Start with a small explicitly writable surface

This is a starting set for an ordinary network application after compatibility review, not a universal hardening profile. It preserves the existing User and Group. Keep the path limited to actual persistent state; use the application’s native state/log directory machinery when appropriate. Do not add broad capability or syscall filters simply to improve a score. A tool that needs home files or shared temporary state requires a deliberate alternative.

text · /etc/systemd/system/example-app.service.d/90-academy-hardening.conf

File contents — not a shell command

[Service]
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/example-app
08

LET’S BUILD

Validate the merged unit before the controlled restart

Inspect the merged configuration and run the native verifier before a planned restart of this one service. The restart briefly interrupts it; it is not a rolling deployment and is unsuitable without the application’s own migration/quorum procedure. Unknown directives, ignored settings or namespace failures require investigation. A daemon reload alone does not retrofit sandboxing into an already running process.

bash

Authorized Linux terminal

sudo systemd-analyze verify 'example-app.service' || exit 1
sudo systemctl daemon-reload
sudo systemctl cat 'example-app.service'
sudo systemctl restart 'example-app.service'
sudo systemctl status 'example-app.service' --no-pager
sudo journalctl -u 'example-app.service' --since '-5 min' --no-pager -n 80
How do I know it worked?

The intended merged settings load and the restarted service is healthy; application write/read/network operations must still be checked.

Test permitted behavior and a real denied boundary

Check an ordinary application transaction, persistence across the controlled restart, log delivery, DNS/network access and required Unix sockets. Then run the application’s own non-destructive negative probe for a path it must not write. The earlier transient probe proves the host mechanism, not compatibility or full confinement of the real application. Do not claim every configured sandbox directive took effect merely because systemctl reports active.

Explore all tutorials ↗