#!/usr/bin/env bash set -Eeuo pipefail DEST_BASE="/run/media/hans/usbsicherung/jacboy_sicherung/system_sicherung" #DEST_BASE="/run/media/hans/usbsicherung/sicherungen" HOST="$(hostname -s)" STAMP="$(date +%F_%H-%M-%S)" TARGET_DIR="${DEST_BASE}/${HOST}/${STAMP}" LATEST_LINK="${DEST_BASE}/${HOST}/latest" LOG_DIR="${DEST_BASE}/${HOST}/logs" LOG_FILE="${LOG_DIR}/netzwerk-backup-${STAMP}.log" KEEP=20 # Quellen WG_SRC="/etc/wireguard" NM_SRC="/etc/NetworkManager/system-connections" APT_SOURCES_LIST="/etc/apt/sources.list" APT_SOURCES_D="/etc/apt/sources.list.d" APT_TRUSTED_D="/etc/apt/trusted.gpg.d" APT_KEYRINGS="/etc/apt/keyrings" USR_KEYRINGS="/usr/share/keyrings" log() { local msg="[$(date +%F\\ %T)] $*" echo "${msg}" mkdir -p "${LOG_DIR}" echo "${msg}" >> "${LOG_FILE}" } fail() { log "FEHLER: $*" exit 1 } # Root nötig, weil /etc/* und ggf. restriktive Rechte if [[ ${EUID} -ne 0 ]]; then fail "Dieses Skript muss mit sudo oder als root ausgeführt werden." fi # Ziel prüfen if [[ ! -d "${DEST_BASE}" ]]; then fail "Zielbasis ${DEST_BASE} existiert nicht oder ist nicht gemountet." fi if [[ ! -w "${DEST_BASE}" ]]; then fail "Zielbasis ${DEST_BASE} ist nicht beschreibbar." fi mkdir -p "${TARGET_DIR}" "${LOG_DIR}" RSYNC_OPTS=( -aHAX --numeric-ids --delete --info=stats2,progress2 --human-readable --partial ) if [[ -L "${LATEST_LINK}" ]] && [[ -d "$(readlink -f "${LATEST_LINK}")" ]]; then RSYNC_OPTS+=(--link-dest="$(readlink -f "${LATEST_LINK}")") log "Verwende link-dest: $(readlink -f "${LATEST_LINK}")" else log "Kein vorheriger Snapshot gefunden – es wird ein vollständiger Lauf erstellt." fi backup_tree() { local src="$1" local name="$2" if [[ -d "${src}" ]]; then log "Sichere Verzeichnis ${src} -> ${TARGET_DIR}/${name}/" rsync "${RSYNC_OPTS[@]}" "${src}/" "${TARGET_DIR}/${name}/" | tee -a "${LOG_FILE}" else log "Quelle (Verzeichnis) ${src} existiert nicht – wird übersprungen." fi } backup_file() { local src="$1" local name="$2" if [[ -f "${src}" ]]; then log "Sichere Datei ${src} -> ${TARGET_DIR}/${name}" mkdir -p "$(dirname "${TARGET_DIR}/${name}")" rsync "${RSYNC_OPTS[@]}" "${src}" "${TARGET_DIR}/${name}" | tee -a "${LOG_FILE}" else log "Quelle (Datei) ${src} existiert nicht – wird übersprungen." fi } # WireGuard & NetworkManager backup_tree "${WG_SRC}" "wireguard" backup_tree "${NM_SRC}" "networkmanager-system-connections" # APT: sources und Keys backup_file "${APT_SOURCES_LIST}" "apt/sources.list" backup_tree "${APT_SOURCES_D}" "apt/sources.list.d" backup_tree "${APT_TRUSTED_D}" "apt/trusted.gpg.d" backup_tree "${APT_KEYRINGS}" "apt/keyrings" backup_tree "${USR_KEYRINGS}" "apt/usr_keyrings" # latest-Link aktualisieren ln -sfn "${TARGET_DIR}" "${LATEST_LINK}" log "latest-Link zeigt jetzt auf ${TARGET_DIR}" # Rotation cd "${DEST_BASE}/${HOST}" SNAPS=(20*/) if (( ${#SNAPS[@]} > KEEP )); then log "Rotationslauf: ${#SNAPS[@]} Snapshots vorhanden, KEEP=${KEEP}." ls -1dt 20*/ | tail -n +$((KEEP + 1)) | while read -r old; do log "Lösche alten Snapshot: ${old}" rm -rf -- "${old}" done else log "Keine Rotation nötig. Snapshots vorhanden: ${#SNAPS[@]}" fi log "Netzwerk- und APT-Konfigurationsbackup abgeschlossen."