diff --git a/app/Console/Commands/BackupRun.php b/app/Console/Commands/BackupRun.php index 098e5cf..3841cba 100644 --- a/app/Console/Commands/BackupRun.php +++ b/app/Console/Commands/BackupRun.php @@ -28,7 +28,7 @@ class BackupRun extends Command $script = '/usr/local/sbin/mailwolt-backup'; if (file_exists($script)) { $output = []; - exec("bash " . escapeshellarg($script) . ' 2>&1', $output, $exitCode); + exec('sudo -n ' . escapeshellarg($script) . ' 2>&1', $output, $exitCode); $artifact = null; foreach ($output as $line) { diff --git a/scripts/mailwolt-backup b/scripts/mailwolt-backup new file mode 100644 index 0000000..2a4ac32 --- /dev/null +++ b/scripts/mailwolt-backup @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Mailwolt Backup – läuft als root via sudo, liest DB-Credentials aus .env +set -euo pipefail + +APP_DIR="/var/www/mailwolt" +APP_USER="www-data" +OUT_DIR="${APP_DIR}/storage/app/backups" +STAMP="$(date +%Y-%m-%d_%H-%M-%S)" +OUT_FILE="${OUT_DIR}/mailwolt_${STAMP}.tar.gz" +TMP_DIR="$(mktemp -d /tmp/mailwolt_backup_XXXXXX)" + +cleanup(){ rm -rf "$TMP_DIR"; } +trap cleanup EXIT + +mkdir -p "$OUT_DIR" +chown "${APP_USER}:${APP_USER}" "$OUT_DIR" 2>/dev/null || true + +# ── Datenbank ────────────────────────────────────────────────────────────── +ENV_FILE="${APP_DIR}/.env" +DB_HOST="127.0.0.1"; DB_PORT="3306"; DB_USER=""; DB_PASS=""; DB_NAME="" +if [[ -f "$ENV_FILE" ]]; then + DB_HOST="$(grep '^DB_HOST=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")" + DB_PORT="$(grep '^DB_PORT=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")" + DB_USER="$(grep '^DB_USERNAME=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")" + DB_PASS="$(grep '^DB_PASSWORD=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")" + DB_NAME="$(grep '^DB_DATABASE=' "$ENV_FILE" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")" +fi + +DUMP_FILE="${TMP_DIR}/database.sql" +if [[ -n "$DB_NAME" ]]; then + MYSQL_PWD="$DB_PASS" mysqldump \ + -h "${DB_HOST:-127.0.0.1}" -P "${DB_PORT:-3306}" \ + -u "$DB_USER" \ + --single-transaction --routines --triggers \ + "$DB_NAME" > "$DUMP_FILE" 2>&1 + echo "✓ Datenbank gesichert" +else + echo "✗ DB_DATABASE nicht in .env gefunden" +fi + +# ── Quellen sammeln ──────────────────────────────────────────────────────── +SOURCES=("$DUMP_FILE") + +for MAIL_DIR in /var/vmail /var/mail/vhosts /home/vmail; do + if [[ -d "$MAIL_DIR" ]]; then + SOURCES+=("$MAIL_DIR") + echo "✓ Maildirs: $MAIL_DIR" + break + fi +done + +for CFG_DIR in /etc/postfix /etc/dovecot /etc/opendkim /etc/rspamd /etc/letsencrypt; do + if [[ -d "$CFG_DIR" ]]; then + SOURCES+=("$CFG_DIR") + echo "✓ $CFG_DIR" + fi +done + +if [[ -f "${APP_DIR}/.env" ]]; then + SOURCES+=("${APP_DIR}/.env") + echo "✓ .env" +fi + +# ── Archiv erstellen ─────────────────────────────────────────────────────── +tar --ignore-failed-read -czf "$OUT_FILE" "${SOURCES[@]}" 2>&1 || true + +chown "${APP_USER}:${APP_USER}" "$OUT_FILE" 2>/dev/null || true + +echo "ARTIFACT:${OUT_FILE}" diff --git a/scripts/update.sh b/scripts/update.sh index 6470b94..c4dfe6d 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -206,11 +206,13 @@ ensure_system(){ _sbin_install "${APP_DIR}/scripts/mailwolt-fetch-tags" /usr/local/sbin/mailwolt-fetch-tags _sbin_install "${APP_DIR}/scripts/update.sh" /usr/local/sbin/mailwolt-update _sbin_install "${APP_DIR}/scripts/mailwolt-clamav" /usr/local/sbin/mailwolt-clamav + _sbin_install "${APP_DIR}/scripts/mailwolt-backup" /usr/local/sbin/mailwolt-backup # Sudoers-Regeln nachrüsten for f in /etc/sudoers.d/mailwolt-certbot /etc/sudoers.d/mailwolt-dkim; do _sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-fetch-tags' _sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update --check-only' _sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-clamav' + _sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-backup' _sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/bin/openssl x509 -enddate -noout -in /etc/letsencrypt/live/*/fullchain.pem' done # Laravel Scheduler cron (schedule:run muss jede Minute laufen)