#1020328 Native systemd units

Package:
logcheck
Source:
logcheck
Submitter:
"Trent W. Buck"
Date:
2025-04-25 03:39:01 UTC
Severity:
wishlist
Tags:
#1020328#5
Date:
2022-09-20 07:02:23 UTC
From:
To:
Please find attached a logcheck.timer and logcheck.service.
I just wrote them; they Work For Meβ„’ so far.

If you just ship these, systemd-cron will automatically skip /etc/cron.d/logcheck.
Vixie cron might need something like this to manually skip /etc/cron.d/logcheck:

    if ! [ -e /run/systemd ]; then ... ; fi

The "Security hardening" section is too aggressive to work for postfix, exim, &c.
If you ship this, you'll want to skip some/all of the hardening for portability reasons.

logcheck calls /usr/bin/mime-construct, which ONLY supports
/usr/sbin/sendmail.  Replacing /usr/sbin/sendmail with SMTP
(e.g. python3's smtplib) is one way to keep maximum systemd-level
hardening, without needing a bunch of MTA-specific workarounds.

#1020328#10
Date:
2022-09-27 02:37:03 UTC
From:
To:
UPDATE: a debian/logcheck.tmpfiles (/etc/tmpfiles.d/logcheck.conf) is also needed.
The security hardening I added prevents logcheck from creating it.
See attached.

#1020328#15
Date:
2022-11-04 00:45:52 UTC
From:
To:
Hi trent - i am interested in this approach:

i see you are binding msmtp over /usr/sbin/sendmail  -  i dont
understand how this would lead to a different outcome: how else does
msmtp know where to send the mail? is there some implicit assumption
about local delivery here? i tried testing but msmtp  does not work at
all out of the box for me - complains that it has no configuration
file)

As far as i can tell, the issue isn't with the "send mail" part, but
the part where the mta (exim/postfix) tries to deliver it


(returning to  logcheck - id agree with losing the 'reboot' mail -
it's pretty clear from the log message that the system has been
rebooted, so not sure what value this is even under cron. I'd suggest
adding Persistent=true in the .timer's [Timer] so that it runs after a
resuming from suspend. Perhaps set it to skip if on battery too )

#1020328#20
Date:
2022-11-09 08:29:50 UTC
From:
To:
If you install msmtp you need to configure it.
Just as if you installed postfix, you would need to configure it.

"dpkg-reconfigure msmtp" ought to prompt you, but
here are some basic examples:

1.  $ sudo apt install msmtp postfix

    $ cat >/etc/msmtprc <<'EOF'
    # Send everything to postfix (smtp://localhost:25)
    account default
      syslog on
      auto_from on
      host localhost
    EOF

2.  $ sudo apt install msmtp-mta

    $ cat >/etc/msmtprc <<'EOF'
    # Send everything to gmail (no "real" MTA on localhost)
    account default
      syslog on
      auto_from on
      host smtp.gmail.com
      port 587
      tls on
      auth on
      user alice
      passwordeval /usr/bin/cat /etc/secret.gmail.password
    EOF

    $ printf swordfish >/etc/secret.gmail.password

    $ chmod 640 /etc/secret.gmail.password
    $ chown -h root:logcheck /etc/secret.gmail.password

I don't know about exim.

