آخر نشاط 1 month ago

تعديل bbb26fa30ffab14b52499680a47f08c818a9a183

arch_hardening_script.sh خام
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
15export TERM=xterm-color
16set +H
17set +e
18
19# 颜色定义
20if 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')
28else
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'
36fi
37
38# 日志和备份目录
39LOG_FILE="/var/log/arch_hardening_$(date +%Y%m%d_%H%M%S).log"
40BACKUP_DIR="/root/security_backup_$(date +%Y%m%d_%H%M%S)"
41ROLLBACK_SCRIPT="$BACKUP_DIR/rollback.sh"
42
43# 全局变量
44FIREWALL_CHOICE=""
45IPS_CHOICE=""
46
47################################################################################
48# 工具函数
49################################################################################
50
51log() {
52 echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
53}
54
55log_info() {
56 echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
57}
58
59log_warning() {
60 echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
61}
62
63log_error() {
64 echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
65}
66
67log_success() {
68 echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
69}
70
71log_critical() {
72 echo -e "${RED}[CRITICAL]${NC} $1" | tee -a "$LOG_FILE"
73}
74
75log_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
81check_root() {
82 if [[ $EUID -ne 0 ]]; then
83 log_error "此脚本必须以root权限运行"
84 exit 1
85 fi
86}
87
88backup_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
117confirm_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
133safe_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
157check_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
172verify_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
202verify_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
221check_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助手(如果需要)
231install_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
262collect_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
275update_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
297manage_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
397configure_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
435EOF
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
450harden_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 安全加固配置
517Protocol 2
518MaxAuthTries 3
519MaxSessions 2
520LoginGraceTime 60
521ClientAliveInterval 300
522ClientAliveCountMax 2
523PermitEmptyPasswords no
524X11Forwarding no
525UseDNS no
526EOF
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# 选择防火墙
548choose_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
576configure_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# 清空现有规则
599flush ruleset
600
601# 定义表
602table 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}
637EOF
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# 防火墙配置入口
659configure_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# 选择入侵防护系统
679choose_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
707configure_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 配置
739BACKEND="$backend"
740LOGREADER="LANG=C /usr/bin/journalctl -afb -p info -n1 -t sshd -o cat"
741THRESHOLD=30
742BLOCK_TIME=3600
743DETECTION_TIME=600
744EOF
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
756configure_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
780configure_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]
795bantime = 3600
796findtime = 600
797maxretry = 5
798
799[sshd]
800enabled = true
801port = $ssh_port
802filter = sshd
803backend = systemd
804maxretry = 3
805bantime = 7200
806EOF
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配置入口
817configure_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# 密码策略
837harden_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 密码策略
851minlen = 12
852dcredit = -1
853ucredit = -1
854lcredit = -1
855ocredit = -1
856maxrepeat = 3
857EOF
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# 内核参数加固
873harden_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 内核安全配置
881net.ipv4.ip_forward = 0
882net.ipv6.conf.all.forwarding = 0
883net.ipv4.tcp_syncookies = 1
884net.ipv4.conf.all.accept_redirects = 0
885net.ipv6.conf.all.accept_redirects = 0
886net.ipv4.conf.default.accept_redirects = 0
887net.ipv6.conf.default.accept_redirects = 0
888net.ipv4.conf.all.secure_redirects = 0
889net.ipv4.conf.default.secure_redirects = 0
890net.ipv4.conf.all.accept_source_route = 0
891net.ipv6.conf.all.accept_source_route = 0
892net.ipv4.conf.default.accept_source_route = 0
893net.ipv6.conf.default.accept_source_route = 0
894net.ipv4.conf.all.log_martians = 1
895net.ipv4.conf.default.log_martians = 1
896net.ipv4.icmp_echo_ignore_broadcasts = 1
897net.ipv4.conf.all.rp_filter = 1
898net.ipv4.conf.default.rp_filter = 1
899net.ipv4.tcp_max_syn_backlog = 2048
900net.ipv4.tcp_synack_retries = 2
901net.ipv4.tcp_syn_retries = 5
902kernel.dmesg_restrict = 1
903kernel.kptr_restrict = 2
904EOF
905
906 safe_execute "sysctl -p '$sysctl_conf'" "应用内核参数" false
907
908 log_success "✓ 内核参数配置完成"
909}
910
911# 安装安全工具
912install_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# 配置自动更新
946configure_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
965EOF
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]
971Description=Arch Linux Auto Update
972After=network-online.target
973
974[Service]
975Type=oneshot
976ExecStart=/usr/local/bin/arch-auto-update.sh
977EOF
978
979 # 创建systemd timer
980 cat > /etc/systemd/system/arch-auto-update.timer << 'EOF'
981[Unit]
982Description=Arch Linux Auto Update Timer
983
984[Timer]
985OnCalendar=daily
986Persistent=true
987
988[Install]
989WantedBy=timers.target
990EOF
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# 生成报告
1001generate_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================================================================================
1008Arch Linux 服务器安全加固报告 v2.0
1009================================================================================
1010生成时间: $(date)
1011主机名: $(hostname)
1012内核版本: $(uname -r)
1013包管理器: $(pacman --version | head -1)
1014
1015--------------------------------------------------------------------------------
10161. SSH配置
1017--------------------------------------------------------------------------------
1018$(grep -E "^(Port|PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config 2>/dev/null)
1019
1020--------------------------------------------------------------------------------
10212. 防火墙: $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 "未配置" ;;
1028esac)
1029
1030--------------------------------------------------------------------------------
10313. 入侵防护: $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 "未配置" ;;
1038esac)
1039
1040--------------------------------------------------------------------------------
10414. 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
1047done)
1048
1049--------------------------------------------------------------------------------
1050备份目录: $BACKUP_DIR
1051日志文件: $LOG_FILE
1052回滚脚本: $ROLLBACK_SCRIPT
1053--------------------------------------------------------------------------------
1054EOF
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
1067main() {
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
1087EOF
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下一步:
11361. 测试SSH登录(不要断开当前连接)
11372. 检查防火墙: nft list ruleset / firewall-cmd --list-all / ufw status
11383. 检查服务: systemctl status sshd $FIREWALL_CHOICE $IPS_CHOICE
11394. 查看日志: journalctl -xe
1140
1141EOF
1142 echo -e "${NC}"
1143
1144 if confirm_action "是否现在重启?"; then
1145 log "5秒后重启..."
1146 sleep 5
1147 reboot
1148 fi
1149}
1150
1151main "$@")端口?"; 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
1182configure_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
1217configure_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