systemd configuration examples, hopefully making your server and services a bit more secure.
Hardening Ubuntu. Systemd edition.
systemd for Administrators, Part XII: Securing Your Services
systemd upstream
systemd execution environment configuration
Since systemd
version 240, systemd-analyze
has the security
option for analyzing the security and sandboxing settings of services in order
to determine an exposure level value for them, indicating whether a service
would benefit from more sand-boxing options turned on for them.
systemd_scan.sh
is a script to check the current systemd
configuration of a local service,
similar to a very basic version of systemd-analyze security
.
$ bash systemd_scan.sh mongodb
[I] mongodb
[I] /lib/systemd/system/mongodb.service used.
[P] User is set.
[P] Group is set.
[P] CapabilityBoundingSet is set.
[F] PrivateTmp is not set.
[F] PrivateDevices is not set.
[F] NoNewPrivileges is not set.
[F] SELinuxContext is not set.
[F] AppArmorProfile is not set.
[P] LimitNOFILE is set.
[P] LimitNPROC is set.
[F] DynamicUser is not set.
[P] ProtectSystem=full is set.
[P] ProtectHome=true is set.
[I] 6 failures, 7 passed.
systemd-analyze security
calculates an exposure value based on the following
configuration options:
AmbientCapabilities=
CapabilityBoundingSet=
Delegate=
DeviceAllow=
IPAddressDeny=
KeyringMode=
LockPersonality=
MemoryDenyWriteExecute=
NoNewPrivileges=
NotifyAccess=
PrivateDevices=
PrivateMounts=
PrivateNetwork=
PrivateTmp=
PrivateUsers=
ProtectControlGroups=
ProtectHome=
ProtectHostname=
ProtectKernelLogs=
ProtectKernelModules=
ProtectKernelTunables=
ProtectSystem=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=/RootImage=
SystemCallArchitectures=
SystemCallFilter=
UMask=
User=/DynamicUser=
See journald.conf.
[Journal]
Compress=yes // (1)
ForwardToSyslog=yes // (2)
Storage=persistent // (3)
-
"data objects that shall be stored in the journal and are larger than a certain threshold are compressed before they are written to the file system."
-
"log messages received by the journal daemon shall be forwarded to a traditional syslog daemon"
-
"data will be stored preferably on disk, i.e. below the /var/log/journal hierarchy (which is created if needed), with a fallback to /run/log/journal (which is created if needed), during early boot and if the disk is not writable."
See coredump.conf.
[Coredump]
Storage=none // (1)
ProcessSizeMax=0 // (2)
-
"When "none", the core dumps will be logged but not stored permanently."
-
"Setting
Storage=none
andProcessSizeMax=0
disables all coredump handling except for a log entry."
See systemd.mount.
[Unit]
Description=Temporary Directory
Documentation=man:hier(7)
Before=local-fs.target
[Mount]
What=tmpfs // (1)
Where=/tmp // (2)
Type=tmpfs // (3)
Options=mode=1777,strictatime,nodev,noexec,nosuid // (4)(5)
-
"an absolute path of a device node, file or other resource to mount."
-
"an absolute path of a directory of the mount point."
-
"a string for the file system type."
-
"options to use when mounting."
See resolved.conf.
[Resolve]
DNS=127.0.0.1 // (1)
FallbackDNS=1.1.1.1 1.0.0.1 // (2)
DNSSEC=allow-downgrade // (3)
DNSOverTLS=opportunistic // (4)
-
"space-separated list of IPv4 and IPv6 addresses to use as system DNS servers."
-
"space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers."
-
"If set to
allow-downgrade
DNSSEC validation is attempted, but if the server does not support DNSSEC properly, DNSSEC mode is automatically disabled." Should be set totrue
if possible. -
"When set to
opportunistic
DNS request are attempted to send encrypted with DNS-over-TLS." Shoule be set totrue
if possible.
See system.conf and systemd, init.
[Manager]
DumpCore=no // (1)
CrashShell=no // (2)
DefaultLimitCORE=0 // (3)
DefaultLimitNOFILE=100 // (4)
DefaultLimitNPROC=100 // (5)
CtrlAltDelBurstAction=none // (6)
-
"If
yes
, the systemd manager (PID 1) dumps core when it crashes. Otherwise, no core dump is created." -
"If
yes
, the system manager (PID 1) spawns a shell when it crashes, after a 10s delay. Otherwise, no shell is spawned." -
Don’t allow daemons to core dump.
-
Default limit for number of open files.
-
Default limit for number of processes.
-
Defines what action will be performed if user presses Ctrl-Alt-Delete more than 7 times in 2s.
See timesyncd.conf.
[Time]
NTP=0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org // (1)
FallbackNTP=2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org // (2)
RootDistanceMaxSec=1 // (3)
-
"space-separated list of NTP server host names or IP addresses."
-
"space-separated list of NTP server host names or IP addresses to be used as the fallback NTP servers."
-
"Maximum acceptable root distance. Takes a time value (in seconds)."
See systemd.exec.
PrivateTmp= // (1)
ProtectSystem= // (2)
ProtectHome= // (3)
NoNewPrivileges= // (4)
ReadWriteDirectories=, ReadOnlyDirectories=, InaccessibleDirectories= // (5)
CapabilityBoundingSet= // (6)
PrivateDevices= // (7)
User=, Group= // (8)
DynamicUser= // (9)
TemporaryFileSystem= // (10)
PrivateUsers= // (11)
-
"Takes a boolean argument. If true, sets up a new file system namespace for the executed processes and mounts private
/tmp
and/var/tmp
directories inside it that is not shared by processes outside of the namespace." -
"If true, mounts the
/usr
and/boot
directories read-only for processes invoked by this unit. If set tofull
, the/etc
directory is mounted read-only, too. If set tostrict
the entire file system hierarchy is mounted read-only, except for the API file system subtrees/dev
,/proc
and/sys
." -
"Takes a boolean argument or the special values
read-only
ortmpfs
. Iftrue
, the directories/home
,/root
, and/run/user
are made inaccessible and empty for processes invoked by this unit. If set toread-only
, the three directories are made read-only instead. If set totmpfs
, temporary file systems are mounted on the three directories in read-only mode." -
"If
true
, ensures that the service process and all its children can never gain new privileges." -
"Sets up a new file system namespace for executed processes."
-
"Controls which capabilities to include in the capability bounding set for the executed process."
-
"If
true
, sets up a new/dev
namespace for the executed processes and only adds API pseudo devices such as/dev/null
,/dev/zero
or/dev/random
(as well as the pseudo TTY subsystem) to it" -
"Sets the Unix user or group that the processes are executed as, respectively"
-
"User and group pair is allocated dynamically when the unit is started, and released as soon as it is stopped."
-
"Takes a space-separated list of mount points for temporary file systems (tmpfs). If set, a new file system namespace is set up for executed processes, and a temporary file system is mounted on each mount point."
-
"Takes a boolean argument. If
true
, sets up a new user namespace for the executed processes and configures a minimal user and group mapping, that maps theroot
user and group as well as the unit’s own user and group to themselves and everything else to thenobody
user and group."
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=yes
ProtectSystem=full
ProtectHome=true
NoNewPrivileges=true
ReadOnlyDirectories=/var/www/html
CapabilityBoundingSet=~CAP_SYS_PTRACE
PrivateDevices=true
[Install]
WantedBy=multi-user.target
See mongo/pull/1224.
[Unit]
Description=MongoDB Database Server
After=network.target
Documentation=https://docs.mongodb.org/manual
[Service]
User=mongodb
Group=mongodb
ExecStart=/usr/bin/mongod --config /etc/mongod.conf
PIDFile=/var/run/mongodb/mongod.pid
LimitFSIZE=infinity
LimitCPU=infinity
LimitAS=infinity
LimitNOFILE=64000
LimitNPROC=64000
LimitMEMLOCK=infinity
TasksMax=infinity
TasksAccounting=false
ProtectSystem=full
ProtectHome=true
CapabilityBoundingSet=~CAP_SYS_PTRACE
[Install]
WantedBy=multi-user.target
See logind.conf.
[Login]
KillUserProcesses=1 // (1)
KillExcludeUsers=root // (2)
IdleAction=lock // (3)
IdleActionSec=15min // (4)
RemoveIPC=yes // (5)
-
"the processes of a user should be killed when the user completely logs out (i.e. after the user’s last session ended)."
-
"Processes of users listed in
KillExcludeUsers=
are excluded from being killed." -
"the action to take when the system is idle."
-
"the delay after which the action configured in
IdleAction=
(see above) is taken after the system is idle." -
"the user may not consume IPC resources after the last of the user’s sessions terminated."
See systemd-user.conf.
[Manager]
DefaultLimitCORE=0 // (1)
DefaultLimitNOFILE=100 // (2)
DefaultLimitNPROC=100 // (3)
CapabilityBoundingSet=~CAP_SYS_PTRACE // (4)
-
Don’t allow core dumps.
-
Default limit for number of open files.
-
Default limit for number of processes.
-
"capabilities to include in the capability bounding set." See capabilities(7).