Остання активність 1 month ago

passive ревизій цього gist 1 month ago. До ревизії

1 file changed, 130 insertions, 1814 deletions

arch_hardening_script.sh

@@ -1,1884 +1,200 @@
1 - #!/bin/bash
1 + #!/usr/bin/env bash
2 + set -euo pipefail
2 3
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 - ################################################################################
4 + # =========================
5 + # Config
6 + # =========================
7 + NEW_USER="${1:-dev}"
8 + SSH_PORT="${2:-22}"
9 + DISABLE_PASSWORD_LOGIN="${3:-false}"
10 + PUBKEY="${4:-}" # optional public key
23 11
24 - # ============================================================================
25 - # 命令行参数解析
26 - # ============================================================================
12 + # =========================
13 + # UI
14 + # =========================
27 15
28 - FORCE_YES=false
16 + log(){ echo -e "\033[1;32m[INFO]\033[0m $1"; }
17 + warn(){ echo -e "\033[1;33m[WARN]\033[0m $1"; }
18 + err(){ echo -e "\033[1;31m[ERROR]\033[0m $1"; }
29 19
30 - while [[ $# -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
49 - done
50 -
51 - # ============================================================================
52 - # 全局配置
53 - # ============================================================================
54 -
55 - LOG_FILE="/var/log/server_hardening_$(date +%Y%m%d_%H%M%S).log"
56 - BACKUP_DIR="/root/security_backup_$(date +%Y%m%d_%H%M%S)"
57 - ROLLBACK_DIR="/root/security_rollback_$(date +%Y%m%d_%H%M%S)"
58 - REPORT_FILE="/root/security_hardening_report_$(date +%Y%m%d_%H%M%S).txt"
59 -
60 - # 错误级别
61 - ERROR_CRITICAL=1
62 - ERROR_SEVERE=2
63 - ERROR_WARNING=3
64 - ERROR_INFO=4
65 -
66 - # ============================================================================
67 - # 依赖检查
68 - # ============================================================================
69 -
70 - check_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 "依赖检查完成"
20 + confirm(){
21 + read -rp "$1 [y/N]: " ans
22 + [[ "$ans" == "y" || "$ans" == "Y" ]]
148 23 }
149 24
150 - # ============================================================================
151 - # 发行版检测
152 - # ============================================================================
153 -
154 - detect_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)"
25 + pause(){
26 + read -rp "Press Enter to continue..."
196 27 }
197 28
198 - # ============================================================================
199 - # 颜色定义
200 - # ============================================================================
29 + # =========================
30 + # System Detect
31 + # =========================
201 32
202 - setup_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')
33 + detect_os(){
34 + if [[ -f /etc/arch-release ]]; then
35 + OS="arch"
36 + elif grep -qi "debian\|ubuntu" /etc/os-release; then
37 + OS="debian"
210 38 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 -
224 - log() {
225 - echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
226 - }
227 -
228 - log_info() {
229 - echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
230 - }
231 -
232 - log_warning() {
233 - echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
234 - }
235 -
236 - log_error() {
237 - echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
238 - }
239 -
240 - log_success() {
241 - echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
242 - }
243 -
244 - log_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 -
250 - log_rollback() {
251 - echo -e "${YELLOW}[ROLLBACK]${NC} $1" | tee -a "$LOG_FILE"
252 - }
253 -
254 - # ============================================================================
255 - # 核心工具函数
256 - # ============================================================================
257 -
258 - check_root() {
259 - if [[ $EUID -ne 0 ]]; then
260 - log_error "此脚本必须以root权限运行"
39 + err "Unsupported OS"
261 40 exit 1
262 41 fi
42 + log "Detected OS: $OS"
263 43 }
264 44
265 - handle_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 - ;;
45 + pkg_install(){
46 + case "$OS" in
47 + arch) pacman -Sy --noconfirm "$@" ;;
48 + debian) apt-get update && apt-get install -y "$@" ;;
289 49 esac
290 50 }
291 51
292 - backup_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 - }
52 + # =========================
53 + # User
54 + # =========================
320 55
321 - init_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 -
328 - execute_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 -
346 - record_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命令不存在的情况)
355 - get_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
56 + create_user(){
57 + if id "$NEW_USER" &>/dev/null; then
58 + warn "User exists"
362 59 else
363 - uname -n
60 + useradd -m -s /bin/bash "$NEW_USER"
61 + passwd "$NEW_USER"
364 62 fi
63 + usermod -aG sudo "$NEW_USER" 2>/dev/null || usermod -aG wheel "$NEW_USER"
365 64 }
366 65
367 - # 安全获取IP地址
368 - get_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 - }
66 + setup_ssh_key(){
67 + mkdir -p /home/$NEW_USER/.ssh
68 + chmod 700 /home/$NEW_USER/.ssh
386 69
387 - confirm_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
70 + if [[ -n "$PUBKEY" ]]; then
71 + log "Adding provided public key"
72 + echo "$PUBKEY" > /home/$NEW_USER/.ssh/authorized_keys
402 73 else
403 - log_info "[非交互模式跳过] $message"
404 - return 1
405 - fi
406 - }
407 -
408 - check_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
74 + warn "No public key provided, please paste one"
75 + read -rp "Paste your SSH public key: " key
76 + echo "$key" > /home/$NEW_USER/.ssh/authorized_keys
433 77 fi
434 -
435 - return 0
436 - }
437 -
438 - # ============================================================================
439 - # 包管理器抽象层
440 - # ============================================================================
441 78
442 - update_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
79 + chmod 600 /home/$NEW_USER/.ssh/authorized_keys
80 + chown -R $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh
451 81 }
452 82
453 - install_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 - }
83 + # =========================
84 + # SSH Hardening
85 + # =========================
478 86
479 - is_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 - }
87 + set_ssh(){
88 + local key="$1"
89 + local val="$2"
491 90
492 - enable_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 -
501 - disable_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 -
514 - install_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"
91 + if grep -q "^#\?$key" /etc/ssh/sshd_config; then
92 + sed -i "s/^#\?$key.*/$key $val/" /etc/ssh/sshd_config
573 93 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 已就绪"
94 + echo "$key $val" >> /etc/ssh/sshd_config
584 95 fi
585 96 }
586 97
587 - install_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 -
619 - configure_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 - # 密码安全策略
635 - minlen = 12
636 - dcredit = -1
637 - ucredit = -1
638 - lcredit = -1
639 - ocredit = -1
640 - maxrepeat = 3
641 - EOF
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 - }
98 + config_ssh(){
99 + cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%s)
661 100
662 - # ============================================================================
663 - # 防火墙配置 (支持 ufw / iptables / nftables)
664 - # ============================================================================
101 + set_ssh "PermitRootLogin" "no"
102 + set_ssh "PubkeyAuthentication" "yes"
103 + set_ssh "Port" "$SSH_PORT"
665 104
666 - configure_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 -
705 - configure_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"
105 + if [[ "$DISABLE_PASSWORD_LOGIN" == "true" ]]; then
106 + warn "Disabling password login"
107 + confirm "Are you sure?" && set_ssh "PasswordAuthentication" "no"
735 108 else
736 - ufw --force enable 2>&1 | tee -a "$LOG_FILE"
109 + set_ssh "PasswordAuthentication" "yes"
737 110 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 111 }
744 112
745 - configure_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防火墙配置完成"
113 + restart_ssh(){
114 + systemctl restart ssh 2>/dev/null || systemctl restart sshd
800 115 }
801 116
802 - configure_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
117 + # =========================
118 + # Firewall (UFW)
119 + # =========================
831 120
832 - # 刷新所有规则
833 - flush ruleset
121 + setup_ufw(){
122 + log "Setting up UFW firewall"
834 123
835 - # 定义表
836 - table 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 - }
865 - EOF
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 - }
124 + pkg_install ufw
890 125
891 - # ============================================================================
892 - # 入侵防御系统 (支持 fail2ban / sshguard / crowdsec)
893 - # ============================================================================
126 + ufw allow "$SSH_PORT"/tcp
127 + ufw allow 80/tcp
128 + ufw allow 443/tcp
894 129
895 - install_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
130 + ufw --force enable
923 131 }
924 132
925 - install_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 - }
133 + # =========================
134 + # Fail2ban
135 + # =========================
941 136
942 - configure_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]
950 - bantime = 3600
951 - findtime = 600
952 - maxretry = 5
137 + setup_fail2ban(){
138 + log "Installing fail2ban"
953 139
954 - [sshd]
955 - enabled = true
956 - port = ssh
957 - filter = sshd
958 - logpath = /var/log/auth.log
959 - maxretry = 3
960 - bantime = 7200
140 + pkg_install fail2ban
961 141
962 - [sshd-ddos]
142 + cat > /etc/fail2ban/jail.local <<EOF
143 + [sshd]
963 144 enabled = true
964 - port = ssh
965 - filter = sshd-ddos
966 - logpath = /var/log/auth.log
967 - maxretry = 2
968 - bantime = 7200
969 - EOF
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 -
981 - install_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 -
998 - configure_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 -
1009 - install_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 -
1047 - configure_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 -
1094 - configure_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
1113 - pacman -Sy 2>/dev/null
1114 - echo "系统检查完成"
1115 - EOF
1116 - chmod +x "$cron_file"
1117 - log_info "已配置每日更新检查"
1118 - fi
1119 - ;;
1120 - esac
1121 - }
1122 -
1123 - # ============================================================================
1124 - # 安全审计工具
1125 - # ============================================================================
1126 -
1127 - install_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 -
1178 - verify_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 -
1199 - setup_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
1213 - setup_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 -
1256 - configure_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 -
1312 - manage_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 -
1464 - validate_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 -
1486 - harden_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 - # 安全加固配置
1562 - Protocol 2
1563 - MaxAuthTries 3
1564 - MaxSessions 2
1565 - LoginGraceTime 60
1566 - ClientAliveInterval 300
1567 - ClientAliveCountMax 2
1568 - PermitEmptyPasswords no
1569 - X11Forwarding no
1570 - UseDNS no
1571 - EOF
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 -
1595 - harden_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 -
1611 - disable_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 -
1628 - harden_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转发禁用
1637 - net.ipv4.ip_forward = 0
1638 - net.ipv6.conf.all.forwarding = 0
1639 -
1640 - # SYN cookies保护
1641 - net.ipv4.tcp_syncookies = 1
1642 -
1643 - # 忽略ICMP重定向
1644 - net.ipv4.conf.all.accept_redirects = 0
1645 - net.ipv6.conf.all.accept_redirects = 0
1646 - net.ipv4.conf.default.accept_redirects = 0
1647 - net.ipv6.conf.default.accept_redirects = 0
1648 -
1649 - # 禁用源路由
1650 - net.ipv4.conf.all.accept_source_route = 0
1651 - net.ipv6.conf.all.accept_source_route = 0
1652 -
1653 - # 记录可疑包
1654 - net.ipv4.conf.all.log_martians = 1
1655 - net.ipv4.conf.default.log_martians = 1
1656 -
1657 - # 忽略ICMP ping请求
1658 - net.ipv4.icmp_echo_ignore_broadcasts = 1
1659 -
1660 - # 反向路径过滤
1661 - net.ipv4.conf.all.rp_filter = 1
1662 - net.ipv4.conf.default.rp_filter = 1
1663 -
1664 - # SYN flood保护
1665 - net.ipv4.tcp_max_syn_backlog = 2048
1666 - net.ipv4.tcp_synack_retries = 2
1667 - net.ipv4.tcp_syn_retries = 5
145 + port = $SSH_PORT
146 + maxretry = 5
147 + bantime = 1h
1668 148 EOF
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 149
1677 - harden_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
150 + systemctl enable fail2ban
151 + systemctl restart fail2ban
1702 152 }
1703 153
1704 - collect_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 - }
154 + # =========================
155 + # Main
156 + # =========================
1711 157
1712 - update_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 "跳过系统更新"
158 + main(){
159 + if [[ $EUID -ne 0 ]]; then
160 + err "Run as root"
161 + exit 1
1730 162 fi
1731 - }
1732 -
1733 - generate_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 - --------------------------------------------------------------------------------
1755 - 1. SSH配置
1756 - --------------------------------------------------------------------------------
1757 - $(grep -E "^(Port|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 2>/dev/null || echo "N/A")
1758 -
1759 - --------------------------------------------------------------------------------
1760 - 2. 防火墙状态
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 163
1769 - --------------------------------------------------------------------------------
1770 - 3. 入侵防御系统
1771 - --------------------------------------------------------------------------------
1772 - $(command -v fail2ban-client &> /dev/null && fail2ban-client status 2>/dev/null || echo "N/A")
164 + detect_os
1773 165
1774 - --------------------------------------------------------------------------------
1775 - 4. 已安装安全工具
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")
166 + echo "================================="
167 + echo "User: $NEW_USER"
168 + echo "Port: $SSH_PORT"
169 + echo "Disable Password: $DISABLE_PASSWORD_LOGIN"
170 + echo "================================="
1781 171
1782 - --------------------------------------------------------------------------------
1783 - 5. AUR助手状态
1784 - --------------------------------------------------------------------------------
1785 - $(command -v yay &> /dev/null && echo "yay: 已安装" || echo "yay: 未安装")
1786 - $(command -v paru &> /dev/null && echo "paru: 已安装" || echo "paru: 未安装")
172 + confirm "Start?" || exit 0
1787 173
1788 - ================================================================================
1789 - EOF
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 - }
174 + pause
175 + log "Create user"
176 + create_user
177 + setup_ssh_key
1797 178
1798 - # ============================================================================
1799 - # 主程序
1800 - # ============================================================================
179 + pause
180 + log "Configure SSH"
181 + config_ssh
1801 182
1802 - main() {
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 - ╚══════════════════════════════════════════════════════════════╝
1818 - EOF
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 - ╚══════════════════════════════════════════════════════════════╝
183 + pause
184 + log "Setup Firewall"
185 + setup_ufw
1861 186
1862 - 重要提醒:
1863 - • 备份: $BACKUP_DIR
1864 - • 回滚: $ROLLBACK_DIR
1865 - • 日志: $LOG_FILE
1866 - • 报告: $REPORT_FILE
1867 - • 建议重启服务器
187 + pause
188 + log "Setup Fail2ban"
189 + setup_fail2ban
1868 190
1869 - 下一步:
1870 - • 测试SSH登录
1871 - • 检查防火墙规则
1872 - • 配置入侵防御
191 + pause
192 + log "Restart SSH"
193 + restart_ssh
1873 194
1874 - EOF
1875 - echo -e "${NC}"
1876 -
1877 - if confirm_action "是否重启服务器?"; then
1878 - log "5秒后重启..."
1879 - sleep 5
1880 - reboot
1881 - fi
195 + echo "================================="
196 + log "DONE"
197 + echo "Test SSH BEFORE exit!"
1882 198 }
1883 199
1884 200 main "$@"

