2b84a8a493
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>
63 lines
2.5 KiB
Bash
Executable File
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"
|