TT#76552 systemd: reduce hardening for sendmail(1) usage

If a customer has email templates enabled, adding a subscriber fails due to:

| Failed to create subscriber: error when closing pipe to sendmail:
| Trace begun at /usr/share/perl5/Email/Sender/Transport/Sendmail.pm line 94
| Email::Sender::Transport::Sendmail::send_email('Email::Sender::Transport::Sendmail=HASH(0x56251dc80e58)', 'Email::Abstract=ARRAY(0x56252f40f0c0)', 'HASH(0x56252f3fd600)') called at /usr/share/perl5/Email/Sender/Role/CommonSending.pm line 45
| Email::Sender::Role::CommonSending::try {...}  at /usr/share/perl5/Try/Tiny.pm line 102
| eval {...} at /usr/share/perl5/Try/Tiny.pm line 93
| [...]

Similar, sending subscriber reset password mails etc are also failing.

When strace-ing ngcp-panel's fcgi process, one can observe:

| 25173 openat(AT_FDCWD, "/var/spool/exim4//input//1ogR98-0006Y1-IA-D", O_RDWR|O_CREAT|O_EXCL, 0640) = -1 EACCES (Permission denied)

Now, when disabling all of the systemd hardening, it works fine.
Comparing such a working with a failing process, the following changes
within /proc/$PID/status can be observed:

| diff -urN proc.failing/status proc.working/status
| --- proc.failing/status 2022-10-06 18:06:08.519873211 +0200
| +++ proc.working/status 2022-10-06 18:08:17.071722642 +0200
| [...]
| CapInh:        0000000000000000
|  CapPrm:        0000000000000000
|  CapEff:        0000000000000000
| -CapBnd:        000001fbffffffff
| +CapBnd:        000001ffffffffff
|  CapAmb:        0000000000000000
| -NoNewPrivs:    1
| -Seccomp:       2
| -Seccomp_filters:       3
| +NoNewPrivs:    0
| +Seccomp:       0

So NoNewPrivs gets enabled, even though we don't explicitly set
NoNewPrivileges but e.g. only ProtectClock=yes. This is expected
behavior though, quoting from systemd.exec(5)

| NoNewPrivileges=
|
| Takes a boolean argument. If true, ensures that the service
| process and all its children can never gain new privileges through
| execve() (e.g. via setuid or setgid bits, or filesystem capabilities).
| This is the simplest and most effective way to ensure that a process and
| its children can never elevate privileges again. Defaults to false, but
| certain settings override this and ignore the value of this setting.
| This is the case when SystemCallFilter=, SystemCallArchitectures=,
| RestrictAddressFamilies=, RestrictNamespaces=, PrivateDevices=,
| ProtectKernelTunables=, ProtectKernelModules=, ProtectKernelLogs=,
| ProtectClock=, MemoryDenyWriteExecute=, RestrictRealtime=,
| RestrictSUIDSGID=, DynamicUser= or LockPersonality= are specified. Note
| that even if this setting is overridden by them, systemctl show shows
| the original value of this setting. Also see No New Privileges Flag[4].

Also see https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html

STR (without having to go through ngcp-panel and a customer/subscriber setup):

| root@spce:~# cat /etc/systemd/system/demo.service
| [Unit]
| Description=sendmail systemd hardening demo
|
| [Service]
| Type=simple
| User=www-data
| Group=www-data
| ExecStart=/usr/bin/perl /usr/bin/send_mail.pl
|
| # this one enough to trigger the problem
| ProtectClock=true
|
| root@spce:~# cat /usr/bin/send_mail.pl
| use strict;
| use Template;
| use Email::Sender::Simple qw();
| use Email::Simple;
| use Email::Simple::Creator;
| use Email::Sender::Transport::Sendmail qw();
|
| send_email(
|   subject => 'exim test',
|   body => 'ohai',
|   from => 'root@localhost',
|   to => 'foobar@example.org',
| );
| print "done\n";
| exit;
|
| sub send_email {
|     my %args = @_;
|     my $subject = $args{subject};
|     my $body = $args{body};
|     my $from = $args{from};
|     my $to = $args{to};
|
|     my $transport = Email::Sender::Transport::Sendmail->new;
|     my $email = Email::Simple->create(
|         header => [
|             To => $to,
|             From => $from,
|             Subject => $subject,
|         ],
|         body => $body,
|     );
|     return Email::Sender::Simple->send($email, { transport => $transport } );
| }
|
| root@spce:~# systemctl daemon-reload && systemctl restart demo

