Run systemd-analyze security [UNIT...]
to check other available protections.
Examples
Create the user with $HOME
at /run/<username>
and configure RuntimeDirectory
, so that systemd will create and chown
the directory automatically.
Set RuntimeDirectoryPreserve
to no
to discard its content when service stops or restarts. If you need more persistence, use StateDirectory
and /var/lib/<username>
instead.
Grant CAP_NET_BIND_SERVICE
so that the service could bind to well-known ports (0 to 1023).
[Unit]
Description=<description>
After=network.target
[Service]
Type=simple
ExecStart=<start-command>
User=<user>
Group=<user>
# Grant user writable access to home and working directory that persists until system reboot,
# because /run is a mount point of "tmpfs".
WorkingDirectory=~
RuntimeDirectory=<username>
RuntimeDirectoryPreserve=yes
# Hardening
NoNewPrivileges=true
RestrictNamespaces=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictSUIDSGID=yes
# User isolation
PrivateTmp=yes
PrivateUsers=yes
ProtectHome=yes
# Mount entire file system hierarchy read-only as much as possible
ProtectSystem=strict
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
ProtectProc=invisible
# Limit capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# Grant capabilities
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
To use BindPaths=
, ProtectHome=tmpfs
should be used instead and DynamicUser=
should be avoided.
# Hardening
NoNewPrivileges=true
RestrictNamespaces=yes
RestrictAddressFamilies=AF_INET AF_INET6
RestrictSUIDSGID=yes
# User isolation
RemoveIPC=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectHome=tmpfs
BindPaths=/home/<user> /home/other/storage
# Mount entire file system hierarchy read-only as much as possible
ProtectSystem=strict
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
ProtectProc=invisible
# Limit capabilities
CapabilityBoundingSet=