Ultima attività 1 month ago

Revisione 62584875f6f34ff9740b1940586a5cc38ca86a83

arch_hardening_script.sh Raw
1#!/bin/bash
2
3################################################################################
4# 服务器自动化加固脚本 - 多发行版增强版
5# 作者: Mercas
6# 日期: 2025-11-09
7# 版本: v4.2.3 Multi-Distro Enhanced (sudo权限修复版)
8#
9# 支持的发行版:
10# - Ubuntu/Debian (apt包管理器)
11# - Arch Linux/Manjaro (pacman + AUR支持)
12#
13# 新增功能:
14# - 防火墙: ufw / iptables / nftables 多选一
15# - 入侵防护: fail2ban / sshguard / crowdsec 多选一
16# - AUR助手: 自动安装yay或paru
17# - SSH密钥: ED25519 (替代RSA 4096)
18#
19# 命令行参数:
20# -y, --yes 跳过所有确认提示 (非交互模式)
21# -h, --help 显示帮助信息
22################################################################################
23
24# ============================================================================
25# 命令行参数解析
26# ============================================================================
27
28FORCE_YES=false
29
30while [[ $# -gt 0 ]]; do
31 case $1 in
32 -y|--yes)
33 FORCE_YES=true
34 shift
35 ;;
36 -h|--help)
37 echo "用法: $0 [选项]"
38 echo "选项:"
39 echo " -y, --yes 跳过所有确认提示 (非交互模式)"
40 echo " -h, --help 显示此帮助信息"
41 exit 0
42 ;;
43 *)
44 echo "未知参数: $1"
45 echo "使用 -h 查看帮助"
46 exit 1
47 ;;
48 esac
49done
50
51# ============================================================================
52# 全局配置
53# ============================================================================
54
55LOG_FILE="/var/log/server_hardening_$(date +%Y%m%d_%H%M%S).log"
56BACKUP_DIR="/root/security_backup_$(date +%Y%m%d_%H%M%S)"
57ROLLBACK_DIR="/root/security_rollback_$(date +%Y%m%d_%H%M%S)"
58REPORT_FILE="/root/security_hardening_report_$(date +%Y%m%d_%H%M%S).txt"
59
60# 错误级别
61ERROR_CRITICAL=1
62ERROR_SEVERE=2
63ERROR_WARNING=3
64ERROR_INFO=4
65
66# ============================================================================
67# 依赖检查
68# ============================================================================
69
70check_dependencies() {
71 log_section "检查脚本依赖"
72
73 local missing_deps=()
74 local core_commands=("bash" "grep" "sed" "awk" "cat" "echo" "chmod" "chown" "cp" "mv" "rm" "mkdir" "date" "sleep" "tail" "head" "find")
75 local distro_commands=()
76
77 case $PACKAGE_MANAGER in
78 apt)
79 distro_commands=("apt" "dpkg" "systemctl")
80 ;;
81 pacman)
82 distro_commands=("pacman" "systemctl")
83 ;;
84 esac
85
86 # 检查核心命令
87 log_info "检查核心命令..."
88 for cmd in "${core_commands[@]}"; do
89 if ! command -v "$cmd" &> /dev/null; then
90 missing_deps+=("$cmd (core)")
91 log_warning "缺少核心命令: $cmd"
92 fi
93 done
94
95 # 检查发行版相关命令
96 log_info "检查发行版相关命令..."
97 for cmd in "${distro_commands[@]}"; do
98 if ! command -v "$cmd" &> /dev/null; then
99 missing_deps+=("$cmd (distro)")
100 log_warning "缺少发行版命令: $cmd"
101 fi
102 done
103
104 # 检查可选但重要的命令
105 local optional_commands=("curl" "wget" "git" "sha256sum" "openssl")
106 log_info "检查可选命令..."
107 for cmd in "${optional_commands[@]}"; do
108 if ! command -v "$cmd" &> /dev/null; then
109 log_info "可选命令未安装: $cmd (某些功能可能受限)"
110 fi
111 done
112
113 # 报告结果
114 if [[ ${#missing_deps[@]} -gt 0 ]]; then
115 log_error "缺少必要的依赖: ${missing_deps[*]}"
116 log_info "尝试安装缺失的包..."
117
118 case $PACKAGE_MANAGER in
119 apt)
120 apt update -y 2>&1 | tee -a "$LOG_FILE"
121 apt install -y coreutils systemd 2>&1 | tee -a "$LOG_FILE" || true
122 ;;
123 pacman)
124 pacman -Sy 2>&1 | tee -a "$LOG_FILE"
125 pacman -S --needed coreutils systemd 2>&1 | tee -a "$LOG_FILE" || true
126 ;;
127 esac
128
129 # 重新检查
130 for cmd in "${core_commands[@]}" "${distro_commands[@]}"; do
131 if ! command -v "$cmd" &> /dev/null; then
132 handle_error $ERROR_SEVERE "命令 $cmd 仍然不可用,脚本无法继续执行" 1 $LINENO
133 fi
134 done
135 fi
136
137 # 检查网络工具
138 if ! command -v ss &> /dev/null && ! command -v netstat &> /dev/null && ! command -v ip &> /dev/null; then
139 log_warning "网络工具(ss/netstat/ip)都不可用,端口检查功能将受限"
140 fi
141
142 # 检查SSH
143 if ! command -v sshd &> /dev/null && ! command -v ssh-keygen &> /dev/null; then
144 log_warning "OpenSSH未安装,SSH加固功能将受限"
145 fi
146
147 log_success "依赖检查完成"
148}
149
150# ============================================================================
151# 发行版检测
152# ============================================================================
153
154detect_distribution() {
155 if [[ -f /etc/os-release ]]; then
156 . /etc/os-release
157 DISTRO_ID="${ID}"
158 DISTRO_NAME="${NAME}"
159 DISTRO_VERSION="${VERSION_ID}"
160 elif [[ -f /etc/lsb-release ]]; then
161 . /etc/lsb-release
162 DISTRO_ID="${DISTRIB_ID}"
163 DISTRO_NAME="${DISTRIB_DESCRIPTION}"
164 DISTRO_VERSION="${DISTRIB_RELEASE}"
165 else
166 DISTRO_ID="unknown"
167 DISTRO_NAME="Unknown Linux"
168 DISTRO_VERSION="unknown"
169 fi
170
171 case "${DISTRO_ID}" in
172 ubuntu)
173 DISTRO_FAMILY="debian"
174 PACKAGE_MANAGER="apt"
175 ;;
176 debian)
177 DISTRO_FAMILY="debian"
178 PACKAGE_MANAGER="apt"
179 ;;
180 arch|manjaro|antergos)
181 DISTRO_FAMILY="arch"
182 PACKAGE_MANAGER="pacman"
183 ;;
184 *)
185 if command -v pacman &> /dev/null; then
186 DISTRO_FAMILY="arch"
187 PACKAGE_MANAGER="pacman"
188 else
189 DISTRO_FAMILY="debian"
190 PACKAGE_MANAGER="apt"
191 fi
192 ;;
193 esac
194
195 echo "检测到发行版: $DISTRO_NAME ($DISTRO_FAMILY)"
196}
197
198# ============================================================================
199# 颜色定义
200# ============================================================================
201
202setup_colors() {
203 if command -v tput &> /dev/null && [[ -n "$TERM" ]]; then
204 RED=$(tput setaf 1 2>/dev/null || echo '\033[0;31m')
205 GREEN=$(tput setaf 2 2>/dev/null || echo '\033[0;32m')
206 YELLOW=$(tput setaf 3 2>/dev/null || echo '\033[1;33m')
207 BLUE=$(tput setaf 4 2>/dev/null || echo '\033[0;34m')
208 CYAN=$(tput setaf 6 2>/dev/null || echo '\033[0;36m')
209 NC=$(tput sgr0 2>/dev/null || echo '\033[0m')
210 else
211 RED='\033[0;31m'
212 GREEN='\033[0;32m'
213 YELLOW='\033[1;33m'
214 BLUE='\033[0;34m'
215 CYAN='\033[0;36m'
216 NC='\033[0m'
217 fi
218}
219
220# ============================================================================
221# 日志函数
222# ============================================================================
223
224log() {
225 echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
226}
227
228log_info() {
229 echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
230}
231
232log_warning() {
233 echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
234}
235
236log_error() {
237 echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
238}
239
240log_success() {
241 echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
242}
243
244log_section() {
245 echo -e "\n${CYAN}========================================${NC}" | tee -a "$LOG_FILE"
246 echo -e "${CYAN}$1${NC}" | tee -a "$LOG_FILE"
247 echo -e "${CYAN}========================================${NC}\n" | tee -a "$LOG_FILE"
248}
249
250log_rollback() {
251 echo -e "${YELLOW}[ROLLBACK]${NC} $1" | tee -a "$LOG_FILE"
252}
253
254# ============================================================================
255# 核心工具函数
256# ============================================================================
257
258check_root() {
259 if [[ $EUID -ne 0 ]]; then
260 log_error "此脚本必须以root权限运行"
261 exit 1
262 fi
263}
264
265handle_error() {
266 local error_level=$1
267 local message=$2
268 local exit_code=${3:-1}
269 local lineno=${4:-0}
270
271 case $error_level in
272 $ERROR_CRITICAL)
273 log_error "[致命错误] $message (行号: $lineno, 退出码: $exit_code)"
274 exit $exit_code
275 ;;
276 $ERROR_SEVERE)
277 log_error "[严重错误] $message (行号: $lineno, 退出码: $exit_code)"
278 if confirm_action "发生严重错误,是否回滚所有更改并退出?"; then
279 execute_rollback
280 exit $exit_code
281 fi
282 ;;
283 $ERROR_WARNING)
284 log_warning "[警告] $message"
285 ;;
286 $ERROR_INFO)
287 log_info "[信息] $message"
288 ;;
289 esac
290}
291
292backup_file() {
293 local file=$1
294 local description=${2:-"备份"}
295
296 if [[ -f "$file" ]]; then
297 mkdir -p "$BACKUP_DIR"
298 local backup_path="$BACKUP_DIR/$(basename "$file").bak"
299 local checksum_file="$backup_path.sha256"
300
301 cp -p "$file" "$backup_path"
302
303 if command -v sha256sum &> /dev/null; then
304 # 检测是否支持--short参数,兼容不同版本的sha256sum
305 local checksum_value
306 if sha256sum --help 2>&1 | grep -q "short"; then
307 checksum_value=$(sha256sum --short "$backup_path" 2>/dev/null | cut -d' ' -f1)
308 else
309 checksum_value=$(sha256sum "$backup_path" 2>/dev/null | cut -d' ' -f1)
310 fi
311 sha256sum "$backup_path" > "$checksum_file"
312 log_info "已备份: $(basename "$file") (SHA256: ${checksum_value:0:16}...)"
313 else
314 log_info "已备份: $(basename "$file")"
315 fi
316
317 echo "$file:$backup_path:$(date +%s)" >> "$ROLLBACK_DIR/rollback_list.txt"
318 fi
319}
320
321init_rollback() {
322 mkdir -p "$ROLLBACK_DIR"
323 touch "$ROLLBACK_DIR/rollback_list.txt"
324 touch "$ROLLBACK_DIR/actions.log"
325 log_info "回滚机制已初始化: $ROLLBACK_DIR"
326}
327
328execute_rollback() {
329 log_rollback "开始执行回滚操作..."
330
331 if [[ ! -f "$ROLLBACK_DIR/rollback_list.txt" ]]; then
332 log_warning "没有回滚记录可执行"
333 return 0
334 fi
335
336 while IFS=: read -r original backup timestamp; do
337 if [[ -f "$backup" ]] && [[ -f "$original" ]]; then
338 cp "$backup" "$original"
339 log_rollback "已还原: $original"
340 fi
341 done < "$ROLLBACK_DIR/rollback_list.txt"
342
343 log_success "回滚操作完成"
344}
345
346record_rollback_action() {
347 local action_type=$1
348 local target=$2
349 local command=$3
350 local timestamp=$(date +%s)
351 echo "$timestamp:$action_type:$target:$command" >> "$ROLLBACK_DIR/actions.log"
352}
353
354# 安全获取主机名 (处理hostname命令不存在的情况)
355get_hostname() {
356 if command -v hostname &> /dev/null; then
357 hostname
358 elif [[ -f /etc/hostname ]]; then
359 cat /etc/hostname
360 elif [[ -f /proc/sys/kernel/hostname ]]; then
361 cat /proc/sys/kernel/hostname
362 else
363 uname -n
364 fi
365}
366
367# 安全获取IP地址
368get_ip_address() {
369 if command -v hostname &> /dev/null; then
370 local ip=$(hostname -I 2>/dev/null | awk '{print $1}')
371 if [[ -n "$ip" ]]; then
372 echo "$ip"
373 elif command -v ip &> /dev/null; then
374 ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A'
375 else
376 echo 'N/A'
377 fi
378 elif command -v ip &> /dev/null; then
379 ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A'
380 elif [[ -f /proc/net/dev ]]; then
381 grep -oP 'inet \K[\d.]+' /proc/net/fib_trie 2>/dev/null | head -1 || echo 'N/A'
382 else
383 echo 'N/A'
384 fi
385}
386
387confirm_action() {
388 local message=$1
389 # 如果强制确认标志为真,直接返回成功
390 if [[ "$FORCE_YES" == "true" ]]; then
391 log_info "[自动确认] $message"
392 return 0
393 fi
394 if [[ -t 0 ]]; then
395 echo -e "${YELLOW}${message} [y/N]: ${NC}\c"
396 read -n 1 -r
397 echo
398 if [[ ! $REPLY =~ ^[Yy]$ ]]; then
399 return 1
400 fi
401 return 0
402 else
403 log_info "[非交互模式跳过] $message"
404 return 1
405 fi
406}
407
408check_port_available() {
409 local port=$1
410 local service_name=$2
411
412 # 检查端口是否被监听
413 if ss -tunlp 2>/dev/null | grep -q ":$port "; then
414 local process_info=$(ss -tunlp 2>/dev/null | grep ":$port " | head -1)
415 log_warning "端口 $port 已被占用"
416 log_info "占用信息: $process_info"
417
418 # 显示哪个进程占用了端口
419 local pid=$(echo "$process_info" | grep -oP 'pid=\K[0-9]+' || echo "unknown")
420 if [[ "$pid" != "unknown" ]] && [[ -f "/proc/$pid/comm" ]]; then
421 local process_name=$(cat /proc/$pid/comm 2>/dev/null || echo "unknown")
422 log_info "占用进程: $process_name (PID: $pid)"
423 fi
424
425 return 1 # 返回失败,让调用者处理重新输入
426 fi
427
428 # 检查sshd_config中是否已有该端口配置
429 if [[ -f "/etc/ssh/sshd_config" ]]; then
430 if grep -q "^Port $port" /etc/ssh/sshd_config; then
431 log_info "端口 $port 已在SSH配置中定义"
432 fi
433 fi
434
435 return 0
436}
437
438# ============================================================================
439# 包管理器抽象层
440# ============================================================================
441
442update_package_list() {
443 case $PACKAGE_MANAGER in
444 apt)
445 apt update -y 2>&1 | tee -a "$LOG_FILE"
446 ;;
447 pacman)
448 pacman -Sy 2>&1 | tee -a "$LOG_FILE"
449 ;;
450 esac
451}
452
453install_package() {
454 local package=$1
455 local description=${2:-"安装软件包"}
456
457 log_info "安装: $package ($description)"
458
459 case $PACKAGE_MANAGER in
460 apt)
461 if dpkg -l 2>/dev/null | grep -q "^ii $package "; then
462 log_info "$package 已安装"
463 return 0
464 fi
465 apt install -y "$package" 2>&1 | tee -a "$LOG_FILE"
466 return ${PIPESTATUS[0]}
467 ;;
468 pacman)
469 if pacman -Qq "$package" &>/dev/null; then
470 log_info "$package 已安装"
471 return 0
472 fi
473 pacman -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE"
474 return ${PIPESTATUS[0]}
475 ;;
476 esac
477}
478
479is_package_installed() {
480 local package=$1
481
482 case $PACKAGE_MANAGER in
483 apt)
484 dpkg -l 2>/dev/null | grep -q "^ii $package"
485 ;;
486 pacman)
487 pacman -Qq "$package" &>/dev/null
488 ;;
489 esac
490}
491
492enable_service() {
493 local service=$1
494
495 if command -v systemctl &> /dev/null; then
496 systemctl enable "$service" 2>&1 | tee -a "$LOG_FILE"
497 systemctl start "$service" 2>&1 | tee -a "$LOG_FILE"
498 fi
499}
500
501disable_service() {
502 local service=$1
503
504 if command -v systemctl &> /dev/null; then
505 systemctl stop "$service" 2>&1 | tee -a "$LOG_FILE" || true
506 systemctl disable "$service" 2>&1 | tee -a "$LOG_FILE" || true
507 fi
508}
509
510# ============================================================================
511# AUR助手安装
512# ============================================================================
513
514install_aur_helper() {
515 log_section "安装AUR助手"
516
517 if ! command -v git &> /dev/null; then
518 log_info "安装git..."
519 pacman -S --noconfirm git 2>&1 | tee -a "$LOG_FILE"
520 fi
521
522 if command -v yay &> /dev/null || command -v paru &> /dev/null; then
523 log_info "AUR助手已安装"
524 return 0
525 fi
526
527 echo -e "${YELLOW}请选择AUR助手:${NC}"
528 echo "1) yay (更流行)"
529 echo "2) paru (功能更丰富)"
530 echo "3) 跳过安装"
531 echo -e "${YELLOW}选择 [1-3]: ${NC}\c"
532 read -p "" aur_choice
533
534 local aur_helper=""
535 local aur_repo=""
536
537 case $aur_choice in
538 1)
539 aur_helper="yay"
540 aur_repo="https://aur.archlinux.org/yay.git"
541 ;;
542 2)
543 aur_helper="paru"
544 aur_repo="https://aur.archlinux.org/paru.git"
545 ;;
546 3)
547 log_info "跳过AUR助手安装"
548 return 0
549 ;;
550 *)
551 log_warning "无效选择"
552 return 1
553 ;;
554 esac
555
556 # 确保base-devel已安装
557 log_info "安装构建依赖..."
558 pacman -S --needed --noconfirm base-devel 2>&1 | tee -a "$LOG_FILE"
559
560 # 克隆并安装AUR助手
561 local temp_dir=$(mktemp -d)
562 cd "$temp_dir"
563
564 log_info "从AUR安装 $aur_helper..."
565 git clone "$aur_repo" 2>&1 | tee -a "$LOG_FILE"
566 cd "$aur_helper"
567
568 if makepkg -si --noconfirm 2>&1 | tee -a "$LOG_FILE"; then
569 log_success "$aur_helper 安装成功"
570
571 # 记录回滚命令
572 record_rollback_action "aur_helper" "$aur_helper" "pacman -Rns $aur_helper"
573 else
574 log_error "$aur_helper 安装失败"
575 rm -rf "$temp_dir"
576 return 1
577 fi
578
579 rm -rf "$temp_dir"
580
581 # 验证安装
582 if command -v $aur_helper &> /dev/null; then
583 log_success "AUR助手 $aur_helper 已就绪"
584 fi
585}
586
587install_aur_package() {
588 local package=$1
589 local description=${2:-"AUR包"}
590
591 # 优先使用pacman
592 if is_package_installed "$package"; then
593 log_info "$package 已安装"
594 return 0
595 fi
596
597 # 尝试pacman
598 if pacman -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE"; then
599 return 0
600 fi
601
602 # 使用AUR助手
603 if command -v yay &> /dev/null; then
604 log_info "使用yay安装 $package..."
605 yay -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE"
606 elif command -v paru &> /dev/null; then
607 log_info "使用paru安装 $package..."
608 paru -S --noconfirm "$package" 2>&1 | tee -a "$LOG_FILE"
609 else
610 log_warning "无AUR助手可用,跳过 $package 安装"
611 return 1
612 fi
613}
614
615# ============================================================================
616# 密码策略配置
617# ============================================================================
618
619configure_password_policy() {
620 log_info "配置密码策略..."
621
622 case $PACKAGE_MANAGER in
623 apt)
624 if ! is_package_installed "libpam-pwquality"; then
625 install_package "libpam-pwquality" "密码质量检查"
626 fi
627
628 local pwquality_file="/etc/security/pwquality.conf"
629 backup_file "$pwquality_file"
630
631 if ! grep -q "^minlen = 12" "$pwquality_file" 2>/dev/null; then
632 cat >> "$pwquality_file" << 'EOF'
633
634# 密码安全策略
635minlen = 12
636dcredit = -1
637ucredit = -1
638lcredit = -1
639ocredit = -1
640maxrepeat = 3
641EOF
642 fi
643 ;;
644 pacman)
645 if ! is_package_installed "libpwquality"; then
646 install_package "libpwquality" "密码质量检查库"
647 fi
648
649 local login_defs="/etc/login.defs"
650 if [[ -f "$login_defs" ]]; then
651 backup_file "$login_defs"
652 sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' "$login_defs" 2>/dev/null || true
653 sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' "$login_defs" 2>/dev/null || true
654 sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 14/' "$login_defs" 2>/dev/null || true
655 fi
656 ;;
657 esac
658
659 log_success "密码策略配置完成"
660}
661
662# ============================================================================
663# 防火墙配置 (支持 ufw / iptables / nftables)
664# ============================================================================
665
666configure_firewall() {
667 log_section "配置防火墙"
668
669 local ssh_port=22
670
671 # 检测SSH端口
672 if [[ -f "/etc/ssh/sshd_config" ]]; then
673 ssh_port=$(grep -E "^Port [0-9]+" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}' | head -1)
674 ssh_port=${ssh_port:-22}
675 fi
676
677 echo -e "${YELLOW}请选择防火墙方案:${NC}"
678 echo "1) UFW (简单防火墙) - 推荐Ubuntu/新手"
679 echo "2) iptables (传统防火墙) - 通用选择"
680 echo "3) nftables (下一代防火墙) - Arch推荐,更高效"
681 echo "4) 跳过防火墙配置"
682 echo -e "${YELLOW}选择 [1-4]: ${NC}\c"
683 read -p "" firewall_choice
684
685 case $firewall_choice in
686 1)
687 configure_ufw "$ssh_port"
688 ;;
689 2)
690 configure_iptables "$ssh_port"
691 ;;
692 3)
693 configure_nftables "$ssh_port"
694 ;;
695 4)
696 log_info "跳过防火墙配置"
697 ;;
698 *)
699 log_warning "无效选择,使用默认iptables"
700 configure_iptables "$ssh_port"
701 ;;
702 esac
703}
704
705configure_ufw() {
706 local ssh_port=$1
707
708 log_info "配置UFW防火墙..."
709
710 if ! is_package_installed "ufw"; then
711 install_package "ufw" "简单防火墙"
712 fi
713
714 ufw default deny incoming 2>&1 | tee -a "$LOG_FILE"
715 ufw default allow outgoing 2>&1 | tee -a "$LOG_FILE"
716
717 if [[ -t 0 ]]; then
718 echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c"
719 read -p "" ssh_port_input
720 ssh_port=${ssh_port_input:-$ssh_port}
721 fi
722
723 ufw allow "$ssh_port/tcp" comment 'SSH' 2>&1 | tee -a "$LOG_FILE"
724
725 if confirm_action "是否开放HTTP(80)端口?"; then
726 ufw allow 80/tcp comment 'HTTP' 2>&1 | tee -a "$LOG_FILE"
727 fi
728
729 if confirm_action "是否开放HTTPS(443)端口?"; then
730 ufw allow 443/tcp comment 'HTTPS' 2>&1 | tee -a "$LOG_FILE"
731 fi
732
733 if [[ -t 0 ]]; then
734 echo "y" | ufw enable 2>&1 | tee -a "$LOG_FILE"
735 else
736 ufw --force enable 2>&1 | tee -a "$LOG_FILE"
737 fi
738
739 ufw status numbered 2>/dev/null | tee -a "$LOG_FILE"
740
741 record_rollback_action "firewall" "ufw" "ufw reset"
742 log "UFW防火墙配置完成"
743}
744
745configure_iptables() {
746 local ssh_port=$1
747
748 log_info "配置iptables防火墙..."
749
750 if ! command -v iptables &> /dev/null; then
751 install_package "iptables" "防火墙工具"
752 fi
753
754 if command -v systemctl &> /dev/null; then
755 systemctl enable iptables 2>/dev/null || true
756 systemctl start iptables 2>/dev/null || true
757 fi
758
759 # 刷新现有规则
760 iptables -F 2>&1 | tee -a "$LOG_FILE" || true
761 iptables -X 2>&1 | tee -a "$LOG_FILE" || true
762
763 # 设置默认策略
764 iptables -P INPUT DROP 2>&1 | tee -a "$LOG_FILE"
765 iptables -P FORWARD DROP 2>&1 | tee -a "$LOG_FILE"
766 iptables -P OUTPUT ACCEPT 2>&1 | tee -a "$LOG_FILE"
767
768 if [[ -t 0 ]]; then
769 echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c"
770 read -p "" ssh_port_input
771 ssh_port=${ssh_port_input:-$ssh_port}
772 fi
773
774 # 允许SSH
775 iptables -A INPUT -p tcp --dport "$ssh_port" -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
776
777 # 允许回环
778 iptables -A INPUT -i lo -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
779 iptables -A OUTPUT -o lo -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
780
781 # 允许已建立的连接
782 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
783
784 if confirm_action "是否开放HTTP(80)端口?"; then
785 iptables -A INPUT -p tcp --dport 80 -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
786 fi
787
788 if confirm_action "是否开放HTTPS(443)端口?"; then
789 iptables -A INPUT -p tcp --dport 443 -j ACCEPT 2>&1 | tee -a "$LOG_FILE"
790 fi
791
792 # 保存规则
793 mkdir -p /etc/iptables
794 iptables-save > /etc/iptables/iptables.rules 2>&1 | tee -a "$LOG_FILE"
795
796 iptables -L -n 2>/dev/null | tee -a "$LOG_FILE"
797
798 record_rollback_action "firewall" "iptables" "iptables -F"
799 log "iptables防火墙配置完成"
800}
801
802configure_nftables() {
803 local ssh_port=$1
804
805 log_info "配置nftables防火墙..."
806
807 if ! command -v nft &> /dev/null; then
808 install_package "nftables" "下一代防火墙"
809 fi
810
811 # 确保服务已启用
812 if command -v systemctl &> /dev/null; then
813 systemctl enable nftables 2>&1 | tee -a "$LOG_FILE"
814 systemctl start nftables 2>&1 | tee -a "$LOG_FILE"
815 fi
816
817 # 备份现有配置
818 if [[ -f /etc/nftables.conf ]]; then
819 backup_file "/etc/nftables.conf"
820 fi
821
822 if [[ -t 0 ]]; then
823 echo -e "${YELLOW}SSH端口 (默认$ssh_port): ${NC}\c"
824 read -p "" ssh_port_input
825 ssh_port=${ssh_port_input:-$ssh_port}
826 fi
827
828 # 创建nftables配置
829 cat > /etc/nftables.conf << EOF
830#!/usr/bin/nft -f
831
832# 刷新所有规则
833flush ruleset
834
835# 定义表
836table inet filter {
837 # 链定义
838 chain input {
839 type filter hook input priority 0; policy drop;
840
841 # 允许回环
842 iif lo accept
843
844 # 允许已建立的连接
845 ct state established,related accept
846
847 # SSH
848 tcp dport $ssh_port accept
849
850 # HTTP/HTTPS (可选)
851 # tcp dport {80, 443} accept
852
853 # 拒绝其他入站
854 reject with icmpx type administratively-prohibited
855 }
856
857 chain forward {
858 type filter hook forward priority 0; policy drop;
859 }
860
861 chain output {
862 type filter hook output priority 0; policy accept;
863 }
864}
865EOF
866
867 log_info "SSH端口: $ssh_port"
868
869 if confirm_action "是否开放HTTP(80)和HTTPS(443)端口?"; then
870 sed -i 's|# tcp dport {80, 443} accept|tcp dport {80, 443} accept|' /etc/nftables.conf
871 log_info "已添加HTTP/HTTPS规则"
872 fi
873
874 # 测试并加载配置
875 nft -f /etc/nftables.conf 2>&1 | tee -a "$LOG_FILE"
876
877 if [[ $? -eq 0 ]]; then
878 log_success "nftables配置已应用"
879 else
880 log_error "nftables配置失败"
881 return 1
882 fi
883
884 # 显示状态
885 nft list ruleset 2>&1 | tee -a "$LOG_FILE"
886
887 record_rollback_action "firewall" "nftables" "nft flush ruleset"
888 log "nftables防火墙配置完成"
889}
890
891# ============================================================================
892# 入侵防御系统 (支持 fail2ban / sshguard / crowdsec)
893# ============================================================================
894
895install_intrusion_prevention() {
896 log_section "配置入侵防御系统"
897
898 echo -e "${YELLOW}请选择入侵防御方案:${NC}"
899 echo "1) Fail2ban (功能丰富,支持多服务)"
900 echo "2) Sshguard (轻量级,专注SSH)"
901 echo "3) Crowdsec (社区驱动,AI增强)"
902 echo "4) 跳过安装"
903 echo -e "${YELLOW}选择 [1-4]: ${NC}\c"
904 read -p "" ips_choice
905
906 case $ips_choice in
907 1)
908 install_fail2ban
909 ;;
910 2)
911 install_sshguard
912 ;;
913 3)
914 install_crowdsec
915 ;;
916 4)
917 log_info "跳过入侵防御系统安装"
918 ;;
919 *)
920 log_warning "无效选择"
921 ;;
922 esac
923}
924
925install_fail2ban() {
926 log_info "安装Fail2ban..."
927
928 case $PACKAGE_MANAGER in
929 apt)
930 install_package "fail2ban" "入侵防御"
931 ;;
932 pacman)
933 install_package "fail2ban" "入侵防御"
934 ;;
935 esac
936
937 if command -v fail2ban-client &> /dev/null; then
938 configure_fail2ban
939 fi
940}
941
942configure_fail2ban() {
943 log_info "配置Fail2ban..."
944
945 local jail_local="/etc/fail2ban/jail.local"
946 backup_file "$jail_local" 2>/dev/null || true
947
948 cat > "$jail_local" << 'EOF'
949[DEFAULT]
950bantime = 3600
951findtime = 600
952maxretry = 5
953
954[sshd]
955enabled = true
956port = ssh
957filter = sshd
958logpath = /var/log/auth.log
959maxretry = 3
960bantime = 7200
961
962[sshd-ddos]
963enabled = true
964port = ssh
965filter = sshd-ddos
966logpath = /var/log/auth.log
967maxretry = 2
968bantime = 7200
969EOF
970
971 enable_service "fail2ban"
972 sleep 2
973
974 if command -v fail2ban-client &> /dev/null; then
975 fail2ban-client status 2>/dev/null | tee -a "$LOG_FILE"
976 fi
977
978 log_success "Fail2ban配置完成"
979}
980
981install_sshguard() {
982 log_info "安装Sshguard..."
983
984 case $PACKAGE_MANAGER in
985 apt)
986 install_package "sshguard" "SSH入侵防御"
987 ;;
988 pacman)
989 install_package "sshguard" "SSH入侵防御"
990 ;;
991 esac
992
993 if command -v sshguard &> /dev/null; then
994 configure_sshguard
995 fi
996}
997
998configure_sshguard() {
999 log_info "配置Sshguard..."
1000
1001 if command -v systemctl &> /dev/null; then
1002 systemctl enable sshguard 2>&1 | tee -a "$LOG_FILE"
1003 systemctl start sshguard 2>&1 | tee -a "$LOG_FILE"
1004 fi
1005
1006 log_success "Sshguard已启动"
1007}
1008
1009install_crowdsec() {
1010 log_section "安装Crowdsec"
1011
1012 log_info "Crowdsec - 社区驱动的入侵防御系统"
1013 echo -e "${YELLOW}特点:${NC}"
1014 echo "• 社区威胁情报共享"
1015 echo "• 轻量级,高性能"
1016 echo "• 自动学习正常行为"
1017 echo "• 支持多种防火墙集成"
1018
1019 if ! confirm_action "是否安装Crowdsec?"; then
1020 log_info "跳过Crowdsec安装"
1021 return 0
1022 fi
1023
1024 case $PACKAGE_MANAGER in
1025 apt)
1026 # 使用官方安装脚本
1027 log_info "从官方源安装Crowdsec..."
1028 curl -s https://install.crowdsec.net | sh 2>&1 | tee -a "$LOG_FILE"
1029 ;;
1030 pacman)
1031 # 从AUR安装
1032 log_info "从AUR安装Crowdsec..."
1033 if ! is_package_installed "crowdsec"; then
1034 install_aur_package "crowdsec" "Crowdsec核心"
1035 fi
1036 if ! is_package_installed "crowdsec-firewall-bouncer"; then
1037 install_aur_package "crowdsec-firewall-bouncer" "防火墙联动"
1038 fi
1039 ;;
1040 esac
1041
1042 if command -v cscli &> /dev/null; then
1043 configure_crowdsec
1044 fi
1045}
1046
1047configure_crowdsec() {
1048 log_info "配置Crowdsec..."
1049
1050 # 初始化
1051 cscli console enable 2>&1 | tee -a "$LOG_FILE"
1052
1053 # 获取推荐场景
1054 log_info "安装推荐场景..."
1055 cscli scenarios list 2>&1 | tee -a "$LOG_FILE" | head -20
1056
1057 # 安装SSH防护
1058 log_info "启用SSH防护..."
1059 cscli scenarios install crowdsecurity/ssh-bf 2>&1 | tee -a "$LOG_FILE"
1060
1061 # 安装防火墙bouncer
1062 log_info "配置防火墙联动..."
1063 case $PACKAGE_MANAGER in
1064 apt)
1065 if is_package_installed "ufw"; then
1066 cscli bouncers add crowdsec-ufw-bouncer 2>&1 | tee -a "$LOG_FILE"
1067 elif command -v nft &> /dev/null; then
1068 cscli bouncers add crowdsec-nft-bouncer 2>&1 | tee -a "$LOG_FILE"
1069 fi
1070 ;;
1071 pacman)
1072 if command -v nft &> /dev/null; then
1073 cscli bouncers add crowdsec-nft-bouncer 2>&1 | tee -a "$LOG_FILE"
1074 fi
1075 ;;
1076 esac
1077
1078 # 启动服务
1079 enable_service "crowdsec"
1080
1081 sleep 2
1082
1083 # 显示状态
1084 cscli metrics 2>&1 | tee -a "$LOG_FILE" | head -20
1085
1086 log_success "Crowdsec配置完成"
1087 log_info "运行 'cscli dashboard setup' 可安装可视化面板"
1088}
1089
1090# ============================================================================
1091# 自动安全更新
1092# ============================================================================
1093
1094configure_auto_updates() {
1095 log_section "配置自动安全更新"
1096
1097 case $PACKAGE_MANAGER in
1098 apt)
1099 if confirm_action "是否启用自动安全更新?"; then
1100 if ! is_package_installed "unattended-upgrades"; then
1101 install_package "unattended-upgrades" "自动更新"
1102 fi
1103 echo unattended-upgrades unattended-upgrades/enable_auto_updates boolean true | debconf-set-selections 2>&1 | tee -a "$LOG_FILE"
1104 log "自动安全更新已启用"
1105 fi
1106 ;;
1107 pacman)
1108 log_warning "Arch Linux不推荐完全自动更新"
1109 if confirm_action "是否配置pacman更新通知?"; then
1110 local cron_file="/etc/cron.daily/check-updates"
1111 cat > "$cron_file" << 'EOF'
1112#!/bin/bash
1113pacman -Sy 2>/dev/null
1114echo "系统检查完成"
1115EOF
1116 chmod +x "$cron_file"
1117 log_info "已配置每日更新检查"
1118 fi
1119 ;;
1120 esac
1121}
1122
1123# ============================================================================
1124# 安全审计工具
1125# ============================================================================
1126
1127install_security_tools() {
1128 log_section "安装额外安全工具"
1129
1130 if [[ "$PACKAGE_MANAGER" == "pacman" ]]; then
1131 # 确保AUR助手已安装
1132 if ! command -v yay &> /dev/null && ! command -v paru &> /dev/null; then
1133 if confirm_action "是否安装AUR助手以获取更多安全工具?"; then
1134 install_aur_helper
1135 fi
1136 fi
1137 fi
1138
1139 local tools=()
1140
1141 case $PACKAGE_MANAGER in
1142 apt)
1143 tools=("auditd")
1144 echo -e "${YELLOW}注意: aide和rkhunter可通过apt安装${NC}"
1145 ;;
1146 pacman)
1147 tools=("auditd")
1148 echo -e "${YELLOW}提示: aide和rkhunter可从AUR安装${NC}"
1149 ;;
1150 esac
1151
1152 for tool in "${tools[@]}"; do
1153 if confirm_action "是否安装 $tool"; then
1154 if is_package_installed "$tool"; then
1155 log_info "$tool 已安装"
1156 continue
1157 fi
1158 install_package "$tool" "安全工具"
1159 enable_service "$tool" 2>/dev/null || true
1160 fi
1161 done
1162
1163 # 额外的AUR选项
1164 if [[ "$PACKAGE_MANAGER" == "pacman" ]]; then
1165 if confirm_action "是否从AUR安装 aide (文件完整性)?"; then
1166 install_aur_package "aide" "文件完整性检查"
1167 fi
1168 if confirm_action "是否从AUR安装 rkhunter (Rootkit检测)?"; then
1169 install_aur_package "rkhunter" "Rootkit检测"
1170 fi
1171 fi
1172}
1173
1174# ============================================================================
1175# SSH密钥和用户管理 (使用ED25519)
1176# ============================================================================
1177
1178verify_ssh_key_exists() {
1179 local username=$1
1180
1181 if [[ -z "$username" ]]; then
1182 return 1
1183 fi
1184
1185 local user_home=$(eval echo "~$username")
1186 local ssh_dir="$user_home/.ssh"
1187 local authorized_keys="$ssh_dir/authorized_keys"
1188
1189 if [[ -f "$authorized_keys" ]] && [[ -s "$authorized_keys" ]]; then
1190 local key_count=$(grep -c "ssh-" "$authorized_keys" 2>/dev/null || echo 0)
1191 if [[ $key_count -gt 0 ]]; then
1192 log_success "用户 $username 已有 $key_count 个SSH公钥"
1193 return 0
1194 fi
1195 fi
1196 return 1
1197}
1198
1199setup_ssh_key() {
1200 local username=$1
1201 local public_key=$2
1202 local user_home=$(eval echo "~$username")
1203 local ssh_dir="$user_home/.ssh"
1204 local authorized_keys="$ssh_dir/authorized_keys"
1205
1206 mkdir -p "$ssh_dir"
1207 echo "$public_key" > "$authorized_keys"
1208 record_rollback_action "ssh_key" "$username" "rm -f $authorized_keys"
1209 log_success "SSH公钥已设置"
1210}
1211
1212# 优化:使用ED25519替代RSA
1213setup_generate_key() {
1214 local username=$1
1215 local user_home=$(eval echo "~$username")
1216 local ssh_dir="$user_home/.ssh"
1217 local key_file="$ssh_dir/id_ed25519"
1218
1219 mkdir -p "$ssh_dir"
1220 chown "$username:$username" "$ssh_dir"
1221
1222 if [[ ! -f "$key_file" ]]; then
1223 log_info "生成ED25519 SSH密钥对..."
1224 log_info "ED25519: 比RSA更安全更快,256位即可达到4096位RSA的安全性"
1225
1226 record_rollback_action "ssh_keygen" "$username" "rm -f $key_file $key_file.pub"
1227
1228 # 使用ED25519生成密钥
1229 su - "$username" -c "ssh-keygen -t ed25519 -f '$key_file' -C '$username@$(get_hostname)'"
1230
1231 if [[ -f "$key_file" ]]; then
1232 log_success "ED25519密钥对生成成功"
1233 log_info "私钥文件: $key_file"
1234 log_info "公钥文件: $key_file.pub"
1235 log_warning "请妥善保管私钥文件"
1236
1237 echo -e "${YELLOW}公钥内容 (添加到authorized_keys):${NC}"
1238 cat "$key_file.pub"
1239 echo ""
1240 else
1241 log_error "SSH密钥生成失败,回退到RSA..."
1242 # 回退到RSA
1243 local rsa_key_file="$ssh_dir/id_rsa"
1244 su - "$username" -c "ssh-keygen -t rsa -b 4096 -f '$rsa_key_file' -N '' -C '$username@$(get_hostname)'"
1245 if [[ -f "$rsa_key_file" ]]; then
1246 log_success "RSA密钥对生成成功"
1247 log_info "密钥文件: $rsa_key_file"
1248 fi
1249 return 1
1250 fi
1251 else
1252 log_info "SSH密钥已存在 ($key_file)"
1253 fi
1254}
1255
1256configure_sudo_settings() {
1257 local username=$1
1258
1259 log_info "配置sudo权限..."
1260
1261 # 确保sudoers.d目录存在
1262 mkdir -p /etc/sudoers.d
1263 chmod 750 /etc/sudoers.d
1264
1265 local sudo_option=""
1266 local sudoers_file="/etc/sudoers.d/$username"
1267
1268 if [[ -t 0 ]]; then
1269 echo -e "${YELLOW}请选择sudo权限级别:${NC}"
1270 echo "1) 完全无密码sudo"
1271 echo "2) 需要密码sudo (推荐)"
1272 echo "3) 仅特定命令无密码"
1273 echo "4) 不配置"
1274 echo -e "${YELLOW}选择 [2-4] (直接回车默认2): ${NC}\c"
1275 read -p "" sudo_option
1276 # 如果直接回车,使用默认值2
1277 [[ -z "$sudo_option" ]] && sudo_option="2"
1278 else
1279 # 非交互模式默认使用选项2(需要密码的sudo)
1280 sudo_option="2"
1281 fi
1282
1283 case $sudo_option in
1284 1)
1285 echo "$username ALL=(ALL) NOPASSWD:ALL" > "$sudoers_file"
1286 chmod 440 "$sudoers_file"
1287 record_rollback_action "sudoers" "$username" "rm -f $sudoers_file"
1288 log_warning "已配置无密码sudo (安全性较低)"
1289 ;;
1290 2)
1291 echo "$username ALL=(ALL) ALL" > "$sudoers_file"
1292 chmod 440 "$sudoers_file"
1293 record_rollback_action "sudoers" "$username" "rm -f $sudoers_file"
1294 log_success "已配置需要密码的sudo"
1295 ;;
1296 3)
1297 echo -e "${YELLOW}输入允许无密码执行的命令: ${NC}\c"
1298 read -p "" sudo_commands
1299 if [[ -n "$sudo_commands" ]]; then
1300 echo "$username ALL=(ALL) NOPASSWD: $sudo_commands" > "$sudoers_file"
1301 chmod 440 "$sudoers_file"
1302 record_rollback_action "sudoers" "$username" "rm -f $sudoers_file"
1303 log_success "已配置限制性sudo"
1304 fi
1305 ;;
1306 *)
1307 log_info "跳过sudo配置"
1308 ;;
1309 esac
1310}
1311
1312manage_users() {
1313 log_section "创建管理用户和配置SSH密钥"
1314
1315 if ! confirm_action "是否创建专用管理用户?"; then
1316 log_warning "跳过用户创建"
1317 return 0
1318 fi
1319
1320 local username=""
1321
1322 if [[ -t 0 ]]; then
1323 echo -e "${YELLOW}请输入新用户名: ${NC}\c"
1324 read -p "" username
1325 else
1326 username="admin"
1327 fi
1328
1329 if [[ -z "$username" ]] || [[ ! "$username" =~ ^[a-z_][a-z0-9_-]*$ ]]; then
1330 handle_error $ERROR_SEVERE "无效的用户名: $username"
1331 return 1
1332 fi
1333
1334 # 检查用户是否存在
1335 if id "$username" &>/dev/null 2>&1; then
1336 log_info "用户 $username 已存在"
1337 else
1338 log_info "创建用户: $username"
1339 useradd -m -s /bin/bash "$username" 2>&1 | tee -a "$LOG_FILE"
1340
1341 # 密码配置选项 - 默认使用密钥认证增强安全性
1342 local password_option="2"
1343 if [[ -t 0 ]]; then
1344 echo -e "${YELLOW}选择认证方式:${NC}"
1345 echo "1) 设置密码 (密码+密钥)"
1346 echo "2) 仅SSH密钥 (无密码,推荐)"
1347 echo "3) 跳过,稍后手动设置"
1348 echo -e "${YELLOW}选择 [直接回车默认2]: ${NC}\c"
1349 read -p "" password_option
1350 # 如果用户直接回车或输入无效选项,使用默认的选项2(仅SSH密钥)
1351 [[ -z "$password_option" ]] && password_option="2"
1352 fi
1353
1354 case $password_option in
1355 1)
1356 echo -e "${YELLOW}为用户 $username 设置密码: ${NC}\c"
1357 passwd "$username"
1358 log_info "已设置密码"
1359 ;;
1360 2)
1361 log_info "使用密钥认证,跳过密码设置"
1362 # 生成随机密码锁定账户(增强安全性)
1363 local temp_password=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 16)
1364 echo "$username:$temp_password" | chpasswd 2>/dev/null || true
1365 log_info "账户已锁定,仅允许密钥登录"
1366 ;;
1367 3)
1368 log_info "跳过密码设置"
1369 ;;
1370 *)
1371 log_warning "无效选择,跳过密码设置"
1372 ;;
1373 esac
1374
1375 case $PACKAGE_MANAGER in
1376 apt)
1377 usermod -aG sudo "$username" 2>&1 | tee -a "$LOG_FILE"
1378 record_rollback_action "usermod" "$username" "gpasswd -d $username sudo"
1379 ;;
1380 pacman)
1381 # 确保sudo软件包已安装
1382 if ! is_package_installed "sudo"; then
1383 log_info "安装sudo软件包..."
1384 pacman -S --noconfirm sudo 2>&1 | tee -a "$LOG_FILE"
1385 fi
1386 usermod -aG wheel "$username" 2>&1 | tee -a "$LOG_FILE"
1387 record_rollback_action "usermod" "$username" "gpasswd -d $username wheel"
1388
1389 # 确保/etc/sudoers.d目录存在
1390 mkdir -p /etc/sudoers.d
1391 chmod 750 /etc/sudoers.d
1392
1393 # 配置wheel组权限
1394 if [[ -f "/etc/sudoers" ]]; then
1395 backup_file "/etc/sudoers"
1396 fi
1397 # 确保wheel组有sudo权限
1398 if ! grep -q "^%wheel ALL=" /etc/sudoers 2>/dev/null; then
1399 echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers
1400 fi
1401 ;;
1402 esac
1403 fi
1404
1405 log_info "配置SSH密钥..."
1406 local key_configured=false
1407
1408 if [[ -t 0 ]]; then
1409 echo -e "${YELLOW}选择密钥配置方式: ${NC}"
1410 echo "1) 现有公钥"
1411 echo "2) 生成ED25519密钥 (推荐)"
1412 echo "3) 跳过"
1413 echo -e "${YELLOW}选择 [1-3]: ${NC}\c"
1414 read -p "" key_option
1415 else
1416 # 非交互模式自动生成ED25519密钥
1417 key_option="2"
1418 fi
1419
1420 case $key_option in
1421 1)
1422 echo -e "${YELLOW}输入SSH公钥: ${NC}\c"
1423 read -p "" public_key
1424 if [[ -n "$public_key" ]] && [[ "$public_key" =~ ^ssh- ]]; then
1425 setup_ssh_key "$username" "$public_key"
1426 key_configured=true
1427 fi
1428 ;;
1429 2)
1430 setup_generate_key "$username"
1431 key_configured=true
1432 ;;
1433 *)
1434 log_info "跳过密钥配置"
1435 ;;
1436 esac
1437
1438 configure_sudo_settings "$username"
1439
1440 local user_home=$(eval echo "~$username")
1441 local ssh_dir="$user_home/.ssh"
1442
1443 if [[ -d "$ssh_dir" ]]; then
1444 chown -R "$username:$username" "$ssh_dir" 2>&1 | tee -a "$LOG_FILE"
1445 chmod 700 "$ssh_dir" 2>&1 | tee -a "$LOG_FILE"
1446 chmod 600 "$ssh_dir/authorized_keys" 2>&1 | tee -a "$LOG_FILE" || true
1447 fi
1448
1449 if id "$username" &>/dev/null; then
1450 log_success "用户 $username 配置成功"
1451 fi
1452
1453 if $key_configured; then
1454 return 0
1455 else
1456 return 1
1457 fi
1458}
1459
1460# ============================================================================
1461# SSH安全加固
1462# ============================================================================
1463
1464validate_ssh_config() {
1465 local ssh_config="/etc/ssh/sshd_config"
1466
1467 if [[ ! -f "$ssh_config" ]]; then
1468 log_error "SSH配置文件不存在"
1469 return 1
1470 fi
1471
1472 log_info "验证SSH配置语法..."
1473
1474 if command -v sshd &> /dev/null; then
1475 if sshd -t 2>&1; then
1476 log_success "SSH配置语法验证通过"
1477 return 0
1478 else
1479 log_error "SSH配置语法验证失败"
1480 return 1
1481 fi
1482 fi
1483 return 0
1484}
1485
1486harden_ssh() {
1487 log_section "SSH安全加固"
1488
1489 local ssh_config="/etc/ssh/sshd_config"
1490 local ssh_port=22
1491 local key_configured=false
1492
1493 if [[ ! -f "$ssh_config" ]]; then
1494 handle_error $ERROR_SEVERE "SSH配置文件不存在"
1495 fi
1496
1497 backup_file "$ssh_config"
1498
1499 ssh_port=$(grep -E "^Port [0-9]+" "$ssh_config" 2>/dev/null | awk '{print $2}' | head -1)
1500 ssh_port=${ssh_port:-22}
1501
1502 log_info "步骤1/3: 配置用户和SSH密钥..."
1503 if manage_users; then
1504 key_configured=true
1505 fi
1506
1507 log_info "步骤2/3: 配置安全参数..."
1508
1509 # 端口配置(带验证和重新输入功能)
1510 if [[ -t 0 ]]; then
1511 local port_valid=false
1512 local max_attempts=3
1513 local attempt=0
1514
1515 while [[ $port_valid == false ]] && [[ $attempt -lt $max_attempts ]]; do
1516 ((attempt++))
1517 echo -e "${YELLOW}修改SSH端口($ssh_port)? 输入新端口(留空使用当前端口): ${NC}\c"
1518 read -p "" new_port
1519
1520 if [[ -z "$new_port" ]]; then
1521 # 使用当前端口
1522 port_valid=true
1523 log_info "使用默认端口: $ssh_port"
1524 elif [[ ! "$new_port" =~ ^[0-9]+$ ]] || [[ "$new_port" -le 1024 ]] || [[ "$new_port" -ge 65536 ]]; then
1525 log_warning "无效端口号,请输入1024-65535之间的数字 (剩余$((max_attempts - attempt))次尝试)"
1526 else
1527 # 检查端口是否可用
1528 if ! check_port_available "$new_port" "SSH"; then
1529 log_warning "端口 $new_port 已被占用,请选择其他端口 (剩余$((max_attempts - attempt))次尝试)"
1530 else
1531 port_valid=true
1532 ssh_port=$new_port
1533 log_success "端口 $new_port 可用"
1534 log_warning "请确保防火墙已开放端口 $new_port"
1535 fi
1536 fi
1537 done
1538
1539 if [[ $port_valid == false ]]; then
1540 log_warning "端口验证失败次数过多,使用默认端口: $ssh_port"
1541 fi
1542 fi
1543
1544 if confirm_action "是否禁用SSH root登录?"; then
1545 sed -i "s/^#*PermitRootLogin.*/PermitRootLogin no/" "$ssh_config"
1546 record_rollback_action "ssh_config" "PermitRootLogin" "sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' $ssh_config"
1547 fi
1548
1549 if $key_configured; then
1550 if confirm_action "SSH密钥已配置,是否禁用密码认证?"; then
1551 sed -i "s/^#*PasswordAuthentication.*/PasswordAuthentication no/" "$ssh_config"
1552 record_rollback_action "ssh_config" "PasswordAuthentication" "sed -i 's/^PasswordAuthentication.*/PasswordAuthentication yes/' $ssh_config"
1553 fi
1554 else
1555 log_warning "SSH密钥未配置,跳过密码认证禁用"
1556 fi
1557
1558 # 添加安全配置
1559 cat >> "$ssh_config" << EOF
1560
1561# 安全加固配置
1562Protocol 2
1563MaxAuthTries 3
1564MaxSessions 2
1565LoginGraceTime 60
1566ClientAliveInterval 300
1567ClientAliveCountMax 2
1568PermitEmptyPasswords no
1569X11Forwarding no
1570UseDNS no
1571EOF
1572
1573 log_info "步骤3/3: 验证并应用配置..."
1574
1575 if ! validate_ssh_config; then
1576 handle_error $ERROR_SEVERE "SSH配置验证失败"
1577 execute_rollback
1578 return 1
1579 fi
1580
1581 if systemctl restart sshd 2>&1 | tee -a "$LOG_FILE"; then
1582 log_success "SSH服务已重启 (端口: $ssh_port)"
1583 else
1584 handle_error $ERROR_SEVERE "SSH服务重启失败"
1585 return 1
1586 fi
1587
1588 log "SSH加固完成"
1589}
1590
1591# ============================================================================
1592# 系统加固
1593# ============================================================================
1594
1595harden_users() {
1596 log_section "用户和权限加固"
1597 configure_password_policy
1598
1599 log_info "锁定系统账户..."
1600 local system_users=("bin" "daemon" "adm" "lp" "sync" "shutdown" "halt" "mail" "news" "uucp" "operator" "games" "gopher" "ftp")
1601
1602 for user in "${system_users[@]}"; do
1603 if id "$user" &>/dev/null 2>&1; then
1604 usermod -L -s /usr/sbin/nologin "$user" 2>/dev/null || true
1605 fi
1606 done
1607
1608 log "用户加固完成"
1609}
1610
1611disable_services() {
1612 log_section "禁用不必要的服务"
1613 local services_to_disable=("avahi-daemon" "cups" "bluetooth")
1614
1615 for service in "${services_to_disable[@]}"; do
1616 if command -v systemctl &> /dev/null; then
1617 if systemctl is-enabled "$service" &>/dev/null 2>&1; then
1618 if confirm_action "是否禁用服务: $service"; then
1619 disable_service "$service"
1620 record_rollback_action "service" "$service" "systemctl enable $service; systemctl start $service"
1621 log_info "已禁用: $service"
1622 fi
1623 fi
1624 fi
1625 done
1626}
1627
1628harden_kernel() {
1629 log_section "内核参数安全配置"
1630 local sysctl_conf="/etc/sysctl.d/99-security.conf"
1631 backup_file "$sysctl_conf" 2>/dev/null || true
1632
1633 log_info "配置内核安全参数..."
1634
1635 cat > "$sysctl_conf" << 'EOF'
1636# IP转发禁用
1637net.ipv4.ip_forward = 0
1638net.ipv6.conf.all.forwarding = 0
1639
1640# SYN cookies保护
1641net.ipv4.tcp_syncookies = 1
1642
1643# 忽略ICMP重定向
1644net.ipv4.conf.all.accept_redirects = 0
1645net.ipv6.conf.all.accept_redirects = 0
1646net.ipv4.conf.default.accept_redirects = 0
1647net.ipv6.conf.default.accept_redirects = 0
1648
1649# 禁用源路由
1650net.ipv4.conf.all.accept_source_route = 0
1651net.ipv6.conf.all.accept_source_route = 0
1652
1653# 记录可疑包
1654net.ipv4.conf.all.log_martians = 1
1655net.ipv4.conf.default.log_martians = 1
1656
1657# 忽略ICMP ping请求
1658net.ipv4.icmp_echo_ignore_broadcasts = 1
1659
1660# 反向路径过滤
1661net.ipv4.conf.all.rp_filter = 1
1662net.ipv4.conf.default.rp_filter = 1
1663
1664# SYN flood保护
1665net.ipv4.tcp_max_syn_backlog = 2048
1666net.ipv4.tcp_synack_retries = 2
1667net.ipv4.tcp_syn_retries = 5
1668EOF
1669
1670 if command -v sysctl &> /dev/null; then
1671 sysctl -p "$sysctl_conf" 2>&1 | tee -a "$LOG_FILE" || log_warning "应用内核参数失败"
1672 fi
1673
1674 log "内核参数配置完成"
1675}
1676
1677harden_filesystem() {
1678 log_section "文件系统权限加固"
1679 log_info "设置重要文件权限..."
1680
1681 local files_to_protect=(
1682 "/etc/ssh/sshd_config:600"
1683 "/etc/passwd:644"
1684 "/etc/shadow:640"
1685 "/etc/group:644"
1686 "/etc/gshadow:600"
1687 )
1688
1689 for file_perm in "${files_to_protect[@]}"; do
1690 local file="${file_perm%:*}"
1691 local perm="${file_perm#*:}"
1692 if [[ -f "$file" ]]; then
1693 chmod "$perm" "$file" 2>&1 | tee -a "$LOG_FILE"
1694 record_rollback_action "chmod" "$file" "chmod 644 $file"
1695 fi
1696 done
1697
1698 log_info "SUID/SGID文件检查..."
1699 if command -v find &> /dev/null; then
1700 find / -perm /6000 -type f 2>/dev/null >> "$LOG_FILE" || true
1701 fi
1702}
1703
1704collect_system_info() {
1705 log_section "收集系统信息"
1706 log_info "操作系统: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown")"
1707 log_info "内核版本: $(uname -r)"
1708 log_info "主机名: $(get_hostname)"
1709 log_info "IP地址: $(get_ip_address)"
1710}
1711
1712update_system() {
1713 log_section "系统更新"
1714
1715 if confirm_action "是否执行系统更新?"; then
1716 case $PACKAGE_MANAGER in
1717 apt)
1718 apt update -y 2>&1 | tee -a "$LOG_FILE"
1719 apt upgrade -y 2>&1 | tee -a "$LOG_FILE"
1720 apt autoremove -y 2>&1 | tee -a "$LOG_FILE"
1721 ;;
1722 pacman)
1723 pacman -Syu --noconfirm 2>&1 | tee -a "$LOG_FILE"
1724 pacman -Rns $(pacman -Qdtq) --noconfirm 2>/dev/null || true
1725 ;;
1726 esac
1727 log "系统更新完成"
1728 else
1729 log_warning "跳过系统更新"
1730 fi
1731}
1732
1733generate_report() {
1734 log_section "生成安全加固报告"
1735
1736 local os_info=$(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown")
1737 local kernel_info=$(uname -r)
1738 local hostname_info=$(get_hostname)
1739
1740 cat > "$REPORT_FILE" << EOF
1741================================================================================
1742服务器安全加固报告
1743================================================================================
1744生成时间: $(date)
1745主机名: $hostname_info
1746操作系统: $os_info
1747内核版本: $kernel_info
1748包管理器: $PACKAGE_MANAGER
1749
1750备份目录: $BACKUP_DIR
1751回滚目录: $ROLLBACK_DIR
1752日志文件: $LOG_FILE
1753
1754--------------------------------------------------------------------------------
17551. SSH配置
1756--------------------------------------------------------------------------------
1757$(grep -E "^(Port|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 2>/dev/null || echo "N/A")
1758
1759--------------------------------------------------------------------------------
17602. 防火墙状态
1761--------------------------------------------------------------------------------
1762$(case $PACKAGE_MANAGER in
1763 apt) ufw status verbose 2>/dev/null ;;
1764 pacman)
1765 if command -v nft &> /dev/null; then nft list ruleset 2>/dev/null;
1766 else iptables -L -n 2>/dev/null; fi
1767 esac || echo "N/A")
1768
1769--------------------------------------------------------------------------------
17703. 入侵防御系统
1771--------------------------------------------------------------------------------
1772$(command -v fail2ban-client &> /dev/null && fail2ban-client status 2>/dev/null || echo "N/A")
1773
1774--------------------------------------------------------------------------------
17754. 已安装安全工具
1776--------------------------------------------------------------------------------
1777$(case $PACKAGE_MANAGER in
1778 apt) dpkg -l 2>/dev/null | grep -E "fail2ban|ufw|auditd" | awk '{print $2 " " $3}' ;;
1779 pacman) pacman -Q 2>/dev/null | grep -E "fail2ban|crowdsec|nftables|auditd" | head -10 ;;
1780 esac || echo "N/A")
1781
1782--------------------------------------------------------------------------------
17835. AUR助手状态
1784--------------------------------------------------------------------------------
1785$(command -v yay &> /dev/null && echo "yay: 已安装" || echo "yay: 未安装")
1786$(command -v paru &> /dev/null && echo "paru: 已安装" || echo "paru: 未安装")
1787
1788================================================================================
1789EOF
1790
1791 if command -v sha256sum &> /dev/null; then
1792 sha256sum "$REPORT_FILE" > "${REPORT_FILE}.sha256"
1793 fi
1794
1795 log_info "报告已生成: $REPORT_FILE"
1796}
1797
1798# ============================================================================
1799# 主程序
1800# ============================================================================
1801
1802main() {
1803 clear
1804
1805 echo -e "${CYAN}"
1806 cat << "EOF"
1807╔══════════════════════════════════════════════════════════════╗
1808║ 服务器安全加固脚本 (多发行版增强版)
1809║ v4.2.1 - 支持 Ubuntu + Arch Linux ║
1810║ • 多防火墙选择 (UFW/iptables/nftables)
1811║ • 多入侵防护 (Fail2ban/Sshguard/Crowdsec)
1812║ • AUR助手自动安装 (yay/paru)
1813║ • ED25519 SSH密钥 (替代RSA)
1814║ • 支持 -y 参数用于非交互模式 ║
1815║ • 依赖自动检查 ║
1816║ • 支持密钥优先认证 (无需密码)
1817╚══════════════════════════════════════════════════════════════╝
1818EOF
1819 echo -e "${NC}"
1820
1821 check_root
1822 detect_distribution
1823 check_dependencies
1824 setup_colors
1825 init_rollback
1826
1827 log "开始服务器安全加固..."
1828 log_info "日志: $LOG_FILE | 备份: $BACKUP_DIR"
1829
1830 echo -e "${YELLOW}警告: 此脚本将修改系统配置${NC}"
1831
1832 if ! confirm_action "确认继续?"; then
1833 if [[ "$FORCE_YES" == "true" ]]; then
1834 log_info "自动模式: 继续执行加固操作"
1835 else
1836 log "用户取消"
1837 exit 0
1838 fi
1839 fi
1840
1841 collect_system_info
1842 update_system
1843 harden_ssh
1844 configure_firewall
1845 install_intrusion_prevention
1846 harden_users
1847 disable_services
1848 harden_kernel
1849 harden_filesystem
1850 configure_auto_updates
1851 install_security_tools
1852 generate_report
1853
1854 log_section "安全加固完成"
1855
1856 echo -e "${GREEN}"
1857 cat << EOF
1858╔══════════════════════════════════════════════════════════════╗
1859║ 安全加固已完成! ║
1860╚══════════════════════════════════════════════════════════════╝
1861
1862重要提醒:
1863• 备份: $BACKUP_DIR
1864• 回滚: $ROLLBACK_DIR
1865• 日志: $LOG_FILE
1866• 报告: $REPORT_FILE
1867• 建议重启服务器
1868
1869下一步:
1870• 测试SSH登录
1871• 检查防火墙规则
1872• 配置入侵防御
1873
1874EOF
1875 echo -e "${NC}"
1876
1877 if confirm_action "是否重启服务器?"; then
1878 log "5秒后重启..."
1879 sleep 5
1880 reboot
1881 fi
1882}
1883
1884main "$@"
1885