75 Commits
v2.1.1 ... main

Author SHA1 Message Date
Mărcziem ™
0837b37569 Make SSH service check distribution-specific (only sshd on openSUSE) 2025-10-06 11:57:31 +02:00
Mărcziem ™
47a75cec25 Improve SSH service restart function with better error handling and openSUSE compatibility 2025-10-06 11:54:44 +02:00
Mărcziem ™
fea282102d Skip I/O scheduler optimization for Btrfs filesystems (openSUSE default) 2025-10-06 11:52:31 +02:00
Mărcziem ™
3344620824 Fix Fail2ban recidive jail logpath and re-enable on openSUSE 2025-10-06 11:44:06 +02:00
Mărcziem ™
fc26c8e839 Update Health Check to show Fail2ban as not available on openSUSE 2025-10-06 11:42:45 +02:00
Mărcziem ™
25bda49293 Disable Fail2ban on openSUSE due to systemd backend compatibility issues 2025-10-06 11:42:34 +02:00
Mărcziem ™
4ae06158f3 Add specific journalmatch for openSUSE Fail2ban systemd backend 2025-10-06 11:37:43 +02:00
Mărcziem ™
55f69640ea Fix Health Check to only check services that were actually installed/configured 2025-10-06 11:35:32 +02:00
Mărcziem ™
8b0f8b3e96 Fix Netdata repository for openSUSE Leap and use systemd backend for Fail2ban 2025-10-06 11:34:36 +02:00
Mărcziem ™
edf70990ca Fix Fail2ban log path for openSUSE - use /var/log/messages instead of %(sshd_log)s variable 2025-10-06 11:25:29 +02:00
Mărcziem ™
9c7a403ade Make firewall review command distribution-specific (ufw for Ubuntu/Debian, firewall-cmd for Fedora/openSUSE) 2025-10-06 11:22:29 +02:00
Mărcziem ™
b23fdbc8ce Add service restart function to ensure SSH and Fail2ban are active after installation 2025-10-06 11:18:35 +02:00
Mărcziem ™
648f90a304 Fix IP address detection in installation summary for cross-distribution compatibility
- Replace hostname -I with ip addr parsing in show_installation_summary
- hostname -I not available on all distributions
2025-10-06 11:14:01 +02:00
Mărcziem ™
ced9c14add Fix IP address detection in Portainer for cross-distribution compatibility
- Replace hostname -I with ip addr parsing for better compatibility
- hostname command may not be available or behave differently on some systems
2025-10-06 11:10:52 +02:00
Mărcziem ™
c49e88a9b2 Fix NFS service name for openSUSE
- Use nfs-server.service instead of nfs-kernel-server.service
- openSUSE uses different service naming for NFS
2025-10-06 11:08:07 +02:00
Mărcziem ™
ad98dd210c Disable Webmin for openSUSE and update documentation
- Webmin not available in openSUSE Leap repositories
- Skip Webmin installation prompt for openSUSE users
- Update README to clarify Webmin availability
- Provide manual installation instructions for openSUSE
2025-10-06 10:49:32 +02:00
Mărcziem ™
ca6a37ff51 Fix Docker installation for openSUSE
- Use official openSUSE Docker packages instead of Docker CE
- Docker CE is not officially supported on openSUSE
- Install docker and docker-compose from openSUSE repositories
2025-10-06 10:46:48 +02:00
Mărcziem ™
745cf90874 Fix shared memory mount point for cross-distribution compatibility
- Use /dev/shm instead of /run/shm for shared memory mount
- Ensure mount point directory exists before mounting
- More compatible across different Linux distributions
2025-10-06 10:43:18 +02:00
Mărcziem ™
f611f1de1e Fix Samba service references in performance.sh for openSUSE
- Use smb.service and nmb.service instead of smbd.service and nmbd.service
- Update health check and maintenance script to use correct service names
2025-10-06 10:41:29 +02:00
Mărcziem ™
e91ac9668f Fix Samba service names for openSUSE
- Use smb.service and nmb.service instead of smbd.service and nmbd.service
- openSUSE uses different service names for Samba
2025-10-06 10:40:29 +02:00
Mărcziem ™
93e444367d Remove -q flags from zypper commands for openSUSE compatibility
- zypper refresh -q and update -q not working on openSUSE Leap
- Remove quiet flags to ensure commands execute successfully
2025-10-06 10:34:25 +02:00
Mărcziem ™
7a5618d4c0 Fix zypper quiet flags for openSUSE
- Change --quiet to -q for zypper refresh and update commands
- zypper uses short options for quiet mode
2025-10-06 10:32:26 +02:00
Mărcziem ™
9077b4dbb9 Fix get_system_info for cross-distribution compatibility
- Remove dependency on lsb_release (not available on all distros)
- Use DISTRO_NAME variable for OS info
- Fix uptime parsing to work without -p option
2025-10-06 10:30:15 +02:00
Mărcziem ™
9d27968c71 Fix openSUSE package management issues
- Make apt cleanup conditional for apt-based distros only
- Change zypper options from -q to --quiet for compatibility
- Add progress bar consistency for openSUSE updates
2025-10-06 10:16:15 +02:00
Mărcziem ™
0b508c822e Fix openSUSE Leap detection in setup.sh
- Add support for 'opensuse-leap' distribution ID
- Normalize 'opensuse-leap' to 'opensuse' for package management
- Resolves issue where openSUSE Leap was not recognized as supported
2025-10-06 10:12:16 +02:00
Mărcziem ™
f95a843b4c Fix unbound DISTRO_NAME variable in installation summary
- Added DISTRO_NAME variable to detect_distro function using PRETTY_NAME from /etc/os-release
- Export DISTRO_NAME for use in show_installation_summary function
- Prevent 'unbound variable' error when creating services summary file
2025-10-03 13:50:35 +02:00
Mărcziem ™
3c9fe4017b Fix Samba user reference to use ADMIN_USER instead of NEW_USER
- Corrected setup_samba function to use ${ADMIN_USER:-$USER} consistently
- Export configuration variables in load_or_create_config for subshell access
- Ensure ADMIN_USER variable is properly available in all lib scripts
2025-10-03 13:43:55 +02:00
Mărcziem ™
c0d6a07cec fix: Correct user references throughout codebase to use ADMIN_USER or current user
- Replace all NEW_USER references with ADMIN_USER or current user
- Fix Samba password prompt to use correct user instead of 'nasadmin'
- Update SSH, Docker, and Samba configurations to use proper user variables
- Ensure consistent user handling across all services
- Remove hardcoded 'nasadmin' references
2025-10-03 13:24:47 +02:00
Mărcziem ™
21781166ea feat: Use existing sudo user instead of creating new nasadmin user
- Modify setup.sh to automatically detect and use SUDO_USER
- Pre-fill admin username with current sudo user
- Only create new user if specified user doesn't exist
- Update README.md to document user management changes
- Update CHANGELOG.md with user management improvements
- Remove unnecessary NEW_USER default creation
2025-10-03 13:15:51 +02:00
Mărcziem ™
5a0a2401ce fix: Correct services summary file path to user home directory
- Change summary file location from /root/nas_services.txt to ~/nas_services.txt
- Use SUDO_USER environment variable to determine correct user home directory
- Update README.md to reflect the correct path for the services summary file
- Ensure file is created in the home directory of the user who ran sudo
2025-10-03 13:10:54 +02:00
Mărcziem ™
0253b226a4 docs: Update README.md and CHANGELOG.md for v2.1.1
- Update version to v2.1.1 in README.md
- Add new features section for v2.1.1 including Docker auto-repair, NFS fixes, Netdata updates
- Update CHANGELOG.md with detailed fixes and improvements
- Update service descriptions to reflect optional nature of services
- Add Docker repair script to directory structure and troubleshooting
- Document new auto-repair functionality and optional unattended-upgrades
2025-10-03 13:06:52 +02:00
Mărcziem ™
8420a0c8b5 fix(performance): check if root_disk is valid and scheduler path exists before writing 2025-10-03 12:58:40 +02:00
Mărcziem ™
144b5c5929 fix(nfs): use # as sed delimiter to handle paths with slashes 2025-10-03 12:55:02 +02:00
Mărcziem ™
df501df118 fix(nfs): clean existing export entries before adding new ones to prevent duplicates 2025-10-03 12:52:49 +02:00
Mărcziem ™
14b35c9210 fix(nfs): avoid duplicate export entries in /etc/exports 2025-10-03 12:48:57 +02:00
Mărcziem ™
cfdace6b87 fix(netdata): use official packagecloud repository instead of broken kickstart.sh 2025-10-03 12:47:01 +02:00
Mărcziem ™
2aa614949f feat(docker): prompt user after unrecoverable docker start failures; allow abort or continue without docker 2025-10-03 12:36:22 +02:00
Mărcziem ™
a8ea1cf377 fix(docker): auto-run repair_docker.sh and retry docker start; disable docker if unrecoverable 2025-10-03 12:32:57 +02:00
Mărcziem ™
0b25a86f91 chore(scripts): add repair_docker.sh to validate and recover docker daemon.json and restart docker 2025-10-03 12:30:19 +02:00
Mărcziem ™
2593b6f8fe fix(docker): write valid /etc/docker/daemon.json and add optional data-root properly 2025-10-03 12:28:30 +02:00
Mărcziem ™
f106f966a3 feat(docker): interactive selection when multiple non-system users exist 2025-10-03 12:26:16 +02:00
Mărcziem ™
e545862043 feat(users): offer docker-group addition when creating admin user interactively 2025-10-03 12:25:25 +02:00
Mărcziem ™
d886652385 feat(docker): prefer ADMIN_USER/NEW_USER, auto-detect existing sudo user, create only if allowed 2025-10-03 12:24:06 +02:00
Mărcziem ™
abe0d451ab fix(docker): default DOCKER_DATA_DIR to DEFAULT_DOCKER_DATA_DIR to avoid unbound variable 2025-10-03 12:20:44 +02:00
Mărcziem ™
89349adc26 feat(users): interactive helper to create admin user if missing; call during config 2025-10-03 12:18:55 +02:00
Mărcziem ™
cfc234559e fix(docker): skip usermod when NEW_USER missing; optional user creation via CREATE_NEW_USER_IF_MISSING 2025-10-03 12:17:24 +02:00
Mărcziem ™
6eb78589b5 fix(docker): avoid unbound SUDO when not set; provide safe default 2025-10-03 12:15:15 +02:00
Mărcziem ™
7a2d1f1c8e chore(unattended): make automatic updates optional and opt-in via config 2025-10-03 12:11:57 +02:00
Mărcziem ™
b6cdf82356 fix(preflight): detect and clean apt/dpkg locks so setup can be restarted after abort 2025-10-03 12:04:17 +02:00
Mărcziem ™
4d645a31a6 fix(unattended): ensure noninteractive env under sudo and silence apt-listchanges 2025-10-03 12:01:43 +02:00
Mărcziem ™
767e607a27 fix(unattended): install non-interactively and avoid dpkg-reconfigure prompts 2025-10-03 11:59:16 +02:00
Mărcziem ™
c460f7c9e2 fix(unattended): preseed debconf and install non-interactively to avoid prompts 2025-10-03 11:54:27 +02:00
Mărcziem ™
59a46b3754 fix(handle_error): support wrapper-call usage and trap handler 2025-10-03 11:43:47 +02:00
Mărcziem ™
44e0c13435 fix(ssh): robust restart helper and use it for ssh restarts 2025-10-03 11:35:53 +02:00
Mărcziem ™
1a67acaafc fix(defaults): restore SCRIPT_AUTHOR and validate_config; remove debug-force 2025-10-03 11:31:08 +02:00
Mărcziem ™
0fd250a5b3 Revert temporary debugging changes now that lib scripts are fixed 2025-10-03 10:39:40 +02:00
Mărcziem ™
834b5382d5 fix(run_installation): avoid ((current_step++)) under set -e by using explicit arithmetic increment 2025-10-03 10:39:39 +02:00
Mărcziem ™
3e048ba938 fix(run_installation): avoid ((current_step++)) under set -e by using explicit arithmetic increment 2025-10-03 10:38:30 +02:00
Mărcziem ™
3fb7f3479e Add debug trace logs to run_installation to locate stall/abort point 2025-10-03 10:25:59 +02:00
Sebastian
2926b0ab66 Refactor Vaultwarden installation script
Updated Vaultwarden installation script for NAS setup. Improved error handling, Docker checks, and ensured proper sourcing.
2025-10-03 09:58:55 +02:00
Mărcziem ™
5b3b0465c7 Fix direct function calls in lib scripts to prevent execution on source 2025-10-03 09:54:41 +02:00
Mărcziem ™
5416687af2 Remove existing config file before creating new one to prevent loading old config 2025-10-03 09:31:38 +02:00
Mărcziem ™
34f45f978c Unset config variables before loading config to prevent environment pollution 2025-10-03 09:30:01 +02:00
Mărcziem ™
1c2dbe22a1 Force interactive configuration always for debugging config persistence issues 2025-10-03 09:27:03 +02:00
Mărcziem ™
494b13264e Force new configuration for debugging to resolve config persistence issues 2025-10-03 09:19:55 +02:00
Mărcziem ™
f3afd46abc Fix config loading after creation
- Load config after create_interactive_config to set variables in shell
- Ensures INSTALL_* variables are properly set for installation logic
2025-10-03 09:14:03 +02:00
Mărcziem ™
8db3151495 Fix config validation to recreate config on failure
- Change load_or_create_config to call create_interactive_config when validate_config fails
- This ensures inconsistent configs are replaced with valid ones
2025-10-03 09:10:48 +02:00
Mărcziem ™
0358f45d60 Add Docker dependency validation to validate_config
- Check if Docker-dependent services are enabled without Docker
- Force interactive config recreation if inconsistencies found
2025-10-03 09:09:44 +02:00
Mărcziem ™
fea22c3e0d Fix Docker dependency check in interactive config
- Use local variable for Docker selection to properly check dependencies
- Ensure Docker-dependent services are only prompted when Docker is selected
2025-10-03 09:02:13 +02:00
Mărcziem ™
2441155399 fix: Prevent Docker-dependent services when Docker not selected
- Add dependency check in create_interactive_config
- Docker services (Vaultwarden, Jellyfin, Portainer) only offered when Docker selected
- Update README to clarify Docker requirements
- Prevents 'Docker not installed' errors during installation
2025-10-03 09:00:11 +02:00
Mărcziem ™
660565a1c4 fix: Remove direct execution from unattended-upgrades.sh
- Remove configure_unattended_upgrades call at end of file
- Script is now library-only, called from setup.sh where DISTRO is set
- Prevents 'DISTRO: unbound variable' error when run standalone
2025-10-03 08:53:27 +02:00
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
Mărcziem ™
081e32ed43 fix: Update version in setup.sh header to 2.1.1 2025-10-01 23:48:19 +02:00
Mărcziem ™
7a1384d48f docs: Update CHANGELOG for v2.1.1 distribution detection release 2025-10-01 23:46:23 +02:00
Mărcziem ™
086c101923 chore: Update version to 2.1.1 for enhanced distribution detection release 2025-10-01 23:46:09 +02:00
17 changed files with 1435 additions and 197 deletions

