4 ms·
Oh yes, thank you for spreading the good news. 'systemd-analyze security' is a fine thing, just make sure to use the latest version of systemd because it was r
by 5e92cb50239222b 5y ago
Oh yes, thank you for spreading the good news.
'systemd-analyze security' is a fine thing, just make sure to use the latest version of systemd because it was really buggy in the past. For example, the version that ships in RHEL 8 is so buggy it's practically useless.
I came up with this for most of my services that do require a JIT compiler (so Java, dotnet, etc):
DynamicUser=yes
CapabilityBoundingSet=
DevicePolicy=closed
InaccessiblePaths=-/usr/bin /usr/sbin /mnt /media /var/www
LockPersonality=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateMounts=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@clock @cpu-emulation @privileged @module @raw-io @reboot @mount @obsolete @swap @debug
You might want to throw away InaccessiblePaths if your application calls external binaries. The stuff I typically write shouldn't do it.
Some of these flags are not strictly necessary because they should be enabled by other switches, but I prefer to keep them to make configuration more obvious and to mitigate possible bugs (there were some in the past).
If your application needs to store anything locally, add some combination of these:
RuntimeDirectory=appname # adds /var/run/appname
StateDirectory=appname # adds /var/lib/appname
CacheDirectory=appname # adds /var/cache/appname
LogsDirectory=appname # adds /var/log/appname
ConfigurationDirectory=appname # adds /etc/appname
and you can read the resulting path in environment variables RUNTIME_DIRECTORY / STATE_DIRECTORY / CACHE_DIRECTORY / LOGS_DIRECTORY / CONFIGURATION_DIRECTORY if your systemd is new enough.
systemd will make sure that your limited user can read and write these paths, including their content.
Add this if your application does not use a JIT compiler:
MemoryDenyWriteExecute=yes
And this to prevent it from listening on wrong ports in an event of misconfiguration.
SocketBindDeny=any
SocketBindAllow=tcp:5000
These firewalling flags can be useful if your service does not do much networking to external APIs:
IPAddressDeny=any
IPAddressAllow=localhost
IPAddressAllow=10.3.42.0/24
- hauleth 5y agoOh, I didn't know about `SocketBindDeny=` and `SocketBindAllow=`. This option may be a little troublesome in case of Distributed Erlang, but in recent versions it can be circumvented. Thanks, I will add it as a better option than adding capabilities.
- the8472 5y agoWith that many options it seems easy to miss something. A whitelist approach would be preferable. The application failing because you missed something would be more obvious than some subtle security hole.
- frankjr 5y agoThere's already a ticket precisely for that but it has been closed in as a duplicate https://github.com/systemd/systemd/issues/20247 https://github.com/systemd/systemd/issues/20247
- acdha 5y agoThis leaves me wishing there was some kind of report-only tooling where you could do something like, say, freeze the system calls used in a test run to reduce trial-and-error, similar to how you can use SELinux with audit2allow to get a decent starting point. Does anything like that already exist?