#!/usr/bin/env bash # ===================================================================================== # Online full-domain backup (AD database + SYSVOL + secrets) via `samba-tool domain backup`. # Copied out of the container into ./backups/.tgz and checksummed. # # ./scripts/backup.sh # uses $AD_CONTAINER / .env # KEEP=14 ./scripts/backup.sh # prune to the newest 14 backups # # A backup you have not restored is a rumour. Restore drill: ./scripts/restore.sh # ===================================================================================== set -euo pipefail cd "$(dirname "$0")/.." . "$(dirname "$0")/envfile.sh"; ad_env_load .env # parsed, never executed (see envfile.sh) CID="${AD_CONTAINER:-ad-dc}" REALM="${AD_REALM:?}" HOST="${AD_HOSTNAME:-dc1}" ZONE="${AD_DNS_DOMAIN:?}" FQDN="${HOST}.${ZONE}" CC=/tmp/backup_ccache DEST="${AD_BACKUP_DIR:-backups}" KEEP="${KEEP:-14}" PW="${AD_ADMIN_PASSWORD:-}" [ -z "$PW" ] && [ -r secrets/ad_admin.password ] && PW="$(tr -d '\r\n' < secrets/ad_admin.password)" [ -n "$PW" ] || { echo "no administrator password available" >&2; exit 2; } docker inspect -f '{{.State.Status}}' "$CID" | grep -q running || { echo "$CID is not running" >&2; exit 1; } mkdir -p "$DEST" # Preflight: a backup that dies after 10 minutes of work is worse than one that never starts. # Docker creates bind-mount source dirs as root, and `docker cp` extracts to the host as the # *invoking* user - so a root-owned backups/ fails with a cryptic "openat: permission denied". if [ ! -w "$DEST" ]; then echo "!! backup dir '$DEST' is not writable by $(whoami) (owned $(stat -c '%U:%G %a' "$DEST"))" >&2 echo " typically created root-owned by Docker for a bind mount. Fix with:" >&2 echo " sudo chown $(whoami) \"$DEST\"" >&2 exit 1 fi ts="$(date -u +%Y%m%dT%H%M%SZ)" echo ">> backing up ${FQDN} (online, consistent snapshot) -> ${DEST}/" printf '%s\n' "$PW" | docker exec -i -e KRB5CCNAME="FILE:${CC}" -e AD_FQDN="$FQDN" "$CID" \ bash -euo pipefail -c ' kinit "${AD_ADMIN_USER:-administrator}@${AD_REALM}" samba-tool domain backup online \ --server="$AD_FQDN" \ --targetdir=/var/backups/ad-dc \ --use-krb5-ccache="$KRB5CCNAME" ' # Samba names the archive samba-backup--.tar.bz2 (older builds: .tgz) newest="$(docker exec "$CID" bash -c 'ls -1t /var/backups/ad-dc/*.tar.bz2 /var/backups/ad-dc/*.tgz 2>/dev/null | head -1')" [ -n "$newest" ] || { echo "backup produced no archive" >&2; exit 1; } docker cp "${CID}:${newest}" "${DEST}/${ts}-$(basename "$newest")" out="${DEST}/${ts}-$(basename "$newest")" ( cd "$DEST" && sha256sum "$(basename "$out")" > "$(basename "$out").sha256" ) # Don't leave archives inside the container layer - they survive restarts and silently grow it. docker exec "$CID" rm -f "$newest" 2>/dev/null || true size="$(du -h "$out" | cut -f1)" echo ">> wrote $out ($size)" echo " checksum: $(cat "$out.sha256")" # rotation mapfile -t existing < <(ls -1t "$DEST"/*.tar.bz2 "$DEST"/*.tgz 2>/dev/null || true) if [ "${#existing[@]}" -gt "$KEEP" ]; then for old in "${existing[@]:$KEEP}"; do echo " pruning $old" rm -f "$old" "$old.sha256" done fi echo ">> done. Copy $out off this host (backup of a backup) and keep the AD_DB key safe."