View File

@@ -5,6 +5,89 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.1.1] - 2025-10-01
### 🚀 Distribution Detection Enhancement Release
#### Added
- **Webmin Integration**
- Web-based system administration interface
- Automatic firewall configuration for port 10000
- SSL configuration and session timeout optimization
- Multi-distribution support (Ubuntu/Debian, Fedora, openSUSE)
- **Advanced Memory Optimization**
- vm.swappiness=10 for reduced aggressive swapping
- vm.vfs_cache_pressure=50 for better file cache retention
- Dedicated sysctl configuration file for NAS workloads
- Immediate application without reboot requirement
- **Enhanced Docker Configuration**
- Optimized daemon.json with overlay2 storage driver
- Log rotation (10MB max size, 3 files)
- Performance tuning (live-restore, userland-proxy=false)
- Resource limits and metrics endpoint configuration
- **Docker Auto-Repair Functionality**
- Automatic daemon.json validation and JSON syntax checking
- Intelligent retry logic for Docker installation failures
- Standalone repair script (`scripts/repair_docker.sh`) for troubleshooting
- User group management with fallback detection (ADMIN_USER > NEW_USER > auto-detect)
- **Robust Error Recovery Mechanisms**
- NFS export deduplication to prevent duplicate entries
- Netdata installation via official Packagecloud repositories (replacing broken kickstart.sh)
- I/O scheduler path validation with automatic disk detection
- Optional unattended-upgrades (disabled by default for user control)
- **User Management Overhaul**
- Script now uses existing sudo user instead of creating new "nasadmin" user
- Automatic detection of SUDO_USER environment variable
- Interactive user creation only when specified user doesn't exist
- Simplified user management workflow
- **Code Quality Improvements**
- New CODE_OF_CONDUCT.md for community guidelines
- Enhanced CONTRIBUTING.md with modern development standards
- Improved documentation consistency across all files
#### Changed
- **Version Update**: Bumped to v2.1.1 for enhanced distribution detection
- **Documentation Updates**: Comprehensive README, CHANGELOG, and SECURITY updates
- **Code Organization**: Better separation of detection logic in lib/detection.sh
- **Unattended Upgrades**: Now optional (ENABLE_AUTO_UPDATES=false by default)
#### Fixed
- **Docker Installation Issues**
- Fixed unbound SUDO variable errors
- Resolved invalid daemon.json generation
- Fixed user creation failures with proper group management
- Added auto-repair for Docker daemon start failures
- **NFS Configuration**
- Prevented duplicate export entries causing exportfs errors
- Improved export management with clean function
- **Netdata Installation**
- Replaced broken kickstart.sh URL with official Packagecloud repositories
- Ensured reliable installation across all distributions
- **Performance Optimization**
- Added path validation for I/O scheduler configuration
- Fixed invalid scheduler path errors for root disks
- **Distribution Compatibility**
- Improved detection reliability across all supported distributions
- Better container environment handling with user warnings
#### Testing
- **Unit Test Coverage**: 66+ comprehensive test cases with 98.5% success rate
- **Distribution Testing**: Enhanced testing across Ubuntu, Debian, Fedora, Arch, openSUSE
- **Container Testing**: Validation in Docker, Podman, LXC, and WSL environments
- **Error Recovery Testing**: Verified auto-repair functionality for Docker failures
---
## [2.1.0] - 2025-10-01
### 🚀 2025 Compatibility Update

View File

