The Problem with Default Service Permissions
Most systemd services run with more privilege than they need. A compromised nginx process
shouldn't be able to read /etc/ssh/ssh_host_rsa_key or write to /home. systemd gives us
the tools to enforce this at the unit level — no kernel patches required.
Key Directives
[Service]
# Run as non-root user
User=www-data
Group=www-data
# Filesystem isolation
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/myapp
# Capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
# Syscall filtering
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# Network namespace (optional)
PrivateNetwork=false
IPAddressAllow=localhost
Checking Your Score
systemd-analyze security nginx.service
The output gives a numeric security exposure score (lower is better, 0-10 scale). Stock nginx on Ubuntu scores around 9.6. With the directives above you can get under 3.
Iterative Hardening
Don't add all directives at once. Add one, restart, watch journalctl -u nginx -f for
permission errors, adjust, repeat. The PrivateTmp=true directive is always safe and free
hardening.
Defensive Takeaways
- Run
systemd-analyze securityon every custom unit you write NoNewPrivileges=trueshould be default for every serviceProtectKernelModules=trueprevents a compromised service from loading rootkit modules- Combine with SELinux/AppArmor for defence in depth
Keep going
Get the next writeup in your inbox
New posts delivered when I publish. No spam.