#!/bin/bash # Install, update or remove the shared discard list on a chatmail relay. # # curl -fsSL https://raw.githubusercontent.com/mrgluek/chatmail-discard-list/main/install.sh | sudo bash # curl -fsSL .../install.sh | sudo bash -s -- --uninstall # curl -fsSL .../install.sh | sudo bash -s -- --docker [DIR] # chatmail/docker, run on the host # # Running it again updates sync-discard-list to the current version and keeps # the schedule. The sync script never updates itself: that would let anyone # with write access to the repo run code as root on every relay. # # Everything is inside main(), which is called on the last line: if the # download is cut off half-way, bash runs nothing instead of half a script. set -euo pipefail REPO=${DISCARD_LIST_REPO:-https://raw.githubusercontent.com/mrgluek/chatmail-discard-list/main} BIN=/usr/local/sbin/sync-discard-list UNIT=chatmail-discard-sync MAP=/etc/postfix/transport_discard_shared OLD_CRON=/etc/cron.d/chatmail-discard-list # used by versions before the timer service_unit() { cat < (content on stdin) [ -e "$1" ] || install -m "$2" /dev/null "$1" cat > "$1" chmod "$2" "$1" } uninstall() { systemctl disable --now "$UNIT.timer" 2>/dev/null || true rm -f "/etc/systemd/system/$UNIT.service" "/etc/systemd/system/$UNIT.timer" "$OLD_CRON" "$BIN" systemctl daemon-reload # Drop only our map from transport_maps, keep whatever else is there. local maps maps=$(postconf -h transport_maps | tr ',' '\n' | sed 's/^[[:space:]]*//' | grep -vxF "hash:$MAP" | paste -sd, - | sed 's/,/, /g' || true) postconf -e "transport_maps = $maps" rm -f "$MAP" "$MAP.db" postfix reload 2>/dev/null echo "removed; transport_maps = $maps" } install_host() { local tmp sched command -v postconf >/dev/null || { echo "postfix not found, is this a mail relay? (chatmail/docker: use --docker)" >&2; exit 1; } command -v systemctl >/dev/null || { echo "systemd not found" >&2; exit 1; } tmp=$(download_script) install -m 755 "$tmp" "$BIN" rm -f "$tmp" echo "installed $BIN" sched=$(schedule "/etc/systemd/system/$UNIT.timer") service_unit > "/etc/systemd/system/$UNIT.service" timer_unit "$sched" > "/etc/systemd/system/$UNIT.timer" rm -f "$OLD_CRON" systemctl daemon-reload systemctl enable --now "$UNIT.timer" 2>/dev/null echo "timer: OnCalendar=*-*-* $sched:00 (and 3 min after boot)" "$BIN" echo "transport_maps = $(postconf -h transport_maps)" echo "entries in shared list: $(grep -cv '^[[:space:]]*\(#\|$\)' "$MAP")" } # chatmail/docker: the container is rebuilt from the image on every update, # so files installed inside it are lost. Keep them on the host and bind-mount # them into the container instead. install_docker() { local dir=$1 tmp sched mkdir -p "$dir" tmp=$(download_script) write_file "$dir/sync-discard-list" 755 < "$tmp" rm -f "$tmp" sched=$(schedule "$dir/$UNIT.timer") service_unit | write_file "$dir/$UNIT.service" 644 timer_unit "$sched" | write_file "$dir/$UNIT.timer" 644 # Enabling a unit means a symlink in timers.target.wants/, but a bind mount # is always a regular file there, which systemd ignores. A drop-in that # adds the dependency to timers.target works as a plain file. printf '[Unit]\nWants=%s.timer\n' "$UNIT" | write_file "$dir/timers-target.conf" 644 echo "files in $dir; timer: OnCalendar=*-*-* $sched:00 (and 3 min after container start)" cat <&2; exit 1; } case "${1:-}" in --uninstall) uninstall ;; --docker) install_docker "${2:-/opt/chatmail-discard-list}" ;; "") install_host ;; *) echo "usage: install.sh [--uninstall | --docker [DIR]]" >&2; exit 1 ;; esac } main "$@"