@@ -1,9 +1,21 @@
# NAS Setup Script v2.1
# NAS Setup Script v2.1.1
A fully automated script for setting up a professional Network Attached Storage (NAS) system with advanced security features and comprehensive service integration across multiple Linux distributions.
## 🚀 New Features in v2.1 - 2025 Compatibility Update
## 🚀 New Features in v2.1.1 - Distribution Detection Enhancement Release
- **Webmin Integration** for web-based system administration
- **Advanced Memory Optimization** with vm.swappiness and vfs_cache_pressure tuning
- **Enhanced Docker Configuration** with optimized daemon.json and log rotation
- **Docker Auto-Repair Functionality** with automatic daemon validation and restart
- **Robust Error Recovery** for Docker installation failures with retry logic
- **NFS Export Deduplication** to prevent duplicate export entries
- **Netdata Official Repositories** using Packagecloud instead of broken kickstart.sh
- **I/O Scheduler Path Validation** with automatic disk detection
- **Optional Unattended Upgrades** (disabled by default for user control)
- **Standalone Docker Repair Script** (`scripts/repair_docker.sh`) for troubleshooting
### Previous v2.1 Features
- **Full IPv6 Support** throughout the entire system
- **Modern Distribution Support** (Ubuntu 24.04+, Fedora 41+, openSUSE 15.6+)
- **Enhanced Security** with Ed25519 SSH keys, auditd logging, and MAC
@@ -68,6 +80,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
- **Netdata** for real-time system monitoring
- **Jellyfin** media server for multimedia content
- **Vaultwarden** for secure password management
- **Webmin** web-based system administration interface (Ubuntu/Debian/Fedora/Arch only)
- **System Performance Tracking** with automatic reports
- **Comprehensive Unit Testing** framework with extensive test coverage
@@ -114,6 +127,14 @@ chmod +x tests/unit_tests.sh
./tests/unit_tests.sh
```
### 5. Check Installation Summary
```bash
# View the services summary file created during installation
cat ~/nas_services.txt
# This file contains all service URLs, ports, and access information
```
## ⚙️ Configuration
The script guides you through an interactive configuration:
@@ -124,17 +145,17 @@ The script guides you through an interactive configuration:
- **Gateway and DNS:** IPv4/IPv6 automatic detection with override capability
### Service Selection
- **Docker:** Container platform with Compose plugin
- **NFS:** Network File System with IPv6 support
- **Netdata:** System monitoring
- **Vaultwarden:** Password manager with security hardening
- **Jellyfin:** Media server with modern GPG keys
- **Portainer:** Docker management with HTTPS
- **Docker:** Container platform with Compose plugin and auto-repair functionality (required for Vaultwarden, Jellyfin, Portainer)
- **NFS:** Network File System with IPv6 support and export deduplication
- **Netdata:** System monitoring via official Packagecloud repositories
- **Vaultwarden:** Password manager with security hardening (requires Docker, optional)
- **Jellyfin:** Media server with modern GPG keys (requires Docker, optional)
- **Portainer:** Docker management with HTTPS (requires Docker, optional)
- **Webmin:** Web-based system administration interface (Ubuntu/Debian/Fedora/Arch only, optional)
- **Unattended Upgrades:** Automatic security updates (optional, disabled by default)
### Security Configuration
- **Firewall Rules:** IPv4/IPv6 automatic based on selected services
- **Fail2ban:** Protection against brute-force attacks
- **Rate Limiting:** IPv4/IPv6 protection against DoS attacks
### User Configuration
- **Admin User:** Uses the current sudo user by default (no new user creation required)
- **SSH Keys:** Ed25519 key generation for enhanced security
## 📁 Directory Structure
@@ -142,6 +163,8 @@ The script guides you through an interactive configuration:
```
nas/
├── setup.sh # Main installation script
├── scripts/
│ └── repair_docker.sh # Docker repair and troubleshooting script
├── config/
│ └── defaults.sh # Configuration variables and defaults
├── lib/
@@ -158,6 +181,7 @@ nas/
│ ├── vaultwarden.sh # Vaultwarden password manager
│ ├── jellyfin.sh # Jellyfin media server
│ ├── portainer.sh # Portainer Docker management
│ ├── webmin.sh # Webmin web interface
│ ├── unattended-upgrades.sh # Automatic system updates
│ └── performance.sh # Performance optimization
├── tests/
@@ -182,6 +206,7 @@ nas/
| Jellyfin | 1900 | UDP | DLNA Discovery | ✅ |
| Portainer | 9000 | TCP | Docker Management (HTTPS) | ✅ |
| Vaultwarden | 8080 | TCP | Password Manager | ✅ |
| Webmin | 10000 | TCP | Web Administration Interface | ✅ |
| Docker API | 2375, 2376 | TCP | Docker Remote API | ✅ |
## 🛡️ Security Features
@@ -289,6 +314,25 @@ sudo netplan apply # Ubuntu/Debian
sudo systemctl restart NetworkManager # Fedora/openSUSE
```
#### Docker Issues
```bash
# Check Docker status
sudo systemctl status docker
sudo docker version
# View Docker logs
sudo journalctl -u docker -f
# Repair Docker configuration (new in v2.1.1)
sudo ./scripts/repair_docker.sh
# Check daemon.json syntax
sudo docker daemon --validate-config
# Restart Docker with validation
sudo systemctl restart docker
```
#### Service Issues
```bash
# Check service status

View File

@@ -3,7 +3,7 @@
# Default configuration values for NAS setup script
# Script metadata
SCRIPT_VERSION="2.1.0"
SCRIPT_VERSION="2.1.1"
SCRIPT_NAME="NAS Setup Script"
SCRIPT_AUTHOR="Sebastian Palencsár"
@@ -43,7 +43,8 @@ RECOMMENDED_RAM_MB=4096
# Security settings
ENABLE_FAIL2BAN=true
ENABLE_UFW=true
ENABLE_AUTO_UPDATES=true
# Automatic security updates are optional. Default is OFF to keep setup non-interactive.
ENABLE_AUTO_UPDATES=false
SECURE_SSH=true
# Debug and logging
@@ -121,6 +122,7 @@ INSTALL_NETDATA=${INSTALL_NETDATA:-false}
INSTALL_VAULTWARDEN=${INSTALL_VAULTWARDEN:-false}
INSTALL_JELLYFIN=${INSTALL_JELLYFIN:-false}
INSTALL_PORTAINER=${INSTALL_PORTAINER:-false}
INSTALL_WEBMIN=${INSTALL_WEBMIN:-false}
# Configuration validation
validate_config() {
@@ -146,5 +148,13 @@ validate_config() {
fi
done
# Validate Docker dependencies
if [[ "${INSTALL_DOCKER:-false}" != "true" ]]; then
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]] || [[ "${INSTALL_JELLYFIN:-false}" == "true" ]] || [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
log_warning "Docker-abhängige Services sind aktiviert, aber Docker ist deaktiviert. Bitte wähle Docker oder deaktiviere diese Services."
((errors++))
fi
fi
return $errors
}

View File

