WIUNIX ACADEMY
systemd hardening: prove the sandbox, then protect the real service
Use a contained mount-namespace probe, inventory application needs, and apply a reversible drop-in without treating an exposure score as proof.
Make this guide yoursYOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
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
THE OUTCOME
What will you have at the end?
Use a contained mount-namespace probe, inventory application needs, and apply a reversible drop-in without treating an exposure score as proof.
Supported operating systems and versions
Primary-documentation review; no production execution or universal installation test is claimed. Examples assume existing tools on Linux and must be adapted to authorized deployment context.
Before you begin
- Authorized operator access, existing tools named in the panels, and a private place to retain diagnostic evidence.
- Use a staging environment or an approved read-only production investigation; review disruptive changes separately.
A lower exposure score is not an acceptance test
Systemd hardening constrains an application’s execution environment; it does not repair vulnerable application code or replace access control. The security analyzer reports configured protections, not a penetration-test result. Start with a disposable, explicitly named transient probe and then inventory one real service. This guide uses the system manager, not an unprivileged user manager or a container without mount-namespace support.
LET’S BUILD
Check the selected distribution and required tools first
Run on the authorized diagnostic host or disposable lab VM. Compare the installed OS and required packages below with the selected path. Obtain missing tools from the distribution’s enabled official repositories through your approved package-management process; this workflow does not reinstall an existing server or switch its repository.
Ubuntu 26.10 is the reviewed default, with 26.04 LTS and 24.04 LTS alternatives. Check the actual package build and local configuration rather than assuming identical versions.
Mount namespaces and Unix permissions do not replace SELinux/AppArmor. Correlate active profile denials on Debian/Ubuntu; on RPM inspect the state path label and matching AVC separately. Do not put SELinux in permissive mode for the sandbox test. An exposure score is not application security acceptance.
Authorized terminal on the host identified in this section
cat /etc/os-release
dpkg-query -W systemd coreutils
command -v systemctl systemd-run systemd-analyzeRequired packages and tools are identified; resolve missing tools before continuing.
LET’S BUILD
Read the actual unit and application requirements
On a staging host with operator approval, inspect the selected unit, user, startup and drop-in paths. Unit files can contain private values; read locally and do not paste their full contents into a public report. Identify every required write path, Unix socket, device, address family, helper binary and privilege. A language runtime using JIT can be incompatible with blanket executable-memory restrictions.
Authorized Linux terminal
systemd --version
sudo systemctl cat 'example-app.service'
sudo systemctl show 'example-app.service' -p User -p Group -p FragmentPath -p DropInPaths -p MainPID -p ProtectSystem -p ReadWritePaths
sudo systemd-analyze security 'example-app.service'The current effective configuration and application requirements are understood; an analyzer score is recorded only as configuration evidence.
LET’S BUILD
Check host policy independently
Inspect kernel messages in the same failure window. On a host with active AppArmor profiles, a denial can be independent of Unix permissions or network reachability. Do not disable the profile just to pass the check; correlate it with application logs and the actual host policy.
Authorized terminal on the host identified in this section
sudo journalctl -k --since '-10 min' --no-pager -n 120Any suspected policy failure is tied to specific evidence; enforcement and firewall configuration remain unchanged.
LET’S BUILD
Create an exclusive lab directory and establish the control
The fixture contains no application data. Refuse an existing directory, create one root-owned directory and its state child, then prove root can normally write the parent. The upcoming probe intentionally runs as root to isolate mount protection from ordinary Unix ownership; it is not a recommendation to run your real application as root. Keep the lab path outside application state and confirm it is not a symlink or a mounted production dataset.
Authorized Linux terminal
sudo test ! -e '/var/lib/academy-sandbox-lab' || exit 1
sudo mkdir -m 0700 -- '/var/lib/academy-sandbox-lab' || exit 1
sudo mkdir -m 0700 -- '/var/lib/academy-sandbox-lab/state'
sudo sh -c 'printf control > "$1/control-before"' sh '/var/lib/academy-sandbox-lab'
sudo stat -c '%U %G %a %n' '/var/lib/academy-sandbox-lab' '/var/lib/academy-sandbox-lab/control-before'A newly created root-owned fixture contains the control file and an empty state directory.
LET’S BUILD
Prove denied and allowed writes inside the same sandbox
This transient oneshot mounts the hierarchy read-only for its child while allowing only the fixture’s state directory to be writable. A denied parent write must print an error such as read-only filesystem and an ALLOWED state write must succeed. If the denied write succeeds, the probe fails explicitly; inspect the host’s support instead of reporting a good score. No network listener is started.
Authorized Linux terminal
test "$(systemctl show -p LoadState --value 'example-app-academy-mount-probe.service')" = not-found || exit 1
sudo systemd-run --wait --pipe --collect --unit='example-app-academy-mount-probe' --property=Type=oneshot --property=ProtectSystem=strict --property='ReadWritePaths=/var/lib/academy-sandbox-lab/state' --property=NoNewPrivileges=yes --property=PrivateTmp=yes /bin/sh -c 'if touch "$1/forbidden-inside"; then echo UNEXPECTED_WRITE; exit 1; fi; printf allowed > "$1/state/allowed-inside"; test -s "$1/state/allowed-inside" || exit 1; echo ALLOWED_STATE_WRITE' sh '/var/lib/academy-sandbox-lab'The parent write is rejected by the read-only mount, the state write succeeds, and the transient unit exits0. Unsupported namespace/property errors are failures, not passed tests.