Add setup.sh with nightly systemd timer; move key and excludes into /opt/backup

This commit is contained in:
Christoph Haas 2026-09-19 20:36:47 +00:00
parent 99c716d35b
commit 2db05c9bb6
6 changed files with 151 additions and 27 deletions

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
# Secrets — never commit the real env file
# Secrets — never commit
backup.env
encryption-key.pem

View file

@ -13,23 +13,35 @@ Everything lives in `/opt/backup/`. The connection details and credentials
|------|---------|
| `backup.env` | Shared config: server, datastore, namespace, token, key (sourced by both scripts) |
| `backup.env.sample` | Template — copy to `backup.env` and fill in real values |
| `setup.sh` | Install the client and create the encryption key (run once as root) |
| `backup.sh` | Full backup of `/` with client-side encryption |
| `restore.sh` | FUSE-mount the encrypted backup to browse/restore files |
| `/etc/proxmox-backup/encryption-key.pem` | Client-side encryption key (store a copy off-host!) |
| `/etc/proxmox-backup/defaults.exclude` | Paths excluded from the backup |
| `/opt/backup/encryption-key.pem` | Client-side encryption key (store a copy off-host!) |
| `/opt/backup/exclude` | Paths excluded from the backup |
## Setup
On a fresh host, as root:
```
./setup.sh
cp backup.env.sample backup.env
$EDITOR backup.env # fill in server, datastore, namespace, token
chmod 600 backup.env
```
## How `backup.sh` works
1. Sources `backup.env`, which exports the connection/auth settings (server,
datastore, port, API token) as separate `PBS_*` component variables. This
matters: `PBS_AUTH_ID` is only honoured when `PBS_REPOSITORY` is *not* set.
2. Builds `--exclude` flags from `/etc/proxmox-backup/defaults.exclude`
2. Builds `--exclude` flags from `/opt/backup/exclude`
(`/proc`, `/sys`, `/dev`, `/run`, `/tmp`, …).
3. Runs:
```
proxmox-backup-client backup "${HOSTNAME}-root.pxar:/" \
--keyfile /etc/proxmox-backup/encryption-key.pem \
--keyfile /opt/backup/encryption-key.pem \
--ns "$BACKUP_NS" \
--exclude /proc/* ...
```
@ -46,8 +58,8 @@ The **encryption key is the only thing that makes the backups readable**. If
this host and the key are both lost, the backup is unrecoverable — no one at
the PBS provider can help.
- File: `/etc/proxmox-backup/encryption-key.pem`
- Fingerprint: `proxmox-backup-client key show /etc/proxmox-backup/encryption-key.pem`
- File: `/opt/backup/encryption-key.pem`
- Fingerprint: `proxmox-backup-client key show /opt/backup/encryption-key.pem`
Keep a copy somewhere safe that is **not this machine**:
- offline USB stick / printed paperkey
@ -138,7 +150,7 @@ First source the shared config (`source /opt/backup/backup.env`), then:
```
proxmox-backup-client restore host/<hostname>/<time> <hostname>-root.pxar /restore/dir \
--ns <namespace> \
--keyfile /etc/proxmox-backup/encryption-key.pem \
--keyfile /opt/backup/encryption-key.pem \
--pattern "etc/postfix/main.cf"
```

View file

@ -23,7 +23,7 @@ export PBS_AUTH_ID="user@pbs!tokenname"
export PBS_PASSWORD="paste-token-secret-here"
# --- Client-side encryption key (passed via --keyfile) ---
ENCRYPTION_KEY_FILE="/etc/proxmox-backup/encryption-key.pem"
ENCRYPTION_KEY_FILE="/opt/backup/encryption-key.pem"
# --- Local hostname (used as backup-id and archive-name prefix) ---
HOSTNAME="$(hostname)"

View file

