#systemd #strace #system #security #hardening

app systemd-hardening-helper

Automatic systemd service hardening guided by strace profiling

3 stable releases

2025.2.7 Feb 7, 2025
2025.2.6 Feb 6, 2025
2025.1.16 Jan 16, 2025

#235 in Command line utilities

Download history 109/week @ 2025-01-14 11/week @ 2025-01-21 254/week @ 2025-02-04 11/week @ 2025-02-11

282 downloads per month

GPL-3.0-only

375KB
8K SLoC

SHH (Systemd Hardening Helper)

CI status crates.io version AUR version License

Automatic systemd service hardening guided by strace profiling.

Official repository - Mirror repository

Documentation

Installation

Dependencies

Strace needs to be installed and available in the path. Strace version >=6.4 is strongly recommended.

From source

You need a Rust build environment for example from rustup.

Run in the current repository:

cargo build --release
install -Dm 755 -t /usr/local/bin target/release/shh

or from crates.io:

sudo cargo install --root /usr/local

Debian (or Debian based distribution)

See GitHub releases for Debian packages built for each tagged version.

Arch Linux

Arch Linux users can install the shh AUR package.

Usage

Hardening a service

To harden a system unit named SERVICE.service:

  1. Start service profiling: shh service start-profile SERVICE. The service will be restarted with strace profiling.
  2. Use the service normally for a while, trying to cover as much features and use cases as possible.
  3. Run shh service finish-profile SERVICE -a. The service will be restarted with a hardened configuration built from previous runtime profiling, to allow it to run safely as was observed during the profiling period, and to deny other dangerous system actions.

Run shh -h for full command line reference, or append -h to a subcommand to get help.

Services running in per-user instances of the service manager (controlled via systemctl --user ...) are not supported.

[!WARNING] The hardening options generated by shh are by construction not portable across different systems. They depend on many factors, and may break the service if any of those change:

  • the code path covered during profiling
  • the Linux kernel version
  • the libc used
  • the systemd version

Reusing options generated by shh on a system with a different environment (ie. different Linux distribution) is very likely to break the service.

Testing locally

If you want to run a quick test to see what options would be generated, you can use shh run -- COMMAND.

Current directory and PATH environment variable both influence the program execution, reset those first:

$ cd /
export PATH=/usr/local/bin:/usr/bin:/bin

Then to see what options would be generated for a curl https://www.example.com invocation:

$ shh run -- curl https://www.example.com
...
-------- Start of suggested service options --------
ProtectSystem=strict
ProtectHome=true
PrivateTmp=disconnected
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectProc=ptraceable
LockPersonality=true
RestrictRealtime=true
ProtectClock=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX
SocketBindDeny=ipv4:tcp
SocketBindDeny=ipv4:udp
SocketBindDeny=ipv6:tcp
SocketBindDeny=ipv6:udp
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN CAP_MKNOD CAP_NET_RAW CAP_PERFMON CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_NICE CAP_SYS_PACCT CAP_SYS_PTRACE CAP_SYS_TIME CAP_SYSLOG CAP_WAKE_ALARM
SystemCallFilter=~@aio:EPERM @chown:EPERM @clock:EPERM @cpu-emulation:EPERM @debug:EPERM @ipc:EPERM @keyring:EPERM @memlock:EPERM @module:EPERM @mount:EPERM @obsolete:EPERM @pkey:EPERM @privileged:EPERM @raw-io:EPERM @reboot:EPERM @resources:EPERM @sandbox:EPERM @setuid:EPERM @swap:EPERM @sync:EPERM @timer:EPERM
-------- End of suggested service options --------

Or to sandbox as much as possible:

$ shh run --mode aggressive --filesystem-whitelisting --network-firewalling -- curl https://www.example.com -o /dev/null
...
-------- Start of suggested service options --------
ProtectSystem=strict
ProtectHome=true
PrivateTmp=disconnected
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectProc=ptraceable
LockPersonality=true
RestrictRealtime=true
ProtectClock=true
MemoryDenyWriteExecute=true
SystemCallArchitectures=native
ReadOnlyPaths=-/
ReadWritePaths=-/dev
InaccessiblePaths=-/boot -/home -/lost+found -/media -/mnt -/opt -/root -/srv -/sys -/tmp -/var
TemporaryFileSystem=/usr:ro
BindReadOnlyPaths=-/usr/bin -/usr/lib -/usr/lib64 -/usr/local -/usr/share
NoExecPaths=-/
ExecPaths=-/usr/bin/curl -/usr/lib/x86_64-linux-gnu
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX
SocketBindDeny=ipv4:tcp
SocketBindDeny=ipv4:udp
SocketBindDeny=ipv6:tcp
SocketBindDeny=ipv6:udp
IPAddressDeny=any
IPAddressAllow=[redacted]
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN CAP_MKNOD CAP_NET_RAW CAP_PERFMON CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_NICE CAP_SYS_PACCT CAP_SYS_PTRACE CAP_SYS_TIME CAP_SYSLOG CAP_WAKE_ALARM
SystemCallFilter=~@aio:EPERM @chown:EPERM @clock:EPERM @cpu-emulation:EPERM @debug:EPERM @ipc:EPERM @keyring:EPERM @memlock:EPERM @module:EPERM @mount:EPERM @obsolete:EPERM @pkey:EPERM @privileged:EPERM @raw-io:EPERM @reboot:EPERM @resources:EPERM @sandbox:EPERM @setuid:EPERM @swap:EPERM @sync:EPERM @timer:EPERM
-------- End of suggested service options --------

License

GPLv3

Dependencies

~13–25MB
~398K SLoC