Files
nas/setup.sh

851 lines
30 KiB
Bash

#!/bin/bash
# NAS Setup Script - Version 2.1.1
#
# This script automates the setup of a NAS system with various services.
# It is designed to run on multiple Linux distributions, including:
# - Ubuntu 24.04+
# - Debian 12+
# - Fedora 41+
# - Arch Linux
# - openSUSE Leap 15.6+
#
# Disclaimer:
# This script is provided "as is", without warranty of any kind, express or implied,
# including but not limited to the warranties of merchantability, fitness for a particular purpose,
# and noninfringement. In no event shall the authors or copyright holders be liable for any claim,
# damages, or other liability, whether in an action of contract, tort, or otherwise, arising from,
# out of, or in connection with the software or the use or other dealings in the software.
#
# Usage:
# Run this script with root privileges on a fresh installation of a supported Linux distribution.
# Ensure you have an active internet connection before starting the setup.
#
# Author: Sebastian Palencsár
# License: MIT License
# (c) 2025 Sebastian Palencsár
set -euo pipefail # Strict error handling
# Script directory and imports
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Import configuration and functions
source "${SCRIPT_DIR}/config/defaults.sh"
source "${SCRIPT_DIR}/lib/logging.sh"
source "${SCRIPT_DIR}/lib/common.sh"
source "${SCRIPT_DIR}/lib/detection.sh"
source "${SCRIPT_DIR}/lib/network.sh"
source "${SCRIPT_DIR}/lib/docker.sh"
source "${SCRIPT_DIR}/lib/security.sh"
source "${SCRIPT_DIR}/lib/internet.sh"
source "${SCRIPT_DIR}/lib/nfs.sh"
source "${SCRIPT_DIR}/lib/netdata.sh"
source "${SCRIPT_DIR}/lib/firewall.sh"
source "${SCRIPT_DIR}/lib/unattended-upgrades.sh"
source "${SCRIPT_DIR}/lib/vaultwarden.sh"
source "${SCRIPT_DIR}/lib/jellyfin.sh"
source "${SCRIPT_DIR}/lib/portainer.sh"
source "${SCRIPT_DIR}/lib/webmin.sh"
source "${SCRIPT_DIR}/lib/performance.sh"
# Initialize logging
mkdir -p "$(dirname "${LOG_FILE}")"
mkdir -p "${TEMP_DIR}"
# Setup logging with rotation
exec > >(tee -a "${LOG_FILE}") 2>&1
# Enhanced error handling with rollback
handle_error() {
# Dual-mode: if called with a non-numeric first argument, treat as wrapper
# e.g. handle_error sudo cp ... -> execute the command and handle failures
if [[ $# -ge 1 && ! "$1" =~ ^[0-9]+$ ]]; then
"$@"
local _rc=$?
if [[ $_rc -ne 0 ]]; then
local _line_number=${BASH_LINENO[0]:-0}
local _command="$*"
log_error "Script failed at line ${_line_number}: ${_command} (exit code: ${_rc})"
if ask_yes_no "An error occurred. Would you like to rollback changes?" "y"; then
execute_rollback
fi
cleanup
exit $_rc
fi
return 0
fi
# Trap handler mode: handle_error <line> "<command>"
local exit_code=$?
local line_number=$1
local command="$2"
log_error "Script failed at line ${line_number}: ${command} (exit code: ${exit_code})"
if ask_yes_no "An error occurred. Would you like to rollback changes?" "y"; then
execute_rollback
fi
cleanup
exit $exit_code
}
# Set error trap
trap 'handle_error ${LINENO} "$BASH_COMMAND"' ERR
# Signal handlers
cleanup_on_exit() {
log_info "Script interrupted. Performing cleanup..."
cleanup
exit 130
}
trap cleanup_on_exit SIGINT SIGTERM
# Detect Linux distribution with comprehensive fallback methods
detect_distro() {
local detected_distro=""
local detected_version=""
local detected_codename=""
local detection_method=""
log_debug "Starting distribution detection..."
# Method 1: /etc/os-release (primary method for modern systems)
if [[ -f /etc/os-release ]]; then
source /etc/os-release 2>/dev/null || true
detected_distro=${ID,,} # Convert to lowercase
detected_version=$VERSION_ID
detected_codename=${VERSION_CODENAME:-${UBUNTU_CODENAME:-""}}
detected_pretty_name=$PRETTY_NAME
detection_method="/etc/os-release"
log_debug "Detected via /etc/os-release: $PRETTY_NAME"
fi
# Method 2: /etc/redhat-release (fallback for RHEL/CentOS/Fedora)
if [[ -z "$detected_distro" ]] && [[ -f /etc/redhat-release ]]; then
local redhat_info=$(cat /etc/redhat-release)
if [[ $redhat_info =~ ^(CentOS|Red Hat Enterprise|Fedora) ]]; then
detected_distro="fedora"
detected_version=$(echo "$redhat_info" | grep -oP '\d+\.\d+' | head -1)
detection_method="/etc/redhat-release"
log_debug "Detected via /etc/redhat-release: $redhat_info"
fi
fi
# Method 3: /etc/debian_version (fallback for Debian/Ubuntu)
if [[ -z "$detected_distro" ]] && [[ -f /etc/debian_version ]]; then
local debian_version=$(cat /etc/debian_version)
if [[ -f /etc/lsb-release ]]; then
source /etc/lsb-release 2>/dev/null || true
detected_distro=${DISTRIB_ID,,}
detected_version=$DISTRIB_RELEASE
detected_codename=${DISTRIB_CODENAME:-""}
detection_method="/etc/lsb-release"
else
# Pure Debian system
detected_distro="debian"
detected_version=$debian_version
detection_method="/etc/debian_version"
fi
log_debug "Detected via Debian method: $detected_distro $detected_version"
fi
# Method 4: lsb_release command (fallback)
if [[ -z "$detected_distro" ]] && command -v lsb_release >/dev/null 2>&1; then
detected_distro=$(lsb_release -si 2>/dev/null | tr '[:upper:]' '[:lower:]')
detected_version=$(lsb_release -sr 2>/dev/null)
detected_codename=$(lsb_release -sc 2>/dev/null)
detection_method="lsb_release command"
log_debug "Detected via lsb_release command: $detected_distro $detected_version"
fi
# Method 5: uname and manual detection (last resort)
if [[ -z "$detected_distro" ]]; then
if [[ -f /etc/arch-release ]]; then
detected_distro="arch"
detected_version="rolling"
detection_method="/etc/arch-release"
elif [[ -f /etc/gentoo-release ]]; then
detected_distro="gentoo"
detected_version=$(cat /etc/gentoo-release | grep -oP '\d+\.\d+' | head -1)
detection_method="/etc/gentoo-release"
elif uname -a | grep -qi "opensuse"; then
detected_distro="opensuse"
detected_version="unknown"
detection_method="uname opensuse"
fi
log_debug "Detected via fallback method: $detected_distro"
fi
# Validate detection
if [[ -z "$detected_distro" ]]; then
log_error "Failed to detect Linux distribution using all available methods"
log_error "Please check your system and ensure it's a supported Linux distribution"
log_error "Supported: ${SUPPORTED_DISTROS[*]}"
exit 1
fi
# Normalize distribution names
case $detected_distro in
ubuntu|debian|fedora|arch|opensuse|opensuse-leap)
if [[ "$detected_distro" == "opensuse-leap" ]]; then
DISTRO="opensuse"
else
DISTRO=$detected_distro
fi
;;
"red hat enterprise linux server"|"rhel")
DISTRO="fedora" # Treat RHEL as Fedora for package management
;;
"centos linux"|"centos")
DISTRO="fedora" # CentOS uses same package manager as Fedora
;;
*)
# Check if it's a known variant
if [[ " ${SUPPORTED_DISTROS[*]} " =~ " ${detected_distro} " ]]; then
DISTRO=$detected_distro
else
log_error "Detected distribution '$detected_distro' is not in supported list"
log_error "Supported distributions: ${SUPPORTED_DISTROS[*]}"
log_error "Detection method: $detection_method"
exit 1
fi
;;
esac
# Parse and normalize version
DISTRO_VERSION=$(normalize_version "$detected_version")
DISTRO_CODENAME=$detected_codename
DISTRO_NAME=${detected_pretty_name:-$DISTRO}
log_info "Distribution detected: $DISTRO $DISTRO_VERSION ($detection_method)"
if [[ -n "$DISTRO_CODENAME" ]]; then
log_debug "Codename: $DISTRO_CODENAME"
fi
# Validate supported distribution
if [[ ! " ${SUPPORTED_DISTROS[*]} " =~ " ${DISTRO} " ]]; then
log_error "Unsupported Linux distribution: $DISTRO"
log_info "Supported distributions: ${SUPPORTED_DISTROS[*]}"
log_info "Detection method: $detection_method"
exit 1
fi
# Check minimum versions with improved parsing
validate_minimum_version "$DISTRO" "$DISTRO_VERSION"
# Check for container environments
detect_container_environment
# Cache the results for performance
export DISTRO DETECTED_DISTRO=$DISTRO
export DISTRO_VERSION DETECTED_VERSION=$DISTRO_VERSION
export DISTRO_CODENAME DETECTED_CODENAME=$DISTRO_CODENAME
export DISTRO_NAME
}
# System requirements check
check_system_requirements() {
log_info "Checking system requirements..."
local requirements_met=true
# Check disk space
if ! check_disk_space $MIN_DISK_SPACE_GB; then
requirements_met=false
fi
# Check RAM
check_ram $MIN_RAM_MB
# Check if running as root
check_root
# Check internet connectivity
if ! check_internet_enhanced; then
requirements_met=false
fi
# Check architecture
local arch=$(uname -m)
case $arch in
x86_64|amd64)
log_info "Architecture check passed: $arch"
;;
aarch64|arm64)
log_warning "ARM64 architecture detected. Some packages may not be available."
;;
*)
log_error "Unsupported architecture: $arch"
requirements_met=false
;;
esac
if [[ "$requirements_met" == false ]]; then
log_error "System requirements not met. Exiting."
exit 1
fi
log_success "All system requirements met"
}
# Configuration management
load_or_create_config() {
log_info "Loading configuration..."
if load_config; then
log_info "Configuration loaded from ${CONFIG_FILE}"
if ! validate_config; then
log_warning "Configuration validation failed - creating new configuration"
create_interactive_config
load_config # Reload the new configuration
fi
else
log_info "Creating new configuration..."
create_interactive_config
load_config # Load the new configuration
fi
# Export configuration variables for use in subshells
export ADMIN_USER SSH_PORT CONFIGURE_STATIC_IP INSTALL_DOCKER INSTALL_NFS INSTALL_NETDATA INSTALL_VAULTWARDEN INSTALL_JELLYFIN INSTALL_PORTAINER INSTALL_WEBMIN ENABLE_AUTO_UPDATES
}
create_interactive_config() {
log_info "Interactive configuration setup"
echo
# SSH configuration
local ssh_port=$(ask_input "SSH port" "$DEFAULT_SSH_PORT" "validate_port")
save_config "SSH_PORT" "$ssh_port"
# User configuration
local current_user
if [[ -n "${SUDO_USER:-}" ]]; then
current_user="$SUDO_USER"
log_info "Using current sudo user: $current_user"
else
current_user="$USER"
log_info "Using current user: $current_user"
fi
# Pre-fill with current user, but allow override
local username=$(ask_input "Admin username" "$current_user" "validate_username")
save_config "ADMIN_USER" "$username"
# Ensure the configured admin user exists (offer interactive creation)
ensure_user_exists_interactive "$username" || log_warning "Admin user '$username' not created; some features may require it."
# Network configuration
if ask_yes_no "Configure static IP?" "n"; then
save_config "CONFIGURE_STATIC_IP" "true"
else
save_config "CONFIGURE_STATIC_IP" "false"
fi
# Service selection
local install_docker=$(ask_yes_no "Install Docker?" "y" && echo "true" || echo "false")
save_config "INSTALL_DOCKER" "$install_docker"
save_config "INSTALL_NFS" "$(ask_yes_no "Install NFS?" "n" && echo "true" || echo "false")"
save_config "INSTALL_NETDATA" "$(ask_yes_no "Install Netdata monitoring?" "y" && echo "true" || echo "false")"
# Docker-dependent services
if [[ "$install_docker" == "true" ]]; then
save_config "INSTALL_VAULTWARDEN" "$(ask_yes_no "Install Vaultwarden password manager?" "n" && echo "true" || echo "false")"
save_config "INSTALL_JELLYFIN" "$(ask_yes_no "Install Jellyfin media server?" "n" && echo "true" || echo "false")"
save_config "INSTALL_PORTAINER" "$(ask_yes_no "Install Portainer Docker management?" "n" && echo "true" || echo "false")"
else
log_warning "Docker not selected - skipping Docker-dependent services (Vaultwarden, Jellyfin, Portainer)"
save_config "INSTALL_VAULTWARDEN" "false"
save_config "INSTALL_JELLYFIN" "false"
save_config "INSTALL_PORTAINER" "false"
fi
# Automatic updates (optional)
save_config "ENABLE_AUTO_UPDATES" "$(ask_yes_no "Enable automatic security updates (unattended-upgrades)?" "n" && echo "true" || echo "false")"
# Webmin (not available on openSUSE)
if [[ "$DISTRO" == "opensuse" ]]; then
log_info "Webmin is not available for openSUSE Leap - skipping"
save_config "INSTALL_WEBMIN" "false"
else
save_config "INSTALL_WEBMIN" "$(ask_yes_no "Install Webmin web interface?" "n" && echo "true" || echo "false")"
fi
log_success "Configuration created and saved to ${CONFIG_FILE}"
}
# System update with progress
update_system() {
log_info "Updating system packages..."
show_progress 1 10 "System Update"
case $DISTRO in
ubuntu|debian)
sudo apt-get update -qq
show_progress 5 10 "System Update"
sudo apt-get upgrade -y -qq
;;
fedora)
sudo dnf update -y -q
;;
arch)
sudo pacman -Syu --noconfirm --quiet
;;
opensuse)
sudo zypper refresh
show_progress 5 10 "System Update"
sudo zypper update -y
;;
esac
show_progress 10 10 "System Update"
add_rollback_action "# System packages updated - no rollback needed"
}
# Main installation orchestrator
run_installation() {
local total_steps=13
local current_step=0
log_info "Starting NAS installation process..."
# Core system setup
current_step=$((current_step + 1)); log_debug "run_installation: about to install dependencies (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing dependencies"
install_dependencies
current_step=$((current_step + 1)); log_debug "run_installation: about to configure network (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Configuring network"
if [[ "${CONFIGURE_STATIC_IP:-false}" == "true" ]]; then
configure_network
fi
current_step=$((current_step + 1)); log_debug "run_installation: about to configure SSH (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Configuring SSH"
configure_ssh
current_step=$((current_step + 1)); log_debug "run_installation: about to setup Samba (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Setting up Samba"
setup_samba
current_step=$((current_step + 1)); log_debug "run_installation: about to configure firewall (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Configuring firewall"
configure_firewall
# Security setup
current_step=$((current_step + 1)); log_debug "run_installation: about to implement security measures (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Implementing security measures"
secure_shared_memory
install_fail2ban
# Configure automatic updates only if user opted in
if [[ "${ENABLE_AUTO_UPDATES:-false}" == "true" ]]; then
configure_automatic_updates
else
log_info "Automatic security updates are disabled by configuration"
fi
# Optional services
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Docker (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Docker"
install_docker
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install NFS (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing NFS"
install_nfs
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Netdata (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Netdata"
install_netdata
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Vaultwarden (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Vaultwarden"
install_vaultwarden
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Jellyfin (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Jellyfin"
install_jellyfin
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Portainer (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Portainer"
install_portainer
else
current_step=$((current_step + 1))
fi
if [[ "${INSTALL_WEBMIN:-false}" == "true" ]]; then
current_step=$((current_step + 1)); log_debug "run_installation: about to install Webmin (step $current_step/$total_steps)"; show_progress $current_step $total_steps "Installing Webmin"
install_webmin
configure_webmin
else
current_step=$((current_step + 1))
fi
}
# Restart all installed services to ensure they're active
restart_all_services() {
log_info "Restarting all installed services to ensure they're active..."
local services_to_restart=()
# Always restart SSH and Fail2ban if configured
if [[ "${CONFIGURE_SSH:-true}" == "true" ]]; then
services_to_restart+=("ssh" "sshd" "fail2ban")
fi
# Add Samba services based on distribution
if [[ "${INSTALL_SAMBA:-true}" == "true" ]]; then
case $DISTRO in
opensuse)
services_to_restart+=("smb" "nmb")
;;
*)
services_to_restart+=("smbd" "nmbd")
;;
esac
fi
# Add other services if installed
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
services_to_restart+=("docker")
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
services_to_restart+=("netdata")
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
case $DISTRO in
opensuse)
services_to_restart+=("nfs-server")
;;
*)
services_to_restart+=("nfs-kernel-server")
;;
esac
fi
# Restart each service if it exists and is enabled
for service in "${services_to_restart[@]}"; do
if systemctl list-unit-files --type=service | grep -q "^${service}.service"; then
if systemctl is-enabled "$service" &>/dev/null 2>&1; then
log_info "Restarting service: $service"
if sudo systemctl restart "$service"; then
log_success "Service $service restarted successfully"
else
log_warning "Failed to restart service: $service"
fi
else
log_debug "Service $service is not enabled, skipping restart"
fi
else
log_debug "Service $service does not exist, skipping"
fi
done
# Special handling for SSH - use our robust restart function
if [[ "${CONFIGURE_SSH:-true}" == "true" ]]; then
log_info "Ensuring SSH service is properly restarted..."
restart_ssh_service
fi
log_success "Service restart process completed"
}
# Installation summary
show_installation_summary() {
local ip_address
ip_address=$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | head -1 | awk '{print $2}' | cut -d/ -f1)
local user_home
if [[ -n "${SUDO_USER:-}" ]]; then
user_home=$(eval echo "~${SUDO_USER}")
else
user_home="$HOME"
fi
local summary_file="${user_home}/nas_services.txt"
echo
log_success "=== NAS Setup Completed Successfully ==="
echo
log_info "Installation Summary:"
echo " ✓ System updated and secured"
echo " ✓ User '${ADMIN_USER:-$USER}' created with sudo access"
echo " ✓ SSH configured on port ${SSH_PORT:-$DEFAULT_SSH_PORT}"
echo " ✓ Samba file sharing configured"
echo " ✓ Firewall configured and enabled"
echo " ✓ Fail2ban installed and configured"
if [[ "${ENABLE_AUTO_UPDATES:-false}" == "true" ]]; then
echo " ✓ Automatic updates enabled"
else
echo " - Automatic updates not enabled (optional)"
fi
# Service summary
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
echo " ✓ Docker installed and configured"
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
echo " ✓ NFS server installed"
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
echo " ✓ Netdata monitoring: http://${ip_address}:${NETDATA_PORT}"
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
echo " ✓ Jellyfin media server: http://${ip_address}:8096"
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
echo " ✓ Portainer Docker management: http://${ip_address}:9000"
fi
if [[ "${INSTALL_WEBMIN:-false}" == "true" ]]; then
echo " ✓ Webmin web interface: https://${ip_address}:10000"
fi
# Create services summary file
log_info "Creating services summary file: ${summary_file}"
cat > "${summary_file}" << EOF
============================================================
NAS Services - Installation Summary
============================================================
Installation Date: $(date)
System IP Address: ${ip_address}
Admin User: ${ADMIN_USER:-$USER}
SSH Port: ${SSH_PORT:-$DEFAULT_SSH_PORT}
------------------------------------------------------------
Installed Services and Access Information:
------------------------------------------------------------
SSH Access:
- Host: ${ip_address}
- Port: ${SSH_PORT:-$DEFAULT_SSH_PORT}
- User: ${ADMIN_USER:-$USER}
- Command: ssh -p ${SSH_PORT:-$DEFAULT_SSH_PORT} ${ADMIN_USER:-$USER}@${ip_address}
EOF
# Add service information to file
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Netdata Monitoring:
- URL: http://${ip_address}:${NETDATA_PORT}
- Description: Real-time system monitoring and metrics
EOF
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Jellyfin Media Server:
- Web Interface: http://${ip_address}:8096
- HTTPS Interface: http://${ip_address}:8920
- DLNA Port: ${ip_address}:1900 (UDP)
- Description: Media streaming and management server
EOF
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Portainer Docker Management:
- HTTP Interface: http://${ip_address}:9000
- HTTPS Interface: https://${ip_address}:9443
- Description: Web-based Docker container management
- Note: Use HTTPS (9443) for secure access
EOF
fi
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Vaultwarden Password Manager:
- URL: http://${ip_address}:8080
- Description: Bitwarden-compatible password manager
EOF
fi
if [[ "${INSTALL_WEBMIN:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Webmin System Administration:
- URL: https://${ip_address}:10000
- Description: Web-based system administration interface
EOF
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
NFS Server:
- Port: ${ip_address}:2049
- Description: Network File System for Unix/Linux clients
- Mount example: mount -t nfs ${ip_address}:/srv/nfs /mnt/nfs
EOF
fi
# Samba information
cat >> "${summary_file}" << EOF
Samba File Sharing:
- Ports: ${ip_address}:139 (TCP), ${ip_address}:445 (TCP), ${ip_address}:137-138 (UDP)
- Description: Windows file sharing (SMB/CIFS)
- Access: \\\\${ip_address} or smb://${ip_address}
EOF
# Docker information if installed
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
cat >> "${summary_file}" << EOF
Docker:
- Status: Installed and running
- Socket: /var/run/docker.sock
- Description: Container runtime for additional services
EOF
fi
cat >> "${summary_file}" << EOF
------------------------------------------------------------
System Information:
------------------------------------------------------------
- Configuration file: ${CONFIG_FILE}
- Installation log: ${LOG_FILE}
- System distribution: ${DISTRO_NAME} ${DISTRO_VERSION}
- Kernel: $(uname -r)
------------------------------------------------------------
Security Notes:
------------------------------------------------------------
- Firewall is active and configured
- Fail2ban is monitoring for suspicious activity
- SSH uses Ed25519 keys for enhanced security
- Root login via SSH is disabled
EOF
if [[ "${ENABLE_AUTO_UPDATES:-false}" == "true" ]]; then
echo "- Automatic security updates are enabled" >> "${summary_file}"
else
echo "- Automatic updates are disabled (manual updates recommended)" >> "${summary_file}"
fi
cat >> "${summary_file}" << EOF
------------------------------------------------------------
Next Steps:
------------------------------------------------------------
1. Reboot the system to ensure all changes take effect
2. Review the services above and access them using the provided URLs
3. Configure user accounts and permissions for file sharing
4. Set up backups for your important data
5. Regularly check system logs and monitoring
============================================================
EOF
log_success "Services summary saved to: ${summary_file}"
log_info "You can view this file with: cat ${summary_file}"
echo
log_info "Next steps:"
echo " 1. Reboot the system to ensure all changes take effect"
echo " 2. Access your NAS via SSH on port ${SSH_PORT:-$DEFAULT_SSH_PORT}"
echo " 3. Configure file shares through Samba"
case $DISTRO in
ubuntu|debian)
echo " 4. Review firewall rules with: sudo ufw status"
;;
fedora|opensuse)
echo " 4. Review firewall rules with: sudo firewall-cmd --list-all"
;;
*)
echo " 4. Review firewall rules (check system documentation for your firewall tool)"
;;
esac
echo " 5. Check the services summary file: cat ${summary_file}"
echo
log_warning "Important: Please save the following information:"
echo " - SSH Port: ${SSH_PORT:-$DEFAULT_SSH_PORT}"
echo " - Admin User: ${ADMIN_USER:-$USER}"
echo " - Configuration saved in: ${CONFIG_FILE}"
echo " - Installation log: ${LOG_FILE}"
echo " - Services summary: ${summary_file}"
}
# Main script execution
main() {
clear
echo -e "${CYAN}${BOLD}"
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "${SCRIPT_NAME} v${SCRIPT_VERSION}"
echo "║ ║"
echo "║ Automated NAS Setup for Multiple Linux Distros ║"
echo "║ ║"
echo "║ by ${SCRIPT_AUTHOR}"
echo "║ ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
echo
log_info "${SCRIPT_NAME} v${SCRIPT_VERSION} started"
log_info "Running on: $(uname -a)"
# Pre-flight checks
detect_distro
check_system_requirements
get_system_info
# Unset any existing config variables to ensure clean state
unset INSTALL_DOCKER INSTALL_NFS INSTALL_NETDATA INSTALL_VAULTWARDEN INSTALL_JELLYFIN INSTALL_PORTAINER INSTALL_WEBMIN CONFIGURE_STATIC_IP SSH_PORT ADMIN_USER
# Configuration
load_or_create_config
# Confirmation
echo
if ! ask_yes_no "Ready to start installation?" "y"; then
log_info "Installation cancelled by user"
exit 0
fi
# Main installation
log_info "Starting installation process..."
# Run apt cleanup only for apt-based distributions
if [[ "$DISTRO" == "ubuntu" || "$DISTRO" == "debian" ]]; then
preflight_apt_cleanup
fi
update_system
run_installation
# Cleanup and summary
cleanup
restart_all_services
optimize_nas_performance
perform_health_check
show_installation_summary
# Reboot prompt
echo
if ask_yes_no "Reboot system now to complete setup?" "y"; then
log_info "System will reboot in 5 seconds..."
sleep 5
sudo reboot
else
log_warning "Please reboot the system manually to complete the setup"
fi
}
# Run main function
main "$@"