From bb6a0f57e5c1a04c9c191af6a37184970003b1c2 Mon Sep 17 00:00:00 2001 From: "Heiko Schlittermann (HS12-RIPE)" Date: Sat, 22 Oct 2022 23:15:44 +0200 Subject: [PATCH] Add systemd units (examples) - daemon - socket activation - socket activation (inetd mode) - queuerunner - maintainance --- .gitignore | 2 +- configs/ABOUT | 11 ++- configs/system-integration/README.md | 8 ++ configs/system-integration/systemd/.gitignore | 1 + configs/system-integration/systemd/README.md | 86 +++++++++++++++++ .../systemd/daemon/exim.service | 29 ++++++ .../systemd/inetd/exim.socket | 11 +++ .../systemd/inetd/exim@.service | 27 ++++++ configs/system-integration/systemd/install | 92 +++++++++++++++++++ .../maintenance/exim-maintenance.service | 24 +++++ .../maintenance/exim-maintenance.timer | 11 +++ .../queuerunner/exim-queuerunner.service | 21 +++++ .../queuerunner/exim-queuerunner.timer | 11 +++ .../systemd/socket/exim.service | 26 ++++++ .../systemd/socket/exim.socket | 10 ++ 15 files changed, 364 insertions(+), 6 deletions(-) create mode 100644 configs/system-integration/README.md create mode 100644 configs/system-integration/systemd/.gitignore create mode 100644 configs/system-integration/systemd/README.md create mode 100644 configs/system-integration/systemd/daemon/exim.service create mode 100644 configs/system-integration/systemd/inetd/exim.socket create mode 100644 configs/system-integration/systemd/inetd/exim@.service create mode 100755 configs/system-integration/systemd/install create mode 100644 configs/system-integration/systemd/maintenance/exim-maintenance.service create mode 100644 configs/system-integration/systemd/maintenance/exim-maintenance.timer create mode 100644 configs/system-integration/systemd/queuerunner/exim-queuerunner.service create mode 100644 configs/system-integration/systemd/queuerunner/exim-queuerunner.timer create mode 100644 configs/system-integration/systemd/socket/exim.service create mode 100644 configs/system-integration/systemd/socket/exim.socket diff --git a/.gitignore b/.gitignore index 8c2660a9f..c55dcdcdd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -exim-* +!/system-integration/ !/test/aux-fixed/exim-ca *~ *.bak diff --git a/configs/ABOUT b/configs/ABOUT index 13d9230b6..f47f96aa8 100644 --- a/configs/ABOUT +++ b/configs/ABOUT @@ -1,7 +1,8 @@ -Exim repository: configs ------------------------- +Exim repository: configs/ +------------------------- -This directory contains sample configurations and similar files that have been -submitted by Exim users. The files are not locally modified. +This directory contains sample configurations and and files for +integrating Exim with the system. These have been submitted by Exim +users and may or may not fit your your environment. -End +But we're interested in feedback and improvements. diff --git a/configs/system-integration/README.md b/configs/system-integration/README.md new file mode 100644 index 000000000..d06afc5ab --- /dev/null +++ b/configs/system-integration/README.md @@ -0,0 +1,8 @@ +# System Integration + +Various systems use various ways to integrate Exim with the system. +Mainly these tasks have to be accomplished: + +- startup procedure (running as a service or on demand) +- queue runs +- regular maintenance tasks (log rotation, database cleanup) diff --git a/configs/system-integration/systemd/.gitignore b/configs/system-integration/systemd/.gitignore new file mode 100644 index 000000000..15c1e5d15 --- /dev/null +++ b/configs/system-integration/systemd/.gitignore @@ -0,0 +1 @@ +.installed diff --git a/configs/system-integration/systemd/README.md b/configs/system-integration/systemd/README.md new file mode 100644 index 000000000..297edbc00 --- /dev/null +++ b/configs/system-integration/systemd/README.md @@ -0,0 +1,86 @@ +# Systemd Unit Examples for Exim + +This directory contains several examples for Systemd units to manage an Exim installation. +There is room for improvement, so please share your ideas or setups that are proven to work +in your environment. + +All the service units try to protect the system from unintentional +writes to locations outside of Exim's spool, and log directories. You +may need to override specific settings, we recommend using Systemd's +override mechanism (`systemd edit …`). + +The .service units use `ProtectSystem=strict`, which implies a read-only +file system structure. Exim needs write access to the spool directory +(main config option: `spool_directory`), and the log directory (main +config option: `log_file_path`). For improved security you can even set +`NoNewPrivileges`, if you don't do local deliveries. + +The provides Systemd units are examples, containing placeholders +`{{…}}`. The [install script](./install) helps substituting them.v +The following placeholders are used currently: +- `exim`: +- `spooldir:` +- `logdir`: + + +## Daemon + +This is best suited for *average to high traffic systems*, it engages +all built-in Exim facilities, as queue runner management and system load +depending message processing. + +The [systemd service unit](./daemon/exim.service) starts the Exim main +process. This process listens on the ports configured in the _runtime +configuration_ (typically `exim.conf`), and supervises all other +activities, including management of queue runner startups. Basically it +calls `exim -odf -q...`. + +For regular maintenance tasks (database cleanup) additional units are +[required](./maintenance). + +## Socket + +This is best suited for *low traffic* systems, which experience a +message *burst* from time to time. Regular desktop, and edge systems fit this +pattern. + +Exim's start is delayed until the first connection. Once a connection is +initiated, Exim starts a listener on the port configured in the [systemd +socket unit](./socket/exim.socket) and waits for more connections. It +exits after being idle for a while. Basically it calls `exim -bw ...`. + +Additional [_queue runner_ timer and service units](#queue-runner) are required. + +For regular maintenance tasks (database cleanup) +additional units are [required](./maintenance). + +## Inetd + +This is best suited for systems with *low traffic*, if the +[socket](#socket) approach doesn't work. + +For each incoming connection a new Exim instance starts, handling +exactly this connection and then exits. The listener port is configured +in the [systemd socket unit](./inetd/exim.socket). + +Additional [_queue runner_ timer and service units](#queue-runner) are required. + +For regular maintenance tasks (database cleanup) +additional units are [required](./maintenance). + +## Queue Runner + +This is a *timer*, and a *service* unit which starts Exim queue runner +processes. This is necessary, as the socket activated Exim instances +(from [socket](#socket) and [inetd](#inetd) do not care, once the first +delivery attempt is done. + +## Maintenance + +This is a *timer* unit, and a *service* unit for regular maintenance +tasks. For security it is recommended to use the `User=` Systemd +directive in a local override file. + +The service unit cares about tidying Exim's hint databases. It *does +not* rotate the log files, as most systems have their own mechanism for +doing this job (e.g. Logrotate). diff --git a/configs/system-integration/systemd/daemon/exim.service b/configs/system-integration/systemd/daemon/exim.service new file mode 100644 index 000000000..5d49ab356 --- /dev/null +++ b/configs/system-integration/systemd/daemon/exim.service @@ -0,0 +1,29 @@ +[Unit] +Description=Exim MTA (as daemon) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +Requires=network.target +After=networking.target + +[Service] +Environment=DAEMON_OPTS= +Environment=QUEUE_OPTS=-q15m +EnvironmentFile=-/etc/default/{{exim}} + +Type=exec +ExecStart={{exim}} -bdf $DAEMON_OPTS $QUEUE_OPTS +ExecReload=kill -HUP ${MAINPID} + +# If you do not need local deliveries, enabling the +# next option can improve security +#NoNewPrivileges=yes + +ProtectSystem=strict +ReadWriteDirectories={{spooldir}} +ReadWriteDirectories={{logdir}} + +Slice=exim.slice + +[Install] +WantedBy=multi-user.target diff --git a/configs/system-integration/systemd/inetd/exim.socket b/configs/system-integration/systemd/inetd/exim.socket new file mode 100644 index 000000000..a802e8ec2 --- /dev/null +++ b/configs/system-integration/systemd/inetd/exim.socket @@ -0,0 +1,11 @@ +[Unit] +Description=Exim MTA (inetd) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Socket] +ListenStream=25 +Accept=yes + +[Install] +WantedBy=sockets.target diff --git a/configs/system-integration/systemd/inetd/exim@.service b/configs/system-integration/systemd/inetd/exim@.service new file mode 100644 index 000000000..7771fde4a --- /dev/null +++ b/configs/system-integration/systemd/inetd/exim@.service @@ -0,0 +1,27 @@ +[Unit] +Description=Exim MTA (socket activated - inetd mode) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Service] +Type=exec + +# We can't use -odf, as this would ask exim to keep the connection +# from the client open until the delivery is done +ExecStart={{exim}} -bs + +StandardInput=socket +StandardError=journal + +# Don't kill the delivery process we spawned as a child +KillMode=process + +# If you do not need local deliveries, enabling the +# next option can improve security +#NoNewPrivileges=yes + +ProtectSystem=strict +ReadWriteDirectories={{spooldir}} +ReadWriteDirectories={{logdir}} + +Slice=exim.slice diff --git a/configs/system-integration/systemd/install b/configs/system-integration/systemd/install new file mode 100755 index 000000000..83a648ab8 --- /dev/null +++ b/configs/system-integration/systemd/install @@ -0,0 +1,92 @@ +#!/bin/bash +# simple helper, mainly for testing the provided Systemd units. + +set -eu +export LC_ALL=C + +: ${EXIM=exim} +: ${EXIM_LOGDIR=/var/log/exim} +: ${EXIM_SPOOLDIR=/var/spool/exim} + +# Packagers should install to $(systemd-path systemd-system-unit) +# which mostly is something like /lib/systemd/system +dstdir= + +usage="$0 [OPTIONS] variant... + This simple script installs Systemd unit files to the desired destination, replacing + the {{Placeholder}}s. + + VARIANT: one of daemon, inet, socket, maintainance, queuerunner + + OPTIONS: + --help print this help and exit cleanly + --uninstall|-u uninstall the installed files + --dstdir|-d DIR the destination directory (mandatory, use 'DEFAULT' + to use Systemd's default location (`systemd-path systemd-system-conf`) + + Placeholders: + {{exim}} from \$EXIM ($EXIM) + {{logdir}} from \$EXIM_LOGDIR ($EXIM_LOGDIR) + {{spooldir}} from \$EXIM_SPOOLDIR ($EXIM_SPOOLDIR) +" + + +tmp=$(getopt -n $0 -o d:n --long dstdir:,help,uninstall -- "$@") +eval set -- "$tmp" +while true +do + o=$1; shift + case $o in + -d|--dstdir) dstdir=$1; shift;; + --help) echo "$usage"; exit;; + -n|--uninstall) uninstall=1;; + --) break + esac +done + +if [[ -v uninstall ]] +then + if ! [[ -r .installed ]] + then + echo "$0: noting to uninstall (.installed is empty or isn't readable)" >&2 + exit + fi + + rm -vf $(<.installed) + rm -f .installed + exit +fi + +case $dstdir in + DEFAULT) dstdir=$(systemd-path systemd-system-conf);; + "") echo "$0: --dstdir is mandatory" >&2; exit 1;; + *) ;; +esac + +if (( $# == 0 )) +then echo "$0: need variant" >&2; exit 1; +fi + +function xform() { + sed -e "s|{{exim}}|${EXIM:?}|g" \ + -e "s|{{logdir}}|${EXIM_LOGDIR:?}|g" \ + -e "s|{{spooldir}}|${EXIM_SPOOLDIR:?}|g" +} + +for dir in ${@:?need source dir(s)} +do + echo "# $dir" + for src in "$dir"/* + do + dst="$dstdir/${src##*/}" + echo "installing $dst" + xform <"$src" >"$dst" + echo $dst >> .installed + done +done + +if [[ $dstdir == $(systemd-path systemd-system-conf) ]] +then + echo "# reloading systemd configuration" + systemctl daemon-reload +fi diff --git a/configs/system-integration/systemd/maintenance/exim-maintenance.service b/configs/system-integration/systemd/maintenance/exim-maintenance.service new file mode 100644 index 000000000..42722d3fc --- /dev/null +++ b/configs/system-integration/systemd/maintenance/exim-maintenance.service @@ -0,0 +1,24 @@ +[Unit] +Description=Exim MTA (maintenance) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Service] +Type=oneshot +ExecReload=kill -HUP ${MAINPID} + +# Dollars are doubled for systemd! +WorkingDirectory={{spooldir}} +ExecStart=sh -ec 'for db in db/* ;\ + do \ + test -f "$$db" && [ "$${db##*.}" != lockfile ] || continue ;\ + exim_tidydb $$PWD "$${db##*/}"; \ + done' + +ProtectSystem=strict +ReadWriteDirectories={{spooldir}}/db + +Slice=exim.slice + +[Install] +WantedBy=multi-user.target diff --git a/configs/system-integration/systemd/maintenance/exim-maintenance.timer b/configs/system-integration/systemd/maintenance/exim-maintenance.timer new file mode 100644 index 000000000..bd192cd07 --- /dev/null +++ b/configs/system-integration/systemd/maintenance/exim-maintenance.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Exim MTA (maintenance timer) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Timer] +OnActiveSec=1h +OnUnitActiveSec=1d + +[Install] +WantedBy=timers.target diff --git a/configs/system-integration/systemd/queuerunner/exim-queuerunner.service b/configs/system-integration/systemd/queuerunner/exim-queuerunner.service new file mode 100644 index 000000000..e6e9ca799 --- /dev/null +++ b/configs/system-integration/systemd/queuerunner/exim-queuerunner.service @@ -0,0 +1,21 @@ +[Unit] +Description=Exim MTA (queue runner service) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Service] +Type=oneshot + +ExecStart={{exim}} -q +KillMode=process + +# If you do not need local deliveries, enabling the +# next option can improve security +#NoNewPrivileges=yes + +ProtectSystem=strict +ReadWriteDirectories={{spooldir}} +ReadWriteDirectories={{logdir}} +ReadWriteDirectories=/var/mail /var/spool/mail + +Slice=exim.slice diff --git a/configs/system-integration/systemd/queuerunner/exim-queuerunner.timer b/configs/system-integration/systemd/queuerunner/exim-queuerunner.timer new file mode 100644 index 000000000..6988b7c29 --- /dev/null +++ b/configs/system-integration/systemd/queuerunner/exim-queuerunner.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Exim MTA (queue runner timer) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Timer] +OnActiveSec=120 +OnUnitActiveSec=15m + +[Install] +WantedBy=timers.target diff --git a/configs/system-integration/systemd/socket/exim.service b/configs/system-integration/systemd/socket/exim.service new file mode 100644 index 000000000..a4576ae24 --- /dev/null +++ b/configs/system-integration/systemd/socket/exim.service @@ -0,0 +1,26 @@ +[Unit] +Description=Exim MTA (socket activated) +Documentation=man:exim +Documentation=https://exim.org/docs.html +PartOf=exim.socket + +[Service] +Type=exec +Environment=INACTIVITY_TIMEOUT=5m +EnvironmentFile=-/etc/default/exim + +ExecStart=exim -bw${INACTIVITY_TIMEOUT} + +StandardInput=socket +StandardError=journal + +# If you do not need local deliveries, enabling the +# next option can improve security +#NoNewPrivileges=yes + +ProtectSystem=strict +ReadWriteDirectories={{spooldir}} +ReadWriteDirectories={{logdir}} +ReadWriteDirectories=/var/mail /var/spool/mail + +Slice=exim.slice diff --git a/configs/system-integration/systemd/socket/exim.socket b/configs/system-integration/systemd/socket/exim.socket new file mode 100644 index 000000000..8b3876663 --- /dev/null +++ b/configs/system-integration/systemd/socket/exim.socket @@ -0,0 +1,10 @@ +[Unit] +Description=Exim MTA (socket) +Documentation=man:exim +Documentation=https://exim.org/docs.html + +[Socket] +ListenStream=25 + +[Install] +WantedBy=sockets.target -- 2.30.2