#!/bin/bash # 服务器安全巡检脚本 # 用途: 系统安全配置检查、异常检测、日志分析 # 使用方法: ./security_audit.sh [-o report.txt] [-q] # -o: 指定报告输出文件 # -q: 静默模式 (仅输出到文件,不打印到屏幕) set -e # ========== 颜色定义 ========== RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # ========== 变量初始化 ========== REPORT_FILE="security_audit_report_$(date +%Y%m%d_%H%M%S).txt" QUIET_MODE=false ISSUES_FOUND=0 WARNINGS_FOUND=0 CHECKS_PASSED=0 # ========== 函数定义 ========== # 日志输出函数 log() { local level=$1 shift local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" # 写入报告文件 echo "$msg" >> "$REPORT_FILE" # 非静默模式输出到屏幕 if [ "$QUIET_MODE" = false ]; then case $level in "PASS") echo -e "${GREEN}$msg${NC}" ;; "WARN") echo -e "${YELLOW}$msg${NC}" ;; "FAIL") echo -e "${RED}$msg${NC}" ;; "INFO") echo -e "${BLUE}$msg${NC}" ;; *) echo "$msg" ;; esac fi } # 检查函数 - 返回0表示通过, 1表示失败 check() { local description=$1 local command=$2 log "INFO" "Checking: $description" if eval "$command" > /dev/null 2>&1; then log "PASS" "$description" ((CHECKS_PASSED++)) return 0 else log "FAIL" "$description" ((ISSUES_FOUND++)) return 1 fi } # 警告函数 warn_check() { local description=$1 local command=$2 log "INFO" "Checking (warning level): $description" if eval "$command" > /dev/null 2>&1; then log "WARN" "$description" ((WARNINGS_FOUND++)) return 0 else log "PASS" "$description" ((CHECKS_PASSED++)) return 0 fi } # ========== 开始检查 ========== # 创建报告文件 log "INFO" "==========================================" log "INFO" "服务器安全巡检报告" log "INFO" "==========================================" log "INFO" "主机名: $(hostname)" log "INFO" "操作系统: $(cat /etc/os-release 2>/dev/null | grep 'PRETTY_NAME' | cut -d'"' -f2)" log "INFO" "内核版本: $(uname -r)" log "INFO" "巡检时间: $(date)" log "INFO" "执行用户: $(whoami)" log "INFO" "==========================================" log "INFO" "" # ========== 1. 系统更新检查 ========== log "INFO" "========== 1. 系统更新检查 ==========" if command -v yum &> /dev/null; then check "YUM包更新检查" "! yum check-update --quiet 2>/dev/null | grep -q 'available'" fi if command -v apt-get &> /dev/null; then check "APT包更新检查" "! apt-get --quiet update 2>/dev/null; ! apt-get --quiet --simulate upgrade 2>/dev/null | grep -q 'to install\|to upgrade\|to remove'" fi # ========== 2. SSH配置检查 ========== log "INFO" "" log "INFO" "========== 2. SSH配置检查 ==========" # 检查SSH版本 if command -v sshd &> /dev/null; then check "SSH版本检查 (应 >= 8.0)" "$(sshd -V 2>&1 | grep -q 'OpenSSH_8\.[0-9]\|OpenSSH_[9-9]\|OpenSSH_[2-9][0-9]')" fi # 检查SSH配置 if [ -f /etc/ssh/sshd_config ]; then check "SSH禁止root直接登录" "grep -q 'PermitRootLogin no' /etc/ssh/sshd_config" # 检查SSH版本 warn_check "SSH使用版本2" "! grep -q 'Protocol 2' /etc/ssh/sshd_config" # 检查密码认证 warn_check "SSH密码认证检查" "grep -q 'PasswordAuthentication no' /etc/ssh/sshd_config || echo '允许密码登录'" # 检查最大登录尝试 local max_tries=$(grep -q 'MaxAuthTries' /etc/ssh/sshd_config && grep 'MaxAuthTries' /etc/ssh/sshd_config | awk '{print $2}') if [ "$max_tries" -le 3 ] 2>/dev/null; then log "PASS" "SSH最大登录尝试次数: $max_tries (建议 ≤3)" ((CHECKS_PASSED++)) else log "FAIL" "SSH最大登录尝试次数: $max_tries (建议 ≤3)" ((ISSUES_FOUND++)) fi fi # ========== 3. 防火墙检查 ========== log "INFO" "" log "INFO" "========== 3. 防火墙检查 ==========" # 检查firewalld if command -v firewall-cmd &> /dev/null; then if firewall-cmd --state 2>/dev/null | grep -q 'running'; then log "PASS" "Firewalld防火墙已启用" ((CHECKS_PASSED++)) log "INFO" "当前开放端口:" firewall-cmd --list-ports | while read line; do log "INFO" " $line" done log "INFO" "当前开放服务:" firewall-cmd --list-services | while read line; do log "INFO" " $line" done else log "FAIL" "Firewalld防火墙未启用" ((ISSUES_FOUND++)) fi fi # 检查iptables if command -v iptables &> /dev/null; then local rules_count=$(iptables -L -n 2>/dev/null | wc -l) if [ $rules_count -gt 3 ]; then log "PASS" "iptables规则检查: 有 $rules_count 条规则" ((CHECKS_PASSED++)) else log "FAIL" "iptables规则检查: 规则不足" ((ISSUES_FOUND++)) fi fi # ========== 4. 用户账户检查 ========== log "INFO" "" log "INFO" "========== 4. 用户账户检查 ==========" # 检查空密码账户 empty_password_users=$(awk -F: '($2 == "" || $2 == " ") {print $1}' /etc/shadow 2>/dev/null) if [ -z "$empty_password_users" ]; then log "PASS" "无空密码账户" ((CHECKS_PASSED++)) else log "FAIL" "发现空密码账户: $empty_password_users" ((ISSUES_FOUND++)) fi # 检查UID为0的非root账户 non_root_uid0=$(awk -F: '($3 == 0 && $1 != "root") {print $1}' /etc/passwd) if [ -z "$non_root_uid0" ]; then log "PASS" "无UID为0的非root账户" ((CHECKS_PASSED++)) else log "FAIL" "发现UID为0的非root账户: $non_root_uid0" ((ISSUES_FOUND++)) fi # 检查长期未登录的账户 log "INFO" "长期未登录账户 (>90天):" lastlog | awk '$3 > 90 {print " " $1}' 2>/dev/null | while read line; do log "INFO" "$line" done # ========== 5. 权限检查 ========== log "INFO" "" log "INFO" "========== 5. 文件权限检查 ==========" # 检查关键文件权限 declare -A file_permissions=( "/etc/passwd"="644" "/etc/shadow"="600" "/etc/gshadow"="600" "/etc/ssh/sshd_config"="600" "/etc/ssh/sshd_config.d"="700" ) for file in "${!file_permissions[@i]}"; do if [ -e "$file" ]; then current_perm=$(stat -c '%a' "$file" 2>/dev/null) expected_perm="${file_permissions[$file]}" if [ "$current_perm" = "$expected_perm" ]; then log "PASS" "文件权限正确: $file ($current_perm)" ((CHECKS_PASSED++)) else log "FAIL" "文件权限异常: $file (当前:$current_perm, 预期:$expected_perm)" ((ISSUES_FOUND++)) fi fi done # 检查SUID文件 log "INFO" "SUID文件列表:" find / -perm -4000 -type f 2>/dev/null | while read file; do log "INFO" " $file" done # 检查SGID文件 log "INFO" "SGID文件列表:" find / -perm -2007 -type f 2>/dev/null | while read file; do log "INFO" " $file" done # ========== 6. 进程检查 ========== log "INFO" "" log "INFO" "========== 6. 异常进程检查 ==========" # 检查僵尸进程 zombie_count=$(ps aux | grep -c 'Z ' || echo 0) if [ $zombie_count -gt 5 ]; then log "FAIL" "发现 $zombie_count 个僵尸进程" ((ISSUES_FOUND++)) ps aux | grep 'Z ' else log "PASS" "僵尸进程检查: $zombie_count 个" ((CHECKS_PASSED++)) fi # 检查CPU占用高的进程 log "INFO" "CPU占用前5的进程:" ps aux --sort=-%cpu | head -6 | while read line; do log "INFO" " $line" done # 检查内存占用高的进程 log "INFO" "内存占用前5的进程:" ps aux --sort=-%mem | head -6 | while read line; do log "INFO" " $line" done # ========== 7. 网络连接检查 ========== log "INFO" "" log "INFO" "========== 7. 网络连接检查 ==========" # 检查监听端口 log "INFO" "当前监听的端口:" ss -tlnp 2>/dev/null | grep 'LISTEN' | while read line; do log "INFO" " $line" done # 检查异常连接 log "INFO" "异常连接状态统计:" ss -s 2>/dev/null | while read line; do log "INFO" " $line" done # ========== 8. 日志检查 ========== log "INFO" "" log "INFO" "========== 8. 日志检查 ==========" # 检查最近登录失败 if [ -f /var/log/auth.log ]; then failed_logins=$(grep -c 'Failed password' /var/log/auth.log 2>/dev/null || echo 0) if [ $failed_logins -gt 10 ]; then log "WARN" "最近发现 $failed_logins 次登录失败" ((WARNINGS_FOUND++)) log "INFO" "登录失败详情:" grep 'Failed password' /var/log/auth.log | tail -10 | while read line; do log "INFO" " $line" done else log "PASS" "登录失败检查: $failed_logins 次" ((CHECKS_PASSED++)) fi fi # 检查systemd journal if command -v journalctl &> /dev/null; then error_count=$(journalctl --since "24 hours" -p err --no-pager 2>/dev/null | wc -l) if [ $error_count -gt 0 ]; then log "WARN" "系统日志发现 $error_count 条错误" ((WARNINGS_FOUND++)) fi fi # ========== 9. 磁盘空间检查 ========== log "INFO" "" log "INFO" "========== 9. 磁盘空间检查 ==========" df -h | grep -E '^/' | while read line; do mount_point=$(echo "$line" | awk '{print $1}') usage=$(echo "$line" | awk '{print $5}' | tr -d '%') if [ $usage -gt 90 ]; then log "FAIL" "磁盘空间不足: $mount_point 使用率 ${usage}%" ((ISSUES_FOUND++)) elif [ $usage -gt 80 ]; then log "WARN" "磁盘空间警告: $mount_point 使用率 ${usage}%" ((WARNINGS_FOUND++)) else log "PASS" "磁盘空间正常: $mount_point 使用率 ${usage}%" ((CHECKS_PASSED++)) fi done # ========== 10. 其他检查 ========== log "INFO" "" log "INFO" "========== 10. 其他检查 ==========" # 检查SELinux if command -v getenforce &> /dev/null; then selinux_status=$(getenforce) if [ "$selinux_status" = "Enforcing" ] || [ "$selinux_status" = "Permissive" ]; then log "PASS" "SELinux已启用: $selinux_status" ((CHECKS_PASSED++)) else log "WARN" "SELinux未启用: $selinux_status" ((WARNINGS_FOUND++)) fi fi # 检查core dump core_enabled=$(sysctl kernel.core_pattern 2>/dev/null | grep -c '^[0-9]' || echo 0) if [ $core_enabled -eq 0 ]; then log "PASS" "Core dump已禁用" ((CHECKS_PASSED++)) else log "WARN" "Core dump可能已启用" ((WARNINGS_FOUND++)) fi # ========== 报告总结 ========== log "INFO" "" log "INFO" "==========================================" log "INFO" "巡检总结" log "INFO" "==========================================" log "INFO" "通过检查: $CHECKS_PASSED" log "INFO" "发现问题: $ISSUES_FOUND" log "INFO" "警告数量: $WARNINGS_FOUND" log "INFO" "报告文件: $REPORT_FILE" log "INFO" "==========================================" if [ $ISSUES_FOUND -gt 0 ]; then exit 1 else exit 0 fi