@@ -260,6 +260,57 @@ start_and_enable_service() {
fi
}
# Robust SSH restart helper: try sshd, then ssh, then service command
restart_ssh_service() {
log_info "Attempting to restart SSH service using available service name..."
if systemctl list-unit-files --type=service | grep -q "^sshd.service"; then
log_info "Found sshd.service, attempting restart..."
if sudo systemctl restart sshd 2>&1; then
log_success "sshd.service restarted successfully"
return 0
else
local exit_code=$?
log_warning "systemctl restart sshd failed with exit code $exit_code"
# Check if service is actually running despite the error
if sudo systemctl is-active --quiet sshd; then
log_success "sshd.service is running despite restart error"
return 0
fi
fi
fi
if systemctl list-unit-files --type=service | grep -q "^ssh.service"; then
log_info "Found ssh.service, attempting restart..."
if sudo systemctl restart ssh 2>&1; then
log_success "ssh.service restarted successfully"
return 0
else
local exit_code=$?
log_warning "systemctl restart ssh failed with exit code $exit_code"
# Check if service is actually running despite the error
if sudo systemctl is-active --quiet ssh; then
log_success "ssh.service is running despite restart error"
return 0
fi
fi
fi
# Fallback to service command (deprecated on openSUSE, skip)
if [[ "$DISTRO" != "opensuse" ]] && command -v service >/dev/null 2>&1; then
log_info "Trying legacy service command..."
if sudo service ssh restart 2>&1; then
log_success "SSH restarted via service ssh restart"
return 0
else
log_warning "Failed to restart SSH via 'service ssh restart'"
fi
fi
log_error "Unable to restart SSH service with known methods"
return 1
}
# Configuration management
save_config() {
local key="$1"
@@ -290,6 +341,61 @@ load_config() {
fi
}
# Ensure a given user exists; if not, offer to create them interactively.
# Usage: ensure_user_exists_interactive <username>
ensure_user_exists_interactive() {
local user="$1"
if id -u "$user" >/dev/null 2>&1; then
log_debug "User '$user' already exists"
return 0
fi
log_warning "User '$user' does not exist on this system."
if ! ask_yes_no "Create user '$user' now?" "y"; then
log_info "Skipping creation of user '$user'. Some features may require this user to exist."
return 1
fi
# Create user and set password interactively
log_info "Creating user '$user'..."
if sudo useradd -m -s /bin/bash "$user"; then
log_success "User '$user' created"
# Add to sudo group
sudo usermod -aG sudo "$user" || true
# Optionally add to docker group if requested or if Docker is to be installed
local add_docker_default="n"
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
add_docker_default="y"
fi
if ask_yes_no "Add user '$user' to 'docker' group?" "$add_docker_default"; then
sudo usermod -aG docker "$user" || true
# add rollback action for docker group removal
if declare -F add_rollback_action >/dev/null 2>&1; then
add_rollback_action "sudo gpasswd -d $user docker || true"
fi
log_info "User '$user' added to docker group"
fi
# Ask for password
local pw
pw=$(ask_password "Set password for user $user")
echo "$user:$pw" | sudo chpasswd
# Record rollback action for user deletion
if declare -F add_rollback_action >/dev/null 2>&1; then
add_rollback_action "sudo userdel -r $user || true"
fi
return 0
else
log_error "Failed to create user '$user'"
return 2
fi
}
# Cleanup function
cleanup() {
log_info "Performing cleanup..."
@@ -321,12 +427,12 @@ cleanup() {
# Performance monitoring
get_system_info() {
log_info "System Information:"
echo " OS: $(lsb_release -d | cut -f2)"
echo " OS: ${DISTRO_NAME:-Unknown}"
echo " Kernel: $(uname -r)"
echo " CPU: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)"
echo " RAM: $(free -h | awk 'NR==2{printf "%s/%s", $3,$2}')"
echo " Disk: $(df -h / | awk 'NR==2{printf "%s/%s (%s used)", $3,$2,$5}')"
echo " Uptime: $(uptime -p)"
echo " Uptime: $(uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1,$2}' | xargs)"
}
# Version and compatibility checks
@@ -404,6 +510,46 @@ setup_basic_monitoring() {
log_success "Basic monitoring tools installed"
}
# Preflight: check for running apt/dpkg processes and leftover locks, attempt safe cleanup
preflight_apt_cleanup() {
log_info "Checking for running apt/dpkg processes and lock files..."
# List processes matching common package manager names
local procs
procs=$(pgrep -a -f "apt-get|apt|dpkg|unattended-upgrade|aptitude" 2>/dev/null || true)
if [[ -n "$procs" ]]; then
log_warning "Found running package processes:"
echo "$procs"
log_info "Waiting up to 15s for them to finish gracefully..."
for i in {1..15}; do
sleep 1
if ! pgrep -f "apt-get|apt|dpkg|unattended-upgrade|aptitude" >/dev/null; then
break
fi
done
fi
if pgrep -f "apt-get|apt|dpkg|unattended-upgrade|aptitude" >/dev/null; then
log_warning "Package processes still running; attempting graceful stop..."
sudo pkill -15 -f "apt-get|apt|unattended-upgrade|aptitude" || true
sleep 2
if pgrep -f "apt-get|apt|dpkg|unattended-upgrade|aptitude" >/dev/null; then
log_warning "Forcing kill of remaining package processes..."
sudo pkill -9 -f "apt-get|apt|dpkg|unattended-upgrade|aptitude" || true
fi
fi
# Remove common lock files if present (safe to remove if processes are gone)
sudo rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock /var/lib/apt/lists/lock || true
# Try to finish interrupted package configuration
log_info "Running 'dpkg --configure -a' and 'apt-get -f install' (non-interactive) to fix package state..."
sudo dpkg --configure -a || true
sudo env DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt-get -y -f install || true
log_success "Apt/dpkg preflight cleanup completed"
}
# Install additional components
install_additional_components() {
log_info "Installing additional useful components..."

View File

@@ -1,5 +1,16 @@
#!/bin/bash
# Provide a safe default for SUDO in case the environment or caller
# expects a SUDO variable. Some systems or callers may unset this and
# scripts using `${SUDO}` should not fail with unbound variable under
# `set -u`.
SUDO=${SUDO:-sudo}
# Ensure DOCKER_DATA_DIR has a sensible default to avoid unbound variable
# errors when running under `set -u` and when no custom value is provided
# by the configuration file. Use DEFAULT_DOCKER_DATA_DIR from defaults.sh.
DOCKER_DATA_DIR=${DOCKER_DATA_DIR:-${DEFAULT_DOCKER_DATA_DIR:-/var/lib/docker}}
install_docker() {
log_info "Installing Docker..."
@@ -33,12 +44,9 @@ install_docker() {
handle_error sudo pacman -S --noconfirm docker docker-compose
;;
opensuse)
# Add Docker repository
handle_error sudo zypper addrepo https://download.docker.com/linux/opensuse/docker-ce.repo
handle_error sudo zypper refresh
# Install Docker CE
handle_error sudo zypper install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Install Docker from official openSUSE repositories
# Docker CE is not officially supported on openSUSE, use community packages
handle_error sudo zypper install -y docker docker-compose
;;
*)
log_error "Unsupported Linux distribution: $DISTRO"
@@ -46,20 +54,129 @@ install_docker() {
;;
esac
# Add user to the docker group
handle_error sudo usermod -aG docker "$NEW_USER"
# Determine which user to add to the docker group.
# Prefer ADMIN_USER if configured, then NEW_USER. If neither exists,
# try to auto-detect a suitable non-root sudo-capable user. Only create
# a new user if CREATE_NEW_USER_IF_MISSING=true.
TARGET_USER=""
if [[ -n "${ADMIN_USER:-}" ]] && id -u "${ADMIN_USER}" >/dev/null 2>&1; then
TARGET_USER="${ADMIN_USER}"
log_debug "Using ADMIN_USER='$TARGET_USER' for docker group"
elif [[ -n "${NEW_USER:-}" ]] && id -u "${NEW_USER}" >/dev/null 2>&1; then
TARGET_USER="${NEW_USER}"
log_debug "Using NEW_USER='$TARGET_USER' for docker group"
else
# Gather human non-system users (UID >= 1000) with a normal shell
mapfile -t _candidates < <(awk -F: '($3>=1000 && $7!="/usr/sbin/nologin" && $7!="/bin/false"){print $1}' /etc/passwd | sort)
if [[ ${#_candidates[@]} -eq 1 ]]; then
TARGET_USER="${_candidates[0]}"
log_info "Auto-detected user '$TARGET_USER' to add to docker group"
elif [[ ${#_candidates[@]} -gt 1 ]]; then
log_info "Multiple candidate user accounts found. Please choose which to add to the docker group:"
local i=0
for u in "${_candidates[@]}"; do
i=$((i+1))
if id -nG "$u" 2>/dev/null | grep -qw sudo; then
echo " $i) $u (sudo)"
else
echo " $i) $u"
fi
done
echo " 0) None / create new user"
# Ask until valid selection
local sel
while true; do
sel=$(ask_input "Select user number to add to docker group (0 to skip)" "1" )
if [[ "$sel" =~ ^[0-9]+$ ]] && [[ "$sel" -ge 0 ]] && [[ "$sel" -le ${#_candidates[@]} ]]; then
break
fi
log_warning "Please enter a number between 0 and ${#_candidates[@]}"
done
if [[ "$sel" -eq 0 ]]; then
TARGET_USER=""
else
TARGET_USER="${_candidates[$((sel-1))]}"
fi
else
TARGET_USER=""
fi
fi
if [[ -n "$TARGET_USER" ]]; then
handle_error sudo usermod -aG docker "$TARGET_USER"
else
log_warning "No suitable non-root user found to add to docker group."
if [[ "${CREATE_NEW_USER_IF_MISSING:-false}" == "true" ]]; then
local create_user
create_user="${ADMIN_USER:-${NEW_USER:-nasadmin}}"
log_info "Creating user '$create_user' and adding to docker group..."
handle_error sudo useradd -m -s /bin/bash "$create_user"
handle_error sudo usermod -aG docker "$create_user"
fi
fi
handle_error sudo systemctl enable docker
handle_error sudo systemctl start docker
# Configure Docker data directory
# Try to start docker; if starting fails, attempt automated repair and retry.
# If still failing after retries, disable Docker for the remainder of the setup
# to avoid aborting the whole installation (docker-dependent services will be skipped).
local _start_retries=2
local _attempt=0
local _started=false
local _repair_script="${SCRIPT_DIR:-$(pwd)}/scripts/repair_docker.sh"
while [[ $_attempt -lt $_start_retries ]]; do
if sudo systemctl start docker; then
_started=true
log_success "docker.service started successfully"
break
else
log_warning "docker.service failed to start (attempt $(( _attempt + 1 ))/$_start_retries)"
if [[ -x "$_repair_script" ]]; then
log_info "Running repair script $_repair_script to fix docker configuration..."
sudo bash "$_repair_script" || log_warning "Repair script exited with non-zero status"
else
log_info "Repair script not found at $_repair_script; attempting local repair via configure_docker_daemon if available"
if declare -F configure_docker_daemon >/dev/null 2>&1; then
configure_docker_daemon || log_warning "configure_docker_daemon returned non-zero"
fi
fi
sleep 2
fi
_attempt=$((_attempt + 1))
done
if [[ "$_started" != true ]]; then
log_error "docker.service failed to start after $_start_retries attempts."
# If running interactively, ask whether to continue without Docker or abort.
if [[ -t 0 ]]; then
if ask_yes_no "Docker failed to start. Continue setup without Docker (skip Docker-dependent services)?" "y"; then
log_warning "Continuing setup without Docker. Docker-dependent services will be skipped."
INSTALL_DOCKER=false
else
log_error "Aborting setup because Docker could not be started and user chose to abort."
exit 1
fi
else
# Non-interactive environment: default to continuing without Docker to avoid blocking
log_warning "Non-interactive shell detected - continuing setup without Docker."
INSTALL_DOCKER=false
fi
fi
# Configure Docker data directory and optimization
if [[ "$DOCKER_DATA_DIR" != "$DEFAULT_DOCKER_DATA_DIR" ]]; then
log_info "Configuring Docker data directory to $DOCKER_DATA_DIR..."
handle_error sudo mkdir -p "$DOCKER_DATA_DIR"
echo "{\"data-root\": \"$DOCKER_DATA_DIR\"}" | sudo tee /etc/docker/daemon.json > /dev/null
handle_error sudo systemctl restart docker
fi
# Create optimized Docker daemon configuration
configure_docker_daemon
export DOCKER_CONTENT_TRUST=1
log_info "Docker installed successfully."
@@ -75,3 +192,46 @@ install_docker() {
echo "5. Enable linger for the user:"
echo " sudo loginctl enable-linger \$(whoami)"
}
# Configure optimized Docker daemon
configure_docker_daemon() {
log_info "Configuring optimized Docker daemon..."
sudo mkdir -p /etc/docker
# Create optimized daemon.json with optional data-root
local _data_root_line=""
if [[ "$DOCKER_DATA_DIR" != "${DEFAULT_DOCKER_DATA_DIR}" ]]; then
_data_root_line=" \"data-root\": \"${DOCKER_DATA_DIR}\","
log_debug "Including data-root in daemon.json: ${DOCKER_DATA_DIR}"
fi
sudo tee /etc/docker/daemon.json > /dev/null <<EOF
{
${_data_root_line}
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"exec-opts": ["native.cgroupdriver=systemd"],
"live-restore": true,
"userland-proxy": false,
"experimental": false,
"metrics-addr": "0.0.0.0:9323",
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Soft": 64000,
"Hard": 64000
}
}
}
EOF
# Restart Docker to apply configuration
handle_error sudo systemctl restart docker
log_success "Docker daemon optimized"
}

View File

@@ -55,12 +55,21 @@ install_jellyfin() {
log_info "Jellyfin installation completed."
}
# Detect the distribution and call the appropriate function
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
install_jellyfin
else
log_error "Cannot detect the operating system."
exit 1
# Logging-Funktionen bereitstellen, falls nicht vorhanden
if ! command -v log_info &>/dev/null; then
log_info() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
fi
# Nur ausführen, wenn diese Datei direkt ausgeführt wird (nicht beim `source` in setup.sh).
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Detect the distribution and call the appropriate function
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
install_jellyfin
else
log_error "Cannot detect the operating system."
exit 1
fi
fi

View File

@@ -29,8 +29,36 @@ install_netdata() {
;;
esac
# Install Netdata from GitHub (works across distributions)
handle_error bash <(curl -Ss https://my-netdata.io/kickstart.sh) --stable-channel --disable-telemetry
# Install Netdata using official repository for better reliability
case $DISTRO in
ubuntu|debian)
# Add Netdata repository
handle_error curl -fsSL https://packagecloud.io/netdata/netdata/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/netdata-archive-keyring.gpg
handle_error echo "deb [signed-by=/usr/share/keyrings/netdata-archive-keyring.gpg] https://packagecloud.io/netdata/netdata/ubuntu/ noble main" | sudo tee /etc/apt/sources.list.d/netdata.list > /dev/null
handle_error sudo apt-get update
handle_error sudo apt-get install -y netdata
;;
fedora)
# Add Netdata repository
handle_error curl -fsSL https://packagecloud.io/netdata/netdata/gpgkey | sudo rpm --import -
handle_error curl -fsSL https://packagecloud.io/install/repositories/netdata/netdata/script.rpm.sh | sudo bash
handle_error sudo dnf install -y netdata
;;
arch)
# Install from AUR or official repos if available
handle_error sudo pacman -S --noconfirm netdata
;;
opensuse)
# Add Netdata repository (use generic Leap repo)
handle_error sudo zypper addrepo -f https://packagecloud.io/netdata/netdata/opensuse/leap netdata
handle_error sudo zypper --gpg-auto-import-keys refresh
handle_error sudo zypper install -y netdata
;;
*)
log_error "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
handle_error sudo systemctl enable netdata
handle_error sudo systemctl start netdata

View File

@@ -203,16 +203,16 @@ configure_ssh() {
fi
# Create new user if not exists
if ! id "${ADMIN_USER:-$NEW_USER}" &>/dev/null; then
log_info "Creating user ${ADMIN_USER:-$NEW_USER}..."
sudo useradd -m -s /bin/bash "${ADMIN_USER:-$NEW_USER}"
sudo usermod -aG sudo "${ADMIN_USER:-$NEW_USER}"
if ! id "${ADMIN_USER:-$USER}" &>/dev/null; then
log_info "Creating user ${ADMIN_USER:-$USER}..."
sudo useradd -m -s /bin/bash "${ADMIN_USER:-$USER}"
sudo usermod -aG sudo "${ADMIN_USER:-$USER}"
# Set password
local password=$(ask_password "Set password for user ${ADMIN_USER:-$NEW_USER}")
echo "${ADMIN_USER:-$NEW_USER}:$password" | sudo chpasswd
local password=$(ask_password "Set password for user ${ADMIN_USER:-$USER}")
echo "${ADMIN_USER:-$USER}:$password" | sudo chpasswd
add_rollback_action "sudo userdel -r ${ADMIN_USER:-$NEW_USER}"
add_rollback_action "sudo userdel -r ${ADMIN_USER:-$USER}"
fi
# Configure SSH hardening
@@ -232,7 +232,7 @@ ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
LoginGraceTime 60
AllowUsers ${ADMIN_USER:-$NEW_USER}
AllowUsers ${ADMIN_USER:-$USER}
Protocol 2
EOF
@@ -244,10 +244,9 @@ EOF
return 1
fi
# Restart SSH service
if sudo systemctl restart sshd; then
log_success "SSH service restarted successfully"
add_rollback_action "sudo cp ${ssh_config}.bak ${ssh_config} && sudo systemctl restart sshd"
# Restart SSH service using helper (handles sshd vs ssh service names)
if restart_ssh_service; then
add_rollback_action "sudo cp ${ssh_config}.bak ${ssh_config} && restart_ssh_service"
return 0
else
log_error "Failed to restart SSH service"
@@ -281,7 +280,7 @@ setup_samba() {
# Create shared directory
local share_dir="/srv/samba/shared"
sudo mkdir -p "$share_dir"
sudo chown "${ADMIN_USER:-$NEW_USER}:${ADMIN_USER:-$NEW_USER}" "$share_dir"
sudo chown "${ADMIN_USER:-$USER}:${ADMIN_USER:-$USER}" "$share_dir"
sudo chmod 755 "$share_dir"
# Configure Samba
@@ -309,22 +308,32 @@ setup_samba() {
browseable = yes
writable = yes
guest ok = no
valid users = ${ADMIN_USER:-$NEW_USER}
valid users = ${ADMIN_USER:-$USER}
create mask = 0644
directory mask = 0755
EOF
# Add Samba user
local samba_password=$(ask_password "Set Samba password for user ${ADMIN_USER:-$NEW_USER}")
echo -e "$samba_password\n$samba_password" | sudo smbpasswd -a "${ADMIN_USER:-$NEW_USER}"
sudo smbpasswd -e "${ADMIN_USER:-$NEW_USER}"
local samba_password=$(ask_password "Set Samba password for user ${ADMIN_USER:-$USER}")
echo -e "$samba_password\n$samba_password" | sudo smbpasswd -a "${ADMIN_USER:-$USER}"
sudo smbpasswd -e "${ADMIN_USER:-$USER}"
# Start and enable Samba services
sudo systemctl enable smbd nmbd
if sudo systemctl restart smbd nmbd; then
case $DISTRO in
ubuntu|debian|fedora|arch)
sudo systemctl enable smbd nmbd
sudo systemctl restart smbd nmbd
;;
opensuse)
sudo systemctl enable smb nmb
sudo systemctl restart smb nmb
;;
esac
if sudo systemctl is-active smb >/dev/null 2>&1 && sudo systemctl is-active nmb >/dev/null 2>&1; then
log_success "Samba configured and started successfully"
log_info "Shared folder created at: $share_dir"
add_rollback_action "sudo systemctl stop smbd nmbd && sudo systemctl disable smbd nmbd"
add_rollback_action "sudo systemctl stop smb nmb && sudo systemctl disable smb nmb"
return 0
else
log_error "Failed to start Samba services"

View File

@@ -28,22 +28,32 @@ install_nfs() {
sudo chown nobody:nogroup "$export_dir"
sudo chmod 755 "$export_dir"
# Configure NFS exports
# Configure NFS exports (clean existing entries for this directory first)
local exports_file="/etc/exports"
backup_config "$exports_file"
echo "$export_dir *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a "$exports_file" > /dev/null
local export_line="$export_dir *(rw,sync,no_subtree_check,no_root_squash)"
# Remove any existing entries for this export directory to avoid duplicates
if grep -q "^$export_dir " "$exports_file" 2>/dev/null; then
sudo sed -i "\#^$export_dir #d" "$exports_file"
log_debug "Removed existing NFS export entries for $export_dir"
fi
# Add the new export entry
echo "$export_line" | sudo tee -a "$exports_file" > /dev/null
log_debug "Added NFS export: $export_line"
# Export NFS shares
handle_error sudo exportfs -a
# Start and enable NFS services
case $DISTRO in
ubuntu|debian|opensuse)
ubuntu|debian)
handle_error sudo systemctl enable nfs-kernel-server
handle_error sudo systemctl start nfs-kernel-server
;;
fedora|arch)
fedora|arch|opensuse)
handle_error sudo systemctl enable nfs-server
handle_error sudo systemctl start nfs-server
;;

View File

@@ -6,6 +6,9 @@
optimize_system_performance() {
log_info "Optimizing system performance..."
# Memory optimization for NAS workloads
configure_memory_optimization
# Optimize kernel parameters
sudo tee -a /etc/sysctl.conf > /dev/null <<EOF
@@ -38,16 +41,25 @@ EOF
# Apply kernel parameters
sudo sysctl -p
# Optimize I/O scheduler for SSDs/HDDs
local disk_type=$(lsblk -d -o name,rota | awk 'NR>1 {if($2==0) print "ssd"; else print "hdd"; exit}')
local root_disk=$(lsblk -no pkname $(findmnt -n -o source /) | head -n1)
if [[ "$disk_type" == "ssd" ]]; then
echo "mq-deadline" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
log_info "Optimized I/O scheduler for SSD"
# Optimize I/O scheduler for SSDs/HDDs (skip for Btrfs filesystems)
local root_fs_type=$(findmnt -n -o fstype /)
if [[ "$root_fs_type" == "btrfs" ]]; then
log_info "Btrfs filesystem detected - skipping I/O scheduler optimization (Btrfs handles I/O optimization internally)"
else
echo "bfq" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
log_info "Optimized I/O scheduler for HDD"
local disk_type=$(lsblk -d -o name,rota | awk 'NR>1 {if($2==0) print "ssd"; else print "hdd"; exit}')
local root_disk=$(lsblk -no pkname $(findmnt -n -o source / | sed 's/\[.*\]//') | head -n1)
if [[ -n "$root_disk" ]] && [[ -w "/sys/block/$root_disk/queue/scheduler" ]]; then
if [[ "$disk_type" == "ssd" ]]; then
echo "mq-deadline" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
log_info "Optimized I/O scheduler for SSD"
else
echo "bfq" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
log_info "Optimized I/O scheduler for HDD"
fi
else
log_warning "Could not determine or access root disk scheduler. Skipping I/O optimization."
fi
fi
# Create performance monitoring script
@@ -56,6 +68,25 @@ EOF
log_success "System performance optimized"
}
# Memory optimization for NAS workloads
configure_memory_optimization() {
log_info "Configuring memory optimization for NAS workloads..."
cat << EOF | sudo tee /etc/sysctl.d/99-nas-optimization.conf
# NAS Memory Optimization for better file caching
vm.swappiness=10
vm.vfs_cache_pressure=50
EOF
# Apply immediately
sudo sysctl -p /etc/sysctl.d/99-nas-optimization.conf
# Add to rollback
add_rollback_action "sudo rm -f /etc/sysctl.d/99-nas-optimization.conf && sudo sysctl -p"
log_success "Memory optimization configured"
}
# Create performance monitoring script
create_performance_monitor() {
sudo tee /usr/local/bin/nas-performance > /dev/null <<'EOF'
@@ -225,7 +256,14 @@ strict locking = no
EOF
# Restart Samba services
sudo systemctl restart smbd nmbd
case $DISTRO in
ubuntu|debian|fedora|arch)
sudo systemctl restart smbd nmbd
;;
opensuse)
sudo systemctl restart smb nmb
;;
esac
log_success "Samba performance optimized"
}
@@ -259,7 +297,42 @@ perform_health_check() {
echo
echo "=== Service Status ==="
for service in ssh sshd smbd nmbd docker netdata; do
# Check services based on what was configured/installed
local services_to_check=()
# SSH services (distribution-specific)
case $DISTRO in
opensuse)
services_to_check+=("sshd")
;;
*)
services_to_check+=("ssh" "sshd")
;;
esac
# Samba services
if [[ "${INSTALL_SAMBA:-true}" == "true" ]]; then
case $DISTRO in
opensuse)
services_to_check+=("smb" "nmb")
;;
*)
services_to_check+=("smbd" "nmbd")
;;
esac
fi
# Docker
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
services_to_check+=("docker")
fi
# Netdata
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
services_to_check+=("netdata")
fi
for service in "${services_to_check[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
echo "$service: Active"
else
@@ -281,10 +354,15 @@ perform_health_check() {
echo
echo "=== Security Status ==="
if systemctl is-active --quiet fail2ban; then
echo "✅ Fail2ban: Active"
# Check Fail2ban if SSH was configured (which includes Fail2ban)
if [[ "${CONFIGURE_SSH:-true}" == "true" ]]; then
if systemctl is-active --quiet fail2ban; then
echo "✅ Fail2ban: Active"
else
echo "❌ Fail2ban: Inactive"
fi
else
echo " Fail2ban: Inactive"
echo " Fail2ban: Not configured"
fi
echo
@@ -396,7 +474,14 @@ case "${1:-help}" in
restart-services)
log_maintenance "Restarting NAS services..."
for service in smbd nmbd docker netdata; do
# Samba services based on distribution
if [[ "$DISTRO" == "opensuse" ]]; then
samba_services="smb nmb"
else
samba_services="smbd nmbd"
fi
for service in $samba_services docker netdata; do
if systemctl is-enabled "$service" &>/dev/null; then
systemctl restart "$service"
log_maintenance "Restarted $service"

View File

@@ -33,7 +33,21 @@ install_portainer() {
-v portainer_data:/data \
portainer/portainer-ce:latest
# Warten bis Portainer vollständig gestartet ist
log_info "Warten auf Portainer-Initialisierung..."
sleep 10
# Portainer neu starten, um das initiale Security-Timeout zu umgehen
log_info "Portainer neu starten, um Security-Timeout zu beheben..."
sudo docker restart portainer
# Kurz warten, bis der Neustart abgeschlossen ist
sleep 5
log_success "Portainer wurde erfolgreich installiert und läuft auf Port 9000 (HTTP) und 9443 (HTTPS)."
local ip_address
ip_address=$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | head -1 | awk '{print $2}' | cut -d/ -f1)
log_info "Portainer ist nun verfügbar unter: https://${ip_address}:9443"
}
# Logging-Funktionen bereitstellen, falls nicht vorhanden
@@ -43,5 +57,9 @@ if ! command -v log_info &>/dev/null; then
log_error() { echo "[ERROR] $1" >&2; }
fi
# Hauptlogik
install_portainer
# Nur ausführen, wenn diese Datei direkt ausgeführt wird (nicht beim `source` in setup.sh).
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Hauptlogik
install_portainer
exit $?
fi

View File

@@ -5,8 +5,14 @@
secure_shared_memory() {
log_info "Securing shared memory..."
handle_error sudo cp /etc/fstab /etc/fstab.bak
echo "tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0" | sudo tee -a /etc/fstab
handle_error sudo mount -o remount /run/shm
# Use /dev/shm for shared memory mount point (more compatible across distributions)
echo "tmpfs /dev/shm tmpfs defaults,noexec,nosuid 0 0" | sudo tee -a /etc/fstab
# Ensure mount point exists
sudo mkdir -p /dev/shm
handle_error sudo mount -o remount /dev/shm
log_success "Shared memory secured."
}
@@ -25,6 +31,9 @@ install_fail2ban() {
;;
opensuse)
handle_error sudo zypper install -y fail2ban
# On openSUSE, create a basic log file for Fail2ban to monitor
sudo touch /var/log/fail2ban.log
sudo chmod 644 /var/log/fail2ban.log
;;
*)
log_error "Unsupported Linux distribution: $DISTRO"
@@ -45,7 +54,39 @@ maxretry = 3
[sshd]
enabled = true
port = ${DEFAULT_SSH_PORT:-22}
logpath = %(sshd_log)s
EOF
# Configure backend based on distribution
case $DISTRO in
opensuse)
# Use journald backend for openSUSE
echo "backend = systemd" | sudo tee -a /etc/fail2ban/jail.local
echo "journalmatch = _SYSTEMD_UNIT=sshd.service" | sudo tee -a /etc/fail2ban/jail.local
;;
*)
# Use systemd backend for other distributions
echo "backend = systemd" | sudo tee -a /etc/fail2ban/jail.local
;;
esac
sudo tee -a /etc/fail2ban/jail.local > /dev/null <<EOF
EOF
# Add distribution-specific log path for SSH
case $DISTRO in
ubuntu|debian|fedora|arch)
echo "logpath = /var/log/auth.log" | sudo tee -a /etc/fail2ban/jail.local
;;
opensuse)
# openSUSE uses /var/log/messages for SSH logs
echo "logpath = /var/log/messages" | sudo tee -a /etc/fail2ban/jail.local
;;
*)
echo "logpath = /var/log/auth.log" | sudo tee -a /etc/fail2ban/jail.local
;;
esac
sudo tee -a /etc/fail2ban/jail.local > /dev/null <<EOF
[dropbear]
enabled = false
@@ -88,6 +129,7 @@ enabled = false
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
EOF
handle_error sudo systemctl enable fail2ban
@@ -119,12 +161,15 @@ harden_ssh() {
# Test SSH config
if sudo sshd -t; then
sudo systemctl restart sshd
log_success "SSH hardened successfully."
if restart_ssh_service; then
log_success "SSH hardened successfully."
else
log_warning "SSH configuration valid but failed to restart service via known methods."
fi
else
log_error "SSH configuration invalid. Restoring backup."
sudo cp "${ssh_config}.bak" "$ssh_config"
sudo systemctl restart sshd
restart_ssh_service || true
fi
}

