feat: Major v2.0.0 rewrite - Enterprise-grade NAS setup script
🚀 BREAKING CHANGE: Complete rewrite to enterprise-grade standards ### ✨ New Features: - Enhanced input validation (IP, port, username, path) - Automatic rollback mechanism on failures - Comprehensive unit testing framework (50+ tests) - Advanced logging with timestamps and levels - Interactive configuration system with persistence - Performance optimization suite (kernel, Docker, Samba) - Advanced firewall configuration with monitoring - System health monitoring and maintenance tools - Multi-distribution support with version validation ### 🛡️ Security Enhancements: - SSH hardening with security policies - Rate limiting for critical services - IP blocking/unblocking tools - Intrusion detection capabilities - Firewall monitoring with alerts - Secure input sanitization ### 🔧 Architecture Improvements: - Modular library structure - Centralized configuration management - Common functions separation - Professional error handling with set -euo pipefail - Signal handling for graceful shutdowns - Resource cleanup mechanisms ### 📚 Documentation: - Professional README with comprehensive guides - Enhanced CONTRIBUTING.md with development standards - Complete CHANGELOG.md with version history - Troubleshooting guides and best practices ### 🧪 Testing & Quality: - Unit tests for all critical functions - Performance regression testing - Multi-distribution integration testing - Input validation testing - Error scenario testing This release transforms the script from a basic tool to a production-ready, enterprise-grade NAS setup solution suitable for professional environments.
This commit is contained in:
484
setup.sh
484
setup.sh
@@ -1,14 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# NAS Setup Script - Version 1.0
|
||||
# NAS Setup Script - Version 2.0.0
|
||||
#
|
||||
# This script automates the setup of a NAS system with various services.
|
||||
# It is designed to run on multiple Linux distributions, including:
|
||||
# - Ubuntu
|
||||
# - Debian
|
||||
# - Fedora
|
||||
# - Ubuntu 20.04+
|
||||
# - Debian 11+
|
||||
# - Fedora 35+
|
||||
# - Arch Linux
|
||||
# - openSUSE
|
||||
# - openSUSE Leap 15.4+
|
||||
#
|
||||
# Disclaimer:
|
||||
# This script is provided "as is", without warranty of any kind, express or implied,
|
||||
@@ -25,116 +25,392 @@
|
||||
# License: MIT License
|
||||
# (c) 2025 Sebastian Palencsár
|
||||
|
||||
# Import configuration and functions
|
||||
source "$(dirname "$0")/config/defaults.sh"
|
||||
source "$(dirname "$0")/lib/logging.sh"
|
||||
source "$(dirname "$0")/lib/network.sh"
|
||||
source "$(dirname "$0")/lib/docker.sh"
|
||||
source "$(dirname "$0")/lib/security.sh"
|
||||
source "$(dirname "$0")/lib/internet.sh"
|
||||
source "$(dirname "$0")/lib/nfs.sh"
|
||||
source "$(dirname "$0")/lib/netdata.sh"
|
||||
source "$(dirname "$0")/lib/firewall.sh"
|
||||
source "$(dirname "$0")/lib/unattended-upgrades.sh"
|
||||
source "$(dirname "$0")/lib/vaultwarden.sh"
|
||||
source "$(dirname "$0")/lib/jellyfin.sh"
|
||||
source "$(dirname "$0")/lib/portainer.sh"
|
||||
set -euo pipefail # Strict error handling
|
||||
|
||||
# Logging configuration
|
||||
# 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/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/performance.sh"
|
||||
|
||||
# Initialize logging
|
||||
mkdir -p "$(dirname "${LOG_FILE}")"
|
||||
mkdir -p "${TEMP_DIR}"
|
||||
|
||||
# Setup logging with rotation
|
||||
exec > >(tee -a "${LOG_FILE}") 2>&1
|
||||
|
||||
# Improved error handling
|
||||
# Enhanced error handling with rollback
|
||||
handle_error() {
|
||||
"$@"
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
log_error "Error executing $* (exit code: $status)"
|
||||
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 version check
|
||||
detect_distro() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
source /etc/os-release
|
||||
DISTRO=$ID
|
||||
DISTRO_VERSION=$VERSION_ID
|
||||
DISTRO_CODENAME=${VERSION_CODENAME:-""}
|
||||
|
||||
log_info "Detected distribution: $PRETTY_NAME"
|
||||
|
||||
# Validate supported distribution
|
||||
if [[ ! " ${SUPPORTED_DISTROS[*]} " =~ " ${DISTRO} " ]]; then
|
||||
log_error "Unsupported Linux distribution: $DISTRO"
|
||||
log_info "Supported distributions: ${SUPPORTED_DISTROS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check minimum versions
|
||||
case $DISTRO in
|
||||
ubuntu)
|
||||
if [[ $(echo "$DISTRO_VERSION >= 20.04" | bc -l) -eq 0 ]]; then
|
||||
log_warning "Ubuntu version $DISTRO_VERSION is not officially supported. Minimum: 20.04"
|
||||
fi
|
||||
;;
|
||||
debian)
|
||||
if [[ ${DISTRO_VERSION%%.*} -lt 11 ]]; then
|
||||
log_warning "Debian version $DISTRO_VERSION is not officially supported. Minimum: 11"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
log_error "Cannot detect Linux distribution. /etc/os-release not found."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Detect Linux distribution
|
||||
detect_distro() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
DISTRO=$ID
|
||||
else
|
||||
log_error "Unsupported Linux distribution."
|
||||
# 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")"
|
||||
|
||||
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=12
|
||||
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
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
log_info "NAS Setup Script started."
|
||||
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
|
||||
}
|
||||
|
||||
detect_distro
|
||||
check_internet_connection
|
||||
|
||||
case $DISTRO in
|
||||
ubuntu|debian)
|
||||
check_ubuntu_version
|
||||
;;
|
||||
fedora|arch|opensuse)
|
||||
# Add specific checks here if needed
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported Linux distribution: $DISTRO"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
check_system_requirements
|
||||
|
||||
load_or_create_config
|
||||
|
||||
update_system
|
||||
|
||||
configure_network
|
||||
configure_ssh
|
||||
setup_samba
|
||||
configure_firewall
|
||||
|
||||
secure_shared_memory
|
||||
install_fail2ban
|
||||
configure_automatic_updates
|
||||
setup_basic_monitoring
|
||||
|
||||
if ask_yes_no "Do you want to install Docker?"; then
|
||||
install_docker
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install additional components?"; then
|
||||
install_additional_components
|
||||
else
|
||||
log_info "Installation of additional components skipped."
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install NFS?"; then
|
||||
install_nfs
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install Netdata for advanced monitoring?"; then
|
||||
install_netdata
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install Vaultwarden?"; then
|
||||
install_vaultwarden
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install Jellyfin?"; then
|
||||
install_jellyfin
|
||||
fi
|
||||
|
||||
if ask_yes_no "Do you want to install Portainer?"; then
|
||||
install_portainer
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
log_info "Setup completed. User $NEW_USER has been created with sudo and Samba access. Installation of optional components completed."
|
||||
show_progress 100 100 "Setup completed"
|
||||
|
||||
log_info "Please reboot your system to ensure all changes take effect."
|
||||
if ask_yes_no "Do you want to reboot now?"; then
|
||||
sudo reboot
|
||||
fi
|
||||
# Run main function
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user