Until we switch the mail interface from sendmail(1) to SMTP or alike
(reported as MT#55576), we need to reduce the systemd hardening, to not
get the NoNewPrivileges=true (AKA prctl(PR_SET_NO_NEW_PRIVS,...))
behavior through any of the related hardening options.

New systemd hardening state of ngcp-panel (as of systemd v247.3-7+deb11u1):

| $ sudo SYSTEMD_COLORS=0 PAGER= COLUMNS=100 unbuffer systemd-analyze security ngcp-panel | grep -v '✓'
|   NAME                                         DESCRIPTION                                  EXPOSURE
| ✗ PrivateNetwork=                              Service has access to the host's network          0.5
| ✗ CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP) Service may change UID/GID identities/capab…      0.3
| ✗ CapabilityBoundingSet=~CAP_SYS_ADMIN         Service has administrator privileges              0.3
| ✗ CapabilityBoundingSet=~CAP_SYS_PTRACE        Service has ptrace() debugging abilities          0.3
| ✗ RestrictAddressFamilies=~AF_(INET|INET6)     Service may allocate Internet sockets             0.3
| ✗ RestrictNamespaces=~CLONE_NEWUSER            Service may create user namespaces                0.3
| ✗ RestrictAddressFamilies=~…                   Service may allocate exotic sockets               0.3
| ✗ CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SE… Service may change file ownership/access mo…      0.2
| ✗ CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IP… Service may override UNIX file/IPC permissi…      0.2
| ✗ CapabilityBoundingSet=~CAP_NET_ADMIN         Service has network configuration privileges      0.2
| ✗ CapabilityBoundingSet=~CAP_SYS_MODULE        Service may load kernel modules                   0.2
| ✗ CapabilityBoundingSet=~CAP_SYS_RAWIO         Service has raw I/O access                        0.2
| ✗ CapabilityBoundingSet=~CAP_SYS_TIME          Service processes may change the system clo…      0.2
| ✗ IPAddressDeny=                               Service does not define an IP address allow…      0.2
| ✗ NoNewPrivileges=                             Service processes may acquire new privileges      0.2
| ✗ PrivateDevices=                              Service potentially has access to hardware …      0.2
| ✗ PrivateUsers=                                Service has access to other users                 0.2
| ✗ ProtectClock=                                Service may write to the hardware clock or …      0.2
| ✗ ProtectKernelLogs=                           Service may read from or write to the kerne…      0.2
| ✗ ProtectKernelModules=                        Service may load or read kernel modules           0.2
| ✗ ProtectKernelTunables=                       Service may alter kernel tunables                 0.2
| ✗ RestrictAddressFamilies=~AF_PACKET           Service may allocate packet sockets               0.2
| ✗ RestrictSUIDSGID=                            Service may create SUID/SGID files                0.2
| ✗ SystemCallArchitectures=                     Service may execute system calls with all A…      0.2
| ✗ SystemCallFilter=~@clock                     Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@debug                     Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@module                    Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@mount                     Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@raw-io                    Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@reboot                    Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@swap                      Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@privileged                Service does not filter system calls              0.2
| ✗ SystemCallFilter=~@resources                 Service does not filter system calls              0.2
| ✗ CapabilityBoundingSet=~CAP_AUDIT_*           Service has audit subsystem access                0.1
| ✗ CapabilityBoundingSet=~CAP_KILL              Service may send UNIX signals to arbitrary …      0.1
| ✗ CapabilityBoundingSet=~CAP_MKNOD             Service may create device nodes                   0.1
| ✗ CapabilityBoundingSet=~CAP_NET_(BIND_SERVIC… Service has elevated networking privileges        0.1
| ✗ CapabilityBoundingSet=~CAP_SYSLOG            Service has access to kernel logging              0.1
| ✗ CapabilityBoundingSet=~CAP_SYS_(NICE|RESOUR… Service has privileges to change resource u…      0.1
| ✗ RestrictNamespaces=~CLONE_NEWCGROUP          Service may create cgroup namespaces              0.1
| ✗ RestrictNamespaces=~CLONE_NEWIPC             Service may create IPC namespaces                 0.1
| ✗ RestrictNamespaces=~CLONE_NEWNET             Service may create network namespaces             0.1
| ✗ RestrictNamespaces=~CLONE_NEWNS              Service may create file system namespaces         0.1
| ✗ RestrictNamespaces=~CLONE_NEWPID             Service may create process namespaces             0.1
| ✗ RestrictRealtime=                            Service may acquire realtime scheduling           0.1
| ✗ SystemCallFilter=~@cpu-emulation             Service does not filter system calls              0.1
| ✗ SystemCallFilter=~@obsolete                  Service does not filter system calls              0.1
| ✗ RestrictAddressFamilies=~AF_NETLINK          Service may allocate netlink sockets              0.1
| ✗ RootDirectory=/RootImage=                    Service runs within the host's root directo…      0.1
| ✗ CapabilityBoundingSet=~CAP_MAC_*             Service may adjust SMACK MAC                      0.1
| ✗ CapabilityBoundingSet=~CAP_SYS_BOOT          Service may issue reboot()                        0.1
| ✗ LockPersonality=                             Service may change ABI personality                0.1
| ✗ MemoryDenyWriteExecute=                      Service may create writable executable memo…      0.1
| ✗ RestrictNamespaces=~CLONE_NEWUTS             Service may create hostname namespaces            0.1
| ✗ CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE   Service may mark files immutable                  0.1
| ✗ CapabilityBoundingSet=~CAP_IPC_LOCK          Service may lock memory into RAM                  0.1
| ✗ CapabilityBoundingSet=~CAP_SYS_CHROOT        Service may issue chroot()                        0.1
| ✗ ProtectHostname=                             Service may change system host/domainname         0.1
| ✗ CapabilityBoundingSet=~CAP_BLOCK_SUSPEND     Service may establish wake locks                  0.1
| ✗ CapabilityBoundingSet=~CAP_LEASE             Service may create file leases                    0.1
| ✗ CapabilityBoundingSet=~CAP_SYS_PACCT         Service may use acct()                            0.1
| ✗ CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG    Service may issue vhangup()                       0.1
| ✗ CapabilityBoundingSet=~CAP_WAKE_ALARM        Service may program timers that wake up the…      0.1
| ✗ RestrictAddressFamilies=~AF_UNIX             Service may allocate local sockets                0.1
|
| → Overall exposure level for ngcp-panel.service: 7.9 EXPOSED 🙁

Related: MT#31936
Related: MT#55569
Thanks: Thomas Georg on spce-user ml for the bug report, and Victor Seva, Richard Fuchs + Rene Krenn for assistance in tracking this down

Change-Id: Ic3be62247bbcae00c99de3b779df838c0daaaf1a
mr11.2
Michael Prokop 3 years ago
parent a86e868a6f
commit 31429319e7

@ -16,14 +16,11 @@ RuntimeDirectoryPreserve=yes
PIDFile=/run/fastcgi/ngcp-panel.pid
ExecStart=/usr/share/ngcp-panel/ngcp_panel_fastcgi.pl --listen /run/fastcgi/ngcp-panel.sock --pidfile /run/fastcgi/ngcp-panel.pid --nproc $NPROC
# Service cannot create writable executable memory mappings that are writable and executable at the same time
MemoryDenyWriteExecute=true
# Files + directories not directly associated are made invisible in the /proc/ file system
ProcSubset=pid
# Writes to the hardware clock or system clock will be denied
ProtectClock=true
# Processes owned by other users are hidden from /proc/
ProtectProc=invisible
# Service cannot modify the control group file system (via /sys/fs/cgroup)
ProtectControlGroups=true
@ -31,50 +28,23 @@ ProtectControlGroups=true
# Service has no access to home directories
ProtectHome=true
# Set up new UTS namespace for the executed processes + changing hostname or domainname is prevented
ProtectHostname=true
# Service cannot load or read kernel modules
ProtectKernelModules=true
# Service cannot alter kernel tunables (/proc + /sys)
ProtectKernelTunables=true
# Service has strict read-only access to the OS file hierarchy
ProtectSystem=strict
# Access to the kernel log ring buffer will be denied
ProtectKernelLogs=true
# Processes owned by other users are hidden from /proc/
ProtectProc=invisible
# Service may execute system calls only with native ABI
SystemCallArchitectures=native
# Limit set of capabilities
CapabilityBoundingSet=
# Service process does not receive ambient capabilities
AmbientCapabilities=
# Service has no access to other software's temporary files
PrivateTmp=true
# Service has no access to hardware devices
PrivateDevices=true
# Service has strict read-only access to the OS file hierarchy
ProtectSystem=strict
# Limit write access
# NOTE: we need r/w access to ngcp-panel/Catalyst tmp folder
ReadWritePaths=/ngcp-data/tmp/www-data/
# NOTE: we need r/w access to /ngcp-data/spool/faxserver for sending fax
ReadWritePaths=-/ngcp-data/spool/faxserver
# Service cannot change ABI personality
LockPersonality=true
# Turn off acquisition of new privileges system-wide
NoNewPrivileges=true
# NOTE: we need r/w access for sendmail usage with exim
ReadWritePaths=-/var/spool/exim4/
ReadWritePaths=-/var/log/exim4/
# Service has own user namespace, only root, nobody, and the uid/gid under which the service is running are mapped
# NOTE: we can't have our own user namespace, as we need proper permissions e.g. to /ngcp-data/spool/faxserver
@ -84,18 +54,6 @@ PrivateUsers=false
# NOTE: service runs as root, so option does not matter
RemoveIPC=true
# Restrict service to allocation of local, ipv4 + ipv6 sockets
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
# Restrict access to the various process namespace types the Linux kernel provides
RestrictNamespaces=true
# Service may not acquire realtime scheduling
RestrictRealtime=true
# Attempts to set SUID or SGID bits on files or directories will be denied
RestrictSUIDSGID=true
# Files created by service are accessible only by service's own user by default
UMask=0077
@ -111,14 +69,36 @@ IPAddressAllow=any
# Maximum number of bytes of memory that may be locked into RAM
LimitMEMLOCK=0
# NOTE: we need to allow acquisition of new privileges, otherwise sendmail fails to work
NoNewPrivileges=false
# {{{
# NOTE: all of the the following hardenings need to stay disabled, as long as we use the
# sendmail(1) interface via perl's Email::Sender::Transport::Sendmail library
#CapabilityBoundingSet=
#LockPersonality=true
#MemoryDenyWriteExecute=true
#PrivateDevices=true
#ProtectClock=true
#ProtectHostname=true
#ProtectKernelLogs=true
#ProtectKernelModules=true
#ProtectKernelTunables=true
#RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
#RestrictNamespaces=true
#RestrictRealtime=true
#RestrictSUIDSGID=true
#SystemCallArchitectures=native
# Restrict system calls that are allowed to be executed
# NOTE: @system-service => reasonable set of system calls used by common system services
SystemCallFilter=@system-service
#SystemCallFilter=@system-service
# NOTE: return with ENOSYS instead of terminating the process immediately
SystemCallErrorNumber=ENOSYS
#SystemCallErrorNumber=ENOSYS
# All system calls except the listed ones will be logged
SystemCallLog=~@system-service seccomp
#SystemCallLog=~@system-service seccomp
# }}}
[Install]
WantedBy=multi-user.target

Loading…
Cancel
Save