#!/bin/bash ################################################################################ # 服务器自动化加固脚本 - 多发行版增强版 # 作者: Mercas # 日期: 2025-11-09 # 版本: v4.2.3 Multi-Distro Enhanced (sudo权限修复版) # # 支持的发行版: # - Ubuntu/Debian (apt包管理器) # - Arch Linux/Manjaro (pacman + AUR支持) # # 新增功能: # - 防火墙: ufw / iptables / nftables 多选一 # - 入侵防护: fail2ban / sshguard / crowdsec 多选一 # - AUR助手: 自动安装yay或paru # - SSH密钥: ED25519 (替代RSA 4096) # # 命令行参数: # -y, --yes 跳过所有确认提示 (非交互模式) # -h, --help 显示帮助信息 ################################################################################ # ============================================================================ # 命令行参数解析 # ============================================================================ FORCE_YES=false while [[ $# -gt 0 ]]; do case $1 in -y|--yes) FORCE_YES=true shift ;; -h|--help) echo "用法: $0 [选项]" echo "选项:" echo " -y, --yes 跳过所有确认提示 (非交互模式)" echo " -h, --help 显示此帮助信息" exit 0 ;; *) echo "未知参数: $1" echo "使用 -h 查看帮助" exit 1 ;; esac done # ============================================================================ # 全局配置 # ============================================================================ LOG_FILE="/var/log/server_hardening_$(date +%Y%m%d_%H%M%S).log" BACKUP_DIR="/root/security_backup_$(date +%Y%m%d_%H%M%S)" ROLLBACK_DIR="/root/security_rollback_$(date +%Y%m%d_%H%M%S)" REPORT_FILE="/root/security_hardening_report_$(date +%Y%m%d_%H%M%S).txt" # 错误级别 ERROR_CRITICAL=1 ERROR_SEVERE=2 ERROR_WARNING=3 ERROR_INFO=4 # ============================================================================ # 依赖检查 # ============================================================================ check_dependencies() { log_section "检查脚本依赖" local missing_deps=() local core_commands=("bash" "grep" "sed" "awk" "cat" "echo" "chmod" "chown" "cp" "mv" "rm" "mkdir" "date" "sleep" "tail" "head" "find") local distro_commands=() case $PACKAGE_MANAGER in apt) distro_commands=("apt" "dpkg" "systemctl") ;; pacman) distro_commands=("pacman" "systemctl") ;; esac # 检查核心命令 log_info "检查核心命令..." for cmd in "${core_commands[@]}"; do if ! command -v "$cmd" &> /dev/null; then missing_deps+=("$cmd (core)") log_warning "缺少核心命令: $cmd" fi done # 检查发行版相关命令 log_info "检查发行版相关命令..." for cmd in "${distro_commands[@]}"; do if ! command -v "$cmd" &> /dev/null; then missing_deps+=("$cmd (distro)") log_warning "缺少发行版命令: $cmd" fi done # 检查可选但重要的命令 local optional_commands=("curl" "wget" "git" "sha256sum" "openssl") log_info "检查可选命令..." for cmd in "${optional_commands[@]}"; do if ! command -v "$cmd" &> /dev/null; then log_info "可选命令未安装: $cmd (某些功能可能受限)" fi done # 报告结果 if [[ ${#missing_deps[@]} -gt 0 ]]; then log_error "缺少必要的依赖: ${missing_deps[*]}" log_info "尝试安装缺失的包..." case $PACKAGE_MANAGER in apt) apt update -y 2>&1 | tee -a "$LOG_FILE" apt install -y coreutils systemd 2>&1 | tee -a "$LOG_FILE" || true ;; pacman) pacman -Sy 2>&1 | tee -a "$LOG_FILE" pacman -S --needed coreutils systemd 2>&1 | tee -a "$LOG_FILE" || true ;; esac # 重新检查 for cmd in "${core_commands[@]}" "${distro_commands[@]}"; do if ! command -v "$cmd" &> /dev/null; then handle_error $ERROR_SEVERE "命令 $cmd 仍然不可用,脚本无法继续执行" 1 $LINENO fi done fi # 检查网络工具 if ! command -v ss &> /dev/null && ! command -v netstat &> /dev/null && ! command -v ip &> /dev/null; then log_warning "网络工具(ss/netstat/ip)都不可用,端口检查功能将受限" fi # 检查SSH if ! command -v sshd &> /dev/null && ! command -v ssh-keygen &> /dev/null; then log_warning "OpenSSH未安装,SSH加固功能将受限" fi log_success "依赖检查完成" } # ============================================================================ # 发行版检测 # ============================================================================ detect_distribution() { if [[ -f /etc/os-release ]]; then . /etc/os-release DISTRO_ID="${ID}" DISTRO_NAME="${NAME}" DISTRO_VERSION="${VERSION_ID}" elif [[ -f /etc/lsb-release ]]; then . /etc/lsb-release DISTRO_ID="${DISTRIB_ID}" DISTRO_NAME="${DISTRIB_DESCRIPTION}" DISTRO_VERSION="${DISTRIB_RELEASE}" else DISTRO_ID="unknown" DISTRO_NAME="Unknown Linux" DISTRO_VERSION="unknown" fi case "${DISTRO_ID}" in ubuntu) DISTRO_FAMILY="debian" PACKAGE_MANAGER="apt" ;; debian) DISTRO_FAMILY="debian" PACKAGE_MANAGER="apt" ;; arch|manjaro|antergos) DISTRO_FAMILY="arch" PACKAGE_MANAGER="pacman" ;; *) if command -v pacman &> /dev/null; then DISTRO_FAMILY="arch" PACKAGE_MANAGER="pacman" else DISTRO_FAMILY="debian" PACKAGE_MANAGER="apt" fi ;; esac echo "检测到发行版: $DISTRO_NAME ($DISTRO_FAMILY)" } # ============================================================================ # 颜色定义 # ============================================================================ setup_colors() { if command -v tput &> /dev/null && [[ -n "$TERM" ]]; then RED=$(tput setaf 1 2>/dev/null || echo '\033[0;31m') GREEN=$(tput setaf 2 2>/dev/null || echo '\033[0;32m') YELLOW=$(tput setaf 3 2>/dev/null || echo '\033[1;33m') BLUE=$(tput setaf 4 2>/dev/null || echo '\033[0;34m') CYAN=$(tput setaf 6 2>/dev/null || echo '\033[0;36m') NC=$(tput sgr0 2>/dev/null || echo '\033[0m') else RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' fi } # ============================================================================ # 日志函数 # ============================================================================ log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE" } log_info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE" } log_error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE" } log_section() { echo -e "\n${CYAN}========================================${NC}" | tee -a "$LOG_FILE" echo -e "${CYAN}$1${NC}" | tee -a "$LOG_FILE" echo -e "${CYAN}========================================${NC}\n" | tee -a "$LOG_FILE" } log_rollback() { echo -e "${YELLOW}[ROLLBACK]${NC} $1" | tee -a "$LOG_FILE" } # ============================================================================ # 核心工具函数 # ============================================================================ check_root() { if [[ $EUID -ne 0 ]]; then log_error "此脚本必须以root权限运行" exit 1 fi } handle_error() { local error_level=$1 local message=$2 local exit_code=${3:-1} local lineno=${4:-0} case $error_level in $ERROR_CRITICAL) log_error "[致命错误] $message (行号: $lineno, 退出码: $exit_code)" exit $exit_code ;; $ERROR_SEVERE) log_error "[严重错误] $message (行号: $lineno, 退出码: $exit_code)" if confirm_action "发生严重错误,是否回滚所有更改并退出?"; then execute_rollback exit $exit_code fi ;; $ERROR_WARNING) log_warning "[警告] $message" ;; $ERROR_INFO) log_info "[信息] $message" ;; esac } backup_file() { local file=$1 local description=${2:-"备份"} if [[ -f "$file" ]]; then mkdir -p "$BACKUP_DIR" local backup_path="$BACKUP_DIR/$(basename "$file").bak" local checksum_file="$backup_path.sha256" cp -p "$file" "$backup_path" if command -v sha256sum &> /dev/null; then # 检测是否支持--short参数,兼容不同版本的sha256sum local checksum_value if sha256sum --help 2>&1 | grep -q "short"; then checksum_value=$(sha256sum --short "$backup_path" 2>/dev/null | cut -d' ' -f1) else checksum_value=$(sha256sum "$backup_path" 2>/dev/null | cut -d' ' -f1) fi sha256sum "$backup_path" > "$checksum_file" log_info "已备份: $(basename "$file") (SHA256: ${checksum_value:0:16}...)" else log_info "已备份: $(basename "$file")" fi echo "$file:$backup_path:$(date +%s)" >> "$ROLLBACK_DIR/rollback_list.txt" fi } init_rollback() { mkdir -p "$ROLLBACK_DIR" touch "$ROLLBACK_DIR/rollback_list.txt" touch "$ROLLBACK_DIR/actions.log" log_info "回滚机制已初始化: $ROLLBACK_DIR" } execute_rollback() { log_rollback "开始执行回滚操作..." if [[ ! -f "$ROLLBACK_DIR/rollback_list.txt" ]]; then log_warning "没有回滚记录可执行" return 0 fi while IFS=: read -r original backup timestamp; do if [[ -f "$backup" ]] && [[ -f "$original" ]]; then cp "$backup" "$original" log_rollback "已还原: $original" fi done < "$ROLLBACK_DIR/rollback_list.txt" log_success "回滚操作完成" } record_rollback_action() { local action_type=$1 local target=$2 local command=$3 local timestamp=$(date +%s) echo "$timestamp:$action_type:$target:$command" >> "$ROLLBACK_DIR/actions.log" } # 安全获取主机名 (处理hostname命令不存在的情况) get_hostname() { if command -v hostname &> /dev/null; then hostname elif [[ -f /etc/hostname ]]; then cat /etc/hostname elif [[ -f /proc/sys/kernel/hostname ]]; then cat /proc/sys/kernel/hostname else uname -n fi } # 安全获取IP地址 get_ip_address() { if command -v hostname &> /dev/null; then local ip=$(hostname -I 2>/dev/null | awk '{print $1}') if [[ -n "$ip" ]]; then echo "$ip" elif command -v ip &> /dev/null; then ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A' else echo 'N/A' fi elif command -v ip &> /dev/null; then ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A' elif [[ -f /proc/net/dev ]]; then grep -oP 'inet \K[\d.]+' /proc/net/fib_trie 2>/dev/null | head -1 || echo 'N/A' else echo 'N/A' fi } confirm_action() { local message=$1 # 如果强制确认标志为真,直接返回成功 if [[ "$FORCE_YES" == "true" ]]; then log_info "[自动确认] $message" return 0 fi if [[ -t 0 ]]; then echo -e "${YELLOW}${message} [y/N]: ${NC}\c" read -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then return 1 fi return 0 else log_info "[非交互模式跳过] $message" return 1 fi } check_port_available() { local port=$1 local service_name=$2 # 检查端口是否被监听 if ss -tunlp 2>/dev/null | grep -q ":$port "; then local process_info=$(ss -tunlp 2>/dev/null | grep ":$port " | head -1) log_warning "端口 $port 已被占用" log_info "占用信息: $process_info" # 显示哪个进程占用了端口 local pid=$(echo "$process_info" | grep -oP 'pid=\K[0-9]+' || echo "unknown") if [[ "$pid" != "unknown" ]] && [[ -f "/proc/$pid/comm" ]]; then local process_name=$(cat /proc/$pid/comm 2>/dev/null || echo "unknown") log_info "占用进程: $process_name (PID: $pid)" fi return 1 # 返回失败,让调用者处理重新输入 fi # 检查sshd_config中是否已有该端口配置 if [[ -f "/etc/ssh/sshd_config" ]]; then if grep -q "^Port $port" /etc/ssh/sshd_config; then log_info "端口 $port 已在SSH配置中定义" fi fi return 0 } # ============================================================================ # 包管理器抽象层 # ============================================================================ update_package_list() { case $PACKAGE_MANAGER in apt) apt update -y 2>&1 | tee -a "$LOG_FILE" ;; pacman) pacman -Sy 2>&1 | tee -a "$LOG_FILE" ;; esac } install_package() { local package=$1 local description=${2:-"安装软件包"} log_info "安装: $package ($description)" case $PACKAGE_MANAGER in apt) if dpkg -l 2>/dev/null | grep -q "^ii $package "; then log_info "$package 已安装" return 0 fi apt install -y "$package" 2>&1 | tee -a "$LOG_FILE" return ${PIPESTATUS[0]} ;; pacman) if pacman -Qq "$package" &>/dev/null; then log_info "$package 已安装" return 0 fi pacman -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE" return ${PIPESTATUS[0]} ;; esac } is_package_installed() { local package=$1 case $PACKAGE_MANAGER in apt) dpkg -l 2>/dev/null | grep -q "^ii $package" ;; pacman) pacman -Qq "$package" &>/dev/null ;; esac } enable_service() { local service=$1 if command -v systemctl &> /dev/null; then systemctl enable "$service" 2>&1 | tee -a "$LOG_FILE" systemctl start "$service" 2>&1 | tee -a "$LOG_FILE" fi } disable_service() { local service=$1 if command -v systemctl &> /dev/null; then systemctl stop "$service" 2>&1 | tee -a "$LOG_FILE" || true systemctl disable "$service" 2>&1 | tee -a "$LOG_FILE" || true fi } # ============================================================================ # AUR助手安装 # ============================================================================ install_aur_helper() { log_section "安装AUR助手" if ! command -v git &> /dev/null; then log_info "安装git..." pacman -S --noconfirm git 2>&1 | tee -a "$LOG_FILE" fi if command -v yay &> /dev/null || command -v paru &> /dev/null; then log_info "AUR助手已安装" return 0 fi echo -e "${YELLOW}请选择AUR助手:${NC}" echo "1) yay (更流行)" echo "2) paru (功能更丰富)" echo "3) 跳过安装" echo -e "${YELLOW}选择 [1-3]: ${NC}\c" read -p "" aur_choice local aur_helper="" local aur_repo="" case $aur_choice in 1) aur_helper="yay" aur_repo="https://aur.archlinux.org/yay.git" ;; 2) aur_helper="paru" aur_repo="https://aur.archlinux.org/paru.git" ;; 3) log_info "跳过AUR助手安装" return 0 ;; *) log_warning "无效选择" return 1 ;; esac # 确保base-devel已安装 log_info "安装构建依赖..." pacman -S --needed --noconfirm base-devel 2>&1 | tee -a "$LOG_FILE" # 克隆并安装AUR助手 local temp_dir=$(mktemp -d) cd "$temp_dir" log_info "从AUR安装 $aur_helper..." git clone "$aur_repo" 2>&1 | tee -a "$LOG_FILE" cd "$aur_helper" if makepkg -si --noconfirm 2>&1 | tee -a "$LOG_FILE"; then log_success "$aur_helper 安装成功" # 记录回滚命令 record_rollback_action "aur_helper" "$aur_helper" "pacman -Rns $aur_helper" else log_error "$aur_helper 安装失败" rm -rf "$temp_dir" return 1 fi rm -rf "$temp_dir" # 验证安装 if command -v $aur_helper &> /dev/null; then log_success "AUR助手 $aur_helper 已就绪" fi } install_aur_package() { local package=$1 local description=${2:-"AUR包"} # 优先使用pacman if is_package_installed "$package"; then log_info "$package 已安装" return 0 fi # 尝试pacman if pacman -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE"; then return 0 fi # 使用AUR助手 if command -v yay &> /dev/null; then log_info "使用yay安装 $package..." yay -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE" elif command -v paru &> /dev/null; then log_info "使用paru安装 $package..." paru -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE" else log_warning "无AUR助手可用,跳过 $package 安装" return 1 fi } # ============================================================================ # 密码策略配置 # ============================================================================ configure_password_policy() { log_info "配置密码策略..." case $PACKAGE_MANAGER in apt) if ! is_package_installed "libpam-pwquality"; then install_package "libpam-pwquality" "密码质量检查" fi local pwquality_file="/etc/security/pwquality.conf" backup_file "$pwquality_file" if ! grep -q "^minlen = 12" "$pwquality_file" 2>/dev/null; then cat >> "$pwquality_file" << 'EOF' # 密码安全策略 minlen = 12 dcredit = -1 ucredit = -1 lcredit = -1 ocredit = -1 maxrepeat = 3 EOF fi ;; pacman) if ! is_package_installed "libpwquality"; then install_package "libpwquality" "密码质量检查库" fi local login_defs="/etc/login.defs" if [[ -f "$login_defs" ]]; then backup_file "$login_defs" sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' "$login_defs" 2>/dev/null || true sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' "$login_defs" 2>/dev/null || true sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 14/' "$login_defs" 2>/dev/null || true fi ;; esac log_success "密码策略配置完成" } # ============================================================================ # 防火墙配置 (支持 ufw / iptables / nftables) # ============================================================================ configure_firewall() { log_section "配置防火墙" local ssh_port=22 # 检测SSH端口 if [[ -f "/etc/ssh/sshd_config" ]]; then ssh_port=$(grep -E "^Port [0-9]+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' | head -1) ssh_port=${ssh_port:-22} fi echo -e "${YELLOW}请选择防火墙方案:${NC}" echo "1) UFW (简单防火墙) - 推荐Ubuntu/新手" echo "2) iptables (传统防火墙) - 通用选择" echo "3) nftables (下一代防火墙) - Arch推荐,更高效" echo "4) 跳过防火墙配置" echo -e "${YELLOW}选择 [1-4]: ${NC}\c" read -p "" firewall_choice case $firewall_choice in 1) configure_ufw "$ssh_port" ;; 2) configure_iptables "$ssh_port" ;; 3) configure_nftables "$ssh_port" ;; 4) log_info "跳过防火墙配置" ;; *) log_warning "无效选择,使用默认iptables" configure_iptables "$ssh_port" ;; esac } configure_ufw() { local ssh_port=$1 log_info "配置UFW防火墙..." if ! is_package_installed "ufw"; then install_package "ufw" "简单防火墙" fi ufw default deny incoming 2>&1 | tee -a "$LOG_FILE" ufw default allow outgoing 2>&1 | tee -a "$LOG_FILE" if [[ -t 0 ]]; then echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c" read -p "" ssh_port_input ssh_port=${ssh_port_input:-$ssh_port} fi ufw allow "$ssh_port/tcp" comment 'SSH' 2>&1 | tee -a "$LOG_FILE" if confirm_action "是否开放HTTP(80)端口?"; then ufw allow 80/tcp comment 'HTTP' 2>&1 | tee -a "$LOG_FILE" fi if confirm_action "是否开放HTTPS(443)端口?"; then ufw allow 443/tcp comment 'HTTPS' 2>&1 | tee -a "$LOG_FILE" fi if [[ -t 0 ]]; then echo "y" | ufw enable 2>&1 | tee -a "$LOG_FILE" else ufw --force enable 2>&1 | tee -a "$LOG_FILE" fi ufw status numbered 2>/dev/null | tee -a "$LOG_FILE" record_rollback_action "firewall" "ufw" "ufw reset" log "UFW防火墙配置完成" } configure_iptables() { local ssh_port=$1 log_info "配置iptables防火墙..." if ! command -v iptables &> /dev/null; then install_package "iptables" "防火墙工具" fi if command -v systemctl &> /dev/null; then systemctl enable iptables 2>/dev/null || true systemctl start iptables 2>/dev/null || true fi # 刷新现有规则 iptables -F 2>&1 | tee -a "$LOG_FILE" || true iptables -X 2>&1 | tee -a "$LOG_FILE" || true # 设置默认策略 iptables -P INPUT DROP 2>&1 | tee -a "$LOG_FILE" iptables -P FORWARD DROP 2>&1 | tee -a "$LOG_FILE" iptables -P OUTPUT ACCEPT 2>&1 | tee -a "$LOG_FILE" if [[ -t 0 ]]; then echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c" read -p "" ssh_port_input ssh_port=${ssh_port_input:-$ssh_port} fi # 允许SSH iptables -A INPUT -p tcp --dport "$ssh_port" -j ACCEPT 2>&1 | tee -a "$LOG_FILE" # 允许回环 iptables -A INPUT -i lo -j ACCEPT 2>&1 | tee -a "$LOG_FILE" iptables -A OUTPUT -o lo -j ACCEPT 2>&1 | tee -a "$LOG_FILE" # 允许已建立的连接 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>&1 | tee -a "$LOG_FILE" if confirm_action "是否开放HTTP(80)端口?"; then iptables -A INPUT -p tcp --dport 80 -j ACCEPT 2>&1 | tee -a "$LOG_FILE" fi if confirm_action "是否开放HTTPS(443)端口?"; then iptables -A INPUT -p tcp --dport 443 -j ACCEPT 2>&1 | tee -a "$LOG_FILE" fi # 保存规则 mkdir -p /etc/iptables iptables-save > /etc/iptables/iptables.rules 2>&1 | tee -a "$LOG_FILE" iptables -L -n 2>/dev/null | tee -a "$LOG_FILE" record_rollback_action "firewall" "iptables" "iptables -F" log "iptables防火墙配置完成" } configure_nftables() { local ssh_port=$1 log_info "配置nftables防火墙..." if ! command -v nft &> /dev/null; then install_package "nftables" "下一代防火墙" fi # 确保服务已启用 if command -v systemctl &> /dev/null; then systemctl enable nftables 2>&1 | tee -a "$LOG_FILE" systemctl start nftables 2>&1 | tee -a "$LOG_FILE" fi # 备份现有配置 if [[ -f /etc/nftables.conf ]]; then backup_file "/etc/nftables.conf" fi if [[ -t 0 ]]; then echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c" read -p "" ssh_port_input ssh_port=${ssh_port_input:-$ssh_port} fi # 创建nftables配置 cat > /etc/nftables.conf << EOF #!/usr/bin/nft -f # 刷新所有规则 flush ruleset # 定义表 table inet filter { # 链定义 chain input { type filter hook input priority 0; policy drop; # 允许回环 iif lo accept # 允许已建立的连接 ct state established,related accept # SSH tcp dport $ssh_port accept # HTTP/HTTPS (可选) # tcp dport {80, 443} accept # 拒绝其他入站 reject with icmpx type administratively-prohibited } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; } } EOF log_info "SSH端口: $ssh_port" if confirm_action "是否开放HTTP(80)和HTTPS(443)端口?"; then sed -i 's|# tcp dport {80, 443} accept|tcp dport {80, 443} accept|' /etc/nftables.conf log_info "已添加HTTP/HTTPS规则" fi # 测试并加载配置 nft -f /etc/nftables.conf 2>&1 | tee -a "$LOG_FILE" if [[ $? -eq 0 ]]; then log_success "nftables配置已应用" else log_error "nftables配置失败" return 1 fi # 显示状态 nft list ruleset 2>&1 | tee -a "$LOG_FILE" record_rollback_action "firewall" "nftables" "nft flush ruleset" log "nftables防火墙配置完成" } # ============================================================================ # 入侵防御系统 (支持 fail2ban / sshguard / crowdsec) # ============================================================================ install_intrusion_prevention() { log_section "配置入侵防御系统" echo -e "${YELLOW}请选择入侵防御方案:${NC}" echo "1) Fail2ban (功能丰富,支持多服务)" echo "2) Sshguard (轻量级,专注SSH)" echo "3) Crowdsec (社区驱动,AI增强)" echo "4) 跳过安装" echo -e "${YELLOW}选择 [1-4]: ${NC}\c" read -p "" ips_choice case $ips_choice in 1) install_fail2ban ;; 2) install_sshguard ;; 3) install_crowdsec ;; 4) log_info "跳过入侵防御系统安装" ;; *) log_warning "无效选择" ;; esac } install_fail2ban() { log_info "安装Fail2ban..." case $PACKAGE_MANAGER in apt) install_package "fail2ban" "入侵防御" ;; pacman) install_package "fail2ban" "入侵防御" ;; esac if command -v fail2ban-client &> /dev/null; then configure_fail2ban fi } configure_fail2ban() { log_info "配置Fail2ban..." local jail_local="/etc/fail2ban/jail.local" backup_file "$jail_local" 2>/dev/null || true cat > "$jail_local" << 'EOF' [DEFAULT] bantime = 3600 findtime = 600 maxretry = 5 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 7200 [sshd-ddos] enabled = true port = ssh filter = sshd-ddos logpath = /var/log/auth.log maxretry = 2 bantime = 7200 EOF enable_service "fail2ban" sleep 2 if command -v fail2ban-client &> /dev/null; then fail2ban-client status 2>/dev/null | tee -a "$LOG_FILE" fi log_success "Fail2ban配置完成" } install_sshguard() { log_info "安装Sshguard..." case $PACKAGE_MANAGER in apt) install_package "sshguard" "SSH入侵防御" ;; pacman) install_package "sshguard" "SSH入侵防御" ;; esac if command -v sshguard &> /dev/null; then configure_sshguard fi } configure_sshguard() { log_info "配置Sshguard..." if command -v systemctl &> /dev/null; then systemctl enable sshguard 2>&1 | tee -a "$LOG_FILE" systemctl start sshguard 2>&1 | tee -a "$LOG_FILE" fi log_success "Sshguard已启动" } install_crowdsec() { log_section "安装Crowdsec" log_info "Crowdsec - 社区驱动的入侵防御系统" echo -e "${YELLOW}特点:${NC}" echo "• 社区威胁情报共享" echo "• 轻量级,高性能" echo "• 自动学习正常行为" echo "• 支持多种防火墙集成" if ! confirm_action "是否安装Crowdsec?"; then log_info "跳过Crowdsec安装" return 0 fi case $PACKAGE_MANAGER in apt) # 使用官方安装脚本 log_info "从官方源安装Crowdsec..." curl -s https://install.crowdsec.net | sh 2>&1 | tee -a "$LOG_FILE" ;; pacman) # 从AUR安装 log_info "从AUR安装Crowdsec..." if ! is_package_installed "crowdsec"; then install_aur_package "crowdsec" "Crowdsec核心" fi if ! is_package_installed "crowdsec-firewall-bouncer"; then install_aur_package "crowdsec-firewall-bouncer" "防火墙联动" fi ;; esac if command -v cscli &> /dev/null; then configure_crowdsec fi } configure_crowdsec() { log_info "配置Crowdsec..." # 初始化 cscli console enable 2>&1 | tee -a "$LOG_FILE" # 获取推荐场景 log_info "安装推荐场景..." cscli scenarios list 2>&1 | tee -a "$LOG_FILE" | head -20 # 安装SSH防护 log_info "启用SSH防护..." cscli scenarios install crowdsecurity/ssh-bf 2>&1 | tee -a "$LOG_FILE" # 安装防火墙bouncer log_info "配置防火墙联动..." case $PACKAGE_MANAGER in apt) if is_package_installed "ufw"; then cscli bouncers add crowdsec-ufw-bouncer 2>&1 | tee -a "$LOG_FILE" elif command -v nft &> /dev/null; then cscli bouncers add crowdsec-nft-bouncer 2>&1 | tee -a "$LOG_FILE" fi ;; pacman) if command -v nft &> /dev/null; then cscli bouncers add crowdsec-nft-bouncer 2>&1 | tee -a "$LOG_FILE" fi ;; esac # 启动服务 enable_service "crowdsec" sleep 2 # 显示状态 cscli metrics 2>&1 | tee -a "$LOG_FILE" | head -20 log_success "Crowdsec配置完成" log_info "运行 'cscli dashboard setup' 可安装可视化面板" } # ============================================================================ # 自动安全更新 # ============================================================================ configure_auto_updates() { log_section "配置自动安全更新" case $PACKAGE_MANAGER in apt) if confirm_action "是否启用自动安全更新?"; then if ! is_package_installed "unattended-upgrades"; then install_package "unattended-upgrades" "自动更新" fi echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | debconf-set-selections 2>&1 | tee -a "$LOG_FILE" log "自动安全更新已启用" fi ;; pacman) log_warning "Arch Linux不推荐完全自动更新" if confirm_action "是否配置pacman更新通知?"; then local cron_file="/etc/cron.daily/check-updates" cat > "$cron_file" << 'EOF' #!/bin/bash pacman -Sy 2>/dev/null echo "系统检查完成" EOF chmod +x "$cron_file" log_info "已配置每日更新检查" fi ;; esac } # ============================================================================ # 安全审计工具 # ============================================================================ install_security_tools() { log_section "安装额外安全工具" if [[ "$PACKAGE_MANAGER" == "pacman" ]]; then # 确保AUR助手已安装 if ! command -v yay &> /dev/null && ! command -v paru &> /dev/null; then if confirm_action "是否安装AUR助手以获取更多安全工具?"; then install_aur_helper fi fi fi local tools=() case $PACKAGE_MANAGER in apt) tools=("auditd") echo -e "${YELLOW}注意: aide和rkhunter可通过apt安装${NC}" ;; pacman) tools=("auditd") echo -e "${YELLOW}提示: aide和rkhunter可从AUR安装${NC}" ;; esac for tool in "${tools[@]}"; do if confirm_action "是否安装 $tool?"; then if is_package_installed "$tool"; then log_info "$tool 已安装" continue fi install_package "$tool" "安全工具" enable_service "$tool" 2>/dev/null || true fi done # 额外的AUR选项 if [[ "$PACKAGE_MANAGER" == "pacman" ]]; then if confirm_action "是否从AUR安装 aide (文件完整性)?"; then install_aur_package "aide" "文件完整性检查" fi if confirm_action "是否从AUR安装 rkhunter (Rootkit检测)?"; then install_aur_package "rkhunter" "Rootkit检测" fi fi } # ============================================================================ # SSH密钥和用户管理 (使用ED25519) # ============================================================================ verify_ssh_key_exists() { local username=$1 if [[ -z "$username" ]]; then return 1 fi local user_home=$(eval echo "~$username") local ssh_dir="$user_home/.ssh" local authorized_keys="$ssh_dir/authorized_keys" if [[ -f "$authorized_keys" ]] && [[ -s "$authorized_keys" ]]; then local key_count=$(grep -c "ssh-" "$authorized_keys" 2>/dev/null || echo 0) if [[ $key_count -gt 0 ]]; then log_success "用户 $username 已有 $key_count 个SSH公钥" return 0 fi fi return 1 } setup_ssh_key() { local username=$1 local public_key=$2 local user_home=$(eval echo "~$username") local ssh_dir="$user_home/.ssh" local authorized_keys="$ssh_dir/authorized_keys" mkdir -p "$ssh_dir" echo "$public_key" > "$authorized_keys" record_rollback_action "ssh_key" "$username" "rm -f $authorized_keys" log_success "SSH公钥已设置" } # 优化:使用ED25519替代RSA setup_generate_key() { local username=$1 local user_home=$(eval echo "~$username") local ssh_dir="$user_home/.ssh" local key_file="$ssh_dir/id_ed25519" mkdir -p "$ssh_dir" chown "$username:$username" "$ssh_dir" if [[ ! -f "$key_file" ]]; then log_info "生成ED25519 SSH密钥对..." log_info "ED25519: 比RSA更安全更快,256位即可达到4096位RSA的安全性" record_rollback_action "ssh_keygen" "$username" "rm -f $key_file $key_file.pub" # 使用ED25519生成密钥 su - "$username" -c "ssh-keygen -t ed25519 -f '$key_file' -C '$username@$(get_hostname)'" if [[ -f "$key_file" ]]; then log_success "ED25519密钥对生成成功" log_info "私钥文件: $key_file" log_info "公钥文件: $key_file.pub" log_warning "请妥善保管私钥文件" echo -e "${YELLOW}公钥内容 (添加到authorized_keys):${NC}" cat "$key_file.pub" echo "" else log_error "SSH密钥生成失败,回退到RSA..." # 回退到RSA local rsa_key_file="$ssh_dir/id_rsa" su - "$username" -c "ssh-keygen -t rsa -b 4096 -f '$rsa_key_file' -N '' -C '$username@$(get_hostname)'" if [[ -f "$rsa_key_file" ]]; then log_success "RSA密钥对生成成功" log_info "密钥文件: $rsa_key_file" fi return 1 fi else log_info "SSH密钥已存在 ($key_file)" fi } configure_sudo_settings() { local username=$1 log_info "配置sudo权限..." # 确保sudoers.d目录存在 mkdir -p /etc/sudoers.d chmod 750 /etc/sudoers.d local sudo_option="" local sudoers_file="/etc/sudoers.d/$username" if [[ -t 0 ]]; then echo -e "${YELLOW}请选择sudo权限级别:${NC}" echo "1) 完全无密码sudo" echo "2) 需要密码sudo (推荐)" echo "3) 仅特定命令无密码" echo "4) 不配置" echo -e "${YELLOW}选择 [2-4] (直接回车默认2): ${NC}\c" read -p "" sudo_option # 如果直接回车,使用默认值2 [[ -z "$sudo_option" ]] && sudo_option="2" else # 非交互模式默认使用选项2(需要密码的sudo) sudo_option="2" fi case $sudo_option in 1) echo "$username ALL=(ALL) NOPASSWD:ALL" > "$sudoers_file" chmod 440 "$sudoers_file" record_rollback_action "sudoers" "$username" "rm -f $sudoers_file" log_warning "已配置无密码sudo (安全性较低)" ;; 2) echo "$username ALL=(ALL) ALL" > "$sudoers_file" chmod 440 "$sudoers_file" record_rollback_action "sudoers" "$username" "rm -f $sudoers_file" log_success "已配置需要密码的sudo" ;; 3) echo -e "${YELLOW}输入允许无密码执行的命令: ${NC}\c" read -p "" sudo_commands if [[ -n "$sudo_commands" ]]; then echo "$username ALL=(ALL) NOPASSWD: $sudo_commands" > "$sudoers_file" chmod 440 "$sudoers_file" record_rollback_action "sudoers" "$username" "rm -f $sudoers_file" log_success "已配置限制性sudo" fi ;; *) log_info "跳过sudo配置" ;; esac } manage_users() { log_section "创建管理用户和配置SSH密钥" if ! confirm_action "是否创建专用管理用户?"; then log_warning "跳过用户创建" return 0 fi local username="" if [[ -t 0 ]]; then echo -e "${YELLOW}请输入新用户名: ${NC}\c" read -p "" username else username="admin" fi if [[ -z "$username" ]] || [[ ! "$username" =~ ^[a-z_][a-z0-9_-]*$ ]]; then handle_error $ERROR_SEVERE "无效的用户名: $username" return 1 fi # 检查用户是否存在 if id "$username" &>/dev/null 2>&1; then log_info "用户 $username 已存在" else log_info "创建用户: $username" useradd -m -s /bin/bash "$username" 2>&1 | tee -a "$LOG_FILE" # 密码配置选项 - 默认使用密钥认证增强安全性 local password_option="2" if [[ -t 0 ]]; then echo -e "${YELLOW}选择认证方式:${NC}" echo "1) 设置密码 (密码+密钥)" echo "2) 仅SSH密钥 (无密码,推荐)" echo "3) 跳过,稍后手动设置" echo -e "${YELLOW}选择 [直接回车默认2]: ${NC}\c" read -p "" password_option # 如果用户直接回车或输入无效选项,使用默认的选项2(仅SSH密钥) [[ -z "$password_option" ]] && password_option="2" fi case $password_option in 1) echo -e "${YELLOW}为用户 $username 设置密码: ${NC}\c" passwd "$username" log_info "已设置密码" ;; 2) log_info "使用密钥认证,跳过密码设置" # 生成随机密码锁定账户(增强安全性) local temp_password=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 16) echo "$username:$temp_password" | chpasswd 2>/dev/null || true log_info "账户已锁定,仅允许密钥登录" ;; 3) log_info "跳过密码设置" ;; *) log_warning "无效选择,跳过密码设置" ;; esac case $PACKAGE_MANAGER in apt) usermod -aG sudo "$username" 2>&1 | tee -a "$LOG_FILE" record_rollback_action "usermod" "$username" "gpasswd -d $username sudo" ;; pacman) # 确保sudo软件包已安装 if ! is_package_installed "sudo"; then log_info "安装sudo软件包..." pacman -S --noconfirm sudo 2>&1 | tee -a "$LOG_FILE" fi usermod -aG wheel "$username" 2>&1 | tee -a "$LOG_FILE" record_rollback_action "usermod" "$username" "gpasswd -d $username wheel" # 确保/etc/sudoers.d目录存在 mkdir -p /etc/sudoers.d chmod 750 /etc/sudoers.d # 配置wheel组权限 if [[ -f "/etc/sudoers" ]]; then backup_file "/etc/sudoers" fi # 确保wheel组有sudo权限 if ! grep -q "^%wheel ALL=" /etc/sudoers 2>/dev/null; then echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers fi ;; esac fi log_info "配置SSH密钥..." local key_configured=false if [[ -t 0 ]]; then echo -e "${YELLOW}选择密钥配置方式: ${NC}" echo "1) 现有公钥" echo "2) 生成ED25519密钥 (推荐)" echo "3) 跳过" echo -e "${YELLOW}选择 [1-3]: ${NC}\c" read -p "" key_option else # 非交互模式自动生成ED25519密钥 key_option="2" fi case $key_option in 1) echo -e "${YELLOW}输入SSH公钥: ${NC}\c" read -p "" public_key if [[ -n "$public_key" ]] && [[ "$public_key" =~ ^ssh- ]]; then setup_ssh_key "$username" "$public_key" key_configured=true fi ;; 2) setup_generate_key "$username" key_configured=true ;; *) log_info "跳过密钥配置" ;; esac configure_sudo_settings "$username" local user_home=$(eval echo "~$username") local ssh_dir="$user_home/.ssh" if [[ -d "$ssh_dir" ]]; then chown -R "$username:$username" "$ssh_dir" 2>&1 | tee -a "$LOG_FILE" chmod 700 "$ssh_dir" 2>&1 | tee -a "$LOG_FILE" chmod 600 "$ssh_dir/authorized_keys" 2>&1 | tee -a "$LOG_FILE" || true fi if id "$username" &>/dev/null; then log_success "用户 $username 配置成功" fi if $key_configured; then return 0 else return 1 fi } # ============================================================================ # SSH安全加固 # ============================================================================ validate_ssh_config() { local ssh_config="/etc/ssh/sshd_config" if [[ ! -f "$ssh_config" ]]; then log_error "SSH配置文件不存在" return 1 fi log_info "验证SSH配置语法..." if command -v sshd &> /dev/null; then if sshd -t 2>&1; then log_success "SSH配置语法验证通过" return 0 else log_error "SSH配置语法验证失败" return 1 fi fi return 0 } harden_ssh() { log_section "SSH安全加固" local ssh_config="/etc/ssh/sshd_config" local ssh_port=22 local key_configured=false if [[ ! -f "$ssh_config" ]]; then handle_error $ERROR_SEVERE "SSH配置文件不存在" fi backup_file "$ssh_config" ssh_port=$(grep -E "^Port [0-9]+" "$ssh_config" 2>/dev/null | awk '{print $2}' | head -1) ssh_port=${ssh_port:-22} log_info "步骤1/3: 配置用户和SSH密钥..." if manage_users; then key_configured=true fi log_info "步骤2/3: 配置安全参数..." # 端口配置(带验证和重新输入功能) if [[ -t 0 ]]; then local port_valid=false local max_attempts=3 local attempt=0 while [[ $port_valid == false ]] && [[ $attempt -lt $max_attempts ]]; do ((attempt++)) echo -e "${YELLOW}修改SSH端口($ssh_port)? 输入新端口(留空使用当前端口): ${NC}\c" read -p "" new_port if [[ -z "$new_port" ]]; then # 使用当前端口 port_valid=true log_info "使用默认端口: $ssh_port" elif [[ ! "$new_port" =~ ^[0-9]+$ ]] || [[ "$new_port" -le 1024 ]] || [[ "$new_port" -ge 65536 ]]; then log_warning "无效端口号,请输入1024-65535之间的数字 (剩余$((max_attempts - attempt))次尝试)" else # 检查端口是否可用 if ! check_port_available "$new_port" "SSH"; then log_warning "端口 $new_port 已被占用,请选择其他端口 (剩余$((max_attempts - attempt))次尝试)" else port_valid=true ssh_port=$new_port log_success "端口 $new_port 可用" log_warning "请确保防火墙已开放端口 $new_port" fi fi done if [[ $port_valid == false ]]; then log_warning "端口验证失败次数过多,使用默认端口: $ssh_port" fi fi if confirm_action "是否禁用SSH root登录?"; then sed -i "s/^#*PermitRootLogin.*/PermitRootLogin no/" "$ssh_config" record_rollback_action "ssh_config" "PermitRootLogin" "sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' $ssh_config" fi if $key_configured; then if confirm_action "SSH密钥已配置,是否禁用密码认证?"; then sed -i "s/^#*PasswordAuthentication.*/PasswordAuthentication no/" "$ssh_config" record_rollback_action "ssh_config" "PasswordAuthentication" "sed -i 's/^PasswordAuthentication.*/PasswordAuthentication yes/' $ssh_config" fi else log_warning "SSH密钥未配置,跳过密码认证禁用" fi # 添加安全配置 cat >> "$ssh_config" << EOF # 安全加固配置 Protocol 2 MaxAuthTries 3 MaxSessions 2 LoginGraceTime 60 ClientAliveInterval 300 ClientAliveCountMax 2 PermitEmptyPasswords no X11Forwarding no UseDNS no EOF log_info "步骤3/3: 验证并应用配置..." if ! validate_ssh_config; then handle_error $ERROR_SEVERE "SSH配置验证失败" execute_rollback return 1 fi if systemctl restart sshd 2>&1 | tee -a "$LOG_FILE"; then log_success "SSH服务已重启 (端口: $ssh_port)" else handle_error $ERROR_SEVERE "SSH服务重启失败" return 1 fi log "SSH加固完成" } # ============================================================================ # 系统加固 # ============================================================================ harden_users() { log_section "用户和权限加固" configure_password_policy log_info "锁定系统账户..." local system_users=("bin" "daemon" "adm" "lp" "sync" "shutdown" "halt" "mail" "news" "uucp" "operator" "games" "gopher" "ftp") for user in "${system_users[@]}"; do if id "$user" &>/dev/null 2>&1; then usermod -L -s /usr/sbin/nologin "$user" 2>/dev/null || true fi done log "用户加固完成" } disable_services() { log_section "禁用不必要的服务" local services_to_disable=("avahi-daemon" "cups" "bluetooth") for service in "${services_to_disable[@]}"; do if command -v systemctl &> /dev/null; then if systemctl is-enabled "$service" &>/dev/null 2>&1; then if confirm_action "是否禁用服务: $service?"; then disable_service "$service" record_rollback_action "service" "$service" "systemctl enable $service; systemctl start $service" log_info "已禁用: $service" fi fi fi done } harden_kernel() { log_section "内核参数安全配置" local sysctl_conf="/etc/sysctl.d/99-security.conf" backup_file "$sysctl_conf" 2>/dev/null || true log_info "配置内核安全参数..." cat > "$sysctl_conf" << 'EOF' # IP转发禁用 net.ipv4.ip_forward = 0 net.ipv6.conf.all.forwarding = 0 # SYN cookies保护 net.ipv4.tcp_syncookies = 1 # 忽略ICMP重定向 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0 # 禁用源路由 net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # 记录可疑包 net.ipv4.conf.all.log_martians = 1 net.ipv4.conf.default.log_martians = 1 # 忽略ICMP ping请求 net.ipv4.icmp_echo_ignore_broadcasts = 1 # 反向路径过滤 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # SYN flood保护 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 5 EOF if command -v sysctl &> /dev/null; then sysctl -p "$sysctl_conf" 2>&1 | tee -a "$LOG_FILE" || log_warning "应用内核参数失败" fi log "内核参数配置完成" } harden_filesystem() { log_section "文件系统权限加固" log_info "设置重要文件权限..." local files_to_protect=( "/etc/ssh/sshd_config:600" "/etc/passwd:644" "/etc/shadow:640" "/etc/group:644" "/etc/gshadow:600" ) for file_perm in "${files_to_protect[@]}"; do local file="${file_perm%:*}" local perm="${file_perm#*:}" if [[ -f "$file" ]]; then chmod "$perm" "$file" 2>&1 | tee -a "$LOG_FILE" record_rollback_action "chmod" "$file" "chmod 644 $file" fi done log_info "SUID/SGID文件检查..." if command -v find &> /dev/null; then find / -perm /6000 -type f 2>/dev/null >> "$LOG_FILE" || true fi } collect_system_info() { log_section "收集系统信息" log_info "操作系统: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown")" log_info "内核版本: $(uname -r)" log_info "主机名: $(get_hostname)" log_info "IP地址: $(get_ip_address)" } update_system() { log_section "系统更新" if confirm_action "是否执行系统更新?"; then case $PACKAGE_MANAGER in apt) apt update -y 2>&1 | tee -a "$LOG_FILE" apt upgrade -y 2>&1 | tee -a "$LOG_FILE" apt autoremove -y 2>&1 | tee -a "$LOG_FILE" ;; pacman) pacman -Syu --noconfirm 2>&1 | tee -a "$LOG_FILE" pacman -Rns $(pacman -Qdtq) --noconfirm 2>/dev/null || true ;; esac log "系统更新完成" else log_warning "跳过系统更新" fi } generate_report() { log_section "生成安全加固报告" local os_info=$(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown") local kernel_info=$(uname -r) local hostname_info=$(get_hostname) cat > "$REPORT_FILE" << EOF ================================================================================ 服务器安全加固报告 ================================================================================ 生成时间: $(date) 主机名: $hostname_info 操作系统: $os_info 内核版本: $kernel_info 包管理器: $PACKAGE_MANAGER 备份目录: $BACKUP_DIR 回滚目录: $ROLLBACK_DIR 日志文件: $LOG_FILE -------------------------------------------------------------------------------- 1. SSH配置 -------------------------------------------------------------------------------- $(grep -E "^(Port|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 2>/dev/null || echo "N/A") -------------------------------------------------------------------------------- 2. 防火墙状态 -------------------------------------------------------------------------------- $(case $PACKAGE_MANAGER in apt) ufw status verbose 2>/dev/null ;; pacman) if command -v nft &> /dev/null; then nft list ruleset 2>/dev/null; else iptables -L -n 2>/dev/null; fi esac || echo "N/A") -------------------------------------------------------------------------------- 3. 入侵防御系统 -------------------------------------------------------------------------------- $(command -v fail2ban-client &> /dev/null && fail2ban-client status 2>/dev/null || echo "N/A") -------------------------------------------------------------------------------- 4. 已安装安全工具 -------------------------------------------------------------------------------- $(case $PACKAGE_MANAGER in apt) dpkg -l 2>/dev/null | grep -E "fail2ban|ufw|auditd" | awk '{print $2 " " $3}' ;; pacman) pacman -Q 2>/dev/null | grep -E "fail2ban|crowdsec|nftables|auditd" | head -10 ;; esac || echo "N/A") -------------------------------------------------------------------------------- 5. AUR助手状态 -------------------------------------------------------------------------------- $(command -v yay &> /dev/null && echo "yay: 已安装" || echo "yay: 未安装") $(command -v paru &> /dev/null && echo "paru: 已安装" || echo "paru: 未安装") ================================================================================ EOF if command -v sha256sum &> /dev/null; then sha256sum "$REPORT_FILE" > "${REPORT_FILE}.sha256" fi log_info "报告已生成: $REPORT_FILE" } # ============================================================================ # 主程序 # ============================================================================ main() { clear echo -e "${CYAN}" cat << "EOF" ╔══════════════════════════════════════════════════════════════╗ ║ 服务器安全加固脚本 (多发行版增强版) ║ ║ v4.2.1 - 支持 Ubuntu + Arch Linux ║ ║ • 多防火墙选择 (UFW/iptables/nftables) ║ ║ • 多入侵防护 (Fail2ban/Sshguard/Crowdsec) ║ ║ • AUR助手自动安装 (yay/paru) ║ ║ • ED25519 SSH密钥 (替代RSA) ║ ║ • 支持 -y 参数用于非交互模式 ║ ║ • 依赖自动检查 ║ ║ • 支持密钥优先认证 (无需密码) ║ ╚══════════════════════════════════════════════════════════════╝ EOF echo -e "${NC}" check_root detect_distribution check_dependencies setup_colors init_rollback log "开始服务器安全加固..." log_info "日志: $LOG_FILE | 备份: $BACKUP_DIR" echo -e "${YELLOW}警告: 此脚本将修改系统配置${NC}" if ! confirm_action "确认继续?"; then if [[ "$FORCE_YES" == "true" ]]; then log_info "自动模式: 继续执行加固操作" else log "用户取消" exit 0 fi fi collect_system_info update_system harden_ssh configure_firewall install_intrusion_prevention harden_users disable_services harden_kernel harden_filesystem configure_auto_updates install_security_tools generate_report log_section "安全加固完成" echo -e "${GREEN}" cat << EOF ╔══════════════════════════════════════════════════════════════╗ ║ 安全加固已完成! ║ ╚══════════════════════════════════════════════════════════════╝ 重要提醒: • 备份: $BACKUP_DIR • 回滚: $ROLLBACK_DIR • 日志: $LOG_FILE • 报告: $REPORT_FILE • 建议重启服务器 下一步: • 测试SSH登录 • 检查防火墙规则 • 配置入侵防御 EOF echo -e "${NC}" if confirm_action "是否重启服务器?"; then log "5秒后重启..." sleep 5 reboot fi } main "$@"