Files
hops/lib/system.sh
T
Stephen Klein 721f7d7a75 Release HOPS v3.1.0 with major security and architecture improvements
🆕 New Features:
- Encrypted secret management with AES-256 encryption
- Privilege separation (root vs user operations)
- Comprehensive input validation and sanitization
- Pinned container versions for security
- Modular architecture with shared libraries

🔒 Security Enhancements:
- Encrypted .env file storage with master key management
- Input validation preventing injection attacks
- Secure password generation with complexity requirements
- Enhanced file permissions and ownership handling
- Security auditing capabilities

🏗️ Architecture Improvements:
- Shared library structure (lib/) for common functions
- Enhanced error handling with detailed context
- Improved service definitions with validation
- Standardized logging and UI components
- Better code organization and maintainability

📝 New Components:
- hops_install.sh: New secure installation wrapper
- hops_privileged_setup.sh: Root-only operations
- hops_user_operations.sh: User operations without sudo
- hops_service_definitions_improved.sh: Enhanced service generation
- lib/: Shared libraries for common functionality
- CLAUDE.md: Complete development documentation

🔧 User Experience:
- Multiple installation methods (new secure, manual, legacy)
- Better error messages and troubleshooting guidance
- Improved service management commands
- Enhanced documentation and help system

This release maintains backward compatibility while adding enterprise-grade security features.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 07:02:43 -04:00

318 lines
8.5 KiB
Bash