@ -9,28 +9,16 @@ source "$SCRIPT_DIR/backup.env"
# BACKUP-SPECIFIC CONFIGURATION
# ==============================================================================
BACKUP_LABEL="${HOSTNAME}-root"
EXCLUDE_FILE="/etc/proxmox-backup/defaults.exclude"
# Create a default exclude file if it doesn't exist
if [ ! -f "$EXCLUDE_FILE" ]; then
mkdir -p "$(dirname "$EXCLUDE_FILE")"
cat << 'EOF' > "$EXCLUDE_FILE"
/proc/*
/sys/*
/dev/*
/run/*
/tmp/*
/mnt/*
/media/*
/lost+found
/var/tmp/*
/var/cache/*
EOF
fi
EXCLUDE_FILE="/opt/backup/exclude"
# ==============================================================================
# EXECUTION
# ==============================================================================
if [ ! -f "$EXCLUDE_FILE" ]; then
echo "error: exclude list not found at $EXCLUDE_FILE — run setup.sh first" >&2
exit 1
fi
echo "Starting Proxmox Backup for ${HOSTNAME}..."
EXCLUDE_ARGS=()

10
exclude Normal file
View file

@ -0,0 +1,10 @@
/proc/*
/sys/*
/dev/*
/run/*
/tmp/*
/mnt/*
/media/*
/lost+found
/var/tmp/*
/var/cache/*

113
setup.sh Executable file
View file

@ -0,0 +1,113 @@
#!/usr/bin/env bash
set -euo pipefail
# ==============================================================================
# Set up Proxmox Backup Client on a Debian trixie host.
#
# Installs the client and creates the client-side encryption key. Everything
# lives in /opt/backup (see backup.env for the connection/auth config).
#
# Run as root. Safe to re-run: it will not overwrite an existing key.
# ==============================================================================
KEYRING="/usr/share/keyrings/proxmox-archive-keyring.gpg"
KEYRING_URL="https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg"
SOURCES_FILE="/etc/apt/sources.list.d/pbs-client.sources"
BACKUP_DIR="/opt/backup"
KEY_FILE="${BACKUP_DIR}/encryption-key.pem"
EXCLUDE_FILE="${BACKUP_DIR}/exclude"
ENV_FILE="${BACKUP_DIR}/backup.env"
ENV_SAMPLE="${BACKUP_DIR}/backup.env.sample"
SERVICE_UNIT="/etc/systemd/system/pbs-backup.service"
TIMER_UNIT="/etc/systemd/system/pbs-backup.timer"
BACKUP_TIME="02:00"
# 1. Proxmox archive signing key
wget "$KEYRING_URL" -O "$KEYRING"
# 2. APT source for the client-only repository
cat > "$SOURCES_FILE" <<'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/pbs-client
Suites: trixie
Components: main
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
# 3. Install the client
apt-get update
apt-get install -y proxmox-backup-client
# 4. Client-side encryption key (no password). Never overwrite an existing key.
mkdir -p "$BACKUP_DIR"
if [ -e "$KEY_FILE" ]; then
echo "Encryption key already exists at $KEY_FILE — leaving it untouched." >&2
else
proxmox-backup-client key create "$KEY_FILE" --kdf none
chmod 600 "$KEY_FILE"
echo "Created encryption key: $KEY_FILE"
fi
# 5. Exclude list (created only if it doesn't exist yet)
if [ ! -f "$EXCLUDE_FILE" ]; then
cat > "$EXCLUDE_FILE" <<'EOF'
/proc/*
/sys/*
/dev/*
/run/*
/tmp/*
/mnt/*
/media/*
/lost+found
/var/tmp/*
/var/cache/*
EOF
echo "Created exclude list: $EXCLUDE_FILE"
fi
# 6. Config template -> backup.env (unless backup.env already exists)
if [ -f "$ENV_FILE" ]; then
echo "backup.env already exists — leaving it untouched." >&2
elif [ -f "$ENV_SAMPLE" ]; then
cp "$ENV_SAMPLE" "$ENV_FILE"
chmod 600 "$ENV_FILE"
echo "Created $ENV_FILE from $ENV_SAMPLE"
else
echo "warning: $ENV_SAMPLE not found — create $ENV_FILE manually" >&2
fi
# 7. systemd timer — run the backup daily at night
cat > "$SERVICE_UNIT" <<EOF
[Unit]
Description=Proxmox Backup Client host backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=${BACKUP_DIR}/backup.sh
Nice=19
IOSchedulingClass=idle
EOF
cat > "$TIMER_UNIT" <<EOF
[Unit]
Description=Daily Proxmox Backup
[Timer]
OnCalendar=*-*-* ${BACKUP_TIME}
RandomizeDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now pbs-backup.timer
echo
echo "Done. Fill in the credentials, then run ${BACKUP_DIR}/backup.sh (or wait for the timer):"
echo " ${EDITOR:-vi} ${ENV_FILE} # server/datastore/namespace/token"
echo " systemctl list-timers pbs-backup.timer"
echo " journalctl -u pbs-backup"