#!/bin/bash # Ubuntu 24.04 Post-Installation Script # Run with: sudo bash post-install.sh # This script is rerunnable - it detects existing installations # ============================================================================ # COMMAND-LINE ARGUMENT PARSING # ============================================================================ DRY_RUN=false UNATTENDED=false LOG_FILE="/var/log/post-install.log" show_help() { echo "Ubuntu 24.04 Post-Installation Script" echo "" echo "Usage: sudo ./post-install.sh [OPTIONS]" echo "" echo "Options:" echo " --dry-run Preview what would be installed without making changes" echo " --unattended Run with default options (no prompts)" echo " Defaults: skip key generation, no SSH imports," echo " install Docker, skip Samba/NetBird/RustDesk/Backup" echo " --help Show this help message" echo "" echo "Examples:" echo " sudo ./post-install.sh # Interactive mode" echo " sudo ./post-install.sh --dry-run # Preview installations" echo " sudo ./post-install.sh --unattended # Automated install" echo "" exit 0 } for arg in "$@"; do case $arg in --dry-run) DRY_RUN=true ;; --unattended) UNATTENDED=true ;; --help|-h) show_help ;; *) echo "Unknown option: $arg" echo "Use --help for usage information" exit 1 ;; esac done # ============================================================================ # LOGGING SETUP # ============================================================================ # Create log file and tee all output exec > >(tee -a "$LOG_FILE") 2>&1 echo "" echo "=== Post-Install Log Started: $(date) ===" >> "$LOG_FILE" echo "=== Ubuntu 24.04 Post-Installation Script ===" echo "" if [ "$DRY_RUN" = true ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DRY RUN MODE - No changes will be made" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" fi if [ "$UNATTENDED" = true ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "UNATTENDED MODE - Using default options" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" fi # Check if running as root if [ "$EUID" -ne 0 ]; then echo "Please run as root (use sudo)" exit 1 fi # Get the actual user (not root) ACTUAL_USER="${SUDO_USER:-$USER}" ACTUAL_HOME=$(getent passwd "$ACTUAL_USER" | cut -d: -f6) echo "Note: Script will continue even if individual packages fail to install" echo "Log file: $LOG_FILE" echo "" # ============================================================================ # SOFTWARE DETECTION FUNCTIONS # ============================================================================ is_docker_installed() { command -v docker &> /dev/null && systemctl is-active --quiet docker 2>/dev/null } is_samba_installed() { command -v smbd &> /dev/null && systemctl is-active --quiet smbd 2>/dev/null } is_netbird_installed() { command -v netbird &> /dev/null } is_rustdesk_installed() { command -v rustdesk &> /dev/null || dpkg -l rustdesk &> /dev/null } is_rclone_installed() { command -v rclone &> /dev/null } is_rsync_installed() { command -v rsync &> /dev/null } is_ufw_installed() { command -v ufw &> /dev/null } # ============================================================================ # DRY-RUN AND UNATTENDED HELPER FUNCTIONS # ============================================================================ # Run a command, but skip it in dry-run mode run_cmd() { if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would execute: $*" return 0 else "$@" fi } # Prompt for yes/no, with unattended default # Usage: prompt_yn "Question?" "default" VARNAME # default can be "y" or "n" prompt_yn() { local question="$1" local default="$2" local varname="$3" if [ "$UNATTENDED" = true ]; then eval "$varname='$default'" echo "$question [auto: $default]" return fi read -p "$question " response eval "$varname='$response'" } # Prompt for text input, with unattended default # Usage: prompt_text "Question?" "default" VARNAME prompt_text() { local question="$1" local default="$2" local varname="$3" if [ "$UNATTENDED" = true ]; then eval "$varname='$default'" echo "$question [auto: $default]" return fi read -p "$question " response eval "$varname='$response'" } # ============================================================================ # SHOW CURRENT INSTALLATION STATUS # ============================================================================ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "CURRENT SYSTEM STATUS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if is_docker_installed; then echo " ✓ Docker: Installed ($(docker --version 2>/dev/null | cut -d' ' -f3 | tr -d ','))" else echo " ○ Docker: Not installed" fi if is_samba_installed; then echo " ✓ Samba: Installed and running" else echo " ○ Samba: Not installed" fi if is_netbird_installed; then echo " ✓ NetBird: Installed" else echo " ○ NetBird: Not installed" fi if is_rustdesk_installed; then echo " ✓ RustDesk: Installed" else echo " ○ RustDesk: Not installed" fi if is_rclone_installed; then echo " ✓ rclone: Installed" else echo " ○ rclone: Not installed" fi if is_rsync_installed; then echo " ✓ rsync: Installed" else echo " ○ rsync: Not installed" fi if is_ufw_installed; then if ufw status 2>/dev/null | grep -q "Status: active"; then echo " ✓ UFW Firewall: Enabled" else echo " ○ UFW Firewall: Installed but disabled" fi else echo " ○ UFW Firewall: Not installed" fi echo "" echo "This script can reinstall/reconfigure any component." echo "" # Update package list echo "Updating package lists..." run_cmd apt update # Install basic utilities echo "" echo "Installing basic utilities..." echo " - net-tools: Network configuration tools (ifconfig, netstat, etc.)" echo " - ncdu: Disk usage analyzer with ncurses interface" echo " - git: Version control system" echo " - curl: Command-line tool for transferring data with URLs" echo " - wget: Network downloader" echo " - htop: Interactive process viewer" echo " - tree: Display directory structure in tree format" echo " - zip/unzip: Archive compression utilities" echo " - rclone: Rsync for cloud storage and local drives (backup tool)" echo "" run_cmd apt install -y \ net-tools \ ncdu \ git \ curl \ wget \ htop \ tree \ zip \ unzip \ rclone || echo "Warning: Some utilities failed to install, continuing..." # Install OpenSSH Server echo "" echo "Installing OpenSSH Server..." echo " - openssh-server: SSH server for remote access" echo "" apt install -y openssh-server || echo "Warning: OpenSSH server installation failed, continuing..." # Start and enable SSH service systemctl start ssh || echo "Warning: Failed to start SSH" systemctl enable ssh || echo "Warning: Failed to enable SSH" # Generate SSH key for this computer echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SSH KEY GENERATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" prompt_yn "Generate a new SSH key for this computer? (y/n):" "n" GENERATE_KEY if [ "$GENERATE_KEY" = "y" ] || [ "$GENERATE_KEY" = "Y" ]; then echo "" read -p "Enter a label/comment for the key (e.g., email or hostname) [default: $ACTUAL_USER@$(hostname)]: " KEY_COMMENT if [ -z "$KEY_COMMENT" ]; then KEY_COMMENT="$ACTUAL_USER@$(hostname)" fi # Check if key already exists if [ -f "$ACTUAL_HOME/.ssh/id_rsa" ]; then echo "" echo "⚠️ WARNING: SSH key already exists at $ACTUAL_HOME/.ssh/id_rsa" read -p "Overwrite existing key? This cannot be undone! (y/n): " OVERWRITE_KEY if [ "$OVERWRITE_KEY" != "y" ] && [ "$OVERWRITE_KEY" != "Y" ]; then echo "Skipping key generation." GENERATE_KEY="n" fi fi if [ "$GENERATE_KEY" = "y" ] || [ "$GENERATE_KEY" = "Y" ]; then echo "" echo "Generating 4096-bit RSA key pair..." echo "This may take a moment..." # Generate key as the actual user, not root sudo -u "$ACTUAL_USER" ssh-keygen -t rsa -b 4096 -C "$KEY_COMMENT" -f "$ACTUAL_HOME/.ssh/id_rsa" -N "" if [ $? -eq 0 ]; then echo "" echo "✓ SSH key generated successfully!" echo "" echo "Private key: $ACTUAL_HOME/.ssh/id_rsa (keep this secret!)" echo "Public key: $ACTUAL_HOME/.ssh/id_rsa.pub" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Your PUBLIC key (safe to share):" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" cat "$ACTUAL_HOME/.ssh/id_rsa.pub" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "To use this key:" echo " • Add to GitHub: Settings → SSH and GPG keys → New SSH key" echo " • Add to servers: Copy above key to remote ~/.ssh/authorized_keys" echo " • View anytime: cat ~/.ssh/id_rsa.pub" echo "" else echo "✗ Failed to generate SSH key" fi fi else echo "Skipping SSH key generation." fi # Import SSH keys from GitHub/Launchpad echo "" prompt_text "Import SSH keys from GitHub? (enter username or leave blank to skip):" "" GITHUB_USER prompt_text "Import SSH keys from Launchpad? (enter username or leave blank to skip):" "" LAUNCHPAD_USER KEYS_IMPORTED=false # Create .ssh directory if it doesn't exist mkdir -p "$ACTUAL_HOME/.ssh" touch "$ACTUAL_HOME/.ssh/authorized_keys" chmod 700 "$ACTUAL_HOME/.ssh" chmod 600 "$ACTUAL_HOME/.ssh/authorized_keys" if [ -n "$GITHUB_USER" ]; then echo "Importing SSH keys from GitHub user: $GITHUB_USER" if curl -fsSL "https://github.com/$GITHUB_USER.keys" >> "$ACTUAL_HOME/.ssh/authorized_keys" 2>/dev/null; then echo "✓ GitHub keys imported successfully" KEYS_IMPORTED=true else echo "✗ Failed to import GitHub keys" fi fi if [ -n "$LAUNCHPAD_USER" ]; then echo "Importing SSH keys from Launchpad user: $LAUNCHPAD_USER" if curl -fsSL "https://launchpad.net/~$LAUNCHPAD_USER/+sshkeys" >> "$ACTUAL_HOME/.ssh/authorized_keys" 2>/dev/null; then echo "✓ Launchpad keys imported successfully" KEYS_IMPORTED=true else echo "✗ Failed to import Launchpad keys" fi fi # Fix ownership chown -R "$ACTUAL_USER:$ACTUAL_USER" "$ACTUAL_HOME/.ssh" # Disable password authentication if keys were imported if [ "$KEYS_IMPORTED" = true ]; then echo "" echo "SSH keys imported. Disabling password authentication..." # Backup sshd_config cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup # Disable password authentication sed -i 's/^#*PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sed -i 's/^#*PasswordAuthentication no/PasswordAuthentication no/' /etc/ssh/sshd_config # Ensure these settings are also set grep -q "^PasswordAuthentication" /etc/ssh/sshd_config || echo "PasswordAuthentication no" >> /etc/ssh/sshd_config # Restart SSH service to apply changes systemctl restart ssh echo "✓ Password authentication disabled. SSH key authentication required." echo "✓ Backup saved to /etc/ssh/sshd_config.backup" else echo "" echo "No SSH keys imported. Password authentication remains enabled." # Offer fail2ban since password auth is still enabled echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "FAIL2BAN (Recommended with password SSH)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Since password authentication is enabled, fail2ban is recommended" echo "to protect against brute-force SSH attacks." echo "" prompt_yn "Install and enable fail2ban? (y/n):" "y" INSTALL_FAIL2BAN if [ "$INSTALL_FAIL2BAN" = "y" ] || [ "$INSTALL_FAIL2BAN" = "Y" ]; then echo "" echo "Installing fail2ban..." run_cmd apt install -y fail2ban if [ "$DRY_RUN" != true ]; then # Create local config to protect SSH cat > /etc/fail2ban/jail.local << 'FAIL2BAN_CONFIG' [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 bantime = 3600 findtime = 600 FAIL2BAN_CONFIG run_cmd systemctl enable fail2ban run_cmd systemctl restart fail2ban echo "✓ fail2ban installed and configured" echo " - 5 failed attempts = 1 hour ban" echo " - View banned IPs: sudo fail2ban-client status sshd" fi else echo "Skipping fail2ban installation." fi fi INSTALL_FAIL2BAN="${INSTALL_FAIL2BAN:-n}" # Docker Installation echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DOCKER INSTALLATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if is_docker_installed; then echo "Docker is already installed: $(docker --version 2>/dev/null)" prompt_yn "Reinstall Docker? (y/n):" "n" INSTALL_DOCKER else prompt_yn "Install Docker? (y/n):" "y" INSTALL_DOCKER fi if [ "$INSTALL_DOCKER" = "y" ] || [ "$INSTALL_DOCKER" = "Y" ]; then echo "" echo "Installing Docker prerequisites..." echo " - ca-certificates: SSL/TLS certificates for secure connections" echo " - gnupg: GNU Privacy Guard for package verification" echo " - lsb-release: Provides Ubuntu version information" echo "" run_cmd apt install -y \ ca-certificates \ gnupg \ lsb-release || echo "Warning: Some prerequisites failed to install, continuing..." echo "" echo "Installing Docker..." echo " - Docker Engine: Container runtime platform" echo " - Docker Compose: Multi-container application orchestration" echo "" if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would remove old Docker packages" echo "[DRY-RUN] Would add Docker GPG key and repository" echo "[DRY-RUN] Would install docker-ce, docker-ce-cli, containerd.io, plugins" echo "[DRY-RUN] Would start and enable Docker service" echo "[DRY-RUN] Would add $SUDO_USER to docker group" else # Remove old Docker packages if they exist apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true # Add Docker's official GPG key install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc # Add Docker repository echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null # Update package list with Docker repo apt update # Install Docker Engine, CLI, containerd, and Docker Compose plugin apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || echo "Warning: Docker installation failed, continuing..." # Start and enable Docker service systemctl start docker || echo "Warning: Failed to start Docker" systemctl enable docker || echo "Warning: Failed to enable Docker" # Add current user to docker group (if not root) if [ -n "$SUDO_USER" ]; then usermod -aG docker "$SUDO_USER" echo "User $SUDO_USER added to docker group" fi # Verify Docker installation echo "" echo "Verifying Docker installation..." docker --version || echo "Warning: Docker verification failed" docker compose version || echo "Warning: Docker Compose verification failed" fi else echo "Skipping Docker installation." fi # Samba File Sharing (Optional) echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SAMBA FILE SHARING (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Samba allows you to share folders over the network to Windows, Mac, and Linux." echo "The script will share your primary drive at ~/drives/primary" echo "" if is_samba_installed; then echo "Samba is already installed and running." if grep -q "\[Primary\]" /etc/samba/smb.conf 2>/dev/null; then echo " Share 'Primary' is configured at: $ACTUAL_HOME/drives/primary" fi echo "" prompt_yn "Reconfigure Samba? (y/n):" "n" INSTALL_SAMBA else prompt_yn "Install and configure Samba file sharing? (y/n):" "n" INSTALL_SAMBA fi if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then echo "" echo "Installing Samba file sharing..." echo " - Samba: SMB/CIFS file server for network file sharing" echo "" if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would install samba" echo "[DRY-RUN] Would configure [Primary] share at $ACTUAL_HOME/drives/primary" echo "[DRY-RUN] Would prompt for Samba password" echo "[DRY-RUN] Would enable and start smbd/nmbd services" else run_cmd apt install -y samba || echo "Warning: Samba installation failed, continuing..." # Configure Samba share for primary drive if command -v smbd &> /dev/null; then echo "" echo "Configuring Samba share for primary drive..." # Backup existing config cp /etc/samba/smb.conf /etc/samba/smb.conf.backup-$(date +%Y%m%d-%H%M%S) # Add Primary share configuration if ! grep -q "\[Primary\]" /etc/samba/smb.conf; then cat >> /etc/samba/smb.conf << SAMBA_CONFIG # Primary drive share - added by post-install script [Primary] comment = Primary Drive path = $ACTUAL_HOME/drives/primary browseable = yes read only = no writable = yes valid users = $ACTUAL_USER create mask = 0775 directory mask = 0775 SAMBA_CONFIG echo "✓ Added [Primary] share to Samba configuration" else echo "Samba [Primary] share already configured, skipping..." fi # Add Samba user (skip in unattended mode - user must set password manually) if [ "$UNATTENDED" != true ]; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SAMBA PASSWORD SETUP" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Set a password for Samba file sharing access." echo "Tip: Using the same password as your system login is convenient." echo "" smbpasswd -a "$ACTUAL_USER" else echo "Skipping Samba password setup (unattended mode)" echo "Set password later with: sudo smbpasswd -a $ACTUAL_USER" fi # Enable and restart Samba services systemctl enable smbd nmbd || echo "Warning: Failed to enable Samba services" systemctl restart smbd nmbd || echo "Warning: Failed to restart Samba services" echo "" echo "✓ Samba configured successfully" echo " Share name: Primary" echo " Path: $ACTUAL_HOME/drives/primary" echo " Access: \\\\$(hostname)\\Primary (Windows) or smb://$(hostname)/Primary (Mac/Linux)" else echo "✗ Samba installation failed, skipping configuration" fi fi else echo "Skipping Samba installation." fi # NetBird Installation echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "NETBIRD MESH VPN (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "NetBird is a secure mesh VPN for connecting devices across networks." echo "" if is_netbird_installed; then echo "NetBird is already installed." netbird status 2>/dev/null || true echo "" prompt_yn "Reinstall NetBird? (y/n):" "n" INSTALL_NETBIRD else prompt_yn "Install NetBird? (y/n):" "n" INSTALL_NETBIRD fi if [ "$INSTALL_NETBIRD" = "y" ] || [ "$INSTALL_NETBIRD" = "Y" ]; then echo "" echo "Installing NetBird..." echo " - NetBird: Secure mesh VPN for connecting devices" echo "" if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would download and run NetBird install script" else curl -fsSL https://pkgs.netbird.io/install.sh | sh || echo "Warning: NetBird installation failed, continuing..." echo "" echo "NetBird installed. Setup instructions:" echo " 1. Create account at https://app.netbird.io (or self-host)" echo " 2. Run 'netbird up' and authenticate via browser" echo "" echo "For NetBird SSH functionality:" echo " • Enable SSH in NetBird dashboard settings" echo " • Use 'netbird ssh ' to connect to peers" echo " • NetBird manages SSH keys automatically when using 'netbird ssh'" echo " • Traditional SSH also works using peer IPs from 'netbird status'" echo " • Configure ACL rules in dashboard for SSH access (port 22)" echo "" fi else echo "Skipping NetBird installation." fi # RustDesk Installation echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "RUSTDESK REMOTE DESKTOP (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "RustDesk is an open-source remote desktop software." echo "" if is_rustdesk_installed; then echo "RustDesk is already installed." echo "" prompt_yn "Reinstall RustDesk? (y/n):" "n" INSTALL_RUSTDESK else prompt_yn "Install RustDesk? (y/n):" "n" INSTALL_RUSTDESK fi if [ "$INSTALL_RUSTDESK" = "y" ] || [ "$INSTALL_RUSTDESK" = "Y" ]; then echo "" echo "Installing RustDesk..." echo " - RustDesk: Open-source remote desktop software" echo "" if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would download latest RustDesk from GitHub" echo "[DRY-RUN] Would install rustdesk .deb package" else # Download latest RustDesk .deb package RUSTDESK_VERSION=$(curl -s https://api.github.com/repos/rustdesk/rustdesk/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")') RUSTDESK_URL="https://github.com/rustdesk/rustdesk/releases/download/${RUSTDESK_VERSION}/rustdesk-${RUSTDESK_VERSION}-x86_64.deb" wget -O /tmp/rustdesk.deb "$RUSTDESK_URL" || echo "Warning: RustDesk download failed, continuing..." if [ -f /tmp/rustdesk.deb ]; then apt install -y /tmp/rustdesk.deb || echo "Warning: RustDesk installation failed, continuing..." rm /tmp/rustdesk.deb echo "✓ RustDesk installed" fi fi else echo "Skipping RustDesk installation." fi # Backup System (Optional) echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "LOCAL BACKUP SYSTEM (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Set up rsync backup from primary drive(s) to backup drive(s)." echo "" echo "Why rsync instead of RAID?" echo " • RAID mirrors corruption instantly - rsync gives you time to notice" echo " • RAID requires identical drives - rsync works with any sizes" echo " • RAID is complex to set up/recover - rsync is simple copy" echo " • rsync can run on schedule - RAID is always-on (more wear)" echo " • With rsync, backup drives can be disconnected for safety" echo "" # Check if backup is already configured BACKUP_CONFIGURED=false if [ -f /usr/local/bin/backup-scripts/rsync-backup.sh ]; then BACKUP_CONFIGURED=true echo "Local backup system is already configured." echo "" prompt_yn "Reconfigure local backup system? (y/n):" "n" SETUP_BACKUP else prompt_yn "Set up local backup system? (y/n):" "n" SETUP_BACKUP fi if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DRIVE CONFIGURATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Configure your drive mount points in ~/drives/" echo "Default names: primary, backup1, backup2, etc." echo "You can customize these (e.g., 'media', 'documents', 'photos-backup')" echo "" # Install rsync if needed if ! is_rsync_installed; then run_cmd apt install -y rsync || echo "Warning: rsync installation failed" fi # Create backup script directory mkdir -p /usr/local/bin/backup-scripts # Ask for primary drive name echo "PRIMARY DRIVE (source for backups):" prompt_text " Mount point name [default: primary]:" "primary" PRIMARY_NAME PRIMARY_NAME="${PRIMARY_NAME:-primary}" # Ask for number of backup drives echo "" echo "BACKUP DRIVES (destinations):" prompt_text " How many backup drives? [1-4, default: 1]:" "1" NUM_BACKUPS NUM_BACKUPS="${NUM_BACKUPS:-1}" # Validate number case $NUM_BACKUPS in 1|2|3|4) ;; *) NUM_BACKUPS=1 ;; esac # Collect backup drive names declare -a BACKUP_NAMES for i in $(seq 1 $NUM_BACKUPS); do prompt_text " Backup drive $i name [default: backup$i]:" "backup$i" "BACKUP_NAME_$i" eval "BACKUP_NAMES[$i]=\${BACKUP_NAME_$i:-backup$i}" done # Create mount point directories echo "" echo "Creating mount point directories..." mkdir -p "$ACTUAL_HOME/drives/$PRIMARY_NAME" for i in $(seq 1 $NUM_BACKUPS); do mkdir -p "$ACTUAL_HOME/drives/${BACKUP_NAMES[$i]}" done chown -R "$ACTUAL_USER:$ACTUAL_USER" "$ACTUAL_HOME/drives" echo "✓ Created mount points:" echo " Primary: $ACTUAL_HOME/drives/$PRIMARY_NAME" for i in $(seq 1 $NUM_BACKUPS); do echo " Backup$i: $ACTUAL_HOME/drives/${BACKUP_NAMES[$i]}" done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "DRIVE MOUNTING" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Available block devices:" echo "" lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,LABEL echo "" prompt_yn "Mount drives now? (y/n):" "n" MOUNT_NOW if [ "$MOUNT_NOW" = "y" ] || [ "$MOUNT_NOW" = "Y" ]; then echo "" echo "Enter device paths (e.g., /dev/sdb1) or leave blank to skip" echo "" read -p "Primary drive ($PRIMARY_NAME) device: " PRIMARY_DEV declare -a BACKUP_DEVS for i in $(seq 1 $NUM_BACKUPS); do read -p "Backup drive ${BACKUP_NAMES[$i]} device: " "BACKUP_DEV_$i" eval "BACKUP_DEVS[$i]=\$BACKUP_DEV_$i" done # Mount primary if [ -n "$PRIMARY_DEV" ] && [ -b "$PRIMARY_DEV" ]; then echo "Mounting $PRIMARY_DEV to $ACTUAL_HOME/drives/$PRIMARY_NAME..." mount "$PRIMARY_DEV" "$ACTUAL_HOME/drives/$PRIMARY_NAME" && echo "✓ $PRIMARY_NAME mounted" || echo "✗ Failed to mount $PRIMARY_NAME" fi # Mount backups for i in $(seq 1 $NUM_BACKUPS); do if [ -n "${BACKUP_DEVS[$i]}" ] && [ -b "${BACKUP_DEVS[$i]}" ]; then echo "Mounting ${BACKUP_DEVS[$i]} to $ACTUAL_HOME/drives/${BACKUP_NAMES[$i]}..." mount "${BACKUP_DEVS[$i]}" "$ACTUAL_HOME/drives/${BACKUP_NAMES[$i]}" && echo "✓ ${BACKUP_NAMES[$i]} mounted" || echo "✗ Failed to mount ${BACKUP_NAMES[$i]}" fi done echo "" echo "Current mounts:" df -h | grep "$ACTUAL_HOME/drives" || echo " (no drives currently mounted)" echo "" prompt_yn "Add to /etc/fstab for auto-mount at boot? (y/n):" "n" ADD_FSTAB if [ "$ADD_FSTAB" = "y" ] || [ "$ADD_FSTAB" = "Y" ]; then echo "" echo "Adding entries to /etc/fstab..." cp /etc/fstab /etc/fstab.backup-$(date +%Y%m%d-%H%M%S) if [ -n "$PRIMARY_DEV" ] && [ -b "$PRIMARY_DEV" ]; then PRIMARY_UUID=$(blkid -s UUID -o value "$PRIMARY_DEV") if [ -n "$PRIMARY_UUID" ]; then echo "UUID=$PRIMARY_UUID $ACTUAL_HOME/drives/$PRIMARY_NAME auto defaults,nofail 0 2" >> /etc/fstab echo "✓ Added $PRIMARY_NAME to fstab" fi fi for i in $(seq 1 $NUM_BACKUPS); do if [ -n "${BACKUP_DEVS[$i]}" ] && [ -b "${BACKUP_DEVS[$i]}" ]; then BACKUP_UUID=$(blkid -s UUID -o value "${BACKUP_DEVS[$i]}") if [ -n "$BACKUP_UUID" ]; then echo "UUID=$BACKUP_UUID $ACTUAL_HOME/drives/${BACKUP_NAMES[$i]} auto defaults,nofail 0 2" >> /etc/fstab echo "✓ Added ${BACKUP_NAMES[$i]} to fstab" fi fi done fi fi # Create rsync backup script echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "CREATING BACKUP SCRIPT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Build backup destinations list BACKUP_DESTS="" for i in $(seq 1 $NUM_BACKUPS); do BACKUP_DESTS="$BACKUP_DESTS \"$ACTUAL_HOME/drives/${BACKUP_NAMES[$i]}\"" done cat > /usr/local/bin/backup-scripts/rsync-backup.sh << BACKUP_SCRIPT #!/bin/bash ################################################################################ # rsync Local Backup Script # Backs up primary drive to all backup drives ################################################################################ PRIMARY="$ACTUAL_HOME/drives/$PRIMARY_NAME" BACKUP_DRIVES=($BACKUP_DESTS) LOG="/var/log/rsync-backup.log" echo "=== rsync Backup Started: \$(date) ===" | tee -a "\$LOG" echo "Source: \$PRIMARY" | tee -a "\$LOG" echo "" | tee -a "\$LOG" if [ ! -d "\$PRIMARY" ] || [ -z "\$(ls -A \$PRIMARY 2>/dev/null)" ]; then echo "ERROR: Primary drive not mounted or empty at \$PRIMARY" | tee -a "\$LOG" exit 1 fi for BACKUP in "\${BACKUP_DRIVES[@]}"; do echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | tee -a "\$LOG" echo "Backing up to: \$BACKUP" | tee -a "\$LOG" if [ ! -d "\$BACKUP" ]; then echo "⚠️ WARNING: \$BACKUP not mounted, skipping" | tee -a "\$LOG" continue fi # rsync options: # -a = archive mode (preserves permissions, timestamps, etc.) # -v = verbose # -h = human-readable sizes # --delete = remove files from backup that don't exist on primary # --progress = show progress rsync -avh --delete --progress "\$PRIMARY/" "\$BACKUP/" 2>&1 | tee -a "\$LOG" if [ \$? -eq 0 ]; then echo "✓ Backup to \$BACKUP completed" | tee -a "\$LOG" else echo "✗ Backup to \$BACKUP FAILED" | tee -a "\$LOG" fi done echo "" | tee -a "\$LOG" echo "=== Backup Completed: \$(date) ===" | tee -a "\$LOG" BACKUP_SCRIPT chmod +x /usr/local/bin/backup-scripts/rsync-backup.sh # Create systemd service cat > /etc/systemd/system/rsync-backup.service << SERVICE [Unit] Description=rsync Local Backup Service After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/backup-scripts/rsync-backup.sh User=root SERVICE # Create systemd timer for daily backups at 2 AM cat > /etc/systemd/system/rsync-backup.timer << TIMER [Unit] Description=Daily rsync Backup Timer Requires=rsync-backup.service [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target TIMER echo "" echo "✓ Backup script created: /usr/local/bin/backup-scripts/rsync-backup.sh" echo "✓ Systemd service/timer created (disabled by default)" echo "" echo "Quick start:" echo " Test backup: sudo /usr/local/bin/backup-scripts/rsync-backup.sh" echo " Enable daily: sudo systemctl enable --now rsync-backup.timer" echo " View log: tail -f /var/log/rsync-backup.log" else echo "Skipping local backup setup." fi # Cloud Backup with rclone (Optional) echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "CLOUD BACKUP (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Set up encrypted cloud backup using rclone." echo "Supports: Google Drive, OneDrive, Dropbox, and 40+ other providers." echo "" echo "Your files are encrypted BEFORE upload - the cloud provider cannot read them." echo "" prompt_yn "Set up encrypted cloud backup? (y/n):" "n" SETUP_CLOUD_BACKUP if [ "$SETUP_CLOUD_BACKUP" = "y" ] || [ "$SETUP_CLOUD_BACKUP" = "Y" ]; then echo "" # Install rclone if needed if ! is_rclone_installed; then echo "Installing rclone..." run_cmd apt install -y rclone || echo "Warning: rclone installation failed" fi if is_rclone_installed; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "CHOOSE CLOUD PROVIDER" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " [1] Google Drive (15GB free)" echo " [2] Microsoft OneDrive (5GB free, 1TB with Microsoft 365)" echo " [3] Other (manual rclone config)" echo "" prompt_text "Select provider [1/2/3]:" "1" CLOUD_PROVIDER echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "RCLONE CONFIGURATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" case $CLOUD_PROVIDER in 1) echo "Setting up Google Drive..." echo "" echo "Step 1: Run 'rclone config' to create a Google Drive remote" echo "Step 2: When prompted:" echo " - Choose 'n' for new remote" echo " - Name it 'gdrive'" echo " - Choose 'drive' (Google Drive)" echo " - Leave client_id and client_secret blank" echo " - Choose scope '1' (full access)" echo " - Leave root_folder_id blank" echo " - Leave service_account_file blank" echo " - Choose 'n' for advanced config" echo " - Choose 'y' for auto config (opens browser)" echo " - Choose 'n' for team drive" echo " - Confirm with 'y'" echo "" CLOUD_REMOTE="gdrive" ;; 2) echo "Setting up Microsoft OneDrive..." echo "" echo "Step 1: Run 'rclone config' to create a OneDrive remote" echo "Step 2: When prompted:" echo " - Choose 'n' for new remote" echo " - Name it 'onedrive'" echo " - Choose 'onedrive' (Microsoft OneDrive)" echo " - Leave client_id and client_secret blank" echo " - Choose region (usually 'global')" echo " - Choose 'n' for advanced config" echo " - Choose 'y' for auto config (opens browser)" echo " - Choose 'onedrive' for account type" echo " - Choose your drive from the list (usually option 0)" echo " - Confirm with 'y'" echo "" CLOUD_REMOTE="onedrive" ;; *) echo "Manual configuration selected." echo "Run 'rclone config' to set up your cloud provider." echo "" prompt_text "Enter the remote name you will create:" "cloud" CLOUD_REMOTE ;; esac if [ "$UNATTENDED" != true ]; then echo "Press Enter to launch rclone config..." read sudo -u "$ACTUAL_USER" rclone config else echo "Skipping interactive rclone config (unattended mode)" echo "Run 'rclone config' manually to complete setup" fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "ENCRYPTION SETUP" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Now we'll create an encrypted wrapper around your cloud storage." echo "This encrypts file names AND contents before upload." echo "" echo "⚠️ IMPORTANT: You will set a password. Without this password and" echo " the rclone config file, your files CANNOT be recovered!" echo "" if [ "$UNATTENDED" != true ]; then echo "In rclone config:" echo " - Choose 'n' for new remote" echo " - Name it '${CLOUD_REMOTE}-crypt'" echo " - Choose 'crypt' (Encrypt/Decrypt)" echo " - Remote: '${CLOUD_REMOTE}:backup' (folder on cloud storage)" echo " - Choose 'standard' for filename encryption" echo " - Choose 'true' for directory name encryption" echo " - Choose 'y' to enter your own password" echo " - Enter a STRONG password (you'll need this to decrypt!)" echo " - Choose 'y' for salt password (or 'n' to skip)" echo " - Confirm with 'y'" echo "" echo "Press Enter to continue rclone config..." read sudo -u "$ACTUAL_USER" rclone config fi # Backup the rclone config to local drives echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "BACKUP YOUR RCLONE CONFIG" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Your rclone config contains your encryption keys." echo "WITHOUT IT, YOUR ENCRYPTED CLOUD FILES CANNOT BE DECRYPTED!" echo "" echo "The config file is at: $ACTUAL_HOME/.config/rclone/rclone.conf" echo "" # Copy config to any mounted backup drives if [ -d "$ACTUAL_HOME/drives" ]; then for drive_dir in "$ACTUAL_HOME/drives"/*/; do if [ -d "$drive_dir" ] && mountpoint -q "$drive_dir" 2>/dev/null; then mkdir -p "${drive_dir}.rclone-config-backup" if [ -f "$ACTUAL_HOME/.config/rclone/rclone.conf" ]; then cp "$ACTUAL_HOME/.config/rclone/rclone.conf" "${drive_dir}.rclone-config-backup/rclone.conf.backup" echo "✓ Config backed up to: ${drive_dir}.rclone-config-backup/" fi fi done fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SECURE OFF-SITE BACKUP OF CONFIG" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "For disaster recovery, store a copy of rclone.conf OFF-SITE:" echo "" echo "Recommended secure methods:" echo " • Signal (end-to-end encrypted, send to yourself or trusted contact)" echo " • Box.com (better privacy policy than Dropbox)" echo " • Password manager (1Password, Bitwarden, etc.)" echo " • Encrypted USB drive stored at another location" echo "" echo "⚠️ Dropbox: Works but has broader data access policies." echo " Consider encrypting the config file before uploading." echo "" echo "To use on another computer:" echo " 1. Install rclone" echo " 2. Copy rclone.conf to ~/.config/rclone/" echo " 3. Run: rclone ls ${CLOUD_REMOTE}-crypt:" echo "" # Create cloud backup script mkdir -p /usr/local/bin/backup-scripts SOURCE_PATH="${PRIMARY_NAME:-primary}" cat > /usr/local/bin/backup-scripts/cloud-backup.sh << CLOUD_SCRIPT #!/bin/bash ################################################################################ # rclone Encrypted Cloud Backup Script ################################################################################ SOURCE="$ACTUAL_HOME/drives/$SOURCE_PATH" REMOTE="${CLOUD_REMOTE:-gdrive}-crypt" LOG="/var/log/cloud-backup.log" echo "=== Cloud Backup Started: \$(date) ===" | tee -a "\$LOG" echo "Source: \$SOURCE" | tee -a "\$LOG" echo "Destination: \$REMOTE:" | tee -a "\$LOG" echo "" | tee -a "\$LOG" if [ ! -d "\$SOURCE" ] || [ -z "\$(ls -A \$SOURCE 2>/dev/null)" ]; then echo "ERROR: Source not mounted or empty at \$SOURCE" | tee -a "\$LOG" exit 1 fi # Sync to encrypted cloud storage rclone sync "\$SOURCE" "\$REMOTE:" \\ --progress \\ --stats=30s \\ --log-file="\$LOG" \\ --log-level INFO if [ \$? -eq 0 ]; then echo "" | tee -a "\$LOG" echo "✓ Cloud backup completed: \$(date)" | tee -a "\$LOG" else echo "" | tee -a "\$LOG" echo "✗ Cloud backup FAILED: \$(date)" | tee -a "\$LOG" fi CLOUD_SCRIPT chmod +x /usr/local/bin/backup-scripts/cloud-backup.sh chown "$ACTUAL_USER:$ACTUAL_USER" /usr/local/bin/backup-scripts/cloud-backup.sh echo "" echo "✓ Cloud backup script created: /usr/local/bin/backup-scripts/cloud-backup.sh" echo "" echo "Quick start:" echo " Test backup: sudo /usr/local/bin/backup-scripts/cloud-backup.sh" echo " View log: tail -f /var/log/cloud-backup.log" else echo "rclone installation failed. Skipping cloud backup setup." fi else echo "Skipping cloud backup setup." fi # UFW Firewall Configuration (Optional) echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "UFW FIREWALL (Optional)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "UFW (Uncomplicated Firewall) provides a simple interface for managing" echo "iptables firewall rules." echo "" UFW_ACTIVE=false if is_ufw_installed && ufw status 2>/dev/null | grep -q "Status: active"; then UFW_ACTIVE=true echo "UFW is already enabled." ufw status 2>/dev/null | head -20 echo "" prompt_yn "Reconfigure UFW? (y/n):" "n" CONFIGURE_UFW else if is_ufw_installed; then echo "UFW is installed but not enabled." else echo "UFW is not installed." fi echo "" prompt_yn "Enable and configure UFW firewall? (y/n):" "y" CONFIGURE_UFW fi if [ "$CONFIGURE_UFW" = "y" ] || [ "$CONFIGURE_UFW" = "Y" ]; then echo "" echo "Configuring UFW firewall..." if [ "$DRY_RUN" = true ]; then echo "[DRY-RUN] Would install ufw if needed" echo "[DRY-RUN] Would allow SSH (port 22)" if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then echo "[DRY-RUN] Would allow Samba" fi echo "[DRY-RUN] Would enable UFW" else # Install UFW if not present if ! is_ufw_installed; then apt install -y ufw || echo "Warning: UFW installation failed" fi if is_ufw_installed; then # Always allow SSH first (before enabling!) ufw allow ssh echo "✓ Allowed SSH (port 22)" # Allow Samba if installed if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ] || is_samba_installed; then ufw allow samba echo "✓ Allowed Samba" fi # Enable UFW (with --force to avoid prompt) ufw --force enable echo "✓ UFW firewall enabled" echo "" echo "Current UFW status:" ufw status else echo "✗ UFW installation failed, skipping configuration" fi fi else echo "Skipping UFW configuration." fi CONFIGURE_UFW="${CONFIGURE_UFW:-n}" # Full system upgrade echo "" echo "Performing full system upgrade..." run_cmd apt upgrade -y # Clean up echo "" echo "Cleaning up..." run_cmd apt autoremove -y run_cmd apt autoclean echo "" echo "=== Installation Complete! ===" echo "" echo "Installed Software:" echo " ✓ net-tools, ncdu, git, curl, wget, htop, tree, zip/unzip" echo " ✓ OpenSSH Server - SSH remote access" if [ "$INSTALL_FAIL2BAN" = "y" ] || [ "$INSTALL_FAIL2BAN" = "Y" ]; then echo " ✓ fail2ban - SSH brute-force protection" fi if [ "$INSTALL_DOCKER" = "y" ] || [ "$INSTALL_DOCKER" = "Y" ]; then echo " ✓ Docker Engine + Docker Compose" fi if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then echo " ✓ Samba - File sharing (Primary drive shared)" fi if [ "$INSTALL_NETBIRD" = "y" ] || [ "$INSTALL_NETBIRD" = "Y" ]; then echo " ✓ NetBird - Mesh VPN" fi if [ "$INSTALL_RUSTDESK" = "y" ] || [ "$INSTALL_RUSTDESK" = "Y" ]; then echo " ✓ RustDesk - Remote desktop" fi if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then echo " ✓ Backup system configured: $BACKUP_TOOL ($BACKUP_MODE mode)" fi if [ "$CONFIGURE_UFW" = "y" ] || [ "$CONFIGURE_UFW" = "Y" ]; then echo " ✓ UFW Firewall - enabled" fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SSH AUTHENTICATION SETUP" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " SSH Key for This Computer:" if [ "$GENERATE_KEY" = "y" ] || [ "$GENERATE_KEY" = "Y" ]; then if [ -f "$ACTUAL_HOME/.ssh/id_rsa.pub" ]; then echo " ✓ 4096-bit RSA key generated" echo " Public key: $ACTUAL_HOME/.ssh/id_rsa.pub" echo " View with: cat ~/.ssh/id_rsa.pub" else echo " ✗ Key generation was attempted but may have failed" fi else echo " Not generated (skipped during install)" echo " Generate later: ssh-keygen -t rsa -b 4096 -C \"your@email.com\"" fi echo "" echo " SSH Server Status:" if [ "$KEYS_IMPORTED" = true ]; then echo " Password authentication: DISABLED (key-only access)" echo " Imported SSH keys: ~/.ssh/authorized_keys" else echo " Password authentication: ENABLED" fi echo "" echo " Traditional SSH Access:" echo " - Uses keys from GitHub/Launchpad (if imported)" echo " - Connect with: ssh user@hostname" if [ "$KEYS_IMPORTED" = true ]; then echo " - Password login: DISABLED (keys required)" else echo " - Password login: ENABLED" fi echo "" echo " NetBird SSH Access (independent of traditional SSH):" echo " - NetBird manages its own keys automatically" echo " - Works even with password auth disabled" echo " - Connect with: netbird ssh " echo " - Enable in NetBird dashboard first" echo "" echo " You can use ANY combination:" echo " ✓ GitHub + Launchpad + NetBird SSH" echo " ✓ GitHub + NetBird SSH" echo " ✓ Launchpad + NetBird SSH" echo " ✓ GitHub + Launchpad (no NetBird)" echo " ✓ Just GitHub or just Launchpad" echo " ✓ Just NetBird SSH" echo " ✓ None (password auth only - if no keys imported)" echo "" if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "BACKUP SYSTEM - $BACKUP_TOOL ($BACKUP_MODE mode)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " Mount points:" echo " $ACTUAL_HOME/drives/primary" echo " $ACTUAL_HOME/drives/backup1" if [ "$BACKUP_MODE" = "split" ]; then echo " $ACTUAL_HOME/drives/backup2" fi echo "" echo " Backup script: /usr/local/bin/backup-scripts/${BACKUP_TOOL}-backup.sh" echo " Log file: /var/log/${BACKUP_TOOL}-backup.log" echo "" if [ "$BACKUP_MODE" = "split" ]; then echo " ⚠️ CONFIGURE BEFORE RUNNING:" echo " Edit the script to set which folders go to which backup drive." echo " sudo nano /usr/local/bin/backup-scripts/${BACKUP_TOOL}-backup.sh" echo "" fi echo " Quick start:" echo " 1. Test (dry-run): sudo ${BACKUP_TOOL}-backup.sh --dry-run # (edit script first)" echo " 2. Run manually: sudo /usr/local/bin/backup-scripts/${BACKUP_TOOL}-backup.sh" echo " 3. Enable auto: sudo systemctl enable ${BACKUP_TOOL}-backup.timer" echo " sudo systemctl start ${BACKUP_TOOL}-backup.timer" echo "" echo " Monitor: tail -f /var/log/${BACKUP_TOOL}-backup.log" fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "OTHER IMPORTANT NOTES" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then echo " Samba File Sharing:" if command -v smbd &> /dev/null && grep -q "\[Primary\]" /etc/samba/smb.conf 2>/dev/null; then echo " ✓ Share 'Primary' is accessible at:" echo " Windows: \\\\$(hostname)\\Primary" echo " Mac/Linux: smb://$(hostname)/Primary" echo " • Username: $ACTUAL_USER" echo " • Use the Samba password you just set" echo "" else echo " ✗ Installation may have failed - check 'systemctl status smbd'" echo "" fi fi if [ "$INSTALL_DOCKER" = "y" ] || [ "$INSTALL_DOCKER" = "Y" ]; then echo " Docker: Log out and back in for group membership to take effect" echo "" fi if [ "$INSTALL_NETBIRD" = "y" ] || [ "$INSTALL_NETBIRD" = "Y" ]; then echo " NetBird:" echo " 1. Run 'netbird up' (opens browser for authentication)" echo " 2. View connected peers: netbird status" echo " 3. Configure ACLs in dashboard: https://app.netbird.io" echo "" fi if [ "$INSTALL_RUSTDESK" = "y" ] || [ "$INSTALL_RUSTDESK" = "Y" ]; then echo " RustDesk: Launch from applications menu or run 'rustdesk'" echo "" fi if [ "$INSTALL_FAIL2BAN" = "y" ] || [ "$INSTALL_FAIL2BAN" = "Y" ]; then echo " fail2ban:" echo " • Check status: sudo fail2ban-client status sshd" echo " • View banned IPs: sudo fail2ban-client status sshd" echo " • Unban IP: sudo fail2ban-client set sshd unbanip " echo "" fi if [ "$CONFIGURE_UFW" = "y" ] || [ "$CONFIGURE_UFW" = "Y" ]; then echo " UFW Firewall:" echo " • Check status: sudo ufw status" echo " • Allow port: sudo ufw allow " echo " • Deny port: sudo ufw deny " echo " • Disable: sudo ufw disable" echo "" fi