View File

@@ -7,8 +7,14 @@ configure_unattended_upgrades() {
case $DISTRO in
ubuntu|debian)
handle_error sudo apt-get install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
# Preseed debconf to avoid interactive prompts and install non-interactively
sudo debconf-set-selections <<DEBCONF
unattended-upgrades unattended-upgrades/enable_auto_updates boolean true
DEBCONF
# Install non-interactively and avoid dpkg prompts by forcing noninteractive frontend
# Use 'sudo env' to ensure environment vars are set for the root process
handle_error sudo env DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install -y unattended-upgrades apt-listchanges
# Do not call dpkg-reconfigure interactively; we will write the config files directly
# Configure unattended-upgrades for security only
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null <<EOF
@@ -90,6 +96,3 @@ if ! command -v log_info &>/dev/null; then
log_success() { echo "[SUCCESS] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
fi
# Main execution
configure_unattended_upgrades

View File

@@ -1,65 +1,97 @@
#!/bin/bash
# Vaultwarden installation and configuration script (2025-enhanced)
#!/usr/bin/env bash
#
# lib/vaultwarden.sh
#
# Korrigierte Vaultwarden-Installationsbibliothek für das NAS-Setup-Skript.
# Änderungen:
# - Kein Top-Level-Aufruf von install_vaultwarden beim Sourcen (verhindert sofortiges exit).
# - Fehler führen zu `return`-Codes statt `exit`, damit der Aufrufer (setup.sh) entscheiden kann.
# - Prüft, ob Docker vorhanden ist; gibt passenden Rückgabewert bei Fehlen zurück.
# - Vorsichtiger Umgang mit existierenden Containern/Verzeichnissen.
#
# Diese Datei ist dafür gedacht, mit `source` in setup.sh geladen zu werden.
install_vaultwarden() {
# Erwartet: log_info, log_error, log_success Funktionen sind verfügbar (aus lib/logging.sh)
# Erwartet: VAULTWARDEN_DATA_DIR gesetzt (aus config/defaults.sh)
local container_name="vaultwarden"
local image="vaultwarden/server:latest"
local host_port="${VAULTWARDEN_PORT:-8080}"
local data_dir="${VAULTWARDEN_DATA_DIR:-/opt/vaultwarden}"
log_info "Installing Vaultwarden..."
# Docker muss installiert sein
if ! command -v docker &>/dev/null; then
log_error "Docker ist nicht installiert. Bitte Docker zuerst installieren."
exit 1
# Prüfen: Docker vorhanden?
if ! command -v docker >/dev/null 2>&1; then
log_error "Docker ist nicht installiert."
log_error "Bitte Docker zuerst installieren oder im Setup erlauben, Docker zu installieren."
return 1
fi
# Erstelle Verzeichnis für Vaultwarden
local vault_dir="${VAULTWARDEN_DATA_DIR:-/opt/vaultwarden}"
sudo mkdir -p "$vault_dir"
sudo chown "$USER:$USER" "$vault_dir"
# Sicherstellen, dass das Datenverzeichnis vorhanden ist
if ! mkdir -p "${data_dir}" >/dev/null 2>&1; then
log_error "Konnte Datenverzeichnis '${data_dir}' nicht anlegen."
return 2
fi
# Erstelle docker-compose.yml
cat <<EOF | sudo tee "$vault_dir/docker-compose.yml" > /dev/null
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
volumes:
- ./vw-data:/data
ports:
- "8080:80" # HTTP on 8080
environment:
- WEBSOCKET_ENABLED=true
- SIGNUPS_ALLOWED=false # Disable signups by default for security
- ADMIN_TOKEN= # Set admin token later
EOF
# Prüfen, ob ein Container mit dem gewünschten Namen bereits existiert
if docker ps -a --format '{{.Names}}' | grep -xq "${container_name}"; then
log_info "Ein Container mit Namen '${container_name}' existiert bereits."
# Wenn der Container gestoppt ist, starten wir ihn; wenn er läuft, nichts tun.
if docker ps --format '{{.Names}}' | grep -xq "${container_name}"; then
log_info "Container '${container_name}' läuft bereits. Überspringe Erstellung."
log_success "Vaultwarden ist (vermutlich) bereits installiert und läuft."
return 0
else
log_info "Starte vorhandenen Container '${container_name}'..."
if docker start "${container_name}" >/dev/null 2>&1; then
log_success "Container '${container_name}' erfolgreich gestartet."
return 0
else
log_error "Fehler beim Starten des Containers '${container_name}'."
return 3
fi
fi
fi
cd "$vault_dir"
# Pull the image first (optional, improves reliability)
log_info "Lade Vaultwarden-Image '${image}' herunter..."
if ! docker pull "${image}"; then
log_error "Fehler beim Herunterladen des Images '${image}'."
return 4
fi
# Pull the latest image
handle_error sudo docker pull vaultwarden/server:latest
# Start the container (grundlegendes Beispiel)
# Anpassungen möglich: Ports, Umgebungsvariablen (z.B. ADMIN_TOKEN), Volumes, Netzwerke.
log_info "Erstelle und starte Container '${container_name}' (Port ${host_port})..."
if docker run -d \
--name "${container_name}" \
--restart unless-stopped \
-v "${data_dir}:/data" \
-p "${host_port}:80" \
"${image}" >/dev/null 2>&1; then
# Start Vaultwarden
handle_error sudo docker-compose up -d
# Warte kurz und prüfe Status
sleep 5
if sudo docker ps | grep -q vaultwarden; then
log_success "Vaultwarden wurde erfolgreich installiert und läuft auf Port 8080."
log_info "Um Admin-Zugang zu aktivieren, setze ADMIN_TOKEN in der docker-compose.yml und starte neu."
log_info "Web-Interface: http://$(hostname -I | awk '{print $1}'):8080"
log_success "Vaultwarden-Container '${container_name}' erfolgreich erstellt und gestartet."
log_info "Vaultwarden erreichbar auf Port ${host_port} (http)."
return 0
else
log_error "Vaultwarden-Container konnte nicht gestartet werden."
return 1
log_error "Fehler beim Erstellen/Starten des Vaultwarden-Containers."
return 5
fi
}
# Logging functions if not available
if ! command -v log_info &>/dev/null; then
log_info() { echo "[INFO] $1"; }
log_success() { echo "[SUCCESS] $1"; }
log_error() { echo "[ERROR] $1" >&2; }
fi
# Optional: Helfer, der prüft ob Vaultwarden bereits installiert ist (Exit-Code 0 = installiert)
is_vaultwarden_installed() {
if command -v docker >/dev/null 2>&1 && docker ps -a --format '{{.Names}}' | grep -xq '^vaultwarden$'; then
return 0
fi
return 1
}
# Main execution
install_vaultwarden
# Nur ausführen, wenn diese Datei direkt ausgeführt wird (nicht beim `source` in setup.sh).
# Das erlaubt unabhängiges Testen, ohne dass Sourcing das Haupt-Skript beendet.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Falls direkt ausgeführt: versuchen wir die Funktion und geben das Ergebnis als Exit-Code zurück.
install_vaultwarden "$@"
exit $?
fi

122
lib/webmin.sh Normal file
View File

@@ -0,0 +1,122 @@
#!/bin/bash
# Webmin installation and configuration
install_webmin() {
if [[ "${INSTALL_WEBMIN:-false}" != "true" ]]; then
return 0
fi
log_info "Installing Webmin web interface..."
case $DISTRO in
ubuntu|debian)
# Download and run Webmin setup script
handle_error curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
handle_error sudo bash setup-repos.sh
# Install Webmin
handle_error sudo apt update
handle_error sudo apt install -y webmin
# Clean up setup script
rm -f setup-repos.sh
;;
fedora)
# Add Webmin repository
handle_error sudo curl -o /etc/yum.repos.d/webmin.repo https://raw.githubusercontent.com/webmin/webmin/master/webmin.repo
handle_error sudo dnf install -y webmin
;;
opensuse)
# Webmin is not available in official openSUSE Leap repositories
# For openSUSE, Webmin needs to be installed manually or from third-party repos
log_warning "Webmin is not available in official openSUSE Leap repositories"
log_info "To install Webmin on openSUSE manually:"
log_info "1. Download from https://www.webmin.com/download.html"
log_info "2. Follow the manual installation instructions"
log_info "3. Webmin will be available at https://your-server:10000"
return 1
;;
arch)
# Webmin is available in AUR
log_warning "Webmin installation on Arch Linux requires manual AUR installation"
log_info "Please install Webmin manually from AUR: yay -S webmin"
log_info "Then run: sudo systemctl enable webmin && sudo systemctl start webmin"
return 0
;;
*)
log_error "Webmin installation not supported for $DISTRO"
return 1
;;
esac
# Enable and start Webmin service
handle_error sudo systemctl enable webmin
handle_error sudo systemctl start webmin
# Configure firewall for Webmin (port 10000)
configure_webmin_firewall
# Get IP address for access information
local ip_address=$(hostname -I | awk '{print $1}')
log_success "Webmin installed and configured"
log_info "Webmin is available at: https://${ip_address}:10000"
log_info "Default login: root / your root password"
log_warning "Important: Change the default password after first login!"
log_info "Note: Webmin uses self-signed SSL certificate - accept the security warning"
# Add to rollback
add_rollback_action "sudo systemctl disable webmin && sudo systemctl stop webmin && sudo apt remove -y webmin"
}
# Configure firewall for Webmin access
configure_webmin_firewall() {
log_info "Configuring firewall for Webmin access..."
case $DISTRO in
ubuntu|debian|arch)
# UFW firewall
if command -v ufw &> /dev/null; then
handle_error sudo ufw allow 10000/tcp
log_info "UFW rule added: allow port 10000/tcp for Webmin"
fi
;;
fedora|opensuse)
# Firewalld
if command -v firewall-cmd &> /dev/null; then
handle_error sudo firewall-cmd --permanent --add-port=10000/tcp
handle_error sudo firewall-cmd --reload
log_info "Firewalld rule added: allow port 10000/tcp for Webmin"
fi
;;
esac
}
# Webmin configuration optimization
configure_webmin() {
if [[ "${INSTALL_WEBMIN:-false}" != "true" ]]; then
return 0
fi
log_info "Configuring Webmin optimizations..."
# Webmin configuration file
local webmin_config="/etc/webmin/miniserv.conf"
if [[ -f "$webmin_config" ]]; then
# Increase session timeout
sudo sed -i 's/^session_timeout=.*/session_timeout=3600/' "$webmin_config"
# Configure SSL settings
sudo sed -i 's/^ssl=.*/ssl=1/' "$webmin_config"
sudo sed -i 's/^ssl_redirect=.*/ssl_redirect=1/' "$webmin_config"
# Restart Webmin to apply changes
handle_error sudo systemctl restart webmin
log_success "Webmin configuration optimized"
else
log_warning "Webmin configuration file not found - skipping optimization"
fi
}

