Fix Athena recovery and installation consistency
This commit is contained in:
+45
-11
@@ -4,23 +4,57 @@ set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CHECK_ONLY=false
|
||||
if [[ ${1:-} == --check ]]; then
|
||||
CHECK_ONLY=true
|
||||
shift
|
||||
fi
|
||||
ARCHIVE=${1:-/data/docker-backups/athena-latest.tar.gz}
|
||||
|
||||
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||
[[ -s $ARCHIVE ]] || die "Backup fehlt: $ARCHIVE"
|
||||
|
||||
validate_archive() {
|
||||
local listing normalized entry required
|
||||
gzip -t "$ARCHIVE" || die "Backup ist beschädigt: $ARCHIVE"
|
||||
listing=$(tar -tzf "$ARCHIVE") || die "Backup-Inhalt kann nicht gelesen werden."
|
||||
|
||||
# docker-volume-backup stores the mounted sources below /backup. GNU tar
|
||||
# removes the leading slash while extracting; accept exactly that tree and
|
||||
# reject every unrelated or traversing member before extracting as root.
|
||||
while IFS= read -r entry; do
|
||||
normalized=${entry#./}
|
||||
normalized=${normalized#/}
|
||||
[[ $normalized == backup || $normalized == backup/* ]] || \
|
||||
die "Unerwarteter Pfad im Backup: $entry"
|
||||
[[ /$normalized/ != */../* ]] || die "Unsicherer Pfad im Backup: $entry"
|
||||
done <<<"$listing"
|
||||
|
||||
for required in backup/etc-mike-ai backup/volumes; do
|
||||
grep -Eq "^/?${required}(/|$)" <<<"$listing" || \
|
||||
die "Backup enthält $required nicht."
|
||||
done
|
||||
}
|
||||
|
||||
validate_archive
|
||||
if [[ $CHECK_ONLY == true ]]; then
|
||||
printf 'ATHENA_BACKUP_CHECK_OK %s\n' "$ARCHIVE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||
command -v docker >/dev/null || die "Docker fehlt. Zuerst ./install.sh ausführen."
|
||||
|
||||
work=$(mktemp -d /tmp/athena-restore.XXXXXX)
|
||||
trap 'rm -rf "$work"' EXIT
|
||||
|
||||
# Refuse absolute paths and parent traversal before extracting as root.
|
||||
if tar -tzf "$ARCHIVE" | grep -Eq '(^/|(^|/)\.\.(/|$))'; then
|
||||
die "Unsichere Pfade im Backup."
|
||||
fi
|
||||
# GNU tar removes the leading slash and extracts the single validated tree as
|
||||
# $work/backup. Keeping that root explicit avoids platform-dependent
|
||||
# --strip-components behaviour between GNU tar and bsdtar.
|
||||
tar -xzf "$ARCHIVE" -C "$work"
|
||||
[[ -d $work/etc-mike-ai ]] || die "Backup enthält etc-mike-ai nicht."
|
||||
[[ -d $work/volumes ]] || die "Backup enthält keine Docker-Volumes."
|
||||
backup_root=$work/backup
|
||||
[[ -d $backup_root/etc-mike-ai ]] || die "Backup enthält etc-mike-ai nicht."
|
||||
[[ -d $backup_root/volumes ]] || die "Backup enthält keine Docker-Volumes."
|
||||
|
||||
# Stop only users of the restored volumes. WireGuard, SSH and networking stay up.
|
||||
for container in mike-ai-router mike-ai-profile-controller mike-ai-piper; do
|
||||
@@ -30,7 +64,7 @@ for container in mike-ai-router mike-ai-profile-controller mike-ai-piper; do
|
||||
done
|
||||
|
||||
install -d -m 0700 /etc/mike-ai
|
||||
rsync -a --delete "$work/etc-mike-ai/" /etc/mike-ai/
|
||||
rsync -a --delete "$backup_root/etc-mike-ai/" /etc/mike-ai/
|
||||
|
||||
restore_volume() {
|
||||
local volume=$1 source=$2 mountpoint
|
||||
@@ -41,9 +75,9 @@ restore_volume() {
|
||||
rsync -a --delete "$source/" "$mountpoint/"
|
||||
}
|
||||
|
||||
restore_volume mike-ai_piper-data "$work/volumes/piper-data"
|
||||
restore_volume mike-ai_router-state "$work/volumes/router-state"
|
||||
restore_volume mike-ai_router-images "$work/volumes/router-images"
|
||||
restore_volume mike-ai_piper-data "$backup_root/volumes/piper-data"
|
||||
restore_volume mike-ai_router-state "$backup_root/volumes/router-state"
|
||||
restore_volume mike-ai_router-images "$backup_root/volumes/router-images"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
./manage.sh deploy core
|
||||
|
||||
Reference in New Issue
Block a user