138 lines
5.3 KiB
Bash
Executable File
138 lines
5.3 KiB
Bash
Executable File
#!/bin/bash
|
||
# ceres-backup.sh – Snapshot-freundliches Mirror-Backup auf Synology NAS
|
||
# Spiegeln statt Tarballs; nur Portainer-DB als einzelne Datei (fixer Name).
|
||
set -euo pipefail
|
||
|
||
# ── NAS-Ziel ───────────────────────────────────────────────────────
|
||
NAS_USER="backup-master"
|
||
NAS_HOST="192.168.178.74"
|
||
NAS_PORT=10022
|
||
NAS_BASE="/volume1/ceres-raspi5/mirror"
|
||
|
||
# ── Optionen / Tools ───────────────────────────────────────────────
|
||
RSYNC_SSH=(-e "ssh -p ${NAS_PORT}")
|
||
RSYNC_OPTS=(-aHAX --delete --numeric-ids)
|
||
DATE="$(date +%F)"
|
||
TMP_DIR="$(mktemp -d)"
|
||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||
|
||
# Prüfe Abhängigkeiten
|
||
for bin in curl jq rsync ssh; do
|
||
command -v "$bin" >/dev/null 2>&1 || { echo "Fehlt: $bin"; exit 1; }
|
||
done
|
||
|
||
# Heimatverzeichnis fest (systemd hat oft kein USER/SUDO_USER gesetzt)
|
||
REAL_USER="${REAL_USER:-pdavidt}"
|
||
HOME_DIR="${HOME_DIR:-/home/pdavidt}"
|
||
[ -d "$HOME_DIR" ] || { echo "HOME_DIR nicht gefunden: $HOME_DIR"; exit 1; }
|
||
|
||
# Remote-Verzeichnis sicher anlegen
|
||
ensure_remote_dir() {
|
||
local remote_dir="$1"
|
||
ssh -p "${NAS_PORT}" "${NAS_USER}@${NAS_HOST}" "mkdir -p ${remote_dir}"
|
||
}
|
||
|
||
# Rsync-Mirror-Helfer
|
||
mirror_dir() {
|
||
local src="$1"
|
||
local dest_rel="$2" # relativ zu NAS_BASE
|
||
ensure_remote_dir "${NAS_BASE}/${dest_rel}"
|
||
rsync "${RSYNC_OPTS[@]}" "${RSYNC_SSH[@]}" "${src%/}/" \
|
||
"${NAS_USER}@${NAS_HOST}:${NAS_BASE}/${dest_rel}/"
|
||
}
|
||
|
||
mirror_files() {
|
||
# mehrere Dateien in dasselbe Ziel spiegeln (kein trailing slash)
|
||
local dest_rel="$1"; shift
|
||
ensure_remote_dir "${NAS_BASE}/${dest_rel}"
|
||
rsync "${RSYNC_OPTS[@]}" "${RSYNC_SSH[@]}" "$@" \
|
||
"${NAS_USER}@${NAS_HOST}:${NAS_BASE}/${dest_rel}/"
|
||
}
|
||
|
||
echo "[*] Backup gestartet: ${DATE}"
|
||
|
||
# ── 1) Portainer-Backup via API (Token, JSON-POST) → fixer Dateiname ─────────
|
||
PORTAINER_URL="http://127.0.0.1:9000" # lokal ist robuster als Traefik
|
||
PORTAINER_TOKEN="$(cat /root/.portainer-token 2>/dev/null || true)"
|
||
# optional: Passphrase aus Datei (falls du das Backup verschlüsseln willst)
|
||
PORTAINER_BACKUP_PASS="$(cat /root/.portainer-backup-pass 2>/dev/null || true)"
|
||
OUT="${TMP_DIR}/portainer-backup.tar.gz"
|
||
|
||
if [ -z "$PORTAINER_TOKEN" ]; then
|
||
echo "WARN: /root/.portainer-token fehlt – Portainer-Backup übersprungen."
|
||
else
|
||
echo "[*] Portainer-Backup via API (Token, JSON POST)…"
|
||
JSON_BODY='{}'
|
||
[ -n "$PORTAINER_BACKUP_PASS" ] && JSON_BODY=$(printf '{"password":"%s"}' "$PORTAINER_BACKUP_PASS")
|
||
|
||
if curl -fsS -X POST \
|
||
-H "X-API-Key: ${PORTAINER_TOKEN}" \
|
||
-H "Content-Type: application/json" \
|
||
-d "${JSON_BODY}" \
|
||
-o "${OUT}" \
|
||
"${PORTAINER_URL}/api/backup"
|
||
then
|
||
if file "${OUT}" | grep -qi 'gzip compressed data'; then
|
||
ensure_remote_dir "${NAS_BASE}/portainer"
|
||
rsync "${RSYNC_OPTS[@]}" "${RSYNC_SSH[@]}" \
|
||
"${OUT}" "${NAS_USER}@${NAS_HOST}:${NAS_BASE}/portainer/portainer-backup.tar.gz"
|
||
echo "[OK] Portainer-Backup gespeichert."
|
||
else
|
||
echo "ERROR: Portainer-Backup ist keine gzip-Datei – Antwort (erste 300 B):"
|
||
head -c 300 "${OUT}" | sed -e 's/[^[:print:]\t]/./g'
|
||
exit 1
|
||
fi
|
||
else
|
||
echo "ERROR: Portainer-Backup-Request fehlgeschlagen."
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# ── Pi-hole Teleporter (neue Syntax via pihole-FTL) ───────────────
|
||
echo "[*] Pi-hole Teleporter-Backup…"
|
||
|
||
cd "${TMP_DIR}"
|
||
OUTFILE=$(sudo pihole-FTL --teleporter | tail -n1)
|
||
|
||
if [ ! -s "${TMP_DIR}/${OUTFILE}" ]; then
|
||
echo "ERROR: Teleporter-Backup wurde nicht erstellt."; exit 1
|
||
fi
|
||
|
||
ensure_remote_dir "${NAS_BASE}/pihole"
|
||
rsync "${RSYNC_OPTS[@]}" "${RSYNC_SSH[@]}" \
|
||
"${TMP_DIR}/${OUTFILE}" \
|
||
"${NAS_USER}@${NAS_HOST}:${NAS_BASE}/pihole/pihole-teleporter.tar.gz"
|
||
|
||
echo "[OK] Pi-hole Teleporter gesichert als ${OUTFILE} → NAS"
|
||
|
||
# ── 3) Homepage ────────────────────────────────────────────────────
|
||
if [ -d "${HOME_DIR}/homepage" ]; then
|
||
echo "[*] Homepage spiegeln…"
|
||
mirror_dir "${HOME_DIR}/homepage" "homepage"
|
||
else
|
||
echo "INFO: ${HOME_DIR}/homepage nicht gefunden – übersprungen."
|
||
fi
|
||
|
||
# ── 4) Traefik (inkl. acme.json) ───────────────────────────────────
|
||
if [ -d "${HOME_DIR}/traefik" ]; then
|
||
echo "[*] Traefik spiegeln…"
|
||
mirror_dir "${HOME_DIR}/traefik" "traefik"
|
||
else
|
||
echo "INFO: ${HOME_DIR}/traefik nicht gefunden – übersprungen."
|
||
fi
|
||
|
||
# ── 5) WireGuard (lokal) ───────────────────────────────────────────
|
||
echo "[*] WireGuard spiegeln…"
|
||
mirror_dir "/etc/wireguard" "wireguard"
|
||
|
||
# ── 6) Wichtige Systemdateien ──────────────────────────────────────
|
||
echo "[*] Systemdateien spiegeln…"
|
||
# Einzeldateien
|
||
mirror_files "system/etc" /etc/hostname /etc/hosts /etc/fstab /etc/sysctl.conf
|
||
# Verzeichnisse
|
||
mirror_dir "/etc/ssh" "system/etc/ssh"
|
||
mirror_dir "/etc/systemd/system" "system/systemd"
|
||
mirror_dir "/usr/local/bin" "system/usr-local-bin"
|
||
|
||
echo "[OK] Backup fertig: ${DATE}"
|