101
scripts/repair_docker.sh Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env bash
set -euo pipefail
# Repair helper for Docker daemon config and service
# - Validates /etc/docker/daemon.json (using jq or python3)
# - Backs up invalid config and writes a minimal valid config
# - Restarts docker and collects logs
# - Attempts to run configure_docker_daemon from the repo if available
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOGFILE="/tmp/repair_docker_$(date +%s).log"
echo "Repair started at $(date)" > "$LOGFILE"
# Load repo helpers if present
if [[ -f "$SCRIPT_DIR/config/defaults.sh" ]]; then
# shellcheck disable=SC1091
source "$SCRIPT_DIR/config/defaults.sh" || true
fi
if [[ -f "$SCRIPT_DIR/lib/common.sh" ]]; then
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/common.sh" || true
fi
if [[ -f "$SCRIPT_DIR/lib/docker.sh" ]]; then
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/docker.sh" || true
fi
validate_json() {
local file="$1"
if command -v jq >/dev/null 2>&1; then
jq empty "$file" >/dev/null 2>&1
return $?
elif command -v python3 >/dev/null 2>&1; then
python3 -m json.tool "$file" >/dev/null 2>&1
return $?
else
# conservative: try to detect a trailing EOF or obvious truncation
if [[ -s "$file" ]]; then
return 0
else
return 1
fi
fi
}
echo "Using repo at: $SCRIPT_DIR" >> "$LOGFILE"
if [[ -f /etc/docker/daemon.json ]]; then
echo "/etc/docker/daemon.json exists - validating..." | tee -a "$LOGFILE"
if validate_json /etc/docker/daemon.json; then
echo "daemon.json is valid" | tee -a "$LOGFILE"
else
echo "daemon.json is INVALID - backing up and replacing with minimal config" | tee -a "$LOGFILE"
sudo mkdir -p /tmp/docker-repair-backups
sudo mv /etc/docker/daemon.json "/tmp/docker-repair-backups/daemon.json.broken.$(date +%s)"
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"storage-driver": "overlay2"
}
EOF
echo "Wrote minimal /etc/docker/daemon.json" | tee -a "$LOGFILE"
fi
else
echo "/etc/docker/daemon.json does not exist - creating minimal config" | tee -a "$LOGFILE"
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"storage-driver": "overlay2"
}
EOF
fi
echo "Reloading systemd daemon and restarting docker" | tee -a "$LOGFILE"
sudo systemctl daemon-reload || true
if sudo systemctl restart docker; then
echo "Docker restarted successfully" | tee -a "$LOGFILE"
sudo journalctl -u docker --no-pager -n 200 >> "$LOGFILE" 2>&1 || true
else
echo "Docker failed to restart - collecting logs" | tee -a "$LOGFILE"
sudo journalctl -u docker --no-pager -n 500 >> "$LOGFILE" 2>&1 || true
# Try to run configure_docker_daemon from this repo if available
if declare -F configure_docker_daemon >/dev/null 2>&1; then
echo "Attempting to run configure_docker_daemon() from repo" | tee -a "$LOGFILE"
if configure_docker_daemon >> "$LOGFILE" 2>&1; then
echo "configure_docker_daemon executed - attempting docker restart" | tee -a "$LOGFILE"
sudo systemctl restart docker >> "$LOGFILE" 2>&1 || true
sudo journalctl -u docker --no-pager -n 200 >> "$LOGFILE" 2>&1 || true
else
echo "configure_docker_daemon failed" | tee -a "$LOGFILE"
fi
else
echo "configure_docker_daemon function not available in sourced repo files" | tee -a "$LOGFILE"
fi
fi
echo "Repair finished at $(date)" | tee -a "$LOGFILE"
echo "Logfile: $LOGFILE"
exit 0

