#!/bin/bash set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script configuration SCRIPT_VERSION="1.0.0" CONFIG_DIR="/etc/proxmox-backup-client" LOG_FILE="/var/log/pbs-client-installer.log" # Helper functions log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } error() { echo -e "${RED}[ERROR]${NC} $1" >&2 echo "[ERROR] $1" >> "$LOG_FILE" } warn() { echo -e "${YELLOW}[WARN]${NC} $1" echo "[WARN] $1" >> "$LOG_FILE" } info() { echo -e "${BLUE}[INFO]${NC} $1" } prompt() { local prompt_text="$1" local default_value="$2" local user_input if [ -n "$default_value" ]; then read -p "$(echo -e ${BLUE}${prompt_text}${NC} [${default_value}]: )" user_input echo "${user_input:-$default_value}" else read -p "$(echo -e ${BLUE}${prompt_text}${NC}: )" user_input echo "$user_input" fi } prompt_password() { local prompt_text="$1" local user_input read -sp "$(echo -e ${BLUE}${prompt_text}${NC}: )" user_input echo echo "$user_input" } # Check if running as root check_root() { if [ "$EUID" -ne 0 ]; then error "This script must be run as root (use sudo)" exit 1 fi } # Detect Linux distribution detect_distro() { log "Detecting Linux distribution..." if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID OS_VERSION=$VERSION_ID OS_PRETTY=$PRETTY_NAME elif [ -f /etc/lsb-release ]; then . /etc/lsb-release OS=$DISTRIB_ID OS_VERSION=$DISTRIB_RELEASE OS_PRETTY=$DISTRIB_DESCRIPTION else error "Cannot detect Linux distribution" exit 1 fi info "Detected: $OS_PRETTY" # Normalize OS names OS=$(echo "$OS" | tr '[:upper:]' '[:lower:]') } # Install PBS client on Ubuntu install_ubuntu() { log "Installing Proxmox Backup Client on Ubuntu $OS_VERSION..." # Determine which repository to use if [[ "$OS_VERSION" == "24.04" ]] || [[ "$OS_VERSION" > "24" ]]; then REPO="bookworm" GPG_FILE="proxmox-release-bookworm.gpg" elif [[ "$OS_VERSION" == "22.04" ]]; then REPO="bullseye" GPG_FILE="proxmox-release-bullseye.gpg" # Need to add Focal security repo for libssl1.1 NEED_FOCAL=true elif [[ "$OS_VERSION" == "20.04" ]]; then REPO="bullseye" GPG_FILE="proxmox-release-bullseye.gpg" else error "Unsupported Ubuntu version: $OS_VERSION" exit 1 fi # Download and install GPG key log "Downloading GPG key..." wget -q "https://enterprise.proxmox.com/debian/${GPG_FILE}" \ -O "/etc/apt/trusted.gpg.d/${GPG_FILE}" # Add PBS client repository log "Adding PBS client repository..." echo "deb [arch=amd64] http://download.proxmox.com/debian/pbs-client ${REPO} main" > \ /etc/apt/sources.list.d/pbs-client.list # Add Focal security repo if needed (Ubuntu 22.04) if [ "$NEED_FOCAL" = true ]; then log "Adding Ubuntu Focal security repository for libssl1.1..." echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu focal-security main" > \ /etc/apt/sources.list.d/focal-security.list fi # Update and install log "Updating package lists..." apt-get update -qq log "Installing proxmox-backup-client..." apt-get install -y proxmox-backup-client log "PBS client installed successfully" } # Install PBS client on Debian install_debian() { log "Installing Proxmox Backup Client on Debian $OS_VERSION..." # Determine repository based on Debian version case "$OS_VERSION" in 12*) REPO="bookworm" GPG_FILE="proxmox-release-bookworm.gpg" ;; 11*) REPO="bullseye" GPG_FILE="proxmox-release-bullseye.gpg" ;; 10*) REPO="buster" GPG_FILE="proxmox-release-buster.gpg" ;; *) error "Unsupported Debian version: $OS_VERSION" exit 1 ;; esac # Download and install GPG key log "Downloading GPG key..." wget -q "https://enterprise.proxmox.com/debian/${GPG_FILE}" \ -O "/etc/apt/trusted.gpg.d/${GPG_FILE}" # Add PBS client repository log "Adding PBS client repository..." echo "deb [arch=amd64] http://download.proxmox.com/debian/pbs-client ${REPO} main" > \ /etc/apt/sources.list.d/pbs-client.list # Update and install log "Updating package lists..." apt-get update -qq log "Installing proxmox-backup-client..." apt-get install -y proxmox-backup-client log "PBS client installed successfully" } # Install PBS client on Arch Linux install_arch() { log "Installing Proxmox Backup Client on Arch Linux..." # Check if yay is installed if ! command -v yay &> /dev/null; then error "yay AUR helper is required but not installed" info "Install yay first: https://github.com/Jguer/yay" exit 1 fi # Get the user who invoked sudo if [ -n "$SUDO_USER" ]; then REAL_USER="$SUDO_USER" else error "Cannot determine original user. Run with sudo." exit 1 fi log "Installing proxmox-backup-client-bin from AUR..." sudo -u "$REAL_USER" yay -S --noconfirm proxmox-backup-client-bin log "PBS client installed successfully" } # Main installation function install_pbs_client() { case "$OS" in ubuntu) install_ubuntu ;; debian) install_debian ;; arch|archlinux) install_arch ;; *) error "Unsupported distribution: $OS" info "Supported distributions: Ubuntu, Debian, Arch Linux" exit 1 ;; esac # Verify installation if command -v proxmox-backup-client &> /dev/null; then local version=$(proxmox-backup-client version 2>/dev/null | head -n1) log "Installation verified: $version" return 0 else error "Installation verification failed" return 1 fi } # Interactive configuration interactive_config() { log "Starting interactive configuration..." echo echo "======================================" echo " PBS Client Configuration" echo "======================================" echo # PBS Server details PBS_SERVER=$(prompt "Enter PBS server IP/hostname" "192.168.1.181") PBS_PORT=$(prompt "Enter PBS server port" "8007") PBS_DATASTORE=$(prompt "Enter datastore name" "backups") echo info "Authentication Method:" echo " 1) Username + Password" echo " 2) API Token (recommended for automation)" AUTH_METHOD=$(prompt "Select authentication method [1/2]" "2") if [ "$AUTH_METHOD" = "1" ]; then PBS_USERNAME=$(prompt "Enter username" "root") PBS_REALM=$(prompt "Enter realm" "pam") PBS_PASSWORD=$(prompt_password "Enter password") PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}" else PBS_USERNAME=$(prompt "Enter username" "backup") PBS_REALM=$(prompt "Enter realm" "pbs") PBS_TOKEN_NAME=$(prompt "Enter token name" "backup-token") PBS_TOKEN_SECRET=$(prompt_password "Enter token secret") PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}!${PBS_TOKEN_NAME}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}" PBS_PASSWORD="$PBS_TOKEN_SECRET" fi echo # Backup configuration info "Backup Configuration:" BACKUP_PATHS=$(prompt "Enter paths to backup (space-separated)" "/") EXCLUDE_PATTERNS=$(prompt "Enter exclusion patterns (space-separated)" "/tmp /var/tmp /var/cache /proc /sys /dev /run") echo info "Backup Schedule:" echo " 1) Hourly" echo " 2) Daily (recommended)" echo " 3) Weekly" echo " 4) Custom" SCHEDULE_TYPE=$(prompt "Select schedule type [1/2/3/4]" "2") case "$SCHEDULE_TYPE" in 1) TIMER_SCHEDULE="hourly" TIMER_ONCALENDAR="hourly" ;; 2) BACKUP_HOUR=$(prompt "Enter hour for daily backup (0-23)" "2") TIMER_SCHEDULE="daily" TIMER_ONCALENDAR="*-*-* ${BACKUP_HOUR}:00:00" ;; 3) BACKUP_DOW=$(prompt "Enter day of week (Mon-Sun)" "Sun") BACKUP_HOUR=$(prompt "Enter hour (0-23)" "2") TIMER_SCHEDULE="weekly" TIMER_ONCALENDAR="${BACKUP_DOW} *-*-* ${BACKUP_HOUR}:00:00" ;; 4) TIMER_ONCALENDAR=$(prompt "Enter systemd OnCalendar format" "*-*-* 02:00:00") TIMER_SCHEDULE="custom" ;; esac echo # Retention policy info "Retention Policy:" KEEP_LAST=$(prompt "Keep last N backups" "3") KEEP_DAILY=$(prompt "Keep daily backups for N days" "7") KEEP_WEEKLY=$(prompt "Keep weekly backups for N weeks" "4") KEEP_MONTHLY=$(prompt "Keep monthly backups for N months" "6") # Encryption echo ENABLE_ENCRYPTION=$(prompt "Enable encryption? (yes/no)" "yes") } # Create encryption key create_encryption_key() { if [[ "$ENABLE_ENCRYPTION" == "yes" ]]; then log "Creating encryption key..." # Create config directory for root mkdir -p /root/.config/proxmox-backup # Generate key without password (for automated backups) if proxmox-backup-client key create --kdf none 2>/dev/null; then log "Encryption key created successfully" # Create paper backup local paper_key="/root/pbs-encryption-key-$(date +%Y%m%d).txt" proxmox-backup-client key paperkey --output-format text > "$paper_key" warn "IMPORTANT: Encryption key paper backup saved to: $paper_key" warn "Print this file and store it securely. Lost keys = permanent data loss!" # Copy to config dir for service cp /root/.config/proxmox-backup/encryption-key.json "$CONFIG_DIR/" else error "Failed to create encryption key" return 1 fi fi } # Test connection to PBS test_connection() { log "Testing connection to PBS server..." export PBS_REPOSITORY="$PBS_REPOSITORY" export PBS_PASSWORD="$PBS_PASSWORD" if proxmox-backup-client snapshot list &>/dev/null; then log "Connection test successful!" return 0 else error "Connection test failed" error "Please verify your server details and credentials" return 1 fi } # Create systemd service create_systemd_service() { log "Creating systemd service and timer..." # Create config directory mkdir -p "$CONFIG_DIR" # Save configuration cat > "$CONFIG_DIR/config" < "$CONFIG_DIR/backup.sh" <<'EOFSCRIPT' #!/bin/bash set -e # Load configuration source /etc/proxmox-backup-client/config # Export for PBS client export PBS_REPOSITORY export PBS_PASSWORD # Build backup command BACKUP_CMD="proxmox-backup-client backup" HOSTNAME=$(hostname) # Add each path as separate archive for path in $BACKUP_PATHS; do # Sanitize path for archive name archive_name=$(echo "$path" | sed 's/^\///' | sed 's/\//-/g') [ -z "$archive_name" ] && archive_name="root" BACKUP_CMD="$BACKUP_CMD ${archive_name}.pxar:${path}" # Add --include-dev for mount points if mountpoint -q "$path" 2>/dev/null; then BACKUP_CMD="$BACKUP_CMD --include-dev ${path}" fi done # Add exclusions for pattern in $EXCLUDE_PATTERNS; do BACKUP_CMD="$BACKUP_CMD --exclude=${pattern}" done # Add other options BACKUP_CMD="$BACKUP_CMD --skip-lost-and-found --change-detection-mode=metadata" # Execute backup echo "[$(date)] Starting backup..." if eval $BACKUP_CMD; then echo "[$(date)] Backup completed successfully" # Prune old backups echo "[$(date)] Pruning old backups..." proxmox-backup-client prune "host/${HOSTNAME}" \ --keep-last $KEEP_LAST \ --keep-daily $KEEP_DAILY \ --keep-weekly $KEEP_WEEKLY \ --keep-monthly $KEEP_MONTHLY echo "[$(date)] Prune completed" else echo "[$(date)] Backup FAILED" >&2 exit 1 fi EOFSCRIPT chmod 700 "$CONFIG_DIR/backup.sh" # Create systemd service file cat > /etc/systemd/system/pbs-backup.service < /etc/systemd/system/pbs-backup.timer < /dev/null; then warn "Proxmox Backup Client is already installed" REINSTALL=$(prompt "Do you want to reinstall? (yes/no)" "no") if [[ "$REINSTALL" != "yes" ]]; then info "Skipping installation, proceeding to configuration..." else install_pbs_client fi else install_pbs_client fi # Interactive configuration interactive_config # Create encryption key create_encryption_key # Test connection if ! test_connection; then error "Cannot proceed without successful connection to PBS" exit 1 fi # Create systemd service create_systemd_service # Offer to run backup now run_backup_now # Show summary show_summary } # Run main function main "$@"