Illustrative systemd lesson with an engineer at a laptop beside a transparent Service, Read Only and State teaching model.

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.

Advanced40 min readUpdated
Make this guide yours

YOUR SYSTEM. YOUR STEPS.

Where are you working?

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

AlmaLinux 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

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.

01

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.

Use an entitled RHEL repository or the matching official Rocky Linux/AlmaLinux repositories and a currently supported minor release. SELinux and system crypto policy remain enforced; do not weaken them to make a diagnostic pass.

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.

bash

Authorized terminal on the host identified in this section

cat /etc/os-release
rpm -q systemd coreutils
command -v systemctl systemd-run systemd-analyze
How do I know it worked?

Required packages and tools are identified; resolve missing tools before continuing.

02

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.

bash

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'
How do I know it worked?

The current effective configuration and application requirements are understood; an analyzer score is recorded only as configuration evidence.

03

LET’S BUILD

Check host policy independently

Correlate a denial with the exact process, path and failure time. An empty result or a failed audit query is not proof that SELinux is uninvolved. Inspect labels, process domain and installed policy; this step generates no allow policy and does not disable enforcement.The restorecon invocation is dry-run only. Unix ownership/mode, SELinux labeling and ReadWritePaths are separate controls; all must fit the actual application state directory.

bash

Authorized terminal on the host identified in this section

getenforce
sudo ausearch -m AVC,USER_AVC -ts recent
ls -Zd '/var/lib/example-app'
sudo restorecon -nRv '/var/lib/example-app'
How do I know it worked?

Any suspected policy failure is tied to specific evidence; enforcement and firewall configuration remain unchanged.

04

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.

bash

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'
How do I know it worked?

A newly created root-owned fixture contains the control file and an empty state directory.

05

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.

bash

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'
How do I know it worked?

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.

Explore all tutorials ↗