From 44fda519f0578ef469f1e76b259bee14069738a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 28 Dec 2025 05:23:49 +0000 Subject: [PATCH] Add QoL improvements: dry-run, unattended, logging, fail2ban, UFW - Add --dry-run flag to preview installations without changes - Add --unattended flag for automated/scripted installs with defaults - Add logging to /var/log/post-install.log - Add fail2ban protection when SSH password auth is enabled - Add UFW firewall configuration option - Update all prompts for unattended mode support - Update README with new features and troubleshooting --- README.md | 99 +++++++- ubuntu-post-install.sh | 509 ++++++++++++++++++++++++++++++++--------- 2 files changed, 500 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index 295927a..0f2a1fb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ Automated setup script for Ubuntu 24.04 Desktop that installs essential tools, c **Key Features:** - **Rerunnable** - Detects existing installations and offers to reinstall/reconfigure - **Modular** - Every component is optional with y/n prompts +- **Dry-run mode** - Preview what would be installed without making changes +- **Unattended mode** - Run with defaults for automated/scripted installs +- **Logging** - All output logged to `/var/log/post-install.log` - **Backup options** - Choose rsync (faster, local) or rclone (cloud support) - **Backup modes** - Full backup (one drive) or split backup (two drives) @@ -25,6 +28,17 @@ Automated setup script for Ubuntu 24.04 Desktop that installs essential tools, c - **Key Import** - Optionally imports public keys from GitHub and/or Launchpad - **Security** - Automatically disables password authentication if keys are imported +### Security Features (Optional) +- **fail2ban** (when password SSH enabled) + - Protects against SSH brute-force attacks + - Bans IPs after 5 failed attempts for 1 hour + - Only offered when SSH password authentication remains enabled +- **UFW Firewall** + - Simple firewall management + - Automatically allows SSH (port 22) + - Automatically allows Samba if installed + - Easy to add/remove port rules + ### Docker Installation (Optional) - **Docker Engine** - Latest version from official Docker repository (not snap) - **Docker Compose** - Installed as a plugin (modern method) @@ -100,11 +114,36 @@ chmod +x post-install.sh sudo ./post-install.sh ``` -### 4. Follow Interactive Prompts +### 4. Command-Line Options + +```bash +# Interactive mode (default) +sudo ./post-install.sh + +# Preview what would be installed (no changes made) +sudo ./post-install.sh --dry-run + +# Automated install with defaults (no prompts) +sudo ./post-install.sh --unattended + +# Show help +sudo ./post-install.sh --help +``` + +**Unattended mode defaults:** +- Skip SSH key generation +- No SSH key imports (password auth stays enabled) +- Install Docker +- Install fail2ban (since password auth is enabled) +- Enable UFW firewall +- Skip Samba, NetBird, RustDesk, Backup system + +### 5. Follow Interactive Prompts The script shows current system status and asks: - **SSH Key Generation**: Generate new 4096-bit RSA key? (y/n) - **Import SSH Keys**: GitHub username, Launchpad username (or leave blank) +- **fail2ban**: Install fail2ban? (only if password SSH remains enabled) - **Docker**: Install Docker? (y/n) - or reinstall if detected - **Samba File Sharing**: Install and configure Samba? (y/n) - If yes: Set password for Samba user @@ -113,6 +152,7 @@ The script shows current system status and asks: - **Backup System**: Set up backup system? (y/n) - If yes: Choose tool (rsync/rclone), mode (full/split) - Mount drives now? Device paths, fstab configuration +- **UFW Firewall**: Enable and configure UFW? (y/n) ### 5. Post-Installation Steps @@ -494,6 +534,16 @@ sudo smbpasswd your_username ## Troubleshooting +### View Installation Log + +```bash +# Check what was installed and any errors +cat /var/log/post-install.log + +# View last 50 lines +tail -50 /var/log/post-install.log +``` + ### Docker Permission Denied ```bash @@ -567,6 +617,42 @@ sudo ufw allow samba sudo systemctl restart smbd nmbd ``` +### fail2ban Issues + +```bash +# Check if fail2ban is running +sudo systemctl status fail2ban + +# View SSH jail status +sudo fail2ban-client status sshd + +# Unban an IP address +sudo fail2ban-client set sshd unbanip 192.168.1.100 + +# Check fail2ban logs +sudo tail -50 /var/log/fail2ban.log +``` + +### UFW Firewall Issues + +```bash +# Check UFW status +sudo ufw status verbose + +# If locked out, disable UFW temporarily +sudo ufw disable + +# Re-enable with SSH allowed first +sudo ufw allow ssh +sudo ufw enable + +# List all rules with numbers +sudo ufw status numbered + +# Delete a specific rule +sudo ufw delete 3 +``` + ## Backup Mode Comparison ### Full Backup @@ -587,11 +673,15 @@ sudo systemctl restart smbd nmbd ``` # Always created +/var/log/post-install.log # Installation log /etc/ssh/sshd_config.backup # SSH config backup (if modified) ~/.ssh/id_rsa # Private SSH key (if generated) ~/.ssh/id_rsa.pub # Public SSH key (if generated) ~/.ssh/authorized_keys # Imported SSH keys (if any) +# If fail2ban is installed +/etc/fail2ban/jail.local # fail2ban SSH jail configuration + # If Samba is installed /etc/samba/smb.conf.backup-TIMESTAMP # Samba config backup @@ -632,6 +722,13 @@ This script is provided as-is for Ubuntu 24.04 Desktop installations. ## Changelog +- **v2.1**: QoL improvements + - Added `--dry-run` flag to preview installations without changes + - Added `--unattended` flag for automated/scripted installs + - Added logging to `/var/log/post-install.log` + - Added fail2ban (offered when SSH password auth is enabled) + - Added UFW firewall configuration + - All prompts support unattended mode with sensible defaults - **v2.0**: Major update - Script is now rerunnable - detects existing installations - All components optional with y/n prompts (Docker, Samba, NetBird, RustDesk) diff --git a/ubuntu-post-install.sh b/ubuntu-post-install.sh index 2d7bb6d..890465a 100644 --- a/ubuntu-post-install.sh +++ b/ubuntu-post-install.sh @@ -4,9 +4,79 @@ # 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)" @@ -18,6 +88,7 @@ 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 "" # ============================================================================ @@ -48,6 +119,59 @@ 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 # ============================================================================ @@ -85,13 +209,22 @@ if is_rsync_installed; then 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..." -apt update +run_cmd apt update # Install basic utilities echo "" @@ -107,7 +240,7 @@ echo " - zip/unzip: Archive compression utilities" echo " - rclone: Rsync for cloud storage and local drives (backup tool)" echo "" -apt install -y \ +run_cmd apt install -y \ net-tools \ ncdu \ git \ @@ -137,7 +270,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━ echo "SSH KEY GENERATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" -read -p "Generate a new SSH key for this computer? (y/n): " GENERATE_KEY +prompt_yn "Generate a new SSH key for this computer? (y/n):" "n" GENERATE_KEY if [ "$GENERATE_KEY" = "y" ] || [ "$GENERATE_KEY" = "Y" ]; then echo "" @@ -195,8 +328,8 @@ fi # Import SSH keys from GitHub/Launchpad echo "" -read -p "Import SSH keys from GitHub? (enter username or leave blank to skip): " GITHUB_USER -read -p "Import SSH keys from Launchpad? (enter username or leave blank to skip): " LAUNCHPAD_USER +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 @@ -252,8 +385,49 @@ if [ "$KEYS_IMPORTED" = true ]; then 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 "" @@ -262,10 +436,11 @@ echo "DOCKER INSTALLATION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" -INSTALL_DOCKER="y" if is_docker_installed; then echo "Docker is already installed: $(docker --version 2>/dev/null)" - read -p "Reinstall Docker? (y/n): " INSTALL_DOCKER + 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 @@ -276,7 +451,7 @@ if [ "$INSTALL_DOCKER" = "y" ] || [ "$INSTALL_DOCKER" = "Y" ]; then echo " - lsb-release: Provides Ubuntu version information" echo "" - apt install -y \ + run_cmd apt install -y \ ca-certificates \ gnupg \ lsb-release || echo "Warning: Some prerequisites failed to install, continuing..." @@ -287,41 +462,49 @@ if [ "$INSTALL_DOCKER" = "y" ] || [ "$INSTALL_DOCKER" = "Y" ]; then echo " - Docker Compose: Multi-container application orchestration" echo "" - # Remove old Docker packages if they exist - apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true + 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'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 + # 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 + # 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..." + # 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" + # 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" + # 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 - - # Verify Docker installation - echo "" - echo "Verifying Docker installation..." - docker --version || echo "Warning: Docker verification failed" - docker compose version || echo "Warning: Docker Compose verification failed" else echo "Skipping Docker installation." fi @@ -342,9 +525,9 @@ if is_samba_installed; then echo " Share 'Primary' is configured at: $ACTUAL_HOME/drives/primary" fi echo "" - read -p "Reconfigure Samba? (y/n): " INSTALL_SAMBA + prompt_yn "Reconfigure Samba? (y/n):" "n" INSTALL_SAMBA else - read -p "Install and configure Samba file sharing? (y/n): " INSTALL_SAMBA + prompt_yn "Install and configure Samba file sharing? (y/n):" "n" INSTALL_SAMBA fi if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then @@ -353,19 +536,25 @@ if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then echo " - Samba: SMB/CIFS file server for network file sharing" echo "" - apt install -y samba || echo "Warning: Samba installation failed, continuing..." + 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..." + # 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) + # 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 + # 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] @@ -378,33 +567,39 @@ if [ "$INSTALL_SAMBA" = "y" ] || [ "$INSTALL_SAMBA" = "Y" ]; then create mask = 0775 directory mask = 0775 SAMBA_CONFIG - echo "✓ Added [Primary] share to Samba configuration" + 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 [Primary] share already configured, skipping..." + echo "✗ Samba installation failed, skipping configuration" fi - - # Add Samba user - 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" - - # 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 else echo "Skipping Samba installation." @@ -419,14 +614,13 @@ echo "" echo "NetBird is a secure mesh VPN for connecting devices across networks." echo "" -INSTALL_NETBIRD="y" if is_netbird_installed; then echo "NetBird is already installed." netbird status 2>/dev/null || true echo "" - read -p "Reinstall NetBird? (y/n): " INSTALL_NETBIRD + prompt_yn "Reinstall NetBird? (y/n):" "n" INSTALL_NETBIRD else - read -p "Install NetBird? (y/n): " INSTALL_NETBIRD + prompt_yn "Install NetBird? (y/n):" "n" INSTALL_NETBIRD fi if [ "$INSTALL_NETBIRD" = "y" ] || [ "$INSTALL_NETBIRD" = "Y" ]; then @@ -435,20 +629,24 @@ if [ "$INSTALL_NETBIRD" = "y" ] || [ "$INSTALL_NETBIRD" = "Y" ]; then echo " - NetBird: Secure mesh VPN for connecting devices" echo "" - curl -fsSL https://pkgs.netbird.io/install.sh | sh || echo "Warning: NetBird installation failed, continuing..." + 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 "" + 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 @@ -462,13 +660,12 @@ echo "" echo "RustDesk is an open-source remote desktop software." echo "" -INSTALL_RUSTDESK="y" if is_rustdesk_installed; then echo "RustDesk is already installed." echo "" - read -p "Reinstall RustDesk? (y/n): " INSTALL_RUSTDESK + prompt_yn "Reinstall RustDesk? (y/n):" "n" INSTALL_RUSTDESK else - read -p "Install RustDesk? (y/n): " INSTALL_RUSTDESK + prompt_yn "Install RustDesk? (y/n):" "n" INSTALL_RUSTDESK fi if [ "$INSTALL_RUSTDESK" = "y" ] || [ "$INSTALL_RUSTDESK" = "Y" ]; then @@ -477,16 +674,21 @@ if [ "$INSTALL_RUSTDESK" = "y" ] || [ "$INSTALL_RUSTDESK" = "Y" ]; then echo " - RustDesk: Open-source remote desktop software" echo "" - # 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" + 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..." + 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" + 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." @@ -513,9 +715,9 @@ if [ -f /usr/local/bin/backup-scripts/rclone-backup.sh ] || [ -f /usr/local/bin/ echo " Current: rsync backup script" fi echo "" - read -p "Reconfigure backup system? (y/n): " SETUP_BACKUP + prompt_yn "Reconfigure backup system? (y/n):" "n" SETUP_BACKUP else - read -p "Set up backup system? (y/n): " SETUP_BACKUP + prompt_yn "Set up backup system? (y/n):" "n" SETUP_BACKUP fi if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then @@ -536,7 +738,7 @@ if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then echo " • File-level sync (copies entire changed files)" echo " • Good for local drives, but rsync is more efficient" echo "" - read -p "Select backup tool [1=rsync, 2=rclone]: " BACKUP_TOOL_CHOICE + prompt_text "Select backup tool [1=rsync, 2=rclone]:" "1" BACKUP_TOOL_CHOICE if [ "$BACKUP_TOOL_CHOICE" = "2" ]; then BACKUP_TOOL="rclone" @@ -561,7 +763,7 @@ if [ "$SETUP_BACKUP" = "y" ] || [ "$SETUP_BACKUP" = "Y" ]; then echo " • Example: 4TB primary → 2TB backup1 + 2TB backup2" echo " • You configure which folders go to which backup" echo "" - read -p "Select backup mode [1=full, 2=split]: " BACKUP_MODE_CHOICE + prompt_text "Select backup mode [1=full, 2=split]:" "1" BACKUP_MODE_CHOICE if [ "$BACKUP_MODE_CHOICE" = "2" ]; then BACKUP_MODE="split" @@ -992,16 +1194,88 @@ else echo "Skipping backup system 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..." -apt upgrade -y +run_cmd apt upgrade -y # Clean up echo "" echo "Cleaning up..." -apt autoremove -y -apt autoclean +run_cmd apt autoremove -y +run_cmd apt autoclean echo "" echo "=== Installation Complete! ===" @@ -1009,6 +1283,9 @@ 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 @@ -1024,6 +1301,9 @@ 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" @@ -1137,4 +1417,19 @@ 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 \ No newline at end of file