Files
nas/setup.sh
Mărcziem ™ b0340adf03 feat: Add advanced NAS performance optimizations
- Memory optimization: vm.swappiness=10, vm.vfs_cache_pressure=50
- Enhanced Docker configuration with overlay2 and log rotation
- Webmin web interface integration with firewall configuration
- Multi-distribution Webmin support (Ubuntu/Debian, Fedora, openSUSE)
- SSL configuration and session timeout optimization
- Updated documentation and installation summary
- Enterprise-grade performance tuning for NAS workloads
2025-10-03 08:10:28 +02:00

531 lines
18 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() {
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:-""}}
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)
DISTRO=$detected_distro
;;
"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
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
}
# 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_error "Configuration validation failed"
exit 1
fi
else
log_info "Creating new configuration..."
create_interactive_config
fi
}
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 username=$(ask_input "Admin username" "$NEW_USER" "validate_username")
save_config "ADMIN_USER" "$username"
# 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
save_config "INSTALL_DOCKER" "$(ask_yes_no "Install Docker?" "y" && echo "true" || echo "false")"
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")"
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")"
save_config "INSTALL_WEBMIN" "$(ask_yes_no "Install Webmin web interface?" "n" && echo "true" || echo "false")"
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 -q
sudo zypper update -y -q
;;
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++)); show_progress $current_step $total_steps "Installing dependencies"
install_dependencies
((current_step++)); show_progress $current_step $total_steps "Configuring network"
if [[ "${CONFIGURE_STATIC_IP:-false}" == "true" ]]; then
configure_network
fi
((current_step++)); show_progress $current_step $total_steps "Configuring SSH"
configure_ssh
((current_step++)); show_progress $current_step $total_steps "Setting up Samba"
setup_samba
((current_step++)); show_progress $current_step $total_steps "Configuring firewall"
configure_firewall
# Security setup
((current_step++)); show_progress $current_step $total_steps "Implementing security measures"
secure_shared_memory
install_fail2ban
configure_automatic_updates
# Optional services
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Docker"
install_docker
else
((current_step++))
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing NFS"
install_nfs
else
((current_step++))
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Netdata"
install_netdata
else
((current_step++))
fi
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Vaultwarden"
install_vaultwarden
else
((current_step++))
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Jellyfin"
install_jellyfin
else
((current_step++))
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Portainer"
install_portainer
else
((current_step++))
fi
if [[ "${INSTALL_WEBMIN:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Webmin"
install_webmin
configure_webmin
else
((current_step++))
fi
}
# Installation summary
show_installation_summary() {
echo
log_success "=== NAS Setup Completed Successfully ==="
echo
log_info "Installation Summary:"
echo " ✓ System updated and secured"
echo " ✓ User '${ADMIN_USER:-$NEW_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"
echo " ✓ Automatic updates enabled"
# 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://$(hostname -I | awk '{print $1}'):${NETDATA_PORT}"
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
echo " ✓ Jellyfin media server: http://$(hostname -I | awk '{print $1}'):8096"
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
echo " ✓ Portainer Docker management: http://$(hostname -I | awk '{print $1}'):9000"
fi
if [[ "${INSTALL_WEBMIN:-false}" == "true" ]]; then
echo " ✓ Webmin web interface: https://$(hostname -I | awk '{print $1}'):10000"
fi
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"
echo " 4. Review firewall rules with: sudo ufw status"
echo
log_warning "Important: Please save the following information:"
echo " - SSH Port: ${SSH_PORT:-$DEFAULT_SSH_PORT}"
echo " - Admin User: ${ADMIN_USER:-$NEW_USER}"
echo " - Configuration saved in: ${CONFIG_FILE}"
echo " - Installation log: ${LOG_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
# 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..."
update_system
run_installation
# Cleanup and summary
cleanup
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 "$@"