From 31429319e738d78ed70323a9fb2a8e0cf4071b8c Mon Sep 17 00:00:00 2001 From: Michael Prokop Date: Thu, 6 Oct 2022 18:43:59 +0200 Subject: [PATCH] TT#76552 systemd: reduce hardening for sendmail(1) usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- debian/ngcp-panel.service | 84 +++++++++++++++------------------------ 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/debian/ngcp-panel.service b/debian/ngcp-panel.service index ad474034e6..881351643a 100644 --- a/debian/ngcp-panel.service +++ b/debian/ngcp-panel.service @@ -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