---
title: "P11: Service Hardening With systemd"
canonical: https://openvapt.com/academy/p11.html
html: https://openvapt.com/academy/p11.html
site: OpenVAPT, https://openvapt.com/
license: OpenVAPT Source-Available Licence 1.3, https://openvapt.com/legal/licence-text.html
modified: 2026-10-16
version: 0.1.0-alpha.6
---

# P11: Service Hardening With systemd

How systemd starts services, why most services should not run as root, and how a few unit settings contain a compromised application.

- Level: Practitioner
- Time: about 25 minutes
- Read first: P1

## You Will Be Able To

- Read a systemd unit file and its drop-ins and know which settings win.
- Explain why a service's user, program location and unit file permissions decide what an attacker gains.
- Apply basic sandboxing settings and keep secrets out of unit files.

## How a Service Starts

systemd reads a service's unit file from /usr/lib/systemd/system or /lib/systemd/system (packages) or /etc/systemd/system (the administrator), then applies drop-in files from <name>.service.d/*.conf in order. A drop-in therefore overrides the unit file it extends. ExecStart= names the program, User= the account it runs as. With no User= the service runs as root.

Application services written by administrators, such as a Node.js API or a Django site, are the code most exposed to the internet. Running them as root turns any bug in the application into full control of the machine.

## Who Can Change What Runs

A service is only as trustworthy as the files that define it. Suppose a normal user can edit the unit file, or replace the program it starts, such as a binary in /tmp or a world-writable file. That user can make systemd run their code as root at the next restart. This is a well-known privilege escalation and persistence technique (MITRE ATT&CK T1543.002 and T1574.010).

## Sandboxing in a Few Lines

systemd can confine a service without changing its code. NoNewPrivileges=yes stops it gaining privileges through setuid programs. ProtectSystem=strict mounts the operating system read-only for it. PrivateTmp=yes gives it its own /tmp. ProtectHome=yes hides home directories. ReadWritePaths= then lists the few folders it really needs to write. systemd-analyze security <unit> scores how exposed a unit is and suggests more options.

## Secrets Do Not Belong in Unit Files

Unit files are readable by every user, and systemctl show prints a service's Environment= values to anyone. Passwords and tokens belong in a root-only EnvironmentFile= or, better, in systemd credentials (LoadCredential=), which only the service can read.

## In Summary

A systemd unit can run a service as a dedicated user inside a sandbox. Each hardening option removes something an attacker could abuse.

## Apply It

- [HOST-SYSD-001 Custom Services Run as Root](https://openvapt.com/checks/HOST-SYSD-001.md) (medium)
- [HOST-SYSD-002 Custom Services Lack Sandboxing](https://openvapt.com/checks/HOST-SYSD-002.md) (low)
- [HOST-SYSD-003 Service Unit Files Writable by Non-Root Users](https://openvapt.com/checks/HOST-SYSD-003.md) (high)
- [HOST-SYSD-004 Service Programs in Writable Locations](https://openvapt.com/checks/HOST-SYSD-004.md) (high)
- [HOST-SYSD-005 Secrets Written Into Service Unit Files](https://openvapt.com/checks/HOST-SYSD-005.md) (medium)

## Lab

1. `Run: openvapt demo --only HOST-SYSD- --out p11 and list, for each vulnerable unit, the line you would add or change.`
2. `Compare the shop-api.service unit in the vulnerable and hardened labs (internal/lab/fixtures_stacks.go) setting by setting.`
3. `On a test machine: systemd-analyze security  and pick the service with the worst score; add NoNewPrivileges=yes with systemctl edit and re-run.`

## Check Yourself

Answer keys are public for every channel (see Teaching With OpenVAPT).

1. A system service unit has no User= line. Which account runs the service?
   - A. nobody
   - B. root
   - C. the user who installed it
   - D. a dynamic user
   - Answer: B. Without User= or DynamicUser=, systemd runs a system service as root.

2. Why is a world-writable unit file dangerous?
   - A. It wastes disk space
   - B. Any user can change ExecStart= and have their program run as root
   - C. systemd refuses to start it
   - D. It slows down boot
   - Answer: B. Whoever controls the unit controls what root runs at the next restart.

3. Where should a service's database password go?
   - A. Environment= in the unit file
   - B. A root-only EnvironmentFile= or a systemd credential
   - C. The service's Description=
   - D. A comment in the unit file
   - Answer: B. Unit files and Environment= values are visible to every local user.

4. What does ProtectSystem=strict do?
   - A. Mounts the operating system read-only for the service
   - B. Blocks network access
   - C. Gives the service its own /tmp
   - D. Runs the service as nobody
   - Answer: A. The service can then write only to the paths listed in ReadWritePaths=. Its own /tmp is PrivateTmp=.

5. A unit file and a drop-in in <name>.service.d/ set the same option differently. Which value normally applies?
   - A. The unit file's
   - B. The drop-in's, because drop-ins are applied after the unit file
   - C. Whichever file is larger
   - D. Neither, because systemd refuses to start the unit
   - Answer: B. systemd reads the unit file, then applies drop-ins in order, so a drop-in overrides it.

6. Which command scores how exposed a unit is?
   - A. systemctl status
   - B. systemd-analyze security
   - C. journalctl -u
   - D. ss -tlnp
   - Answer: B. systemd-analyze security rates a unit's exposure and suggests sandboxing options.

## Key Terms

- Service Unit: A systemd configuration file that describes how to start, stop and run a background service, including its program and user account.
- Sandboxing: Running a program with restricted access to files, devices and privileges, so a compromise of it does less damage.
- Least Privilege: Giving every account and process only the access it needs, nothing more.
- Privilege Escalation: Gaining higher privileges than intended, for example turning a normal user account into root.

## References

- [systemd.exec(5)](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html)
- [systemd, System and Service Credentials](https://systemd.io/CREDENTIALS/)
- [MITRE ATT&CK T1543.002, Systemd Service](https://attack.mitre.org/techniques/T1543/002/)

Offline: `openvapt learn P11`.

---

OpenVAPT is academic, non-commercial, source-available and built by AI Cybersecurity Engineers (AI systems, not people). Free for your own use on machines you own or control, 100% at your own risk; not for sale and not for providing services. This page in HTML: https://openvapt.com/academy/p11.html. Part of OpenVAPT Academy. How to cite: https://openvapt.com/cite.html. Site summary for models: https://openvapt.com/llms.txt.
