Files
root 2b84a8a493 Everything in English: documentation, comments, identifiers
The user works in German but wants the artefacts in English throughout.
Translated were roughly 1,500 lines: every comment in the configuration
files, all comments in the Go sources, both shell scripts, and the three
manuals.

Renamed along with it, so nothing is left half-translated:

- install/vorlagen-pruefen      -> install/verify-templates
- install/mailserver.conf.beispiel -> install/mailserver.conf.example
- docs/betrieb.md               -> docs/operations.md
- shell functions and variables (schritt/abbruch/einsetzen/ZIEL/BEHALTEN
  -> stage/die/deploy/DEST/KEEP), the ten installer stages, the Dovecot
  quota root "Postfach" -> "Mailbox" and the sieve_script names
  lernspam/lernham -> learnspam/learnham
- the comment headers that mailctl writes into the generated map files

Two things this dug up while translating:

- Perl treats $) and $/ as variables. A careless s{}{} put a NUL byte into
  the regular expression documented in lang.go and mangled a line in
  list.go. Both repaired; the sources were checked for NUL bytes and the
  generated maps compared against the previous ones - the expressions
  themselves are unchanged.
- The map files are only rewritten on a change, so their German headers
  survived the first pass. Regenerated and verified line by line.

The origin server was brought along in the same step: configuration
deployed, Sieve scripts recompiled, mailctl rebuilt, services reloaded,
maps regenerated. Checked afterwards: postfix check, doveconf -n,
nft -c, unbound-checkconf, rspamadm configtest all pass; block list and
language filter still fire; a test message went through the full chain
into the mailbox. install/verify-templates reports 37 files identical,
no differences at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 12:23:20 +02:00

63 lines
2.5 KiB
Bash
Executable File

#!/bin/sh
# Backs up the parts of the mail server that cannot be recreated:
#
# - the user database (domains, mailboxes, password hashes, aliases)
# - the private DKIM keys (generating new ones would mean changing DNS and
# waiting until the old signature has expired everywhere)
# - the configuration files
#
# NOT included is the mail data under /var/vmail - depending on usage it can
# grow very large and needs a backup scheme of its own (restic or borg to
# remote storage, say).
#
# Runs daily from a systemd timer: systemctl list-timers mailbackup
set -eu
DEST=/var/backups/mailserver
KEEP=30
STAMP=$(date +%Y-%m-%d_%H%M)
ARCHIVE="$DEST/mailserver_$STAMP.tar.gz"
mkdir -p "$DEST"
chmod 700 "$DEST"
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
# Write the database out consistently - a plain copy would be unsafe if a
# write happened to be in progress.
sqlite3 /etc/mailserver/mail.db ".backup '$TMP/mail.db'"
mkdir -p "$TMP/config"
cp -a /etc/mailserver/schema.sql "$TMP/config/" 2>/dev/null || true
# Carries the identity of the server (name, address, SSH port). Without it a
# restore does not know what the certificates were for, nor which address
# belongs in the DNS recommendations.
cp -a /etc/mailserver/server.conf "$TMP/config/" 2>/dev/null || true
cp -a /etc/mailserver/README.md "$TMP/config/" 2>/dev/null || true
cp -a /etc/postfix/main.cf "$TMP/config/" 2>/dev/null || true
cp -a /etc/postfix/master.cf "$TMP/config/" 2>/dev/null || true
cp -a /etc/postfix/sqlite "$TMP/config/" 2>/dev/null || true
cp -a /etc/dovecot/dovecot.conf "$TMP/config/" 2>/dev/null || true
cp -a /etc/dovecot/sieve "$TMP/config/" 2>/dev/null || true
cp -a /etc/rspamd/local.d "$TMP/config/" 2>/dev/null || true
cp -a /etc/nftables.conf "$TMP/config/" 2>/dev/null || true
cp -a /etc/fail2ban/jail.d "$TMP/config/" 2>/dev/null || true
mkdir -p "$TMP/dkim"
cp -a /var/lib/rspamd/dkim/. "$TMP/dkim/" 2>/dev/null || true
tar czf "$ARCHIVE" -C "$TMP" .
chmod 600 "$ARCHIVE"
# Clean up older backups. The names carry the date as YYYY-MM-DD_hhmm, so
# sorting them in reverse alphabetical order puts the newest on top - no
# "ls -t" needed, which could trip over a shell alias.
printf '%s\n' "$DEST"/mailserver_*.tar.gz 2>/dev/null | sort -r | tail -n +$((KEEP + 1)) | while read -r old; do
[ -f "$old" ] && rm -f "$old"
done
logger -t mailbackup "Backup written: $ARCHIVE ($(du -h "$ARCHIVE" | cut -f1))"
echo "Backup written: $ARCHIVE"