6 stable releases
Uses new Rust 2024
new 2025.4.12 | Apr 12, 2025 |
---|---|
2025.3.13 | Mar 12, 2025 |
2025.2.7 | Feb 7, 2025 |
2025.2.6 | Feb 6, 2025 |
2025.1.16 | Jan 16, 2025 |
#126 in Command line utilities
109 downloads per month
370KB
8K
SLoC
SHH (Systemd Hardening Helper)
Automatic systemd service hardening guided by strace profiling.
Official repository - Mirror repository
Documentation
- High level introduction: Systemd hardening made easy with SHH
- FAQ
- Changelog
- Currently supported systemd options
Installation
Dependencies
Strace needs to be installed and its executable available in the path. A recent Strace version 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
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
:
- Start service profiling:
shh service start-profile SERVICE
. The service will be restarted with strace profiling. - Use the service normally for a while, trying to cover as much features and use cases as possible.
- 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
Dependencies
~14–30MB
~418K SLoC