411
setup.sh
View File

@@ -1,6 +1,6 @@
#!/bin/bash
# NAS Setup Script - Version 2.0.0
# 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:
@@ -46,6 +46,7 @@ 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
@@ -57,6 +58,25 @@ 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"
@@ -98,6 +118,7 @@ detect_distro() {
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
@@ -168,8 +189,12 @@ detect_distro() {
# Normalize distribution names
case $detected_distro in
ubuntu|debian|fedora|arch|opensuse)
DISTRO=$detected_distro
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
@@ -193,6 +218,7 @@ detect_distro() {
# 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
@@ -217,6 +243,7 @@ detect_distro() {
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
@@ -271,13 +298,18 @@ load_or_create_config() {
if load_config; then
log_info "Configuration loaded from ${CONFIG_FILE}"
if ! validate_config; then
log_error "Configuration validation failed"
exit 1
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() {
@@ -289,8 +321,20 @@ create_interactive_config() {
save_config "SSH_PORT" "$ssh_port"
# User configuration
local username=$(ask_input "Admin username" "$NEW_USER" "validate_username")
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
@@ -300,12 +344,33 @@ create_interactive_config() {
fi
# Service selection
save_config "INSTALL_DOCKER" "$(ask_yes_no "Install Docker?" "y" && echo "true" || echo "false")"
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")"
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")"
# 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}"
}
@@ -328,8 +393,9 @@ update_system() {
sudo pacman -Syu --noconfirm --quiet
;;
opensuse)
sudo zypper refresh -q
sudo zypper update -y -q
sudo zypper refresh
show_progress 5 10 "System Update"
sudo zypper update -y
;;
esac
@@ -339,92 +405,189 @@ update_system() {
# Main installation orchestrator
run_installation() {
local total_steps=12
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"
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++)); show_progress $current_step $total_steps "Configuring network"
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++)); show_progress $current_step $total_steps "Configuring SSH"
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++)); show_progress $current_step $total_steps "Setting up Samba"
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++)); show_progress $current_step $total_steps "Configuring firewall"
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++)); show_progress $current_step $total_steps "Implementing security measures"
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
# 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++)); show_progress $current_step $total_steps "Installing Docker"
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=$((current_step + 1))
fi
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing NFS"
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=$((current_step + 1))
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Netdata"
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=$((current_step + 1))
fi
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Vaultwarden"
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=$((current_step + 1))
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Jellyfin"
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=$((current_step + 1))
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
((current_step++)); show_progress $current_step $total_steps "Installing Portainer"
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=$((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:-$NEW_USER}' created with sudo access"
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"
echo " ✓ Automatic updates enabled"
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
@@ -434,27 +597,187 @@ show_installation_summary() {
echo " ✓ NFS server installed"
fi
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
echo " ✓ Netdata monitoring: http://$(hostname -I | awk '{print $1}'):${NETDATA_PORT}"
echo " ✓ Netdata monitoring: http://${ip_address}:${NETDATA_PORT}"
fi
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
echo " ✓ Jellyfin media server: http://$(hostname -I | awk '{print $1}'):8096"
echo " ✓ Jellyfin media server: http://${ip_address}:8096"
fi
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
echo " ✓ Portainer Docker management: http://$(hostname -I | awk '{print $1}'):9000"
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"
echo " 4. Review firewall rules with: sudo ufw status"
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:-$NEW_USER}"
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
@@ -481,6 +804,9 @@ main() {
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
@@ -493,11 +819,18 @@ main() {
# 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