mailwolt-installer/scripts/93-backup-tools.sh

268 lines
8.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
set -euo pipefail
source ./lib.sh
log "Backup/Restore Tools, Config & Timer (installer.env) …"
# ─────────────────────────────────────────────────────────────
# 1) installer.env laden (ENV > installer.env > Defaults)
# ─────────────────────────────────────────────────────────────
if [[ -f /etc/mailwolt/installer.env ]]; then
# automatisch exportieren, damit ${VAR} später überall wirkt
set -a
# shellcheck disable=SC1091
source /etc/mailwolt/installer.env
set +a
else
log "[i] /etc/mailwolt/installer.env nicht gefunden nutze Defaults."
fi
# ─────────────────────────────────────────────────────────────
# 2) Pfade & Defaults (werden durch ENV/installer.env überschrieben)
# ─────────────────────────────────────────────────────────────
CONF_DIR="/etc/mailwolt"
CONF_FILE="${CONF_DIR}/backup.conf"
BIN_DIR="/usr/local/sbin"
UNIT_DIR="/etc/systemd/system"
APP_DIR="${APP_DIR:-/var/www/mailwolt}"
# DB-Parameter aus installer.env (bzw. ENV) oder Fallbacks
DB_HOST="${DB_HOST:-127.0.0.1}"
DB_NAME="${DB_NAME:-mailwolt}"
DB_USER="${DB_USER:-mailwolt}"
DB_PASS="${DB_PASS:-}"
# Backup-Settings aus installer.env (bzw. ENV)
BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}"
BACKUP_RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
BACKUP_USE_ZSTD="${BACKUP_USE_ZSTD:-1}"
BACKUP_ENABLED="${BACKUP_ENABLED:-0}" # 0|1
BACKUP_INTERVAL="${BACKUP_INTERVAL:-daily}" # daily|weekly|monthly
install -d -m 0755 "$CONF_DIR" "$BACKUP_DIR"
# ─────────────────────────────────────────────────────────────
# 3) /etc/mailwolt/backup.conf (von UI/APP überschreibbar)
# ─────────────────────────────────────────────────────────────
cat > "$CONF_FILE" <<EOF
# MailWolt Backup Konfiguration (UI kann überschreiben)
APP_DIR="$APP_DIR"
BACKUP_DIR="$BACKUP_DIR"
RETENTION_DAYS="$BACKUP_RETENTION_DAYS"
USE_ZSTD="$BACKUP_USE_ZSTD"
# DB-Parameter
MYSQL_DB="$DB_NAME"
MYSQL_USER="$DB_USER"
MYSQL_PASS="$DB_PASS"
MYSQL_HOST="$DB_HOST"
MYSQL_PORT="3306"
EOF
chmod 0644 "$CONF_FILE"
log "[✓] config geschrieben: $CONF_FILE"
# ─────────────────────────────────────────────────────────────
# 4) /usr/local/sbin/mailwolt-backup (schreibt backup.status)
# ─────────────────────────────────────────────────────────────
cat > "${BIN_DIR}/mailwolt-backup" <<'EOSH'
#!/usr/bin/env bash
set -euo pipefail
log(){ echo "[$(date -Is)] $*"; }
# Konfiguration laden (ENV > Datei)
CONF="/etc/mailwolt/backup.conf"
[[ -f "$CONF" ]] && # shellcheck disable=SC1090
source "$CONF"
APP_DIR="${APP_DIR:-/var/www/mailwolt}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/mailwolt}"
RETENTION_DAYS="${RETENTION_DAYS:-7}"
USE_ZSTD="${USE_ZSTD:-1}"
MYSQL_DB="${MYSQL_DB:-mailwolt}"
MYSQL_USER="${MYSQL_USER:-mailwolt}"
MYSQL_PASS="${MYSQL_PASS:-}"
MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}"
MYSQL_PORT="${MYSQL_PORT:-3306}"
STATE_DIR="/var/lib/mailwolt"
STATUS_FILE="${STATE_DIR}/backup.status"
install -d -m 0755 "$STATE_DIR" "$BACKUP_DIR"
START_TS="$(date +%s)"
TS="$(date -u +%Y%m%dT%H%M%SZ)"
TMP="$(mktemp -d /tmp/mwbackup.XXXXXX)"
trap 'rm -rf "$TMP"' EXIT
fail(){
local msg="${1:-backup failed}"
local now="$(date -Is)"
{
echo "time=${now}"
echo "size=0"
echo "dur=$(( $(date +%s) - START_TS ))s"
echo "ok=0"
echo "error=${msg}"
} > "$STATUS_FILE"
echo "[$now] ${msg}" >&2
exit 1
}
trap 'fail "unexpected error (exit $?)"' ERR
OUT="${BACKUP_DIR}/mailwolt-${TS}.tar"
log "⇒ starte Backup in $OUT …"
# 1) DB
log " • mysqldump …"
MYSQL_PWD="$MYSQL_PASS" mysqldump \
-h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \
--single-transaction --routines --events --triggers \
"$MYSQL_DB" > "$TMP/mysql.sql"
# 2) Maildir
log " • Maildir …"
tar -C / -cf "$TMP/mail.tar" var/mail/vhosts 2>/dev/null || true
# 3) App (ohne heavy dirs)
log " • App …"
tar -C / -cf "$TMP/app.tar" \
--exclude='var/www/mailwolt/vendor' \
--exclude='var/www/mailwolt/node_modules' \
--exclude='var/www/mailwolt/public/build' \
var/www/mailwolt
# 4) Configs
log " • Configs …"
mkdir -p "$TMP/files"
cp -a /etc/mailwolt "$TMP/files/" 2>/dev/null || true
cp -a /etc/postfix "$TMP/files/" 2>/dev/null || true
cp -a /etc/dovecot "$TMP/files/" 2>/dev/null || true
cp -a /etc/opendkim "$TMP/files/" 2>/dev/null || true
cp -a /etc/opendmarc "$TMP/files/" 2>/dev/null || true
cp -a /etc/rspamd "$TMP/files/" 2>/dev/null || true
cp -a /etc/ssl/ui "$TMP/files/" 2>/dev/null || true
tar -C "$TMP" -cf "$TMP/files.tar" files
# 5) Paket
log " • Archiviere …"
tar -C "$TMP" -cf "$OUT" mysql.sql mail.tar app.tar files.tar
# 6) Komprimieren (optional)
if [[ "${USE_ZSTD:-1}" = "1" ]] && command -v zstd >/dev/null 2>&1; then
log " • komprimiere (zstd) …"
zstd -f --rm -19 "$OUT"
OUT="${OUT}.zst"
fi
# 7) Retention
if [[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]]; then
log " • Retention: lösche älter als ${RETENTION_DAYS} Tage …"
find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -name 'mailwolt-*' -delete || true
fi
# 8) Statusfile fürs UI
SIZE_BYTES="$(stat -c '%s' "$OUT" 2>/dev/null || echo 0)"
{
echo "time=$(date -Is)"
echo "size=${SIZE_BYTES}"
echo "dur=$(( $(date +%s) - START_TS ))s"
echo "ok=1"
echo "file=${OUT}"
} > "$STATUS_FILE"
chmod 0644 "$STATUS_FILE" 2>/dev/null || true
log "[✓] Backup fertig: $OUT"
EOSH
chmod 0755 "${BIN_DIR}/mailwolt-backup"
# ─────────────────────────────────────────────────────────────
# 5) /usr/local/sbin/mailwolt-restore
# ─────────────────────────────────────────────────────────────
cat > "${BIN_DIR}/mailwolt-restore" <<'EOSH'
#!/usr/bin/env bash
set -euo pipefail
log(){ echo "[$(date -Is)] $*"; }
ARCHIVE="${1:-}"
[[ -n "$ARCHIVE" ]] || { echo "Usage: mailwolt-restore <backup.tar[.zst]>"; exit 1; }
[[ -f "$ARCHIVE" ]] || { echo "Backup nicht gefunden: $ARCHIVE"; exit 1; }
TMP="$(mktemp -d /tmp/mwrestore.XXXXXX)"
trap 'rm -rf "$TMP"' EXIT
case "$ARCHIVE" in
*.zst) zstd -d -c "$ARCHIVE" > "$TMP/backup.tar" ;;
*) cp -a "$ARCHIVE" "$TMP/backup.tar" ;;
esac
log "⇒ entpacke …"
tar -C "$TMP" -xf "$TMP/backup.tar"
# Reihenfolge: DB → App → Mail → Config
if [[ -f "$TMP/mysql.sql" ]]; then
log " • MySQL wiederherstellen …"
mysql < "$TMP/mysql.sql"
fi
if [[ -f "$TMP/app.tar" ]]; then
log " • App → /var/www/mailwolt …"
tar -C / -xf "$TMP/app.tar"
fi
if [[ -f "$TMP/mail.tar" ]]; then
log " • Maildir → /var/mail/vhosts …"
tar -C / -xf "$TMP/mail.tar"
fi
if [[ -f "$TMP/files.tar" ]]; then
log " • Configs → /etc/* …"
tar -C / -xf "$TMP/files.tar"
fi
log "[✓] Restore abgeschlossen."
EOSH
chmod 0755 "${BIN_DIR}/mailwolt-restore"
log "[✓] Tools installiert: ${BIN_DIR}/mailwolt-backup, mailwolt-restore"
# ─────────────────────────────────────────────────────────────
# 6) systemd Service + Timer (Timer default via installer.env)
# ─────────────────────────────────────────────────────────────
cat > "${UNIT_DIR}/mailwolt-backup.service" <<'EOSVC'
[Unit]
Description=MailWolt Backup
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/mailwolt-backup
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOSVC
cat > "${UNIT_DIR}/mailwolt-backup.timer" <<EOTIM
[Unit]
Description=MailWolt Backup Timer
[Timer]
OnCalendar=${BACKUP_ONCALENDAR:-*-*-* 03:00:00}
Persistent=true
[Install]
WantedBy=timers.target
EOTIM
systemctl daemon-reload
if [[ "${BACKUP_ENABLED}" = "1" ]]; then
log "Aktiviere Backup-Timer (${BACKUP_ONCALENDAR}) …"
systemctl enable --now mailwolt-backup.timer
else
log "Timer bleibt deaktiviert (BACKUP_ENABLED=0)."
systemctl disable --now mailwolt-backup.timer >/dev/null 2>&1 || true
fi
log "[✓] Backup-Setup abgeschlossen."