passive ревизій цього gist 4 months ago. До ревизії

1 file changed, 131 insertions, 35 deletions

arch_hardening_script.sh

@@ -4,7 +4,7 @@
4 4 # 服务器自动化加固脚本 - 多发行版增强版
5 5 # 作者: Mercas
6 6 # 日期: 2025-11-09
7 - # 版本: v4.2 Multi-Distro Enhanced (依赖检查版)
7 + # 版本: v4.2.3 Multi-Distro Enhanced (sudo权限修复版)
8 8 #
9 9 # 支持的发行版:
10 10 # - Ubuntu/Debian (apt包管理器)
@@ -301,8 +301,15 @@ backup_file() {
301 301 cp -p "$file" "$backup_path"
302 302
303 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
304 311 sha256sum "$backup_path" > "$checksum_file"
305 - log_info "已备份: $(basename "$file") (SHA256: $(sha256sum --short "$backup_path"))"
312 + log_info "已备份: $(basename "$file") (SHA256: ${checksum_value:0:16}...)"
306 313 else
307 314 log_info "已备份: $(basename "$file")"
308 315 fi
@@ -402,12 +409,29 @@ check_port_available() {
402 409 local port=$1
403 410 local service_name=$2
404 411
412 + # 检查端口是否被监听
405 413 if ss -tunlp 2>/dev/null | grep -q ":$port "; then
414 + local process_info=$(ss -tunlp 2>/dev/null | grep ":$port " | head -1)
406 415 log_warning "端口 $port 已被占用"
407 - if ! confirm_action "是否继续使用端口 $port?"; then
408 - return 1
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配置中定义"
409 432 fi
410 433 fi
434 +
411 435 return 0
412 436 }
413 437
@@ -1231,19 +1255,31 @@ setup_generate_key() {
1231 1255
1232 1256 configure_sudo_settings() {
1233 1257 local username=$1
1234 -
1258 +
1235 1259 log_info "配置sudo权限..."
1236 -
1237 - echo -e "${YELLOW}请选择sudo权限级别:${NC}"
1238 - echo "1) 完全无密码sudo"
1239 - echo "2) 需要密码sudo (推荐)"
1240 - echo "3) 仅特定命令无密码"
1241 - echo "4) 不配置"
1242 - echo -e "${YELLOW}选择 [1-4]: ${NC}\c"
1243 - read -p "" sudo_option
1244 -
1260 +
1261 + # 确保sudoers.d目录存在
1262 + mkdir -p /etc/sudoers.d
1263 + chmod 750 /etc/sudoers.d
1264 +
1265 + local sudo_option=""
1245 1266 local sudoers_file="/etc/sudoers.d/$username"
1246 -
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 +
1247 1283 case $sudo_option in
1248 1284 1)
1249 1285 echo "$username ALL=(ALL) NOPASSWD:ALL" > "$sudoers_file"
@@ -1302,29 +1338,65 @@ manage_users() {
1302 1338 log_info "创建用户: $username"
1303 1339 useradd -m -s /bin/bash "$username" 2>&1 | tee -a "$LOG_FILE"
1304 1340
1341 + # 密码配置选项 - 默认使用密钥认证增强安全性
1342 + local password_option="2"
1305 1343 if [[ -t 0 ]]; then
1306 - echo -e "${YELLOW}为用户 $username 设置密码: ${NC}\c"
1307 - passwd "$username"
1308 - else
1309 - local temp_password=$(openssl rand -base64 32)
1310 - echo "$username:$temp_password" | chpasswd
1311 - record_rollback_action "password" "$username" "passwd -d $username"
1312 - log_warning "临时密码: $temp_password"
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"
1313 1352 fi
1314 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 +
1315 1375 case $PACKAGE_MANAGER in
1316 1376 apt)
1317 1377 usermod -aG sudo "$username" 2>&1 | tee -a "$LOG_FILE"
1318 1378 record_rollback_action "usermod" "$username" "gpasswd -d $username sudo"
1319 1379 ;;
1320 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
1321 1386 usermod -aG wheel "$username" 2>&1 | tee -a "$LOG_FILE"
1322 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组权限
1323 1394 if [[ -f "/etc/sudoers" ]]; then
1324 - if ! grep -q "^%wheel ALL=" /etc/sudoers 2>/dev/null; then
1325 - backup_file "/etc/sudoers"
1326 - echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers
1327 - fi
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
1328 1400 fi
1329 1401 ;;
1330 1402 esac
@@ -1341,7 +1413,8 @@ manage_users() {
1341 1413 echo -e "${YELLOW}选择 [1-3]: ${NC}\c"
1342 1414 read -p "" key_option
1343 1415 else
1344 - key_option="3"
1416 + # 非交互模式自动生成ED25519密钥
1417 + key_option="2"
1345 1418 fi
1346 1419
1347 1420 case $key_option in
@@ -1433,16 +1506,38 @@ harden_ssh() {
1433 1506
1434 1507 log_info "步骤2/3: 配置安全参数..."
1435 1508
1509 + # 端口配置(带验证和重新输入功能)
1436 1510 if [[ -t 0 ]]; then
1437 - echo -e "${YELLOW}修改SSH端口($ssh_port)? 输入新端口或留空: ${NC}\c"
1438 - read -p "" new_port
1439 - if [[ -n "$new_port" ]] && [[ "$new_port" =~ ^[0-9]+$ ]] && [[ "$new_port" -gt 1024 ]] && [[ "$new_port" -lt 65536 ]]; then
1440 - if ! check_port_available "$new_port" "SSH"; then
1441 - log_warning "端口检查未通过"
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))次尝试)"
1442 1526 else
1443 - log_warning "请更新防火墙开放端口 $new_port"
1444 - ssh_port=$new_port
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
1445 1536 fi
1537 + done
1538 +
1539 + if [[ $port_valid == false ]]; then
1540 + log_warning "端口验证失败次数过多,使用默认端口: $ssh_port"
1446 1541 fi
1447 1542 fi
1448 1543
@@ -1711,13 +1806,14 @@ main() {
1711 1806 cat << "EOF"
1712 1807 ╔══════════════════════════════════════════════════════════════╗
1713 1808 ║ 服务器安全加固脚本 (多发行版增强版) ║
1714 - ║ v4.2 - 支持 Ubuntu + Arch Linux ║
1809 + ║ v4.2.1 - 支持 Ubuntu + Arch Linux ║
1715 1810 ║ • 多防火墙选择 (UFW/iptables/nftables) ║
1716 1811 ║ • 多入侵防护 (Fail2ban/Sshguard/Crowdsec) ║
1717 1812 ║ • AUR助手自动安装 (yay/paru) ║
1718 1813 ║ • ED25519 SSH密钥 (替代RSA) ║
1719 1814 ║ • 支持 -y 参数用于非交互模式 ║
1720 1815 ║ • 依赖自动检查 ║
1816 + ║ • 支持密钥优先认证 (无需密码) ║
1721 1817 ╚══════════════════════════════════════════════════════════════╝
1722 1818 EOF
1723 1819 echo -e "${NC}"

passive ревизій цього gist 4 months ago. До ревизії

1 file changed, 170 insertions, 10 deletions

arch_hardening_script.sh

@@ -4,7 +4,7 @@
4 4 # 服务器自动化加固脚本 - 多发行版增强版
5 5 # 作者: Mercas
6 6 # 日期: 2025-11-09
7 - # 版本: v4.0 Multi-Distro Enhanced
7 + # 版本: v4.2 Multi-Distro Enhanced (依赖检查版)
8 8 #
9 9 # 支持的发行版:
10 10 # - Ubuntu/Debian (apt包管理器)
@@ -15,8 +15,39 @@
15 15 # - 入侵防护: fail2ban / sshguard / crowdsec 多选一
16 16 # - AUR助手: 自动安装yay或paru
17 17 # - SSH密钥: ED25519 (替代RSA 4096)
18 + #
19 + # 命令行参数:
20 + # -y, --yes 跳过所有确认提示 (非交互模式)
21 + # -h, --help 显示帮助信息
18 22 ################################################################################
19 23
24 + # ============================================================================
25 + # 命令行参数解析
26 + # ============================================================================
27 +
28 + FORCE_YES=false
29 +
30 + while [[ $# -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
49 + done
50 +
20 51 # ============================================================================
21 52 # 全局配置
22 53 # ============================================================================
@@ -32,6 +63,90 @@ ERROR_SEVERE=2
32 63 ERROR_WARNING=3
33 64 ERROR_INFO=4
34 65
66 + # ============================================================================
67 + # 依赖检查
68 + # ============================================================================
69 +
70 + check_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 +
35 150 # ============================================================================
36 151 # 发行版检测
37 152 # ============================================================================
@@ -229,8 +344,46 @@ record_rollback_action() {
229 344 echo "$timestamp:$action_type:$target:$command" >> "$ROLLBACK_DIR/actions.log"
230 345 }
231 346
347 + # 安全获取主机名 (处理hostname命令不存在的情况)
348 + get_hostname() {
349 + if command -v hostname &> /dev/null; then
350 + hostname
351 + elif [[ -f /etc/hostname ]]; then
352 + cat /etc/hostname
353 + elif [[ -f /proc/sys/kernel/hostname ]]; then
354 + cat /proc/sys/kernel/hostname
355 + else
356 + uname -n
357 + fi
358 + }
359 +
360 + # 安全获取IP地址
361 + get_ip_address() {
362 + if command -v hostname &> /dev/null; then
363 + local ip=$(hostname -I 2>/dev/null | awk '{print $1}')
364 + if [[ -n "$ip" ]]; then
365 + echo "$ip"
366 + elif command -v ip &> /dev/null; then
367 + ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A'
368 + else
369 + echo 'N/A'
370 + fi
371 + elif command -v ip &> /dev/null; then
372 + ip addr show 2>/dev/null | grep -oP 'inet \K[\d.]+' | head -1 || echo 'N/A'
373 + elif [[ -f /proc/net/dev ]]; then
374 + grep -oP 'inet \K[\d.]+' /proc/net/fib_trie 2>/dev/null | head -1 || echo 'N/A'
375 + else
376 + echo 'N/A'
377 + fi
378 + }
379 +
232 380 confirm_action() {
233 381 local message=$1
382 + # 如果强制确认标志为真,直接返回成功
383 + if [[ "$FORCE_YES" == "true" ]]; then
384 + log_info "[自动确认] $message"
385 + return 0
386 + fi
234 387 if [[ -t 0 ]]; then
235 388 echo -e "${YELLOW}${message} [y/N]: ${NC}\c"
236 389 read -n 1 -r
@@ -240,7 +393,7 @@ confirm_action() {
240 393 fi
241 394 return 0
242 395 else
243 - log_info "非交互模式: $message - 跳过"
396 + log_info "[非交互模式跳过] $message"
244 397 return 1
245 398 fi
246 399 }
@@ -1049,7 +1202,7 @@ setup_generate_key() {
1049 1202 record_rollback_action "ssh_keygen" "$username" "rm -f $key_file $key_file.pub"
1050 1203
1051 1204 # 使用ED25519生成密钥
1052 - su - "$username" -c "ssh-keygen -t ed25519 -f '$key_file' -C '$username@$(hostname)'"
1205 + su - "$username" -c "ssh-keygen -t ed25519 -f '$key_file' -C '$username@$(get_hostname)'"
1053 1206
1054 1207 if [[ -f "$key_file" ]]; then
1055 1208 log_success "ED25519密钥对生成成功"
@@ -1064,7 +1217,7 @@ setup_generate_key() {
1064 1217 log_error "SSH密钥生成失败,回退到RSA..."
1065 1218 # 回退到RSA
1066 1219 local rsa_key_file="$ssh_dir/id_rsa"
1067 - su - "$username" -c "ssh-keygen -t rsa -b 4096 -f '$rsa_key_file' -N '' -C '$username@$(hostname)'"
1220 + su - "$username" -c "ssh-keygen -t rsa -b 4096 -f '$rsa_key_file' -N '' -C '$username@$(get_hostname)'"
1068 1221 if [[ -f "$rsa_key_file" ]]; then
1069 1222 log_success "RSA密钥对生成成功"
1070 1223 log_info "密钥文件: $rsa_key_file"
@@ -1457,8 +1610,8 @@ collect_system_info() {
1457 1610 log_section "收集系统信息"
1458 1611 log_info "操作系统: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown")"
1459 1612 log_info "内核版本: $(uname -r)"
1460 - log_info "主机名: $(hostname)"
1461 - log_info "IP地址: $(hostname -I 2>/dev/null | awk '{print $1}' || echo 'N/A')"
1613 + log_info "主机名: $(get_hostname)"
1614 + log_info "IP地址: $(get_ip_address)"
1462 1615 }
1463 1616
1464 1617 update_system() {
@@ -1487,7 +1640,7 @@ generate_report() {
1487 1640
1488 1641 local os_info=$(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 || echo "Unknown")
1489 1642 local kernel_info=$(uname -r)
1490 - local hostname_info=$(hostname)
1643 + local hostname_info=$(get_hostname)
1491 1644
1492 1645 cat > "$REPORT_FILE" << EOF
1493 1646 ================================================================================
@@ -1558,17 +1711,20 @@ main() {
1558 1711 cat << "EOF"
1559 1712 ╔══════════════════════════════════════════════════════════════╗
1560 1713 ║ 服务器安全加固脚本 (多发行版增强版) ║
1561 - ║ v4.0 - 支持 Ubuntu + Arch Linux ║
1714 + ║ v4.2 - 支持 Ubuntu + Arch Linux ║
1562 1715 ║ • 多防火墙选择 (UFW/iptables/nftables) ║
1563 1716 ║ • 多入侵防护 (Fail2ban/Sshguard/Crowdsec) ║
1564 1717 ║ • AUR助手自动安装 (yay/paru) ║
1565 1718 ║ • ED25519 SSH密钥 (替代RSA) ║
1719 + ║ • 支持 -y 参数用于非交互模式 ║
1720 + ║ • 依赖自动检查 ║
1566 1721 ╚══════════════════════════════════════════════════════════════╝
1567 1722 EOF
1568 1723 echo -e "${NC}"
1569 1724
1570 1725 check_root
1571 1726 detect_distribution
1727 + check_dependencies
1572 1728 setup_colors
1573 1729 init_rollback
1574 1730
@@ -1578,8 +1734,12 @@ EOF
1578 1734 echo -e "${YELLOW}警告: 此脚本将修改系统配置${NC}"
1579 1735
1580 1736 if ! confirm_action "确认继续?"; then
1581 - log "用户取消"
1582 - exit 0
1737 + if [[ "$FORCE_YES" == "true" ]]; then
1738 + log_info "自动模式: 继续执行加固操作"
1739 + else
1740 + log "用户取消"
1741 + exit 0
1742 + fi
1583 1743 fi
1584 1744
1585 1745 collect_system_info

passive ревизій цього gist 4 months ago. До ревизії

Без змін

passive ревизій цього gist 4 months ago. До ревизії

1 file changed, 1249 insertions, 853 deletions

arch_hardening_script.sh

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

passive ревизій цього gist 4 months ago. До ревизії

1 file changed, 1232 insertions

arch_hardening_script.sh(файл створено)

@@ -0,0 +1,1232 @@
1 + #!/bin/bash
2 +
3 + ################################################################################
4 + # Arch Linux 服务器安全加固脚本
5 + # 版本: v2.0
6 + # 日期: 2025-01-19
7 + #
8 + # 针对Arch Linux优化的工具选择:
9 + # - 防火墙: nftables (现代化,替代iptables) 或 firewalld (更易用)
10 + # - 入侵防护: sshguard (轻量级,原生nftables支持) 或 crowdsec (社区驱动)
11 + # - 包管理: pacman (Arch原生)
12 + # - 安全工具: 优先使用官方仓库和AUR
13 + ################################################################################
14 +
15 + export TERM=xterm-color
16 + set +H
17 + set +e
18 +
19 + # 颜色定义
20 + if command -v tput &> /dev/null; then
21 + RED=$(tput setaf 1 2>/dev/null || echo '\033[0;31m')
22 + GREEN=$(tput setaf 2 2>/dev/null || echo '\033[0;32m')
23 + YELLOW=$(tput setaf 3 2>/dev/null || echo '\033[1;33m')
24 + BLUE=$(tput setaf 4 2>/dev/null || echo '\033[0;34m')
25 + CYAN=$(tput setaf 6 2>/dev/null || echo '\033[0;36m')
26 + MAGENTA=$(tput setaf 5 2>/dev/null || echo '\033[0;35m')
27 + NC=$(tput sgr0 2>/dev/null || echo '\033[0m')
28 + else
29 + RED='\033[0;31m'
30 + GREEN='\033[0;32m'
31 + YELLOW='\033[1;33m'
32 + BLUE='\033[0;34m'
33 + CYAN='\033[0;36m'
34 + MAGENTA='\033[0;35m'
35 + NC='\033[0m'
36 + fi
37 +
38 + # 日志和备份目录
39 + LOG_FILE="/var/log/arch_hardening_$(date +%Y%m%d_%H%M%S).log"
40 + BACKUP_DIR="/root/security_backup_$(date +%Y%m%d_%H%M%S)"
41 + ROLLBACK_SCRIPT="$BACKUP_DIR/rollback.sh"
42 +
43 + # 全局变量
44 + FIREWALL_CHOICE=""
45 + IPS_CHOICE=""
46 +
47 + ################################################################################
48 + # 工具函数
49 + ################################################################################
50 +
51 + log() {
52 + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
53 + }
54 +
55 + log_info() {
56 + echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
57 + }
58 +
59 + log_warning() {
60 + echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
61 + }
62 +
63 + log_error() {
64 + echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
65 + }
66 +
67 + log_success() {
68 + echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
69 + }
70 +
71 + log_critical() {
72 + echo -e "${RED}[CRITICAL]${NC} $1" | tee -a "$LOG_FILE"
73 + }
74 +
75 + log_section() {
76 + echo -e "\n${CYAN}========================================${NC}" | tee -a "$LOG_FILE"
77 + echo -e "${CYAN}$1${NC}" | tee -a "$LOG_FILE"
78 + echo -e "${CYAN}========================================${NC}\n" | tee -a "$LOG_FILE"
79 + }
80 +
81 + check_root() {
82 + if [[ $EUID -ne 0 ]]; then
83 + log_error "此脚本必须以root权限运行"
84 + exit 1
85 + fi
86 + }
87 +
88 + backup_file() {
89 + local file=$1
90 +
91 + if [[ ! -f "$file" ]]; then
92 + log_warning "文件不存在,跳过备份: $file"
93 + return 1
94 + fi
95 +
96 + mkdir -p "$BACKUP_DIR"
97 +
98 + local backup_name="$(basename $file).$(date +%Y%m%d_%H%M%S).bak"
99 + local backup_path="$BACKUP_DIR/$backup_name"
100 +
101 + if cp -p "$file" "$backup_path" 2>/dev/null; then
102 + if cmp -s "$file" "$backup_path"; then
103 + log_success "✓ 备份成功: $file -> $backup_name"
104 + echo "cp -p '$backup_path' '$file'" >> "$ROLLBACK_SCRIPT"
105 + return 0
106 + else
107 + log_error "✗ 备份验证失败: $file"
108 + rm -f "$backup_path"
109 + return 1
110 + fi
111 + else
112 + log_error "✗ 备份失败: $file"
113 + return 1
114 + fi
115 + }
116 +
117 + confirm_action() {
118 + local message=$1
119 + if [[ -t 0 ]]; then
120 + echo -e "${YELLOW}${message} [y/N]: ${NC}\c"
121 + read -n 1 -r
122 + echo
123 + if [[ ! $REPLY =~ ^[Yy]$ ]]; then
124 + return 1
125 + fi
126 + return 0
127 + else
128 + log_info "非交互模式: $message - 跳过"
129 + return 1
130 + fi
131 + }
132 +
133 + safe_execute() {
134 + local cmd="$1"
135 + local description="$2"
136 + local critical="${3:-false}"
137 +
138 + log_info "执行: $description"
139 +
140 + if eval "$cmd" >> "$LOG_FILE" 2>&1; then
141 + log_success "✓ 成功: $description"
142 + return 0
143 + else
144 + local exit_code=$?
145 +
146 + if [[ "$critical" == "true" ]]; then
147 + log_critical "✗ 关键操作失败: $description (退出码: $exit_code)"
148 + log_critical "脚本终止,可使用回滚: bash $ROLLBACK_SCRIPT"
149 + exit $exit_code
150 + else
151 + log_warning "⚠ 警告: $description 失败 (退出码: $exit_code)"
152 + return $exit_code
153 + fi
154 + fi
155 + }
156 +
157 + check_port_available() {
158 + local port=$1
159 +
160 + log_info "检查端口 $port 可用性..."
161 +
162 + if ss -tuln 2>/dev/null | grep -q ":$port " || netstat -tuln 2>/dev/null | grep -q ":$port "; then
163 + log_error "✗ 端口 $port 已被占用:"
164 + ss -tlnp 2>/dev/null | grep ":$port " || netstat -tlnp 2>/dev/null | grep ":$port "
165 + return 1
166 + fi
167 +
168 + log_success "✓ 端口 $port 可用"
169 + return 0
170 + }
171 +
172 + verify_ssh_keys_exist() {
173 + log_info "验证SSH密钥配置..."
174 +
175 + local has_keys=false
176 + local key_locations=()
177 +
178 + for user_home in /home/* /root; do
179 + local authorized_keys="$user_home/.ssh/authorized_keys"
180 +
181 + if [[ -f "$authorized_keys" ]]; then
182 + local key_count=$(grep -c "^ssh-" "$authorized_keys" 2>/dev/null || echo 0)
183 +
184 + if [[ $key_count -gt 0 ]]; then
185 + has_keys=true
186 + key_locations+=("$authorized_keys ($key_count 个密钥)")
187 + log_success "✓ 找到 $key_count 个SSH密钥: $authorized_keys"
188 + fi
189 + fi
190 + done
191 +
192 + if [[ "$has_keys" == true ]]; then
193 + log_success "✓ SSH密钥验证通过"
194 + printf '%s\n' "${key_locations[@]}"
195 + return 0
196 + else
197 + log_error "✗ 未找到任何SSH密钥配置"
198 + return 1
199 + fi
200 + }
201 +
202 + verify_ssh_config() {
203 + local ssh_config="/etc/ssh/sshd_config"
204 +
205 + log_info "验证SSH配置语法..."
206 +
207 + if sshd -t -f "$ssh_config" 2>&1 | tee -a "$LOG_FILE"; then
208 + log_success "✓ SSH配置语法正确"
209 + return 0
210 + else
211 + log_error "✗ SSH配置语法错误"
212 + return 1
213 + fi
214 + }
215 +
216 + ################################################################################
217 + # Arch Linux 特有函数
218 + ################################################################################
219 +
220 + # 检查是否为Arch Linux
221 + check_arch_linux() {
222 + if [[ ! -f /etc/arch-release ]]; then
223 + log_error "此脚本仅支持Arch Linux系统"
224 + exit 1
225 + fi
226 +
227 + log_success "✓ 检测到Arch Linux系统"
228 + }
229 +
230 + # 安装AUR助手(如果需要)
231 + install_aur_helper() {
232 + if command -v yay &> /dev/null; then
233 + log_info "AUR助手已安装: yay"
234 + return 0
235 + fi
236 +
237 + if command -v paru &> /dev/null; then
238 + log_info "AUR助手已安装: paru"
239 + return 0
240 + fi
241 +
242 + if confirm_action "是否安装AUR助手 yay?(用于安装AUR包)"; then
243 + log_info "安装yay..."
244 +
245 + # 需要非root用户安装yay
246 + local build_user="nobody"
247 + if id "nobody" &>/dev/null; then
248 + cd /tmp
249 + safe_execute "pacman -S --needed --noconfirm git base-devel" "安装构建依赖" false
250 + safe_execute "git clone https://aur.archlinux.org/yay.git" "克隆yay仓库" false
251 + safe_execute "chown -R nobody:nobody /tmp/yay" "设置权限" false
252 + safe_execute "cd yay && sudo -u nobody makepkg -si --noconfirm" "构建yay" false
253 + cd -
254 + fi
255 + fi
256 + }
257 +
258 + ################################################################################
259 + # 主要加固功能
260 + ################################################################################
261 +
262 + collect_system_info() {
263 + log_section "收集系统信息"
264 +
265 + log_info "操作系统: Arch Linux $(uname -r)"
266 + log_info "内核版本: $(uname -r)"
267 + log_info "主机名: $(hostname)"
268 + log_info "IP地址: $(hostname -I 2>/dev/null | awk '{print $1}' || echo "无法获取")"
269 + log_info "当前用户: $(whoami)"
270 + log_info "包管理器: pacman $(pacman --version | head -1)"
271 +
272 + echo "系统信息收集完成" >> "$LOG_FILE"
273 + }
274 +
275 + update_system() {
276 + log_section "系统更新"
277 +
278 + if confirm_action "是否执行系统更新?"; then
279 + # 更新pacman数据库
280 + safe_execute "pacman -Sy" "同步包数据库" false
281 +
282 + # 升级系统
283 + safe_execute "pacman -Su --noconfirm" "升级系统" false
284 +
285 + # 清理孤立包
286 + safe_execute "pacman -Rns \$(pacman -Qtdq) --noconfirm" "清理孤立包" false 2>/dev/null || true
287 +
288 + # 清理缓存
289 + safe_execute "pacman -Sc --noconfirm" "清理包缓存" false
290 +
291 + log_success "系统更新完成"
292 + else
293 + log_warning "跳过系统更新"
294 + fi
295 + }
296 +
297 + manage_users() {
298 + log_section "用户管理和SSH密钥配置"
299 +
300 + if ! confirm_action "是否创建专用管理用户?"; then
301 + log_warning "跳过用户创建"
302 + return 0
303 + fi
304 +
305 + local username=""
306 +
307 + if [[ -t 0 ]]; then
308 + echo -e "${YELLOW}请输入新用户名 (建议:admin、operator、sysadmin): ${NC}\c"
309 + read -p "" username
310 + else
311 + username="admin"
312 + fi
313 +
314 + if [[ -z "$username" ]] || [[ ! "$username" =~ ^[a-z_][a-z0-9_-]*$ ]]; then
315 + log_error "无效的用户名: $username"
316 + return 1
317 + fi
318 +
319 + if id "$username" &>/dev/null 2>&1; then
320 + log_info "用户 $username 已存在"
321 + if ! confirm_action "用户已存在,是否继续配置?"; then
322 + return 0
323 + fi
324 + else
325 + log_info "创建用户: $username"
326 + safe_execute "useradd -m -G wheel -s /bin/bash '$username'" "创建用户并添加到wheel组" false
327 +
328 + if [[ -t 0 ]]; then
329 + echo -e "${YELLOW}为用户 $username 设置密码: ${NC}"
330 + passwd "$username"
331 + else
332 + local temp_password=$(openssl rand -base64 32)
333 + echo "$username:$temp_password" | chpasswd
334 + log_warning "临时密码: $temp_password"
335 + fi
336 + fi
337 +
338 + # 配置SSH密钥
339 + local user_home=$(eval echo "~$username")
340 + local ssh_dir="$user_home/.ssh"
341 + local authorized_keys="$ssh_dir/authorized_keys"
342 +
343 + if [[ -t 0 ]]; then
344 + echo -e "${CYAN}选择SSH密钥配置方式:${NC}"
345 + echo "1) 粘贴现有公钥(推荐)"
346 + echo "2) 自动生成新密钥对"
347 + echo "3) 跳过"
348 + echo -e "${YELLOW}选择 [1-3]: ${NC}\c"
349 + read -p "" key_option
350 + else
351 + key_option="3"
352 + fi
353 +
354 + case $key_option in
355 + 1)
356 + echo -e "${YELLOW}粘贴SSH公钥: ${NC}"
357 + read -p "" public_key
358 +
359 + if [[ -n "$public_key" ]] && [[ "$public_key" =~ ^ssh- ]]; then
360 + mkdir -p "$ssh_dir"
361 + echo "$public_key" >> "$authorized_keys"
362 + chown -R "$username:$username" "$ssh_dir"
363 + chmod 700 "$ssh_dir"
364 + chmod 600 "$authorized_keys"
365 + log_success "✓ SSH公钥已添加"
366 + fi
367 + ;;
368 + 2)
369 + mkdir -p "$ssh_dir"
370 + chown "$username:$username" "$ssh_dir"
371 +
372 + local key_file="$ssh_dir/id_ed25519"
373 +
374 + if [[ ! -f "$key_file" ]]; then
375 + log_info "生成ED25519密钥对(更安全)..."
376 + su - "$username" -c "ssh-keygen -t ed25519 -f '$key_file' -N '' -C '$username@$(hostname)'"
377 +
378 + if [[ -f "$key_file" ]]; then
379 + log_success "✓ SSH密钥对生成成功"
380 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
381 + echo -e "${YELLOW}公钥内容:${NC}"
382 + cat "$key_file.pub"
383 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
384 + fi
385 + fi
386 + ;;
387 + esac
388 +
389 + # 配置sudo权限
390 + configure_sudo_access "$username"
391 +
392 + if id "$username" &>/dev/null; then
393 + log_success "✓ 用户 $username 配置完成"
394 + fi
395 + }
396 +
397 + configure_sudo_access() {
398 + local username=$1
399 +
400 + log_info "配置sudo权限..."
401 +
402 + # 确保wheel组可以使用sudo
403 + if ! grep -q "^%wheel ALL=(ALL:ALL) ALL" /etc/sudoers; then
404 + backup_file "/etc/sudoers"
405 + echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers
406 + fi
407 +
408 + if [[ -t 0 ]]; then
409 + echo -e "${CYAN}选择sudo配置:${NC}"
410 + echo "1) 需要密码(推荐)"
411 + echo "2) 无需密码"
412 + echo "3) 部分命令无需密码"
413 + echo -e "${YELLOW}选择 [1-3]: ${NC}\c"
414 + read -p "" sudo_option
415 + else
416 + sudo_option="1"
417 + fi
418 +
419 + local sudoers_file="/etc/sudoers.d/$username"
420 +
421 + case $sudo_option in
422 + 1)
423 + echo "$username ALL=(ALL:ALL) ALL" > "$sudoers_file"
424 + log_success "✓ sudo需要密码"
425 + ;;
426 + 2)
427 + echo "$username ALL=(ALL) NOPASSWD:ALL" > "$sudoers_file"
428 + log_warning "⚠ sudo无需密码"
429 + ;;
430 + 3)
431 + cat > "$sudoers_file" << EOF
432 + $username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/systemctl status *
433 + $username ALL=(ALL) NOPASSWD: /usr/bin/pacman -Syu, /usr/bin/pacman -Sy
434 + $username ALL=(ALL) ALL
435 + EOF
436 + log_success "✓ 部分命令无需密码"
437 + ;;
438 + esac
439 +
440 + chmod 440 "$sudoers_file"
441 +
442 + if visudo -c -f "$sudoers_file" >/dev/null 2>&1; then
443 + log_success "✓ sudoers配置验证通过"
444 + else
445 + log_error "✗ sudoers配置错误,已删除"
446 + rm -f "$sudoers_file"
447 + fi
448 + }
449 +
450 + harden_ssh() {
451 + log_section "SSH安全加固"
452 +
453 + local ssh_config="/etc/ssh/sshd_config"
454 +
455 + if [[ ! -f "$ssh_config" ]]; then
456 + log_error "SSH配置文件不存在"
457 + return 1
458 + fi
459 +
460 + backup_file "$ssh_config"
461 +
462 + # 禁用root登录
463 + if confirm_action "是否禁用SSH root登录?"; then
464 + sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' "$ssh_config"
465 + log_success "✓ 已禁用root登录"
466 + fi
467 +
468 + # 禁用密码认证
469 + if confirm_action "是否禁用SSH密码认证?"; then
470 + echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
471 + echo -e "${RED}⚠ 警告:禁用密码认证后只能通过SSH密钥登录${NC}"
472 + echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
473 +
474 + if ! verify_ssh_keys_exist; then
475 + log_critical "✗ 未找到SSH密钥!"
476 + if confirm_action "是否现在配置SSH密钥?"; then
477 + manage_users
478 + if ! verify_ssh_keys_exist; then
479 + log_error "密钥配置失败,跳过禁用密码认证"
480 + return 1
481 + fi
482 + else
483 + log_warning "跳过禁用密码认证"
484 + return 0
485 + fi
486 + fi
487 +
488 + if confirm_action "确认禁用密码认证?"; then
489 + sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' "$ssh_config"
490 + log_success "✓ 已禁用密码认证"
491 + fi
492 + fi
493 +
494 + # 修改SSH端口
495 + local new_port=""
496 + if [[ -t 0 ]]; then
497 + echo -e "${YELLOW}当前SSH端口: $(grep "^Port" "$ssh_config" 2>/dev/null | awk '{print $2}' || echo 22)${NC}"
498 + echo -e "${YELLOW}输入新端口号(1024-65535,留空跳过): ${NC}\c"
499 + read -p "" new_port
500 +
501 + if [[ -n "$new_port" ]]; then
502 + if [[ "$new_port" =~ ^[0-9]+$ ]] && [[ "$new_port" -gt 1024 ]] && [[ "$new_port" -lt 65536 ]]; then
503 + if check_port_available "$new_port"; then
504 + sed -i "s/^#*Port.*/Port $new_port/" "$ssh_config"
505 + log_success "✓ SSH端口已修改为: $new_port"
506 + else
507 + new_port=""
508 + fi
509 + fi
510 + fi
511 + fi
512 +
513 + # 其他安全配置
514 + cat >> "$ssh_config" << 'EOF'
515 +
516 + # Arch Linux 安全加固配置
517 + Protocol 2
518 + MaxAuthTries 3
519 + MaxSessions 2
520 + LoginGraceTime 60
521 + ClientAliveInterval 300
522 + ClientAliveCountMax 2
523 + PermitEmptyPasswords no
524 + X11Forwarding no
525 + UseDNS no
526 + EOF
527 +
528 + if ! verify_ssh_config; then
529 + log_critical "✗ SSH配置验证失败!正在回滚..."
530 + local backup_file=$(ls -t "$BACKUP_DIR"/sshd_config*.bak 2>/dev/null | head -1)
531 + if [[ -n "$backup_file" ]]; then
532 + cp -p "$backup_file" "$ssh_config"
533 + log_success "✓ 已回滚"
534 + fi
535 + return 1
536 + fi
537 +
538 + log_success "✓ SSH配置验证通过"
539 +
540 + if confirm_action "是否立即重启SSH服务?"; then
541 + if systemctl restart sshd; then
542 + log_success "✓ SSH服务重启成功"
543 + fi
544 + fi
545 + }
546 +
547 + # 选择防火墙
548 + choose_firewall() {
549 + log_section "选择防火墙方案"
550 +
551 + echo -e "${CYAN}Arch Linux 防火墙选项:${NC}"
552 + echo "1) nftables (现代化,推荐,原生内核支持)"
553 + echo "2) firewalld (易用,动态管理)"
554 + echo "3) ufw (简单,兼容iptables-nft)"
555 + echo "4) 跳过防火墙配置"
556 +
557 + if [[ -t 0 ]]; then
558 + echo -e "${YELLOW}选择 [1-4]: ${NC}\c"
559 + read -p "" fw_choice
560 + else
561 + fw_choice="1"
562 + fi
563 +
564 + case $fw_choice in
565 + 1) FIREWALL_CHOICE="nftables" ;;
566 + 2) FIREWALL_CHOICE="firewalld" ;;
567 + 3) FIREWALL_CHOICE="ufw" ;;
568 + 4) FIREWALL_CHOICE="none" ;;
569 + *) FIREWALL_CHOICE="nftables" ;;
570 + esac
571 +
572 + log_info "已选择防火墙: $FIREWALL_CHOICE"
573 + }
574 +
575 + # 配置nftables
576 + configure_nftables() {
577 + log_section "配置nftables防火墙"
578 +
579 + # 安装nftables
580 + if ! command -v nft &> /dev/null; then
581 + safe_execute "pacman -S --noconfirm nftables" "安装nftables" true
582 + fi
583 +
584 + # 获取SSH端口
585 + local ssh_port=$(grep "^Port" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')
586 + ssh_port=${ssh_port:-22}
587 +
588 + log_info "检测到SSH端口: $ssh_port"
589 +
590 + # 创建nftables配置
591 + local nft_conf="/etc/nftables.conf"
592 + backup_file "$nft_conf" 2>/dev/null || true
593 +
594 + cat > "$nft_conf" << EOF
595 + #!/usr/bin/nft -f
596 + # Arch Linux 防火墙配置
597 +
598 + # 清空现有规则
599 + flush ruleset
600 +
601 + # 定义表
602 + table inet filter {
603 + # 输入链
604 + chain input {
605 + type filter hook input priority 0; policy drop;
606 +
607 + # 允许环回接口
608 + iif lo accept
609 +
610 + # 允许已建立的连接
611 + ct state established,related accept
612 +
613 + # 允许SSH
614 + tcp dport $ssh_port accept
615 +
616 + # 允许HTTP/HTTPS (如果需要)
617 + # tcp dport { 80, 443 } accept
618 +
619 + # 允许ICMP (ping)
620 + icmp type echo-request limit rate 1/second accept
621 + icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
622 +
623 + # 记录并拒绝其他流量
624 + counter drop
625 + }
626 +
627 + # 转发链
628 + chain forward {
629 + type filter hook forward priority 0; policy drop;
630 + }
631 +
632 + # 输出链
633 + chain output {
634 + type filter hook output priority 0; policy accept;
635 + }
636 + }
637 + EOF
638 +
639 + # 询问是否开放其他端口
640 + if confirm_action "是否开放HTTP(80)?"; then
641 + safe_execute "ufw allow 80/tcp comment 'HTTP'" "开放HTTP" false
642 + fi
643 +
644 + if confirm_action "是否开放HTTPS(443)?"; then
645 + safe_execute "ufw allow 443/tcp comment 'HTTPS'" "开放HTTPS" false
646 + fi
647 +
648 + echo "y" | ufw enable >> "$LOG_FILE" 2>&1
649 +
650 + ufw status verbose
651 +
652 + # 启用UFW服务
653 + safe_execute "systemctl enable --now ufw.service" "启用UFW服务" false
654 +
655 + log_success "✓ UFW配置完成"
656 + }
657 +
658 + # 防火墙配置入口
659 + configure_firewall() {
660 + choose_firewall
661 +
662 + case $FIREWALL_CHOICE in
663 + "nftables")
664 + configure_nftables
665 + ;;
666 + "firewalld")
667 + configure_firewalld
668 + ;;
669 + "ufw")
670 + configure_ufw
671 + ;;
672 + "none")
673 + log_warning "跳过防火墙配置"
674 + ;;
675 + esac
676 + }
677 +
678 + # 选择入侵防护系统
679 + choose_ips() {
680 + log_section "选择入侵防护系统"
681 +
682 + echo -e "${CYAN}Arch Linux IPS选项:${NC}"
683 + echo "1) sshguard (轻量级,原生nftables/firewalld支持,推荐)"
684 + echo "2) crowdsec (现代化,社区驱动,支持多种防火墙)"
685 + echo "3) fail2ban (传统,功能全面)"
686 + echo "4) 跳过"
687 +
688 + if [[ -t 0 ]]; then
689 + echo -e "${YELLOW}选择 [1-4]: ${NC}\c"
690 + read -p "" ips_choice
691 + else
692 + ips_choice="1"
693 + fi
694 +
695 + case $ips_choice in
696 + 1) IPS_CHOICE="sshguard" ;;
697 + 2) IPS_CHOICE="crowdsec" ;;
698 + 3) IPS_CHOICE="fail2ban" ;;
699 + 4) IPS_CHOICE="none" ;;
700 + *) IPS_CHOICE="sshguard" ;;
701 + esac
702 +
703 + log_info "已选择IPS: $IPS_CHOICE"
704 + }
705 +
706 + # 配置sshguard
707 + configure_sshguard() {
708 + log_section "配置SSHGuard"
709 +
710 + if ! command -v sshguard &> /dev/null; then
711 + safe_execute "pacman -S --noconfirm sshguard" "安装sshguard" false
712 + fi
713 +
714 + # 根据防火墙选择后端
715 + local backend=""
716 + case $FIREWALL_CHOICE in
717 + "nftables")
718 + backend="nftables"
719 + ;;
720 + "firewalld")
721 + backend="firewalld"
722 + ;;
723 + "ufw")
724 + backend="iptables"
725 + ;;
726 + *)
727 + backend="nftables"
728 + ;;
729 + esac
730 +
731 + log_info "使用防火墙后端: $backend"
732 +
733 + # 创建配置
734 + local sshguard_conf="/etc/sshguard.conf"
735 + backup_file "$sshguard_conf" 2>/dev/null || true
736 +
737 + cat > "$sshguard_conf" << EOF
738 + # SSHGuard 配置
739 + BACKEND="$backend"
740 + LOGREADER="LANG=C /usr/bin/journalctl -afb -p info -n1 -t sshd -o cat"
741 + THRESHOLD=30
742 + BLOCK_TIME=3600
743 + DETECTION_TIME=600
744 + EOF
745 +
746 + # 启用服务
747 + safe_execute "systemctl enable --now sshguard.service" "启用sshguard" false
748 +
749 + # 检查状态
750 + systemctl status sshguard.service --no-pager || true
751 +
752 + log_success "✓ SSHGuard配置完成"
753 + }
754 +
755 + # 配置CrowdSec
756 + configure_crowdsec() {
757 + log_section "配置CrowdSec"
758 +
759 + log_info "CrowdSec需要从AUR安装..."
760 +
761 + if command -v yay &> /dev/null; then
762 + safe_execute "yay -S --noconfirm crowdsec crowdsec-firewall-bouncer-nftables" "安装CrowdSec" false
763 + elif command -v paru &> /dev/null; then
764 + safe_execute "paru -S --noconfirm crowdsec crowdsec-firewall-bouncer-nftables" "安装CrowdSec" false
765 + else
766 + log_warning "未安装AUR助手,跳过CrowdSec安装"
767 + return 1
768 + fi
769 +
770 + # 启用服务
771 + safe_execute "systemctl enable --now crowdsec.service" "启用CrowdSec" false
772 +
773 + # 配置bouncer
774 + safe_execute "cscli bouncers add firewall-bouncer" "添加防火墙bouncer" false
775 +
776 + log_success "✓ CrowdSec配置完成"
777 + }
778 +
779 + # 配置Fail2ban
780 + configure_fail2ban() {
781 + log_section "配置Fail2ban"
782 +
783 + if ! command -v fail2ban-client &> /dev/null; then
784 + safe_execute "pacman -S --noconfirm fail2ban" "安装fail2ban" false
785 + fi
786 +
787 + local ssh_port=$(grep "^Port" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')
788 + ssh_port=${ssh_port:-ssh}
789 +
790 + local jail_local="/etc/fail2ban/jail.local"
791 + backup_file "$jail_local" 2>/dev/null || true
792 +
793 + cat > "$jail_local" << EOF
794 + [DEFAULT]
795 + bantime = 3600
796 + findtime = 600
797 + maxretry = 5
798 +
799 + [sshd]
800 + enabled = true
801 + port = $ssh_port
802 + filter = sshd
803 + backend = systemd
804 + maxretry = 3
805 + bantime = 7200
806 + EOF
807 +
808 + safe_execute "systemctl enable --now fail2ban.service" "启用fail2ban" false
809 +
810 + sleep 2
811 + fail2ban-client status 2>/dev/null || true
812 +
813 + log_success "✓ Fail2ban配置完成"
814 + }
815 +
816 + # IPS配置入口
817 + configure_ips() {
818 + choose_ips
819 +
820 + case $IPS_CHOICE in
821 + "sshguard")
822 + configure_sshguard
823 + ;;
824 + "crowdsec")
825 + configure_crowdsec
826 + ;;
827 + "fail2ban")
828 + configure_fail2ban
829 + ;;
830 + "none")
831 + log_warning "跳过IPS配置"
832 + ;;
833 + esac
834 + }
835 +
836 + # 密码策略
837 + harden_users() {
838 + log_section "密码策略加固"
839 +
840 + # 安装pam_pwquality
841 + if ! pacman -Q libpwquality &> /dev/null; then
842 + safe_execute "pacman -S --noconfirm libpwquality" "安装密码质量检查" false
843 + fi
844 +
845 + local pwquality_conf="/etc/security/pwquality.conf"
846 + backup_file "$pwquality_conf"
847 +
848 + cat >> "$pwquality_conf" << 'EOF'
849 +
850 + # Arch Linux 密码策略
851 + minlen = 12
852 + dcredit = -1
853 + ucredit = -1
854 + lcredit = -1
855 + ocredit = -1
856 + maxrepeat = 3
857 + EOF
858 +
859 + # 配置PAM
860 + local common_password="/etc/pam.d/passwd"
861 + if [[ -f "$common_password" ]]; then
862 + backup_file "$common_password"
863 +
864 + if ! grep -q "pam_pwquality.so" "$common_password"; then
865 + sed -i '/password.*required/a password required pam_pwquality.so retry=3' "$common_password"
866 + fi
867 + fi
868 +
869 + log_success "✓ 密码策略配置完成"
870 + }
871 +
872 + # 内核参数加固
873 + harden_kernel() {
874 + log_section "内核参数加固"
875 +
876 + local sysctl_conf="/etc/sysctl.d/99-security.conf"
877 + backup_file "$sysctl_conf" 2>/dev/null || true
878 +
879 + cat > "$sysctl_conf" << 'EOF'
880 + # Arch Linux 内核安全配置
881 + net.ipv4.ip_forward = 0
882 + net.ipv6.conf.all.forwarding = 0
883 + net.ipv4.tcp_syncookies = 1
884 + net.ipv4.conf.all.accept_redirects = 0
885 + net.ipv6.conf.all.accept_redirects = 0
886 + net.ipv4.conf.default.accept_redirects = 0
887 + net.ipv6.conf.default.accept_redirects = 0
888 + net.ipv4.conf.all.secure_redirects = 0
889 + net.ipv4.conf.default.secure_redirects = 0
890 + net.ipv4.conf.all.accept_source_route = 0
891 + net.ipv6.conf.all.accept_source_route = 0
892 + net.ipv4.conf.default.accept_source_route = 0
893 + net.ipv6.conf.default.accept_source_route = 0
894 + net.ipv4.conf.all.log_martians = 1
895 + net.ipv4.conf.default.log_martians = 1
896 + net.ipv4.icmp_echo_ignore_broadcasts = 1
897 + net.ipv4.conf.all.rp_filter = 1
898 + net.ipv4.conf.default.rp_filter = 1
899 + net.ipv4.tcp_max_syn_backlog = 2048
900 + net.ipv4.tcp_synack_retries = 2
901 + net.ipv4.tcp_syn_retries = 5
902 + kernel.dmesg_restrict = 1
903 + kernel.kptr_restrict = 2
904 + EOF
905 +
906 + safe_execute "sysctl -p '$sysctl_conf'" "应用内核参数" false
907 +
908 + log_success "✓ 内核参数配置完成"
909 + }
910 +
911 + # 安装安全工具
912 + install_security_tools() {
913 + log_section "安装安全工具"
914 +
915 + local tools=(
916 + "rkhunter:Rootkit检测"
917 + "lynis:安全审计"
918 + "aide:文件完整性检查"
919 + "logwatch:日志分析"
920 + )
921 +
922 + for tool_desc in "${tools[@]}"; do
923 + local tool="${tool_desc%:*}"
924 + local desc="${tool_desc#*:}"
925 +
926 + if confirm_action "是否安装 $tool ($desc)?"; then
927 + if pacman -Q "$tool" &> /dev/null; then
928 + log_info "$tool 已安装"
929 + continue
930 + fi
931 +
932 + safe_execute "pacman -S --noconfirm '$tool'" "安装 $tool" false
933 +
934 + # 特殊配置
935 + case "$tool" in
936 + "rkhunter")
937 + safe_execute "rkhunter --update" "更新rkhunter" false
938 + safe_execute "rkhunter --propupd" "更新属性" false
939 + ;;
940 + esac
941 + fi
942 + done
943 + }
944 +
945 + # 配置自动更新
946 + configure_auto_updates() {
947 + log_section "配置自动更新"
948 +
949 + echo -e "${CYAN}Arch Linux 自动更新选项:${NC}"
950 + echo "1) 使用systemd timer (推荐)"
951 + echo "2) 跳过"
952 +
953 + if [[ -t 0 ]]; then
954 + echo -e "${YELLOW}选择 [1-2]: ${NC}\c"
955 + read -p "" update_choice
956 + else
957 + update_choice="2"
958 + fi
959 +
960 + if [[ "$update_choice" == "1" ]]; then
961 + # 创建更新脚本
962 + cat > /usr/local/bin/arch-auto-update.sh << 'EOF'
963 + #!/bin/bash
964 + /usr/bin/pacman -Syu --noconfirm >> /var/log/arch-auto-update.log 2>&1
965 + EOF
966 + chmod +x /usr/local/bin/arch-auto-update.sh
967 +
968 + # 创建systemd service
969 + cat > /etc/systemd/system/arch-auto-update.service << 'EOF'
970 + [Unit]
971 + Description=Arch Linux Auto Update
972 + After=network-online.target
973 +
974 + [Service]
975 + Type=oneshot
976 + ExecStart=/usr/local/bin/arch-auto-update.sh
977 + EOF
978 +
979 + # 创建systemd timer
980 + cat > /etc/systemd/system/arch-auto-update.timer << 'EOF'
981 + [Unit]
982 + Description=Arch Linux Auto Update Timer
983 +
984 + [Timer]
985 + OnCalendar=daily
986 + Persistent=true
987 +
988 + [Install]
989 + WantedBy=timers.target
990 + EOF
991 +
992 + safe_execute "systemctl enable --now arch-auto-update.timer" "启用自动更新" false
993 +
994 + log_success "✓ 自动更新配置完成(每天执行)"
995 + else
996 + log_warning "跳过自动更新配置"
997 + fi
998 + }
999 +
1000 + # 生成报告
1001 + generate_report() {
1002 + log_section "生成安全报告"
1003 +
1004 + local report_file="/root/arch_hardening_report_$(date +%Y%m%d_%H%M%S).txt"
1005 +
1006 + cat > "$report_file" << EOF
1007 + ================================================================================
1008 + Arch Linux 服务器安全加固报告 v2.0
1009 + ================================================================================
1010 + 生成时间: $(date)
1011 + 主机名: $(hostname)
1012 + 内核版本: $(uname -r)
1013 + 包管理器: $(pacman --version | head -1)
1014 +
1015 + --------------------------------------------------------------------------------
1016 + 1. SSH配置
1017 + --------------------------------------------------------------------------------
1018 + $(grep -E "^(Port|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 2>/dev/null)
1019 +
1020 + --------------------------------------------------------------------------------
1021 + 2. 防火墙: $FIREWALL_CHOICE
1022 + --------------------------------------------------------------------------------
1023 + $(case $FIREWALL_CHOICE in
1024 + "nftables") nft list ruleset 2>/dev/null ;;
1025 + "firewalld") firewall-cmd --list-all 2>/dev/null ;;
1026 + "ufw") ufw status verbose 2>/dev/null ;;
1027 + *) echo "未配置" ;;
1028 + esac)
1029 +
1030 + --------------------------------------------------------------------------------
1031 + 3. 入侵防护: $IPS_CHOICE
1032 + --------------------------------------------------------------------------------
1033 + $(case $IPS_CHOICE in
1034 + "sshguard") systemctl status sshguard --no-pager 2>/dev/null || echo "未运行" ;;
1035 + "crowdsec") cscli metrics 2>/dev/null || echo "未运行" ;;
1036 + "fail2ban") fail2ban-client status 2>/dev/null || echo "未运行" ;;
1037 + *) echo "未配置" ;;
1038 + esac)
1039 +
1040 + --------------------------------------------------------------------------------
1041 + 4. SSH密钥配置
1042 + --------------------------------------------------------------------------------
1043 + $(for home in /home/* /root; do
1044 + if [[ -f "$home/.ssh/authorized_keys" ]]; then
1045 + echo "$(basename $home): $(grep -c '^ssh-' "$home/.ssh/authorized_keys" 2>/dev/null || echo 0) 个密钥"
1046 + fi
1047 + done)
1048 +
1049 + --------------------------------------------------------------------------------
1050 + 备份目录: $BACKUP_DIR
1051 + 日志文件: $LOG_FILE
1052 + 回滚脚本: $ROLLBACK_SCRIPT
1053 + --------------------------------------------------------------------------------
1054 + EOF
1055 +
1056 + log_success "✓ 报告已生成: $report_file"
1057 +
1058 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
1059 + cat "$report_file"
1060 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
1061 + }
1062 +
1063 + ################################################################################
1064 + # 主程序
1065 + ################################################################################
1066 +
1067 + main() {
1068 + clear
1069 +
1070 + echo -e "${BLUE}"
1071 + cat << "EOF"
1072 + ╔══════════════════════════════════════════════════════════════╗
1073 + ║ Arch Linux 服务器安全加固脚本 v2.0 ║
1074 + ║ 针对Arch Linux优化的现代化工具选择 ║
1075 + ╚══════════════════════════════════════════════════════════════╝
1076 +
1077 + 防火墙选项:
1078 + • nftables (现代化,推荐)
1079 + • firewalld (易用)
1080 + • ufw (简单)
1081 +
1082 + 入侵防护:
1083 + • sshguard (轻量,原生支持nftables/firewalld)
1084 + • crowdsec (社区驱动,现代化)
1085 + • fail2ban (传统,功能全面)
1086 +
1087 + EOF
1088 + echo -e "${NC}"
1089 +
1090 + check_root
1091 + check_arch_linux
1092 +
1093 + # 初始化
1094 + mkdir -p "$BACKUP_DIR"
1095 + echo "#!/bin/bash" > "$ROLLBACK_SCRIPT"
1096 + echo "# Arch Linux 回滚脚本 - $(date)" >> "$ROLLBACK_SCRIPT"
1097 + chmod +x "$ROLLBACK_SCRIPT"
1098 +
1099 + log "开始Arch Linux安全加固..."
1100 + log_info "日志: $LOG_FILE"
1101 + log_info "备份: $BACKUP_DIR"
1102 +
1103 + if ! confirm_action "确认继续?"; then
1104 + exit 0
1105 + fi
1106 +
1107 + # 执行加固
1108 + collect_system_info
1109 + install_aur_helper
1110 + update_system
1111 + manage_users
1112 + harden_ssh
1113 + configure_firewall
1114 + configure_ips
1115 + harden_users
1116 + harden_kernel
1117 + install_security_tools
1118 + configure_auto_updates
1119 + generate_report
1120 +
1121 + log_section "加固完成"
1122 +
1123 + echo -e "${GREEN}"
1124 + cat << EOF
1125 + ╔══════════════════════════════════════════════════════════════╗
1126 + ║ Arch Linux 加固完成! ║
1127 + ╚══════════════════════════════════════════════════════════════╝
1128 +
1129 + 配置信息:
1130 + 防火墙: $FIREWALL_CHOICE
1131 + 入侵防护: $IPS_CHOICE
1132 + 备份: $BACKUP_DIR
1133 + 日志: $LOG_FILE
1134 +
1135 + 下一步:
1136 + 1. 测试SSH登录(不要断开当前连接)
1137 + 2. 检查防火墙: nft list ruleset / firewall-cmd --list-all / ufw status
1138 + 3. 检查服务: systemctl status sshd $FIREWALL_CHOICE $IPS_CHOICE
1139 + 4. 查看日志: journalctl -xe
1140 +
1141 + EOF
1142 + echo -e "${NC}"
1143 +
1144 + if confirm_action "是否现在重启?"; then
1145 + log "5秒后重启..."
1146 + sleep 5
1147 + reboot
1148 + fi
1149 + }
1150 +
1151 + main "$@")端口?"; then
1152 + sed -i 's/# tcp dport { 80, 443 } accept/tcp dport 80 accept/' "$nft_conf"
1153 + fi
1154 +
1155 + if confirm_action "是否开放HTTPS(443)端口?"; then
1156 + sed -i '/tcp dport 80 accept/a\ tcp dport 443 accept' "$nft_conf"
1157 + fi
1158 +
1159 + # 测试配置
1160 + if nft -c -f "$nft_conf" 2>&1 | tee -a "$LOG_FILE"; then
1161 + log_success "✓ nftables配置验证通过"
1162 +
1163 + # 应用规则
1164 + safe_execute "nft -f '$nft_conf'" "应用nftables规则" true
1165 +
1166 + # 启用服务
1167 + safe_execute "systemctl enable nftables.service" "启用nftables服务" false
1168 + safe_execute "systemctl start nftables.service" "启动nftables服务" false
1169 +
1170 + # 显示当前规则
1171 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
1172 + nft list ruleset
1173 + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
1174 +
1175 + log_success "✓ nftables配置完成"
1176 + else
1177 + log_error "✗ nftables配置验证失败"
1178 + fi
1179 + }
1180 +
1181 + # 配置firewalld
1182 + configure_firewalld() {
1183 + log_section "配置firewalld防火墙"
1184 +
1185 + if ! command -v firewall-cmd &> /dev/null; then
1186 + safe_execute "pacman -S --noconfirm firewalld" "安装firewalld" true
1187 + fi
1188 +
1189 + # 启动服务
1190 + safe_execute "systemctl enable --now firewalld" "启用firewalld" true
1191 +
1192 + # 获取SSH端口
1193 + local ssh_port=$(grep "^Port" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')
1194 + ssh_port=${ssh_port:-22}
1195 +
1196 + # 配置规则
1197 + safe_execute "firewall-cmd --permanent --add-port=$ssh_port/tcp" "开放SSH端口" true
1198 +
1199 + if confirm_action "是否开放HTTP(80)?"; then
1200 + safe_execute "firewall-cmd --permanent --add-service=http" "开放HTTP" false
1201 + fi
1202 +
1203 + if confirm_action "是否开放HTTPS(443)?"; then
1204 + safe_execute "firewall-cmd --permanent --add-service=https" "开放HTTPS" false
1205 + fi
1206 +
1207 + # 重载配置
1208 + safe_execute "firewall-cmd --reload" "重载firewall配置" false
1209 +
1210 + # 显示状态
1211 + firewall-cmd --list-all
1212 +
1213 + log_success "✓ firewalld配置完成"
1214 + }
1215 +
1216 + # 配置UFW
1217 + configure_ufw() {
1218 + log_section "配置UFW防火墙"
1219 +
1220 + # 安装UFW和iptables-nft
1221 + if ! command -v ufw &> /dev/null; then
1222 + safe_execute "pacman -S --noconfirm ufw iptables-nft" "安装UFW" true
1223 + fi
1224 +
1225 + local ssh_port=$(grep "^Port" /etc/ssh/sshd_config 2>/dev/null | awk '{print $2}')
1226 + ssh_port=${ssh_port:-22}
1227 +
1228 + safe_execute "ufw default deny incoming" "设置默认策略" false
1229 + safe_execute "ufw default allow outgoing" "设置默认策略" false
1230 + safe_execute "ufw allow $ssh_port/tcp comment 'SSH'" "开放SSH" true
1231 +
1232 + if confirm_action "是否开放HTTP(80
Новіше Пізніше