Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0837b37569 | ||
|
|
47a75cec25 | ||
|
|
fea282102d | ||
|
|
3344620824 | ||
|
|
fc26c8e839 | ||
|
|
25bda49293 | ||
|
|
4ae06158f3 | ||
|
|
55f69640ea | ||
|
|
8b0f8b3e96 | ||
|
|
edf70990ca | ||
|
|
9c7a403ade | ||
|
|
b23fdbc8ce | ||
|
|
648f90a304 | ||
|
|
ced9c14add | ||
|
|
c49e88a9b2 | ||
|
|
ad98dd210c | ||
|
|
ca6a37ff51 | ||
|
|
745cf90874 | ||
|
|
f611f1de1e | ||
|
|
e91ac9668f | ||
|
|
93e444367d | ||
|
|
7a5618d4c0 | ||
|
|
9077b4dbb9 | ||
|
|
9d27968c71 | ||
|
|
0b508c822e | ||
|
|
f95a843b4c | ||
|
|
3c9fe4017b | ||
|
|
c0d6a07cec | ||
|
|
21781166ea | ||
|
|
5a0a2401ce | ||
|
|
0253b226a4 | ||
|
|
8420a0c8b5 | ||
|
|
144b5c5929 | ||
|
|
df501df118 | ||
|
|
14b35c9210 | ||
|
|
cfdace6b87 | ||
|
|
2aa614949f | ||
|
|
a8ea1cf377 | ||
|
|
0b25a86f91 | ||
|
|
2593b6f8fe | ||
|
|
f106f966a3 | ||
|
|
e545862043 | ||
|
|
d886652385 | ||
|
|
abe0d451ab | ||
|
|
89349adc26 | ||
|
|
cfc234559e | ||
|
|
6eb78589b5 | ||
|
|
7a2d1f1c8e | ||
|
|
b6cdf82356 | ||
|
|
4d645a31a6 | ||
|
|
767e607a27 | ||
|
|
c460f7c9e2 | ||
|
|
59a46b3754 | ||
|
|
44e0c13435 | ||
|
|
1a67acaafc | ||
|
|
0fd250a5b3 | ||
|
|
834b5382d5 | ||
|
|
3e048ba938 | ||
|
|
3fb7f3479e | ||
|
|
2926b0ab66 | ||
|
|
5b3b0465c7 | ||
|
|
5416687af2 | ||
|
|
34f45f978c | ||
|
|
1c2dbe22a1 | ||
|
|
494b13264e | ||
|
|
f3afd46abc | ||
|
|
8db3151495 | ||
|
|
0358f45d60 | ||
|
|
fea22c3e0d | ||
|
|
2441155399 | ||
|
|
660565a1c4 | ||
|
|
b0340adf03 | ||
|
|
081e32ed43 | ||
|
|
7a1384d48f | ||
|
|
086c101923 |
83
CHANGELOG.md
83
CHANGELOG.md
@@ -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/),
|
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).
|
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
|
## [2.1.0] - 2025-10-01
|
||||||
|
|
||||||
### 🚀 2025 Compatibility Update
|
### 🚀 2025 Compatibility Update
|
||||||
|
|||||||
68
README.md
68
README.md
@@ -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.
|
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
|
- **Full IPv6 Support** throughout the entire system
|
||||||
- **Modern Distribution Support** (Ubuntu 24.04+, Fedora 41+, openSUSE 15.6+)
|
- **Modern Distribution Support** (Ubuntu 24.04+, Fedora 41+, openSUSE 15.6+)
|
||||||
- **Enhanced Security** with Ed25519 SSH keys, auditd logging, and MAC
|
- **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
|
- **Netdata** for real-time system monitoring
|
||||||
- **Jellyfin** media server for multimedia content
|
- **Jellyfin** media server for multimedia content
|
||||||
- **Vaultwarden** for secure password management
|
- **Vaultwarden** for secure password management
|
||||||
|
- **Webmin** web-based system administration interface (Ubuntu/Debian/Fedora/Arch only)
|
||||||
- **System Performance Tracking** with automatic reports
|
- **System Performance Tracking** with automatic reports
|
||||||
- **Comprehensive Unit Testing** framework with extensive test coverage
|
- **Comprehensive Unit Testing** framework with extensive test coverage
|
||||||
|
|
||||||
@@ -114,6 +127,14 @@ chmod +x tests/unit_tests.sh
|
|||||||
./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
|
## ⚙️ Configuration
|
||||||
|
|
||||||
The script guides you through an interactive 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
|
- **Gateway and DNS:** IPv4/IPv6 automatic detection with override capability
|
||||||
|
|
||||||
### Service Selection
|
### Service Selection
|
||||||
- **Docker:** Container platform with Compose plugin
|
- **Docker:** Container platform with Compose plugin and auto-repair functionality (required for Vaultwarden, Jellyfin, Portainer)
|
||||||
- **NFS:** Network File System with IPv6 support
|
- **NFS:** Network File System with IPv6 support and export deduplication
|
||||||
- **Netdata:** System monitoring
|
- **Netdata:** System monitoring via official Packagecloud repositories
|
||||||
- **Vaultwarden:** Password manager with security hardening
|
- **Vaultwarden:** Password manager with security hardening (requires Docker, optional)
|
||||||
- **Jellyfin:** Media server with modern GPG keys
|
- **Jellyfin:** Media server with modern GPG keys (requires Docker, optional)
|
||||||
- **Portainer:** Docker management with HTTPS
|
- **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
|
### User Configuration
|
||||||
- **Firewall Rules:** IPv4/IPv6 automatic based on selected services
|
- **Admin User:** Uses the current sudo user by default (no new user creation required)
|
||||||
- **Fail2ban:** Protection against brute-force attacks
|
|
||||||
- **Rate Limiting:** IPv4/IPv6 protection against DoS attacks
|
|
||||||
- **SSH Keys:** Ed25519 key generation for enhanced security
|
- **SSH Keys:** Ed25519 key generation for enhanced security
|
||||||
|
|
||||||
## 📁 Directory Structure
|
## 📁 Directory Structure
|
||||||
@@ -142,6 +163,8 @@ The script guides you through an interactive configuration:
|
|||||||
```
|
```
|
||||||
nas/
|
nas/
|
||||||
├── setup.sh # Main installation script
|
├── setup.sh # Main installation script
|
||||||
|
├── scripts/
|
||||||
|
│ └── repair_docker.sh # Docker repair and troubleshooting script
|
||||||
├── config/
|
├── config/
|
||||||
│ └── defaults.sh # Configuration variables and defaults
|
│ └── defaults.sh # Configuration variables and defaults
|
||||||
├── lib/
|
├── lib/
|
||||||
@@ -158,6 +181,7 @@ nas/
|
|||||||
│ ├── vaultwarden.sh # Vaultwarden password manager
|
│ ├── vaultwarden.sh # Vaultwarden password manager
|
||||||
│ ├── jellyfin.sh # Jellyfin media server
|
│ ├── jellyfin.sh # Jellyfin media server
|
||||||
│ ├── portainer.sh # Portainer Docker management
|
│ ├── portainer.sh # Portainer Docker management
|
||||||
|
│ ├── webmin.sh # Webmin web interface
|
||||||
│ ├── unattended-upgrades.sh # Automatic system updates
|
│ ├── unattended-upgrades.sh # Automatic system updates
|
||||||
│ └── performance.sh # Performance optimization
|
│ └── performance.sh # Performance optimization
|
||||||
├── tests/
|
├── tests/
|
||||||
@@ -182,6 +206,7 @@ nas/
|
|||||||
| Jellyfin | 1900 | UDP | DLNA Discovery | ✅ |
|
| Jellyfin | 1900 | UDP | DLNA Discovery | ✅ |
|
||||||
| Portainer | 9000 | TCP | Docker Management (HTTPS) | ✅ |
|
| Portainer | 9000 | TCP | Docker Management (HTTPS) | ✅ |
|
||||||
| Vaultwarden | 8080 | TCP | Password Manager | ✅ |
|
| Vaultwarden | 8080 | TCP | Password Manager | ✅ |
|
||||||
|
| Webmin | 10000 | TCP | Web Administration Interface | ✅ |
|
||||||
| Docker API | 2375, 2376 | TCP | Docker Remote API | ✅ |
|
| Docker API | 2375, 2376 | TCP | Docker Remote API | ✅ |
|
||||||
|
|
||||||
## 🛡️ Security Features
|
## 🛡️ Security Features
|
||||||
@@ -289,6 +314,25 @@ sudo netplan apply # Ubuntu/Debian
|
|||||||
sudo systemctl restart NetworkManager # Fedora/openSUSE
|
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
|
#### Service Issues
|
||||||
```bash
|
```bash
|
||||||
# Check service status
|
# Check service status
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Default configuration values for NAS setup script
|
# Default configuration values for NAS setup script
|
||||||
|
|
||||||
# Script metadata
|
# Script metadata
|
||||||
SCRIPT_VERSION="2.1.0"
|
SCRIPT_VERSION="2.1.1"
|
||||||
SCRIPT_NAME="NAS Setup Script"
|
SCRIPT_NAME="NAS Setup Script"
|
||||||
SCRIPT_AUTHOR="Sebastian Palencsár"
|
SCRIPT_AUTHOR="Sebastian Palencsár"
|
||||||
|
|
||||||
@@ -43,7 +43,8 @@ RECOMMENDED_RAM_MB=4096
|
|||||||
# Security settings
|
# Security settings
|
||||||
ENABLE_FAIL2BAN=true
|
ENABLE_FAIL2BAN=true
|
||||||
ENABLE_UFW=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
|
SECURE_SSH=true
|
||||||
|
|
||||||
# Debug and logging
|
# Debug and logging
|
||||||
@@ -121,6 +122,7 @@ INSTALL_NETDATA=${INSTALL_NETDATA:-false}
|
|||||||
INSTALL_VAULTWARDEN=${INSTALL_VAULTWARDEN:-false}
|
INSTALL_VAULTWARDEN=${INSTALL_VAULTWARDEN:-false}
|
||||||
INSTALL_JELLYFIN=${INSTALL_JELLYFIN:-false}
|
INSTALL_JELLYFIN=${INSTALL_JELLYFIN:-false}
|
||||||
INSTALL_PORTAINER=${INSTALL_PORTAINER:-false}
|
INSTALL_PORTAINER=${INSTALL_PORTAINER:-false}
|
||||||
|
INSTALL_WEBMIN=${INSTALL_WEBMIN:-false}
|
||||||
|
|
||||||
# Configuration validation
|
# Configuration validation
|
||||||
validate_config() {
|
validate_config() {
|
||||||
@@ -146,5 +148,13 @@ validate_config() {
|
|||||||
fi
|
fi
|
||||||
done
|
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
|
return $errors
|
||||||
}
|
}
|
||||||
|
|||||||
150
lib/common.sh
150
lib/common.sh
@@ -260,6 +260,57 @@ start_and_enable_service() {
|
|||||||
fi
|
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
|
# Configuration management
|
||||||
save_config() {
|
save_config() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
@@ -290,6 +341,61 @@ load_config() {
|
|||||||
fi
|
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 function
|
||||||
cleanup() {
|
cleanup() {
|
||||||
log_info "Performing cleanup..."
|
log_info "Performing cleanup..."
|
||||||
@@ -321,12 +427,12 @@ cleanup() {
|
|||||||
# Performance monitoring
|
# Performance monitoring
|
||||||
get_system_info() {
|
get_system_info() {
|
||||||
log_info "System Information:"
|
log_info "System Information:"
|
||||||
echo " OS: $(lsb_release -d | cut -f2)"
|
echo " OS: ${DISTRO_NAME:-Unknown}"
|
||||||
echo " Kernel: $(uname -r)"
|
echo " Kernel: $(uname -r)"
|
||||||
echo " CPU: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)"
|
echo " CPU: $(lscpu | grep 'Model name' | cut -d: -f2 | xargs)"
|
||||||
echo " RAM: $(free -h | awk 'NR==2{printf "%s/%s", $3,$2}')"
|
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 " 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
|
# Version and compatibility checks
|
||||||
@@ -404,6 +510,46 @@ setup_basic_monitoring() {
|
|||||||
log_success "Basic monitoring tools installed"
|
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
|
||||||
install_additional_components() {
|
install_additional_components() {
|
||||||
log_info "Installing additional useful components..."
|
log_info "Installing additional useful components..."
|
||||||
|
|||||||
184
lib/docker.sh
184
lib/docker.sh
@@ -1,5 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/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() {
|
install_docker() {
|
||||||
log_info "Installing Docker..."
|
log_info "Installing Docker..."
|
||||||
|
|
||||||
@@ -33,12 +44,9 @@ install_docker() {
|
|||||||
handle_error sudo pacman -S --noconfirm docker docker-compose
|
handle_error sudo pacman -S --noconfirm docker docker-compose
|
||||||
;;
|
;;
|
||||||
opensuse)
|
opensuse)
|
||||||
# Add Docker repository
|
# Install Docker from official openSUSE repositories
|
||||||
handle_error sudo zypper addrepo https://download.docker.com/linux/opensuse/docker-ce.repo
|
# Docker CE is not officially supported on openSUSE, use community packages
|
||||||
handle_error sudo zypper refresh
|
handle_error sudo zypper install -y docker docker-compose
|
||||||
|
|
||||||
# Install Docker CE
|
|
||||||
handle_error sudo zypper install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
log_error "Unsupported Linux distribution: $DISTRO"
|
log_error "Unsupported Linux distribution: $DISTRO"
|
||||||
@@ -46,20 +54,129 @@ install_docker() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Add user to the docker group
|
# Determine which user to add to the docker group.
|
||||||
handle_error sudo usermod -aG docker "$NEW_USER"
|
# 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 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
|
if [[ "$DOCKER_DATA_DIR" != "$DEFAULT_DOCKER_DATA_DIR" ]]; then
|
||||||
log_info "Configuring Docker data directory to $DOCKER_DATA_DIR..."
|
log_info "Configuring Docker data directory to $DOCKER_DATA_DIR..."
|
||||||
handle_error sudo mkdir -p "$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
|
fi
|
||||||
|
|
||||||
|
# Create optimized Docker daemon configuration
|
||||||
|
configure_docker_daemon
|
||||||
|
|
||||||
export DOCKER_CONTENT_TRUST=1
|
export DOCKER_CONTENT_TRUST=1
|
||||||
|
|
||||||
log_info "Docker installed successfully."
|
log_info "Docker installed successfully."
|
||||||
@@ -75,3 +192,46 @@ install_docker() {
|
|||||||
echo "5. Enable linger for the user:"
|
echo "5. Enable linger for the user:"
|
||||||
echo " sudo loginctl enable-linger \$(whoami)"
|
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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ install_jellyfin() {
|
|||||||
log_info "Jellyfin installation completed."
|
log_info "Jellyfin installation completed."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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
|
# Detect the distribution and call the appropriate function
|
||||||
if [ -f /etc/os-release ]; then
|
if [ -f /etc/os-release ]; then
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
@@ -64,3 +72,4 @@ else
|
|||||||
log_error "Cannot detect the operating system."
|
log_error "Cannot detect the operating system."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
@@ -29,8 +29,36 @@ install_netdata() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Install Netdata from GitHub (works across distributions)
|
# Install Netdata using official repository for better reliability
|
||||||
handle_error bash <(curl -Ss https://my-netdata.io/kickstart.sh) --stable-channel --disable-telemetry
|
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 enable netdata
|
||||||
handle_error sudo systemctl start netdata
|
handle_error sudo systemctl start netdata
|
||||||
|
|||||||
@@ -203,16 +203,16 @@ configure_ssh() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Create new user if not exists
|
# Create new user if not exists
|
||||||
if ! id "${ADMIN_USER:-$NEW_USER}" &>/dev/null; then
|
if ! id "${ADMIN_USER:-$USER}" &>/dev/null; then
|
||||||
log_info "Creating user ${ADMIN_USER:-$NEW_USER}..."
|
log_info "Creating user ${ADMIN_USER:-$USER}..."
|
||||||
sudo useradd -m -s /bin/bash "${ADMIN_USER:-$NEW_USER}"
|
sudo useradd -m -s /bin/bash "${ADMIN_USER:-$USER}"
|
||||||
sudo usermod -aG sudo "${ADMIN_USER:-$NEW_USER}"
|
sudo usermod -aG sudo "${ADMIN_USER:-$USER}"
|
||||||
|
|
||||||
# Set password
|
# Set password
|
||||||
local password=$(ask_password "Set password for user ${ADMIN_USER:-$NEW_USER}")
|
local password=$(ask_password "Set password for user ${ADMIN_USER:-$USER}")
|
||||||
echo "${ADMIN_USER:-$NEW_USER}:$password" | sudo chpasswd
|
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
|
fi
|
||||||
|
|
||||||
# Configure SSH hardening
|
# Configure SSH hardening
|
||||||
@@ -232,7 +232,7 @@ ClientAliveInterval 300
|
|||||||
ClientAliveCountMax 2
|
ClientAliveCountMax 2
|
||||||
MaxAuthTries 3
|
MaxAuthTries 3
|
||||||
LoginGraceTime 60
|
LoginGraceTime 60
|
||||||
AllowUsers ${ADMIN_USER:-$NEW_USER}
|
AllowUsers ${ADMIN_USER:-$USER}
|
||||||
Protocol 2
|
Protocol 2
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -244,10 +244,9 @@ EOF
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restart SSH service
|
# Restart SSH service using helper (handles sshd vs ssh service names)
|
||||||
if sudo systemctl restart sshd; then
|
if restart_ssh_service; then
|
||||||
log_success "SSH service restarted successfully"
|
add_rollback_action "sudo cp ${ssh_config}.bak ${ssh_config} && restart_ssh_service"
|
||||||
add_rollback_action "sudo cp ${ssh_config}.bak ${ssh_config} && sudo systemctl restart sshd"
|
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Failed to restart SSH service"
|
log_error "Failed to restart SSH service"
|
||||||
@@ -281,7 +280,7 @@ setup_samba() {
|
|||||||
# Create shared directory
|
# Create shared directory
|
||||||
local share_dir="/srv/samba/shared"
|
local share_dir="/srv/samba/shared"
|
||||||
sudo mkdir -p "$share_dir"
|
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"
|
sudo chmod 755 "$share_dir"
|
||||||
|
|
||||||
# Configure Samba
|
# Configure Samba
|
||||||
@@ -309,22 +308,32 @@ setup_samba() {
|
|||||||
browseable = yes
|
browseable = yes
|
||||||
writable = yes
|
writable = yes
|
||||||
guest ok = no
|
guest ok = no
|
||||||
valid users = ${ADMIN_USER:-$NEW_USER}
|
valid users = ${ADMIN_USER:-$USER}
|
||||||
create mask = 0644
|
create mask = 0644
|
||||||
directory mask = 0755
|
directory mask = 0755
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Add Samba user
|
# Add Samba user
|
||||||
local samba_password=$(ask_password "Set Samba password for user ${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:-$NEW_USER}"
|
echo -e "$samba_password\n$samba_password" | sudo smbpasswd -a "${ADMIN_USER:-$USER}"
|
||||||
sudo smbpasswd -e "${ADMIN_USER:-$NEW_USER}"
|
sudo smbpasswd -e "${ADMIN_USER:-$USER}"
|
||||||
|
|
||||||
# Start and enable Samba services
|
# Start and enable Samba services
|
||||||
|
case $DISTRO in
|
||||||
|
ubuntu|debian|fedora|arch)
|
||||||
sudo systemctl enable smbd nmbd
|
sudo systemctl enable smbd nmbd
|
||||||
if sudo systemctl restart smbd nmbd; then
|
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_success "Samba configured and started successfully"
|
||||||
log_info "Shared folder created at: $share_dir"
|
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
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Failed to start Samba services"
|
log_error "Failed to start Samba services"
|
||||||
|
|||||||
18
lib/nfs.sh
18
lib/nfs.sh
@@ -28,22 +28,32 @@ install_nfs() {
|
|||||||
sudo chown nobody:nogroup "$export_dir"
|
sudo chown nobody:nogroup "$export_dir"
|
||||||
sudo chmod 755 "$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"
|
local exports_file="/etc/exports"
|
||||||
backup_config "$exports_file"
|
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
|
# Export NFS shares
|
||||||
handle_error sudo exportfs -a
|
handle_error sudo exportfs -a
|
||||||
|
|
||||||
# Start and enable NFS services
|
# Start and enable NFS services
|
||||||
case $DISTRO in
|
case $DISTRO in
|
||||||
ubuntu|debian|opensuse)
|
ubuntu|debian)
|
||||||
handle_error sudo systemctl enable nfs-kernel-server
|
handle_error sudo systemctl enable nfs-kernel-server
|
||||||
handle_error sudo systemctl start 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 enable nfs-server
|
||||||
handle_error sudo systemctl start nfs-server
|
handle_error sudo systemctl start nfs-server
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
optimize_system_performance() {
|
optimize_system_performance() {
|
||||||
log_info "Optimizing system performance..."
|
log_info "Optimizing system performance..."
|
||||||
|
|
||||||
|
# Memory optimization for NAS workloads
|
||||||
|
configure_memory_optimization
|
||||||
|
|
||||||
# Optimize kernel parameters
|
# Optimize kernel parameters
|
||||||
sudo tee -a /etc/sysctl.conf > /dev/null <<EOF
|
sudo tee -a /etc/sysctl.conf > /dev/null <<EOF
|
||||||
|
|
||||||
@@ -38,10 +41,15 @@ EOF
|
|||||||
# Apply kernel parameters
|
# Apply kernel parameters
|
||||||
sudo sysctl -p
|
sudo sysctl -p
|
||||||
|
|
||||||
# Optimize I/O scheduler for SSDs/HDDs
|
# 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
|
||||||
local disk_type=$(lsblk -d -o name,rota | awk 'NR>1 {if($2==0) print "ssd"; else print "hdd"; exit}')
|
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)
|
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
|
if [[ "$disk_type" == "ssd" ]]; then
|
||||||
echo "mq-deadline" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
|
echo "mq-deadline" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
|
||||||
log_info "Optimized I/O scheduler for SSD"
|
log_info "Optimized I/O scheduler for SSD"
|
||||||
@@ -49,6 +57,10 @@ EOF
|
|||||||
echo "bfq" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
|
echo "bfq" | sudo tee "/sys/block/$root_disk/queue/scheduler" > /dev/null
|
||||||
log_info "Optimized I/O scheduler for HDD"
|
log_info "Optimized I/O scheduler for HDD"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
log_warning "Could not determine or access root disk scheduler. Skipping I/O optimization."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Create performance monitoring script
|
# Create performance monitoring script
|
||||||
create_performance_monitor
|
create_performance_monitor
|
||||||
@@ -56,6 +68,25 @@ EOF
|
|||||||
log_success "System performance optimized"
|
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 monitoring script
|
||||||
create_performance_monitor() {
|
create_performance_monitor() {
|
||||||
sudo tee /usr/local/bin/nas-performance > /dev/null <<'EOF'
|
sudo tee /usr/local/bin/nas-performance > /dev/null <<'EOF'
|
||||||
@@ -225,7 +256,14 @@ strict locking = no
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Restart Samba services
|
# Restart Samba services
|
||||||
|
case $DISTRO in
|
||||||
|
ubuntu|debian|fedora|arch)
|
||||||
sudo systemctl restart smbd nmbd
|
sudo systemctl restart smbd nmbd
|
||||||
|
;;
|
||||||
|
opensuse)
|
||||||
|
sudo systemctl restart smb nmb
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
log_success "Samba performance optimized"
|
log_success "Samba performance optimized"
|
||||||
}
|
}
|
||||||
@@ -259,7 +297,42 @@ perform_health_check() {
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
echo "=== Service Status ==="
|
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
|
if systemctl is-active --quiet "$service" 2>/dev/null; then
|
||||||
echo "✅ $service: Active"
|
echo "✅ $service: Active"
|
||||||
else
|
else
|
||||||
@@ -281,11 +354,16 @@ perform_health_check() {
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
echo "=== Security Status ==="
|
echo "=== Security Status ==="
|
||||||
|
# Check Fail2ban if SSH was configured (which includes Fail2ban)
|
||||||
|
if [[ "${CONFIGURE_SSH:-true}" == "true" ]]; then
|
||||||
if systemctl is-active --quiet fail2ban; then
|
if systemctl is-active --quiet fail2ban; then
|
||||||
echo "✅ Fail2ban: Active"
|
echo "✅ Fail2ban: Active"
|
||||||
else
|
else
|
||||||
echo "❌ Fail2ban: Inactive"
|
echo "❌ Fail2ban: Inactive"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "ℹ️ Fail2ban: Not configured"
|
||||||
|
fi
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "=== Docker Status ==="
|
echo "=== Docker Status ==="
|
||||||
@@ -396,7 +474,14 @@ case "${1:-help}" in
|
|||||||
restart-services)
|
restart-services)
|
||||||
log_maintenance "Restarting NAS 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
|
if systemctl is-enabled "$service" &>/dev/null; then
|
||||||
systemctl restart "$service"
|
systemctl restart "$service"
|
||||||
log_maintenance "Restarted $service"
|
log_maintenance "Restarted $service"
|
||||||
|
|||||||
@@ -33,7 +33,21 @@ install_portainer() {
|
|||||||
-v portainer_data:/data \
|
-v portainer_data:/data \
|
||||||
portainer/portainer-ce:latest
|
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)."
|
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
|
# Logging-Funktionen bereitstellen, falls nicht vorhanden
|
||||||
@@ -43,5 +57,9 @@ if ! command -v log_info &>/dev/null; then
|
|||||||
log_error() { echo "[ERROR] $1" >&2; }
|
log_error() { echo "[ERROR] $1" >&2; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Nur ausführen, wenn diese Datei direkt ausgeführt wird (nicht beim `source` in setup.sh).
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
# Hauptlogik
|
# Hauptlogik
|
||||||
install_portainer
|
install_portainer
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
@@ -5,8 +5,14 @@
|
|||||||
secure_shared_memory() {
|
secure_shared_memory() {
|
||||||
log_info "Securing shared memory..."
|
log_info "Securing shared memory..."
|
||||||
handle_error sudo cp /etc/fstab /etc/fstab.bak
|
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."
|
log_success "Shared memory secured."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,6 +31,9 @@ install_fail2ban() {
|
|||||||
;;
|
;;
|
||||||
opensuse)
|
opensuse)
|
||||||
handle_error sudo zypper install -y fail2ban
|
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"
|
log_error "Unsupported Linux distribution: $DISTRO"
|
||||||
@@ -45,7 +54,39 @@ maxretry = 3
|
|||||||
[sshd]
|
[sshd]
|
||||||
enabled = true
|
enabled = true
|
||||||
port = ${DEFAULT_SSH_PORT:-22}
|
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]
|
[dropbear]
|
||||||
enabled = false
|
enabled = false
|
||||||
@@ -88,6 +129,7 @@ enabled = false
|
|||||||
|
|
||||||
[recidive]
|
[recidive]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
logpath = /var/log/fail2ban.log
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
handle_error sudo systemctl enable fail2ban
|
handle_error sudo systemctl enable fail2ban
|
||||||
@@ -119,12 +161,15 @@ harden_ssh() {
|
|||||||
|
|
||||||
# Test SSH config
|
# Test SSH config
|
||||||
if sudo sshd -t; then
|
if sudo sshd -t; then
|
||||||
sudo systemctl restart sshd
|
if restart_ssh_service; then
|
||||||
log_success "SSH hardened successfully."
|
log_success "SSH hardened successfully."
|
||||||
|
else
|
||||||
|
log_warning "SSH configuration valid but failed to restart service via known methods."
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log_error "SSH configuration invalid. Restoring backup."
|
log_error "SSH configuration invalid. Restoring backup."
|
||||||
sudo cp "${ssh_config}.bak" "$ssh_config"
|
sudo cp "${ssh_config}.bak" "$ssh_config"
|
||||||
sudo systemctl restart sshd
|
restart_ssh_service || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ configure_unattended_upgrades() {
|
|||||||
|
|
||||||
case $DISTRO in
|
case $DISTRO in
|
||||||
ubuntu|debian)
|
ubuntu|debian)
|
||||||
handle_error sudo apt-get install -y unattended-upgrades apt-listchanges
|
# Preseed debconf to avoid interactive prompts and install non-interactively
|
||||||
sudo dpkg-reconfigure -plow unattended-upgrades
|
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
|
# Configure unattended-upgrades for security only
|
||||||
sudo tee /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null <<EOF
|
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_success() { echo "[SUCCESS] $1"; }
|
||||||
log_error() { echo "[ERROR] $1" >&2; }
|
log_error() { echo "[ERROR] $1" >&2; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Main execution
|
|
||||||
configure_unattended_upgrades
|
|
||||||
@@ -1,65 +1,97 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
# Vaultwarden installation and configuration script (2025-enhanced)
|
# 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() {
|
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..."
|
log_info "Installing Vaultwarden..."
|
||||||
|
|
||||||
# Docker muss installiert sein
|
# Prüfen: Docker vorhanden?
|
||||||
if ! command -v docker &>/dev/null; then
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
log_error "Docker ist nicht installiert. Bitte Docker zuerst installieren."
|
log_error "Docker ist nicht installiert."
|
||||||
exit 1
|
log_error "Bitte Docker zuerst installieren oder im Setup erlauben, Docker zu installieren."
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Erstelle Verzeichnis für Vaultwarden
|
# Sicherstellen, dass das Datenverzeichnis vorhanden ist
|
||||||
local vault_dir="${VAULTWARDEN_DATA_DIR:-/opt/vaultwarden}"
|
if ! mkdir -p "${data_dir}" >/dev/null 2>&1; then
|
||||||
sudo mkdir -p "$vault_dir"
|
log_error "Konnte Datenverzeichnis '${data_dir}' nicht anlegen."
|
||||||
sudo chown "$USER:$USER" "$vault_dir"
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
# Erstelle docker-compose.yml
|
# Prüfen, ob ein Container mit dem gewünschten Namen bereits existiert
|
||||||
cat <<EOF | sudo tee "$vault_dir/docker-compose.yml" > /dev/null
|
if docker ps -a --format '{{.Names}}' | grep -xq "${container_name}"; then
|
||||||
version: '3.8'
|
log_info "Ein Container mit Namen '${container_name}' existiert bereits."
|
||||||
services:
|
# Wenn der Container gestoppt ist, starten wir ihn; wenn er läuft, nichts tun.
|
||||||
vaultwarden:
|
if docker ps --format '{{.Names}}' | grep -xq "${container_name}"; then
|
||||||
image: vaultwarden/server:latest
|
log_info "Container '${container_name}' läuft bereits. Überspringe Erstellung."
|
||||||
container_name: vaultwarden
|
log_success "Vaultwarden ist (vermutlich) bereits installiert und läuft."
|
||||||
restart: unless-stopped
|
return 0
|
||||||
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
|
|
||||||
|
|
||||||
cd "$vault_dir"
|
|
||||||
|
|
||||||
# Pull the latest image
|
|
||||||
handle_error sudo docker pull vaultwarden/server:latest
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
else
|
else
|
||||||
log_error "Vaultwarden-Container konnte nicht gestartet werden."
|
log_info "Starte vorhandenen Container '${container_name}'..."
|
||||||
return 1
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
log_success "Vaultwarden-Container '${container_name}' erfolgreich erstellt und gestartet."
|
||||||
|
log_info "Vaultwarden erreichbar auf Port ${host_port} (http)."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "Fehler beim Erstellen/Starten des Vaultwarden-Containers."
|
||||||
|
return 5
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Logging functions if not available
|
# Optional: Helfer, der prüft ob Vaultwarden bereits installiert ist (Exit-Code 0 = installiert)
|
||||||
if ! command -v log_info &>/dev/null; then
|
is_vaultwarden_installed() {
|
||||||
log_info() { echo "[INFO] $1"; }
|
if command -v docker >/dev/null 2>&1 && docker ps -a --format '{{.Names}}' | grep -xq '^vaultwarden$'; then
|
||||||
log_success() { echo "[SUCCESS] $1"; }
|
return 0
|
||||||
log_error() { echo "[ERROR] $1" >&2; }
|
|
||||||
fi
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# Main execution
|
# Nur ausführen, wenn diese Datei direkt ausgeführt wird (nicht beim `source` in setup.sh).
|
||||||
install_vaultwarden
|
# 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
122
lib/webmin.sh
Normal 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
101
scripts/repair_docker.sh
Normal 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
|
||||||
397
setup.sh
397
setup.sh
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/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.
|
# This script automates the setup of a NAS system with various services.
|
||||||
# It is designed to run on multiple Linux distributions, including:
|
# 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/vaultwarden.sh"
|
||||||
source "${SCRIPT_DIR}/lib/jellyfin.sh"
|
source "${SCRIPT_DIR}/lib/jellyfin.sh"
|
||||||
source "${SCRIPT_DIR}/lib/portainer.sh"
|
source "${SCRIPT_DIR}/lib/portainer.sh"
|
||||||
|
source "${SCRIPT_DIR}/lib/webmin.sh"
|
||||||
source "${SCRIPT_DIR}/lib/performance.sh"
|
source "${SCRIPT_DIR}/lib/performance.sh"
|
||||||
|
|
||||||
# Initialize logging
|
# Initialize logging
|
||||||
@@ -57,6 +58,25 @@ exec > >(tee -a "${LOG_FILE}") 2>&1
|
|||||||
|
|
||||||
# Enhanced error handling with rollback
|
# Enhanced error handling with rollback
|
||||||
handle_error() {
|
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 exit_code=$?
|
||||||
local line_number=$1
|
local line_number=$1
|
||||||
local command="$2"
|
local command="$2"
|
||||||
@@ -98,6 +118,7 @@ detect_distro() {
|
|||||||
detected_distro=${ID,,} # Convert to lowercase
|
detected_distro=${ID,,} # Convert to lowercase
|
||||||
detected_version=$VERSION_ID
|
detected_version=$VERSION_ID
|
||||||
detected_codename=${VERSION_CODENAME:-${UBUNTU_CODENAME:-""}}
|
detected_codename=${VERSION_CODENAME:-${UBUNTU_CODENAME:-""}}
|
||||||
|
detected_pretty_name=$PRETTY_NAME
|
||||||
detection_method="/etc/os-release"
|
detection_method="/etc/os-release"
|
||||||
log_debug "Detected via /etc/os-release: $PRETTY_NAME"
|
log_debug "Detected via /etc/os-release: $PRETTY_NAME"
|
||||||
fi
|
fi
|
||||||
@@ -168,8 +189,12 @@ detect_distro() {
|
|||||||
|
|
||||||
# Normalize distribution names
|
# Normalize distribution names
|
||||||
case $detected_distro in
|
case $detected_distro in
|
||||||
ubuntu|debian|fedora|arch|opensuse)
|
ubuntu|debian|fedora|arch|opensuse|opensuse-leap)
|
||||||
|
if [[ "$detected_distro" == "opensuse-leap" ]]; then
|
||||||
|
DISTRO="opensuse"
|
||||||
|
else
|
||||||
DISTRO=$detected_distro
|
DISTRO=$detected_distro
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
"red hat enterprise linux server"|"rhel")
|
"red hat enterprise linux server"|"rhel")
|
||||||
DISTRO="fedora" # Treat RHEL as Fedora for package management
|
DISTRO="fedora" # Treat RHEL as Fedora for package management
|
||||||
@@ -193,6 +218,7 @@ detect_distro() {
|
|||||||
# Parse and normalize version
|
# Parse and normalize version
|
||||||
DISTRO_VERSION=$(normalize_version "$detected_version")
|
DISTRO_VERSION=$(normalize_version "$detected_version")
|
||||||
DISTRO_CODENAME=$detected_codename
|
DISTRO_CODENAME=$detected_codename
|
||||||
|
DISTRO_NAME=${detected_pretty_name:-$DISTRO}
|
||||||
|
|
||||||
log_info "Distribution detected: $DISTRO $DISTRO_VERSION ($detection_method)"
|
log_info "Distribution detected: $DISTRO $DISTRO_VERSION ($detection_method)"
|
||||||
if [[ -n "$DISTRO_CODENAME" ]]; then
|
if [[ -n "$DISTRO_CODENAME" ]]; then
|
||||||
@@ -217,6 +243,7 @@ detect_distro() {
|
|||||||
export DISTRO DETECTED_DISTRO=$DISTRO
|
export DISTRO DETECTED_DISTRO=$DISTRO
|
||||||
export DISTRO_VERSION DETECTED_VERSION=$DISTRO_VERSION
|
export DISTRO_VERSION DETECTED_VERSION=$DISTRO_VERSION
|
||||||
export DISTRO_CODENAME DETECTED_CODENAME=$DISTRO_CODENAME
|
export DISTRO_CODENAME DETECTED_CODENAME=$DISTRO_CODENAME
|
||||||
|
export DISTRO_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
# System requirements check
|
# System requirements check
|
||||||
@@ -271,13 +298,18 @@ load_or_create_config() {
|
|||||||
if load_config; then
|
if load_config; then
|
||||||
log_info "Configuration loaded from ${CONFIG_FILE}"
|
log_info "Configuration loaded from ${CONFIG_FILE}"
|
||||||
if ! validate_config; then
|
if ! validate_config; then
|
||||||
log_error "Configuration validation failed"
|
log_warning "Configuration validation failed - creating new configuration"
|
||||||
exit 1
|
create_interactive_config
|
||||||
|
load_config # Reload the new configuration
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_info "Creating new configuration..."
|
log_info "Creating new configuration..."
|
||||||
create_interactive_config
|
create_interactive_config
|
||||||
|
load_config # Load the new configuration
|
||||||
fi
|
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() {
|
create_interactive_config() {
|
||||||
@@ -289,8 +321,20 @@ create_interactive_config() {
|
|||||||
save_config "SSH_PORT" "$ssh_port"
|
save_config "SSH_PORT" "$ssh_port"
|
||||||
|
|
||||||
# User configuration
|
# 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"
|
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
|
# Network configuration
|
||||||
if ask_yes_no "Configure static IP?" "n"; then
|
if ask_yes_no "Configure static IP?" "n"; then
|
||||||
@@ -300,12 +344,33 @@ create_interactive_config() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Service selection
|
# 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_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_NETDATA" "$(ask_yes_no "Install Netdata monitoring?" "y" && echo "true" || echo "false")"
|
||||||
|
|
||||||
|
# Docker-dependent services
|
||||||
|
if [[ "$install_docker" == "true" ]]; then
|
||||||
save_config "INSTALL_VAULTWARDEN" "$(ask_yes_no "Install Vaultwarden password manager?" "n" && echo "true" || echo "false")"
|
save_config "INSTALL_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_JELLYFIN" "$(ask_yes_no "Install Jellyfin media server?" "n" && echo "true" || echo "false")"
|
||||||
save_config "INSTALL_PORTAINER" "$(ask_yes_no "Install Portainer Docker management?" "n" && echo "true" || echo "false")"
|
save_config "INSTALL_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}"
|
log_success "Configuration created and saved to ${CONFIG_FILE}"
|
||||||
}
|
}
|
||||||
@@ -328,8 +393,9 @@ update_system() {
|
|||||||
sudo pacman -Syu --noconfirm --quiet
|
sudo pacman -Syu --noconfirm --quiet
|
||||||
;;
|
;;
|
||||||
opensuse)
|
opensuse)
|
||||||
sudo zypper refresh -q
|
sudo zypper refresh
|
||||||
sudo zypper update -y -q
|
show_progress 5 10 "System Update"
|
||||||
|
sudo zypper update -y
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -339,92 +405,189 @@ update_system() {
|
|||||||
|
|
||||||
# Main installation orchestrator
|
# Main installation orchestrator
|
||||||
run_installation() {
|
run_installation() {
|
||||||
local total_steps=12
|
local total_steps=13
|
||||||
local current_step=0
|
local current_step=0
|
||||||
|
|
||||||
log_info "Starting NAS installation process..."
|
log_info "Starting NAS installation process..."
|
||||||
|
|
||||||
# Core system setup
|
# 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
|
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
|
if [[ "${CONFIGURE_STATIC_IP:-false}" == "true" ]]; then
|
||||||
configure_network
|
configure_network
|
||||||
fi
|
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
|
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
|
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
|
configure_firewall
|
||||||
|
|
||||||
# Security setup
|
# 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
|
secure_shared_memory
|
||||||
install_fail2ban
|
install_fail2ban
|
||||||
|
# Configure automatic updates only if user opted in
|
||||||
|
if [[ "${ENABLE_AUTO_UPDATES:-false}" == "true" ]]; then
|
||||||
configure_automatic_updates
|
configure_automatic_updates
|
||||||
|
else
|
||||||
|
log_info "Automatic security updates are disabled by configuration"
|
||||||
|
fi
|
||||||
|
|
||||||
# Optional services
|
# Optional services
|
||||||
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
|
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
|
install_docker
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${INSTALL_NFS:-false}" == "true" ]]; then
|
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
|
install_nfs
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
|
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
|
install_netdata
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${INSTALL_VAULTWARDEN:-false}" == "true" ]]; then
|
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
|
install_vaultwarden
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
|
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
|
install_jellyfin
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
|
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
|
install_portainer
|
||||||
else
|
else
|
||||||
((current_step++))
|
current_step=$((current_step + 1))
|
||||||
fi
|
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
|
# Installation summary
|
||||||
show_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
|
echo
|
||||||
log_success "=== NAS Setup Completed Successfully ==="
|
log_success "=== NAS Setup Completed Successfully ==="
|
||||||
echo
|
echo
|
||||||
log_info "Installation Summary:"
|
log_info "Installation Summary:"
|
||||||
echo " ✓ System updated and secured"
|
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 " ✓ SSH configured on port ${SSH_PORT:-$DEFAULT_SSH_PORT}"
|
||||||
echo " ✓ Samba file sharing configured"
|
echo " ✓ Samba file sharing configured"
|
||||||
echo " ✓ Firewall configured and enabled"
|
echo " ✓ Firewall configured and enabled"
|
||||||
echo " ✓ Fail2ban installed and configured"
|
echo " ✓ Fail2ban installed and configured"
|
||||||
|
if [[ "${ENABLE_AUTO_UPDATES:-false}" == "true" ]]; then
|
||||||
echo " ✓ Automatic updates enabled"
|
echo " ✓ Automatic updates enabled"
|
||||||
|
else
|
||||||
|
echo " - Automatic updates not enabled (optional)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Service summary
|
# Service summary
|
||||||
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
|
if [[ "${INSTALL_DOCKER:-false}" == "true" ]]; then
|
||||||
@@ -434,27 +597,187 @@ show_installation_summary() {
|
|||||||
echo " ✓ NFS server installed"
|
echo " ✓ NFS server installed"
|
||||||
fi
|
fi
|
||||||
if [[ "${INSTALL_NETDATA:-false}" == "true" ]]; then
|
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
|
fi
|
||||||
if [[ "${INSTALL_JELLYFIN:-false}" == "true" ]]; then
|
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
|
fi
|
||||||
if [[ "${INSTALL_PORTAINER:-false}" == "true" ]]; then
|
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
|
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
|
echo
|
||||||
log_info "Next steps:"
|
log_info "Next steps:"
|
||||||
echo " 1. Reboot the system to ensure all changes take effect"
|
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 " 2. Access your NAS via SSH on port ${SSH_PORT:-$DEFAULT_SSH_PORT}"
|
||||||
echo " 3. Configure file shares through Samba"
|
echo " 3. Configure file shares through Samba"
|
||||||
|
case $DISTRO in
|
||||||
|
ubuntu|debian)
|
||||||
echo " 4. Review firewall rules with: sudo ufw status"
|
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
|
echo
|
||||||
log_warning "Important: Please save the following information:"
|
log_warning "Important: Please save the following information:"
|
||||||
echo " - SSH Port: ${SSH_PORT:-$DEFAULT_SSH_PORT}"
|
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 " - Configuration saved in: ${CONFIG_FILE}"
|
||||||
echo " - Installation log: ${LOG_FILE}"
|
echo " - Installation log: ${LOG_FILE}"
|
||||||
|
echo " - Services summary: ${summary_file}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main script execution
|
# Main script execution
|
||||||
@@ -481,6 +804,9 @@ main() {
|
|||||||
check_system_requirements
|
check_system_requirements
|
||||||
get_system_info
|
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
|
# Configuration
|
||||||
load_or_create_config
|
load_or_create_config
|
||||||
|
|
||||||
@@ -493,11 +819,18 @@ main() {
|
|||||||
|
|
||||||
# Main installation
|
# Main installation
|
||||||
log_info "Starting installation process..."
|
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
|
update_system
|
||||||
run_installation
|
run_installation
|
||||||
|
|
||||||
# Cleanup and summary
|
# Cleanup and summary
|
||||||
cleanup
|
cleanup
|
||||||
|
restart_all_services
|
||||||
optimize_nas_performance
|
optimize_nas_performance
|
||||||
perform_health_check
|
perform_health_check
|
||||||
show_installation_summary
|
show_installation_summary
|
||||||
|
|||||||
Reference in New Issue
Block a user