#!/bin/bash
# HOPS - System Validation Functions
# Functions for system checks, OS detection, and requirements validation
# Version: 3.1.0
# Source common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
# Global variables for system info
OS_NAME=""
OS_VERSION=""
OS_NAME_LOWER=""
# Detect operating system
detect_os() {
info "🔍 Detecting operating system..."
if command_exists lsb_release; then
OS_NAME=$(lsb_release -is)
OS_VERSION=$(lsb_release -rs)
elif [[ -f /etc/os-release ]]; then
OS_NAME=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
OS_VERSION=$(grep '^VERSION_ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
else
error_exit "Unable to detect operating system"
fi
OS_NAME_LOWER=$(echo "$OS_NAME" | tr '[:upper:]' '[:lower:]')
# Validate supported OS
case "$OS_NAME_LOWER" in
ubuntu|debian|linuxmint|mint)
success "Detected supported OS: $OS_NAME $OS_VERSION"
;;
*)
error_exit "Unsupported OS: $OS_NAME $OS_VERSION. Only Ubuntu/Debian/Linux Mint are supported."
;;
esac
}
# Check system requirements
check_system_requirements() {
local min_ram_gb=${1:-2}
local min_disk_gb=${2:-10}
local target_dir="${3:-/}"
info "🔍 Checking system requirements..."
# Check architecture
local arch=$(uname -m)
if [[ "$arch" != "x86_64" ]]; then
error_exit "Unsupported architecture: $arch. Only x86_64 is supported."
fi
# Check RAM
local ram_gb
if command_exists free; then
ram_gb=$(free -g | awk '/^Mem:/{print $2}')
else
ram_gb=$(awk '/MemTotal/ {print int($2/1024/1024)}' /proc/meminfo)
fi
if [[ $ram_gb -lt $min_ram_gb ]]; then
error_exit "Insufficient RAM: ${ram_gb}GB detected, ${min_ram_gb}GB required"
fi
# Check disk space
local disk_avail_gb
if command_exists df; then
disk_avail_gb=$(df -BG --output=avail "$target_dir" | tail -n 1 | tr -d 'G')
else
error_exit "Unable to check disk space - 'df' command not available"
fi
if [[ $disk_avail_gb -lt $min_disk_gb ]]; then
error_exit "Insufficient disk space: ${disk_avail_gb}GB available in $target_dir, ${min_disk_gb}GB required"
fi
# Check CPU cores
local cpu_cores=$(nproc)
if [[ $cpu_cores -lt 2 ]]; then
warning "Only ${cpu_cores} CPU core(s) detected. 2+ cores recommended for optimal performance."
fi
success "System requirements met: ${ram_gb}GB RAM, ${disk_avail_gb}GB disk space, ${cpu_cores} CPU cores"
}
# Check if running in a container
check_container_environment() {
if [[ -f /.dockerenv ]] || grep -q 'container=docker' /proc/1/environ 2>/dev/null; then
warning "Running inside a container. Some features may not work correctly."
return 0
fi
return 1
}
# Check internet connectivity
check_internet() {
local test_urls=(
"google.com"
"github.com"
"docker.com"
)
info "🌐 Checking internet connectivity..."
for url in "${test_urls[@]}"; do
if ping -c 1 -W 5 "$url" >/dev/null 2>&1; then
success "Internet connectivity verified"
return 0
fi
done
error_exit "No internet connectivity detected. Please check your network connection."
}
# Check Docker requirements
check_docker_requirements() {
info "🐳 Checking Docker requirements..."
# Check if Docker is installed
if ! command_exists docker; then
warning "Docker not installed. Will be installed automatically."
return 1
fi
# Check if Docker daemon is running
if ! docker info >/dev/null 2>&1; then
warning "Docker daemon not running. Will be started automatically."
return 1
fi
# Check Docker version
local docker_version=$(docker --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
local min_version="20.10.0"
if ! version_compare "$docker_version" "$min_version"; then
error_exit "Docker version $docker_version is too old. Minimum required: $min_version"
fi
# Check Docker Compose
if ! docker compose version >/dev/null 2>&1; then
error_exit "Docker Compose not available. Please install Docker Compose v2+"
fi
success "Docker requirements met"
return 0
}
# Compare version strings (returns 0 if version1 >= version2)
version_compare() {
local version1="$1"
local version2="$2"
# Convert versions to arrays
local IFS='.'
local -a ver1=($version1)
local -a ver2=($version2)
# Compare each component
for i in {0..2}; do
local v1=${ver1[$i]:-0}
local v2=${ver2[$i]:-0}
if [[ $v1 -gt $v2 ]]; then
return 0
elif [[ $v1 -lt $v2 ]]; then
return 1
fi
done
return 0
}
# Check if user has sudo privileges
check_sudo() {
if [[ $EUID -eq 0 ]]; then
return 0
fi
if ! sudo -n true 2>/dev/null; then
error_exit "This script requires sudo privileges. Please run with sudo or as root."
fi
return 0
}
# Get system timezone
get_system_timezone() {
if [[ -f /etc/timezone ]]; then
cat /etc/timezone
elif [[ -L /etc/localtime ]]; then
readlink /etc/localtime | sed 's|/usr/share/zoneinfo/||'
else
timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC"
fi
}
# Validate timezone
validate_timezone() {
local timezone="$1"
if [[ -z "$timezone" ]]; then
return 1
fi
if [[ -f "/usr/share/zoneinfo/$timezone" ]]; then
return 0
fi
return 1
}
# Check available storage space for specific path
check_storage_space() {
local path="$1"
local required_gb="$2"
# Create directory if it doesn't exist
mkdir -p "$path" 2>/dev/null || true
local available_gb=$(df -BG --output=avail "$path" | tail -n 1 | tr -d 'G')
if [[ $available_gb -lt $required_gb ]]; then
error_exit "Insufficient storage space in $path: ${available_gb}GB available, ${required_gb}GB required"
fi
success "Storage space check passed: ${available_gb}GB available in $path"
}
# Check if directory is writable
check_directory_writable() {
local dir="$1"
# Try to create directory if it doesn't exist
if ! mkdir -p "$dir" 2>/dev/null; then
error_exit "Cannot create directory: $dir"
fi
# Check if writable
if ! [[ -w "$dir" ]]; then
error_exit "Directory not writable: $dir"
fi
return 0
}
# Get current user info (handles sudo correctly)
get_user_info() {
local -A user_info
if [[ -n "$SUDO_USER" ]]; then
user_info["username"]="$SUDO_USER"
user_info["uid"]=$(id -u "$SUDO_USER")
user_info["gid"]=$(id -g "$SUDO_USER")
user_info["home"]=$(eval echo "~$SUDO_USER")
else
user_info["username"]="$USER"
user_info["uid"]=$(id -u)
user_info["gid"]=$(id -g)
user_info["home"]="$HOME"
fi
# Return as key=value pairs
for key in "${!user_info[@]}"; do
echo "${key}=${user_info[$key]}"
done
}
# Check if firewall is available and configured
check_firewall() {
info "🔥 Checking firewall status..."
if command_exists ufw; then
local ufw_status=$(ufw status | head -n1 | awk '{print $2}')
case "$ufw_status" in
"active")
success "UFW firewall is active"
return 0
;;
"inactive")
warning "UFW firewall is inactive. Will be configured automatically."
return 1
;;
*)
warning "UFW firewall status unknown: $ufw_status"
return 1
;;
esac
else
warning "UFW not installed. Will be installed automatically."
return 1
fi
}
# Comprehensive system check
run_system_checks() {
local min_ram_gb=${1:-2}
local min_disk_gb=${2:-10}
local target_dir="${3:-/}"
info "🔍 Running comprehensive system checks..."
check_root
detect_os
check_system_requirements "$min_ram_gb" "$min_disk_gb" "$target_dir"
check_internet
check_docker_requirements
check_firewall
# Check for container environment (warning only)
check_container_environment
success "All system checks passed"
}