When /usr/sbin/sendmail is implemented by postfix (i.e. "apt install postfix),

    1. sendmail calls postdrop
    2. postdrop is sgid postdrop, so now you run with elevated privileges
    3. postdrop writes to /var/spool/postfix/maildrop, which normal users can't write to

Note the sgid bit:

    -rwxr-xr-x 1 root    root      /usr/sbin/sendmail
    -r-xr-sr-x 1 root    postdrop  /usr/sbin/postdrop
    drwx-wx--T 2 postfix postdrop  /var/spool/postfix/maildrop

If you use systemd hardening NoNewPrivileges=yes, that DISABLES SETGID -- by design.

    So logcheck.service run /usr/bin/logcheck
    which runs /usr/bin/mail
    which runs /usr/sbin/sendmail
    which runs /usr/sbin/postdrop
    which DOESN'T get escalated privileges (group postdrop)
    which FAILS to write to /var/spool/postfix/maildrop/<random file name>.

By telling logcheck.service "actually just use msmtp", the path instead becomes

    logcheck.service runs /usr/bin/logcheck
    which runs /usr/bin/mail
    which runs /usr/sbin/sendmail (actually /usr/bin/msmtp)
    which connects to (say) smtp://localhost:25 or smtp://smtp.gmail.com:587

Thereafter, the rest of the flow (whatever is listening on
localhost:25) is not running inside the logcheck.service hardened
namespace/cgroup.  So it can do whatever it wants.


In short, what I'm saying is:

  1. you can't harden a script/daemon that uses the "fork+exec /usr/sbin/sendmail" API, because
     different /usr/sbin/sendmail implementations (e.g. postfix) require different privileges.

     In particular, "requires setgid" prevents ALL of the following hardening options:

        DynamicUser             LockPersonality         MemoryDenyWriteExecute
        NoNewPrivileges         PrivateDevices          ProtectClock
        ProtectHostname         ProtectKernelLogs       ProtectKernelModules
        ProtectKernelTunables   RestrictAddressFamilies RestrictNamespaces
        RestrictRealtime        RestrictSUIDSGID        SystemCallArchitectures
        SystemCallFilter        SystemCallLog

  2. the smtp://localhost:25 API is usually available.

     It prevents fewer hardening options:

        PrivateNetwork=yes
        IPAddressDeny=any
        RestrictAddressFamilies=~AF_TCP

     Basically you have to leave TCP/IP unblocked, but that's all.

  3. msmtp is a quick and easy way to convert (1) to (2).

  4. "apt install msmtp-mta" does (3) easily, but
     won't work if a "real" MTA is already installed.

  5. BindReadOnlyPaths=/usr/bin/msmtp:/usr/sbin/sendmail does (3), and
     works even if a "real" MTA is installed.

#1020328#25
Date:
2022-11-09 08:37:34 UTC
From:
To:
I made a minor braino here, it should be AF_INET AF_INET6 not AF_TCP.

My old (Debian 9) notes about different techniques are here:

https://github.com/cyberitsolutions/prisonpc-systemd-lockdown/tree/main/systemd/system/0-EXAMPLES

    30-allow-mail-msmtp.conf:                         # β†’ Overall exposure level: 1.0 OK πŸ™‚

    30-allow-mail-postfix-via-msmtp.conf:             # β†’ Overall exposure level: 0.9 SAFE πŸ˜€

    30-allow-mail-postfix-root-dac-override.conf:     # β†’ Overall exposure level: 1.1 OK πŸ™‚
    30-allow-mail-postfix-root-sys-admin.conf:        # β†’ Overall exposure level: 1.4 OK πŸ™‚

    30-allow-mail-postfix-non-root-addgroup.conf:     # β†’ Overall exposure level: 0.5 SAFE πŸ˜€
    30-allow-mail-postfix-non-root-dac-override.conf: # β†’ Overall exposure level: 0.9 SAFE πŸ˜€
    30-allow-mail-postfix-non-root-setgid.conf:       # β†’ Overall exposure level: 2.4 OK πŸ™‚

#1020328#30
Date:
2022-12-07 23:46:43 UTC
From:
To:
This is an amazing resource! (did you consider trying to introduce it into
a debian package somehow?)

I have been studying and experimenting - and learning a lot.

For exim4, i found that it depends on
- whether the unit is 'oneshot' - if so the unit needs to ensure exim has
delivered the mail before the script exits - adding a small 'sleep' is
enough - i think otherwise systemd gets confused about what process to
monitor if the script causes exim to launch.
- whether or not the unit runs as root or a different user. With User=root
you can get away with more hardening directives, but i think better to
continue running as a non-root user

This all makes me want to abandon exim for postfix.... but exim is still
debian's default.

I think this is consistent with what you found for other mtas, but for
future reference and to help people searching for how to use exim in a
systemd unit, ive been using the following with exim:
------------------------------------------------ ExecStart=/usr/sbin/logcheck ExecStart=sleep 0.1s User=logcheck UMask=0066 ProtectSystem=strict ReadWritePaths=/var/lib/logcheck # for exim - probably debian should allow all of /var/spool and /var/log here ReadWritePaths=-/var/spool/exim4 -/var/mail -/var/log/exim4 # ProtectHome=true is possible, but the message will be # frozen as exim wants to cd to $HOME (for .forward) ProtectHome=read-only PrivateTmp=true PrivateMounts=true DevicePolicy=strict # *cannot set: PrivateDevices=true DeviceAllow=/dev/stdout w DeviceAllow=/dev/stdin r DeviceAllow=/dev/stderr w DeviceAllow=/dev/null rw ProtectProc=invisible ProcSubset=pid RemoveIPC=true ProtectControlGroups=true AmbientCapabilities= # exim needs to change ownership of mail - both when it # receives the mail and when it delivers it to the local user # see capabilities(7) CapabilityBoundingSet=CAP_SETGID CapabilityBoundingSet=CAP_SETUID CapabilityBoundingSet=CAP_FSETID CapabilityBoundingSet=CAP_CHOWN CapabilityBoundingSet=CAP_DAC_OVERRIDE CapabilityBoundingSet=CAP_FOWNER # Anything that implies NoNewPrivileges cannot be set # cannot set: NoNewPrivileges=yes # cannot set: DynamicUser=yes # cannot set: PrivateUsers=true # *cannot set: RestrictNamespaces=true # *cannot set: LockPersonality=true # *cannot set: ProtectKernelModules=true # *cannot set: ProtectKernelLogs=true # *cannot set: ProtectHostname=true # *cannot set: ProtectClock=true # *cannot set: RestrictRealtime=true # *cannot set: MemoryDenyWriteExecute=true # *cannot set - and would break remote delivery: RestrictAddressFamilies=AF_INET AF_INET6 # *cannot set: RestrictSUIDSGID=true # *cannot set: SystemCallArchitectures=native # *cannot set: SystemCallFilter=@system-service # cannot set (due to chown): SystemCallFilter=~@privileged # directives with a * can be set if we use User=root instead of User=logcheck ------------------------------------------------------------- This still needs the tmpfiles.d dropin from your earlier email. However, i wonder if it is better to make logcheck use a single dir for both the lockfile (currently /run/lock/logcheck) and the scratch dir ( currently created in /tmp each time) and then we could use a single RuntimeDirectory=logcheck with RuntimeDirectoryPreserve=yes (to ensure it is not deleted and no clashes if logcheck dies it would retain the current code in logcheck to use a random subdir and delete it on success)
#1020328#35
Date:
2022-12-08 05:40:22 UTC
From:
To:
I slurped your exim notes into my repo.
I probably won't do any actual testing with exim myself :-)

https://github.com/cyberitsolutions/prisonpc-systemd-lockdown/commits/main/systemd/system/0-EXAMPLES/30-allow-mail-exim.conf

I was indeed originally plannign to push it into Debian as a kind of "apt install increased-hardening" package.
But I ran into enough nitpicking and static, my current approach is to instead try to work on individual packages, and
try to push upstreams to be more hardened by default.

e.g. SyscallFilters=@foo isn't backwards-compatible with old systemd, so
you can't use it if you don't know a minimum systemd version.

e.g. Architecture=native works fine until someone does something like "apt install curl:armhf".

e.g. the whole "if you call /usr/sbin/sendmail, everything becomes messy"

That doesn't sound right, but I suppose it's possible.
KillMode=process might trigger that.
If you set User=root, then "systemctl daemon-reload" then "systemd-analyze security foo.service",
you will see a bunch of stuff like:

    Service runs as root, option does not matter
    Service runs as root, option does not apply

PS: "systemd-analyze syscall-filter" is a good thing to look at when dealing with chown/seteuid.

#1020328#46
Date:
2025-04-25 01:16:30 UTC
From:
To:
  I've added a very simple timer and service definition for
logcheck[0], which should make it into the trixie release.

  I'm hesitant to go too crazy adding systemd hardening options to the
service, although I'd be open to ones that don't require specific
changes to support a given MTA, such as some of the exim ones mentioned
in this bug report. Changes along these lines should probably go
through experimental first, then land in unstable after trixie is out
the door.

Mathias

[0] -- https://salsa.debian.org/debian/logcheck/-/commit/8d0dc9b30f508822ebe417866ccd2dc1395d117a

#1020328#51
Date:
2025-04-25 03:34:31 UTC
From:
To:
That is an entirely reasonable attitude.
Even with no hardening, it’s NO WORSE than crontab.

To reliably handle mail you need at least to avoid NoNewPrivileges (postfix maildrop needs setgid) and of course PrivateNetworking and TCP (msmtp).

In case it’s not obvious, systemd-analyze security foo.service lists hardening in descending utility (that is, most important first).