Linux系统安全加固与审计脚本

#!/bin/bash

# ============================================
# Linux系统安全加固与审计脚本
# 功能:自动化安全检查和系统加固
# 使用方法:sudo ./security-audit.sh [选项]
## 保存脚本
## sudo nano /usr/local/bin/security-audit.sh

## 赋予执行权限
## sudo chmod +x /usr/local/bin/security-audit.sh

## 运行交互式菜单
## sudo security-audit.sh

## 或直接运行完整审计
## sudo security-audit.sh --audit
# ============================================

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color

# 配置
REPORT_FILE="/var/log/security-audit-$(date +%Y%m%d).report"
LOG_FILE="/var/log/security-audit.log"
BACKUP_DIR="/root/security-backup-$(date +%Y%m%d)"
AUDIT_SCORE=0
MAX_SCORE=100
CHECK_COUNT=0
PASS_COUNT=0
FAIL_COUNT=0
WARN_COUNT=0

# 函数:打印带颜色的消息
print_color() {
    local color=$1
    local msg=$2
    echo -e "${color}${msg}${NC}"
}

# 函数:记录日志
log_message() {
    local message=$1
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $message" >> "$LOG_FILE"
}

# 函数:打印检查结果
print_result() {
    local level=$1
    local check_name=$2
    local status=$3
    local message=$4
    
    CHECK_COUNT=$((CHECK_COUNT + 1))
    
    case $status in
        "PASS")
            PASS_COUNT=$((PASS_COUNT + 1))
            AUDIT_SCORE=$((AUDIT_SCORE + 2))
            printf "[${GREEN}✓${NC}] %-50s ${GREEN}通过${NC}\n" "$check_name"
            log_message "PASS - $check_name: $message"
            ;;
        "FAIL")
            FAIL_COUNT=$((FAIL_COUNT + 1))
            printf "[${RED}✗${NC}] %-50s ${RED}失败${NC}\n" "$check_name"
            printf "  ${YELLOW}建议: $message${NC}"
            log_message "FAIL - $check_name: $message"
            ;;
        "WARN")
            WARN_COUNT=$((WARN_COUNT + 1))
            AUDIT_SCORE=$((AUDIT_SCORE + 1))
            printf "[${YELLOW}!${NC}] %-50s ${YELLOW}警告${NC}\n" "$check_name"
            printf "  ${CYAN}信息: $message${NC}"
            log_message "WARN - $check_name: $message"
            ;;
        "INFO")
            printf "[${BLUE}i${NC}] %-50s ${BLUE}信息${NC}\n" "$check_name"
            printf "  ${CYAN}详情: $message${NC}"
            log_message "INFO - $check_name: $message"
            ;;
    esac
}

# 函数:生成报告头部
report_header() {
    cat > "$REPORT_FILE" << EOF ============================================ Linux系统安全审计报告 生成时间: $(date) 主机名: $(hostname) 操作系统: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2 2>/dev/null || uname -o)
内核版本: $(uname -r)
============================================

EOF
    log_message "生成审计报告头部"
}

# 函数:添加报告内容
add_report() {
    local section=$1
    local content=$2
    echo -e "\n=== $section ===" >> "$REPORT_FILE"
    echo "$content" >> "$REPORT_FILE"
}

# 函数:显示进度条
show_progress() {
    local current=$1
    local total=$2
    local width=50
    local percent=$((current * 100 / total))
    local filled=$((current * width / total))
    local empty=$((width - filled))
    
    printf "\r["
    printf "%${filled}s" | tr " " "="
    printf "%${empty}s" | tr " " " "
    printf "] %3d%%" "$percent"
}

# 函数:显示标题
show_section() {
    local title=$1
    echo ""
    print_color "$PURPLE" "┌──────────────────────────────────────────────────────┐"
    print_color "$PURPLE" "│ $(printf "%-52s" "$title") │"
    print_color "$PURPLE" "└──────────────────────────────────────────────────────┘"
}

# 函数:检查是否为root用户
check_root() {
    if [ "$EUID" -ne 0 ]; then
        print_color "$RED" "错误:此脚本需要root权限运行!"
        print_color "$CYAN" "请使用: sudo $0"
        exit 1
    fi
}

# 函数:备份配置文件
backup_config() {
    local file=$1
    if [ -f "$file" ]; then
        mkdir -p "$BACKUP_DIR"
        cp "$file" "$BACKUP_DIR/" 2>/dev/null
        if [ $? -eq 0 ]; then
            print_color "$CYAN" "已备份: $file -> $BACKUP_DIR/"
        else
            print_color "$YELLOW" "备份失败: $file (可能权限不足)"
        fi
    fi
}

# 函数:暂停等待
pause() {
    echo ""
    print_color "$CYAN" "按回车键继续..."
    read -n 1
}

# ==================== 安全检查模块 ====================

# 1. 检查SSH安全配置
check_ssh_security() {
    show_section "SSH服务安全检查"
    
    local ssh_config="/etc/ssh/sshd_config"
    
    if [ -f "$ssh_config" ]; then
        backup_config "$ssh_config"
        
        # 检查PermitRootLogin
        if grep -q "^PermitRootLogin no" "$ssh_config" || grep -q "^#PermitRootLogin no" "$ssh_config"; then
            print_result "HIGH" "SSH禁止root登录" "PASS" "已禁止root通过SSH登录"
        else
            print_result "HIGH" "SSH禁止root登录" "FAIL" "建议设置 PermitRootLogin no"
        fi
        
        # 检查密码认证
        if grep -q "^PasswordAuthentication no" "$ssh_config"; then
            print_result "MEDIUM" "SSH密码认证" "PASS" "已禁用密码认证"
        elif grep -q "^#PasswordAuthentication no" "$ssh_config"; then
            print_result "MEDIUM" "SSH密码认证" "WARN" "考虑禁用密码认证,使用密钥"
        else
            print_result "MEDIUM" "SSH密码认证" "FAIL" "建议禁用密码认证"
        fi
        
        # 检查空闲超时
        if grep -q "^ClientAliveInterval 300" "$ssh_config" && grep -q "^ClientAliveCountMax 3" "$ssh_config"; then
            print_result "LOW" "SSH连接超时" "PASS" "已设置连接超时"
        else
            print_result "LOW" "SSH连接超时" "WARN" "建议设置 ClientAliveInterval 300 和 ClientAliveCountMax 3"
        fi
        
        # 检查端口
        local ssh_port=$(grep "^Port" "$ssh_config" | awk '{print $2}' | head -1)
        if [ "$ssh_port" = "22" ] || [ -z "$ssh_port" ]; then
            print_result "MEDIUM" "SSH默认端口" "WARN" "考虑修改默认SSH端口"
        else
            print_result "MEDIUM" "SSH默认端口" "PASS" "已修改默认端口: $ssh_port"
        fi
        
        # 检查协议版本
        if grep -q "^Protocol 2" "$ssh_config"; then
            print_result "HIGH" "SSH协议版本" "PASS" "已使用SSH协议版本2"
        else
            print_result "HIGH" "SSH协议版本" "FAIL" "必须使用SSH协议版本2"
        fi
        
        # 检查失败登录限制
        if grep -q "^MaxAuthTries 3" "$ssh_config"; then
            print_result "MEDIUM" "SSH登录尝试次数" "PASS" "已限制登录尝试次数"
        else
            print_result "MEDIUM" "SSH登录尝试次数" "WARN" "建议设置 MaxAuthTries 3"
        fi
        
    else
        print_result "HIGH" "SSH配置文件" "WARN" "SSH配置文件不存在"
    fi
}

# 2. 检查防火墙配置
check_firewall() {
    show_section "防火墙安全检查"
    
    # 检查iptables
    if command -v iptables &> /dev/null; then
        local iptables_rules=$(iptables -L -n 2>/dev/null | grep -c "^ACCEPT")
        if [ $iptables_rules -gt 0 ]; then
            print_result "HIGH" "iptables防火墙" "PASS" "iptables规则已配置"
            
            # 检查默认策略
            local input_policy=$(iptables -L INPUT -n 2>/dev/null | grep "policy" | awk '{print $4}')
            local forward_policy=$(iptables -L FORWARD -n 2>/dev/null | grep "policy" | awk '{print $4}')
            
            if [ "$input_policy" = "DROP" ] || [ "$input_policy" = "REJECT" ]; then
                print_result "HIGH" "INPUT链默认策略" "PASS" "INPUT策略: $input_policy"
            else
                print_result "HIGH" "INPUT链默认策略" "FAIL" "建议设置INPUT链为DROP"
            fi
        else
            print_result "HIGH" "iptables防火墙" "FAIL" "未配置iptables规则"
        fi
    fi
    
    # 检查firewalld
    if command -v systemctl &> /dev/null && systemctl is-active firewalld &> /dev/null; then
        print_result "MEDIUM" "firewalld防火墙" "PASS" "firewalld正在运行"
        if command -v firewall-cmd &> /dev/null; then
            local firewalld_zones=$(firewall-cmd --get-active-zones 2>/dev/null | wc -l)
            if [ $firewalld_zones -gt 0 ]; then
                print_result "MEDIUM" "firewalld区域" "PASS" "已配置防火墙区域"
            fi
        fi
    fi
    
    # 检查UFW
    if command -v ufw &> /dev/null; then
        local ufw_status=$(ufw status 2>/dev/null | grep -i "active")
        if [ -n "$ufw_status" ]; then
            print_result "MEDIUM" "UFW防火墙" "PASS" "UFW已启用"
        else
            print_result "MEDIUM" "UFW防火墙" "WARN" "UFW已安装但未启用"
        fi
    fi
}

# 3. 检查密码策略
check_password_policy() {
    show_section "密码策略检查"
    
    local login_defs="/etc/login.defs"
    local common_auth="/etc/pam.d/common-auth"
    local common_password="/etc/pam.d/common-password"
    
    if [ -f "$login_defs" ]; then
        # 检查密码最大天数
        local pass_max_days=$(grep "^PASS_MAX_DAYS" "$login_defs" | awk '{print $2}')
        if [ -n "$pass_max_days" ] && [ "$pass_max_days" -le 90 ]; then
            print_result "MEDIUM" "密码最大有效期" "PASS" "密码有效期: $pass_max_days 天"
        else
            print_result "MEDIUM" "密码最大有效期" "FAIL" "建议设置密码有效期不超过90天"
        fi
        
        # 检查密码最小天数
        local pass_min_days=$(grep "^PASS_MIN_DAYS" "$login_defs" | awk '{print $2}')
        if [ -n "$pass_min_days" ] && [ "$pass_min_days" -ge 1 ]; then
            print_result "LOW" "密码最小修改间隔" "PASS" "最小修改间隔: $pass_min_days 天"
        else
            print_result "LOW" "密码最小修改间隔" "WARN" "建议设置密码最小修改间隔为1天"
        fi
        
        # 检查密码警告天数
        local pass_warn_age=$(grep "^PASS_WARN_AGE" "$login_defs" | awk '{print $2}')
        if [ -n "$pass_warn_age" ] && [ "$pass_warn_age" -ge 7 ]; then
            print_result "LOW" "密码过期警告" "PASS" "过期前警告: $pass_warn_age 天"
        else
            print_result "LOW" "密码过期警告" "WARN" "建议设置密码过期前7天警告"
        fi
    fi
    
    # 检查PAM密码复杂度
    if [ -f "$common_password" ]; then
        if grep -q "pam_pwquality.so" "$common_password"; then
            print_result "HIGH" "密码复杂度检查" "PASS" "已启用密码复杂度检查"
            
            # 检查具体复杂度设置
            if grep -q "minlen=12" "$common_password" || grep -q "minlen=14" "$common_password"; then
                print_result "HIGH" "密码最小长度" "PASS" "密码最小长度12+字符"
            else
                print_result "HIGH" "密码最小长度" "WARN" "建议密码最小长度12字符"
            fi
        else
            print_result "HIGH" "密码复杂度检查" "FAIL" "未启用密码复杂度检查"
        fi
    fi
    
    # 检查失败登录锁定
    if [ -f "$common_auth" ]; then
        if grep -q "pam_tally2.so" "$common_auth" || grep -q "pam_faillock.so" "$common_auth"; then
            print_result "HIGH" "失败登录锁定" "PASS" "已启用失败登录锁定"
        else
            print_result "HIGH" "失败登录锁定" "WARN" "建议启用失败登录锁定机制"
        fi
    fi
}

# 4. 检查用户和权限
check_users_permissions() {
    show_section "用户和权限检查"
    
    # 检查空密码账户
    local empty_passwords=$(awk -F: '($2 == "" ) {print $1}' /etc/shadow 2>/dev/null)
    if [ -z "$empty_passwords" ]; then
        print_result "CRITICAL" "空密码账户" "PASS" "没有空密码账户"
    else
        print_result "CRITICAL" "空密码账户" "FAIL" "发现空密码账户: $empty_passwords"
    fi
    
    # 检查UID为0的用户
    local uid0_users=$(awk -F: '($3 == 0) {print $1}' /etc/passwd)
    local uid0_count=$(echo "$uid0_users" | wc -l)
    if [ "$uid0_count" -eq 1 ] && echo "$uid0_users" | grep -q "^root$"; then
        print_result "CRITICAL" "UID 0用户检查" "PASS" "只有root用户UID为0"
    else
        print_result "CRITICAL" "UID 0用户检查" "FAIL" "多个用户UID为0: $uid0_users"
    fi
    
    # 检查sudo权限
    local sudo_users=$(grep -E "^[^#].*ALL.*NOPASSWD" /etc/sudoers /etc/sudoers.d/* 2>/dev/null | wc -l)
    if [ "$sudo_users" -eq 0 ]; then
        print_result "HIGH" "无密码sudo" "PASS" "没有配置无密码sudo"
    else
        print_result "HIGH" "无密码sudo" "WARN" "发现无密码sudo配置,建议审查"
    fi
    
    # 检查登录shell
    local valid_shells="/bin/bash|/bin/sh|/bin/zsh|/bin/tcsh|/bin/csh"
    local invalid_shell_users=$(awk -F: -v shells="$valid_shells" '$7 !~ shells && $7 != "/usr/sbin/nologin" && $7 != "/bin/false" && $7 != "/sbin/nologin" {print $1":"$7}' /etc/passwd 2>/dev/null)
    
    if [ -z "$invalid_shell_users" ]; then
        print_result "MEDIUM" "无效登录shell" "PASS" "没有无效的登录shell"
    else
        print_result "MEDIUM" "无效登录shell" "WARN" "发现无效登录shell: $invalid_shell_users"
    fi
    
    # 检查umask设置
    local umask_global=$(grep -r "^umask" /etc/profile /etc/bash.bashrc /etc/profile.d/* 2>/dev/null | head -1)
    if echo "$umask_global" | grep -q "022\|027"; then
        print_result "MEDIUM" "全局umask设置" "PASS" "全局umask设置正确"
    else
        print_result "MEDIUM" "全局umask设置" "WARN" "建议设置全局umask为022或027"
    fi
}

# 5. 检查文件系统安全
check_filesystem_security() {
    show_section "文件系统安全检查"
    
    # 检查关键目录权限
    local critical_dirs=(
        "/etc/passwd:644:root:root"
        "/etc/shadow:640:root:shadow"
        "/etc/group:644:root:root"
        "/etc/sudoers:440:root:root"
        "/etc/ssh/sshd_config:600:root:root"
        "/root:700:root:root"
        "/etc/crontab:644:root:root"
    )
    
    for dir_info in "${critical_dirs[@]}"; do
        IFS=':' read -r file expected_mode expected_owner expected_group <<< "$dir_info" if [ -e "$file" ]; then local actual_mode=$(stat -c "%a" "$file" 2>/dev/null)
            local actual_owner=$(stat -c "%U" "$file" 2>/dev/null)
            local actual_group=$(stat -c "%G" "$file" 2>/dev/null)
            
            if [ "$actual_mode" = "$expected_mode" ] && [ "$actual_owner" = "$expected_owner" ] && [ "$actual_group" = "$expected_group" ]; then
                print_result "HIGH" "文件权限: $file" "PASS" "权限正确"
            else
                print_result "HIGH" "文件权限: $file" "FAIL" "当前: $actual_mode $actual_owner:$actual_group, 期望: $expected_mode $expected_owner:$expected_group"
            fi
        fi
    done
    
    # 检查SUID/SGID文件
    local suid_files=$(find / -xdev -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | head -20)
    local suid_count=$(echo "$suid_files" | wc -l)
    
    if [ "$suid_count" -lt 50 ]; then
        print_result "MEDIUM" "SUID/SGID文件检查" "PASS" "发现 $suid_count 个SUID/SGID文件"
    else
        print_result "MEDIUM" "SUID/SGID文件检查" "WARN" "发现较多SUID/SGID文件 ($suid_count),建议审查"
    fi
    
    # 检查world-writable文件
    local world_writable=$(find / -xdev -type f -perm -0002 ! -path "/proc/*" ! -path "/sys/*" 2>/dev/null | head -20)
    local ww_count=$(echo "$world_writable" | wc -l)
    
    if [ "$ww_count" -eq 0 ]; then
        print_result "HIGH" "全局可写文件" "PASS" "未发现全局可写文件"
    else
        print_result "HIGH" "全局可写文件" "FAIL" "发现 $ww_count 个全局可写文件,建议审查"
    fi
    
    # 检查未授权文件
    local no_owner_files=$(find / -xdev -nouser -o -nogroup 2>/dev/null | head -10)
    if [ -z "$no_owner_files" ]; then
        print_result "MEDIUM" "无属主文件" "PASS" "未发现无属主文件"
    else
        print_result "MEDIUM" "无属主文件" "WARN" "发现无属主文件,建议清理"
    fi
}

# 6. 检查服务安全
check_services_security() {
    show_section "服务安全检查"
    
    # 检查不必要的服务
    local dangerous_services=(
        "telnet" "rsh" "rlogin" "rexec" "ypbind" "ypserv"
        "tftp" "chargen" "daytime" "echo" "discard"
        "vsftpd" "proftpd" "pure-ftpd"
    )
    
    for service in "${dangerous_services[@]}"; do
        if systemctl is-enabled "$service" 2>/dev/null | grep -q "enabled"; then
            print_result "HIGH" "危险服务: $service" "FAIL" "发现危险服务启用"
        elif netstat -tulpn 2>/dev/null | grep -q "$service"; then
            print_result "HIGH" "危险服务: $service" "WARN" "发现危险服务运行"
        fi
    done
    
    # 检查网络服务
    local listening_services=$(netstat -tulpn 2>/dev/null | grep "LISTEN" | awk '{print $4, $7}' | head -15)
    if [ -n "$listening_services" ]; then
        print_result "MEDIUM" "网络监听服务" "INFO" "当前监听服务:\n$listening_services"
    fi
    
    # 检查自动启动服务
    if command -v systemctl &> /dev/null; then
        local enabled_services=$(systemctl list-unit-files --state=enabled 2>/dev/null | grep -E "\.service" | wc -l)
        if [ "$enabled_services" -lt 50 ]; then
            print_result "LOW" "自动启动服务数量" "PASS" "启用服务数量: $enabled_services"
        else
            print_result "LOW" "自动启动服务数量" "WARN" "启用服务较多 ($enabled_services),建议优化"
        fi
    fi
}

# 7. 检查日志配置
check_logging_config() {
    show_section "日志配置检查"
    
    # 检查rsyslog
    if systemctl is-active rsyslog &> /dev/null; then
        print_result "MEDIUM" "rsyslog服务" "PASS" "rsyslog正在运行"
        
        # 检查日志转发
        if grep -q "@" /etc/rsyslog.conf 2>/dev/null || grep -r "@" /etc/rsyslog.d/ 2>/dev/null; then
            print_result "MEDIUM" "日志远程存储" "PASS" "已配置远程日志"
        else
            print_result "MEDIUM" "日志远程存储" "WARN" "建议配置远程日志存储"
        fi
    else
        print_result "MEDIUM" "rsyslog服务" "WARN" "rsyslog未运行"
    fi
    
    # 检查journald
    if command -v journalctl &> /dev/null; then
        local journal_size=$(grep "^SystemMaxUse=" /etc/systemd/journald.conf 2>/dev/null | cut -d= -f2)
        if [ -n "$journal_size" ]; then
            print_result "LOW" "journald日志大小" "PASS" "日志大小限制: $journal_size"
        else
            print_result "LOW" "journald日志大小" "WARN" "建议设置journald日志大小限制"
        fi
    fi
    
    # 检查日志轮转
    if [ -f "/etc/logrotate.conf" ]; then
        print_result "LOW" "日志轮转配置" "PASS" "已配置日志轮转"
    fi
    
    # 检查auditd
    if command -v auditctl &> /dev/null; then
        if systemctl is-active auditd &> /dev/null; then
            print_result "HIGH" "auditd审计服务" "PASS" "auditd正在运行"
            
            # 检查审计规则
            local audit_rules=$(auditctl -l 2>/dev/null | wc -l)
            if [ "$audit_rules" -gt 5 ]; then
                print_result "HIGH" "审计规则数量" "PASS" "配置了 $audit_rules 条审计规则"
            else
                print_result "HIGH" "审计规则数量" "WARN" "审计规则较少,建议增加"
            fi
        else
            print_result "HIGH" "auditd审计服务" "WARN" "auditd未运行"
        fi
    fi
}

# 8. 检查内核安全参数
check_kernel_security() {
    show_section "内核安全参数检查"
    
    local sysctl_conf="/etc/sysctl.conf"
    local sysctl_files=("/etc/sysctl.d/*.conf")
    
    backup_config "$sysctl_conf"
    
    # 重要内核参数检查
    local kernel_params=(
        "net.ipv4.ip_forward:0:IP转发"
        "net.ipv4.conf.all.accept_redirects:0:接受重定向"
        "net.ipv4.conf.all.send_redirects:0:发送重定向"
        "net.ipv4.conf.all.accept_source_route:0:源路由"
        "net.ipv4.conf.all.rp_filter:1:反向路径过滤"
        "net.ipv4.icmp_echo_ignore_broadcasts:1:忽略广播ping"
        "net.ipv4.icmp_ignore_bogus_error_responses:1:忽略错误响应"
        "net.ipv4.tcp_syncookies:1:SYN cookies"
        "net.ipv4.tcp_max_syn_backlog:4096:SYN队列大小"
        "kernel.randomize_va_space:2:地址空间随机化"
    )
    
    for param_info in "${kernel_params[@]}"; do
        IFS=':' read -r param expected_value description <<< "$param_info" local current_value=$(sysctl -n "$param" 2>/dev/null)
        
        if [ -n "$current_value" ]; then
            if [ "$current_value" = "$expected_value" ]; then
                print_result "HIGH" "内核参数: $description" "PASS" "$param = $current_value"
            else
                print_result "HIGH" "内核参数: $description" "FAIL" "当前: $current_value, 期望: $expected_value"
            fi
        else
            print_result "MEDIUM" "内核参数: $description" "WARN" "参数未设置"
        fi
    done
    
    # 检查YAMA配置(ptrace限制)
    if [ -f "/proc/sys/kernel/yama/ptrace_scope" ]; then
        local ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope 2>/dev/null)
        if [ "$ptrace_scope" -ge 1 ]; then
            print_result "MEDIUM" "ptrace限制" "PASS" "ptrace_scope = $ptrace_scope"
        else
            print_result "MEDIUM" "ptrace限制" "WARN" "建议设置ptrace_scope为1或更高"
        fi
    fi
}

# 9. 检查网络安全
check_network_security() {
    show_section "网络安全检查"
    
    # 检查IPv6配置
    if ip -6 addr show &> /dev/null; then
        local ipv6_enabled=$(sysctl -n net.ipv6.conf.all.disable_ipv6 2>/dev/null)
        if [ "$ipv6_enabled" = "1" ]; then
            print_result "MEDIUM" "IPv6支持" "PASS" "IPv6已禁用"
        else
            print_result "MEDIUM" "IPv6支持" "WARN" "IPv6已启用,如不需要建议禁用"
        fi
    fi
    
    # 检查TCP时间戳
    local tcp_timestamps=$(sysctl -n net.ipv4.tcp_timestamps 2>/dev/null)
    if [ "$tcp_timestamps" = "0" ]; then
        print_result "LOW" "TCP时间戳" "PASS" "TCP时间戳已禁用"
    else
        print_result "LOW" "TCP时间戳" "INFO" "TCP时间戳已启用"
    fi
    
    # 检查SYN flood防护
    local tcp_syncookies=$(sysctl -n net.ipv4.tcp_syncookies 2>/dev/null)
    if [ "$tcp_syncookies" = "1" ]; then
        print_result "MEDIUM" "SYN flood防护" "PASS" "已启用SYN cookies"
    fi
    
    # 检查ICMP响应
    local icmp_echo=$(sysctl -n net.ipv4.icmp_echo_ignore_all 2>/dev/null)
    if [ "$icmp_echo" = "1" ]; then
        print_result "LOW" "ICMP响应" "PASS" "已禁用ICMP响应"
    fi
}

# 10. 检查恶意软件和Rootkit
check_malware_rootkit() {
    show_section "恶意软件和Rootkit检查"
    
    # 检查常见rootkit检测工具
    local rootkit_tools=("rkhunter" "chkrootkit" "lynis")
    local installed_tools=()
    
    for tool in "${rootkit_tools[@]}"; do
        if command -v "$tool" &> /dev/null; then
            installed_tools+=("$tool")
        fi
    done
    
    if [ ${#installed_tools[@]} -gt 0 ]; then
        print_result "HIGH" "Rootkit检测工具" "PASS" "已安装: ${installed_tools[*]}"
        
        # 建议运行检测
        for tool in "${installed_tools[@]}"; do
            case $tool in
                "rkhunter")
                    print_result "INFO" "安全扫描建议" "INFO" "运行: sudo rkhunter --check --skip-keypress"
                    ;;
                "chkrootkit")
                    print_result "INFO" "安全扫描建议" "INFO" "运行: sudo chkrootkit"
                    ;;
                "lynis")
                    print_result "INFO" "安全扫描建议" "INFO" "运行: sudo lynis audit system"
                    ;;
            esac
        done
    else
        print_result "HIGH" "Rootkit检测工具" "FAIL" "未安装Rootkit检测工具"
        print_result "INFO" "安全工具安装" "INFO" "建议安装: sudo apt install rkhunter chkrootkit lynis"
    fi
    
    # 检查可疑进程
    local suspicious_procs=$(ps aux 2>/dev/null | grep -E "(nc |telnet |nmap |nessus|metasploit|john |hashcat)" | grep -v grep)
    if [ -z "$suspicious_procs" ]; then
        print_result "HIGH" "可疑进程检查" "PASS" "未发现可疑进程"
    else
        print_result "HIGH" "可疑进程检查" "FAIL" "发现可疑进程: $suspicious_procs"
    fi
    
    # 检查隐藏文件
    local hidden_files=$(find / -name ".*" -type f 2>/dev/null | grep -E "\.(bash_history|ssh|mysql_history)$" | head -10)
    if [ -n "$hidden_files" ]; then
        print_result "MEDIUM" "敏感隐藏文件" "INFO" "发现敏感隐藏文件"
    fi
}

# 11. 检查Docker安全(如果安装)
check_docker_security() {
    if command -v docker &> /dev/null; then
        show_section "Docker安全配置检查"
        
        # 检查Docker守护进程配置
        local docker_config="/etc/docker/daemon.json"
        if [ -f "$docker_config" ]; then
            print_result "MEDIUM" "Docker配置文件" "PASS" "已配置Docker守护进程"
            
            # 检查是否启用用户命名空间
            if grep -q "userns-remap" "$docker_config"; then
                print_result "HIGH" "Docker用户命名空间" "PASS" "已启用用户命名空间"
            else
                print_result "HIGH" "Docker用户命名空间" "WARN" "建议启用用户命名空间"
            fi
            
            # 检查是否启用seccomp
            if grep -q "seccomp" "$docker_config" && ! grep -q '"seccomp": "unconfined"' "$docker_config"; then
                print_result "HIGH" "Docker seccomp配置" "PASS" "已启用seccomp"
            else
                print_result "HIGH" "Docker seccomp配置" "WARN" "建议启用seccomp"
            fi
        else
            print_result "MEDIUM" "Docker配置文件" "WARN" "未找到Docker配置文件"
        fi
        
        # 检查容器运行状态
        local running_containers=$(docker ps -q 2>/dev/null | wc -l)
        if [ "$running_containers" -gt 0 ]; then
            print_result "MEDIUM" "运行中的容器" "INFO" "有 $running_containers 个容器正在运行"
        fi
        
        # 检查特权容器
        local privileged_containers=$(docker ps --filter "ancestor=privileged" -q 2>/dev/null | wc -l)
        if [ "$privileged_containers" -eq 0 ]; then
            print_result "HIGH" "特权容器检查" "PASS" "未发现特权容器"
        else
            print_result "HIGH" "特权容器检查" "FAIL" "发现特权容器,建议审查"
        fi
    fi
}

# 12. 检查Cron作业和定时任务
check_cron_jobs() {
    show_section "Cron作业检查"
    
    # 检查系统cron
    local cron_files=("/etc/crontab" "/etc/cron.d/*" "/etc/cron.hourly/*" "/etc/cron.daily/*" "/etc/cron.weekly/*" "/etc/cron.monthly/*")
    local suspicious_cron=()
    
    for file in ${cron_files[@]}; do
        if [ -f "$file" ]; then
            local file_perm=$(stat -c "%a" "$file" 2>/dev/null)
            local file_owner=$(stat -c "%U" "$file" 2>/dev/null)
            
            if [ "$file_owner" != "root" ] || [ "$file_perm" -gt "644" ]; then
                suspicious_cron+=("$file (权限: $file_perm, 属主: $file_owner)")
            fi
        fi
    done
    
    if [ ${#suspicious_cron[@]} -eq 0 ]; then
        print_result "HIGH" "系统cron作业" "PASS" "系统cron配置正常"
    else
        print_result "HIGH" "系统cron作业" "FAIL" "发现可疑cron配置: ${suspicious_cron[*]}"
    fi
    
    # 检查用户cron
    local users_with_cron=$(cut -d: -f1 /etc/passwd)
    for user in $users_with_cron; do
        local user_cron=$(crontab -l -u "$user" 2>/dev/null)
        if [ -n "$user_cron" ]; then
            print_result "MEDIUM" "用户cron作业: $user" "INFO" "用户 $user 有cron作业"
        fi
    done
}

# 13. 生成安全加固建议
generate_hardening_suggestions() {
    show_section "安全加固建议"
    
    local suggestions=()
    
    # 收集失败和建议项
    if [ $FAIL_COUNT -gt 0 ]; then
        suggestions+=("发现 $FAIL_COUNT 个严重问题,建议立即修复")
    fi
    
    if [ $WARN_COUNT -gt 0 ]; then
        suggestions+=("发现 $WARN_COUNT 个警告项,建议尽快处理")
    fi
    
    # 具体建议
    if ! command -v fail2ban &> /dev/null; then
        suggestions+=("安装fail2ban防止暴力破解: sudo apt install fail2ban")
    fi
    
    if ! grep -q "pam_tally2.so" /etc/pam.d/common-auth 2>/dev/null; then
        suggestions+=("配置登录失败锁定: 在/etc/pam.d/common-auth添加 pam_tally2.so")
    fi
    
    if ! sysctl -n net.ipv4.tcp_syncookies 2>/dev/null | grep -q "1"; then
        suggestions+=("启用SYN cookies防护: echo 'net.ipv4.tcp_syncookies = 1' >> /etc/sysctl.conf")
    fi
    
    if [ ! -f "/etc/ssh/sshd_config.d/99-hardening.conf" ]; then
        suggestions+=("创建SSH加固配置: /etc/ssh/sshd_config.d/99-hardening.conf")
    fi
    
    # 显示建议
    if [ ${#suggestions[@]} -eq 0 ]; then
        print_result "INFO" "安全状况" "PASS" "系统安全状况良好"
    else
        for ((i=0; i<${#suggestions[@]}; i++)); do print_result "INFO" "建议 $((i+1))" "INFO" "${suggestions[$i]}" done fi } # 14. 生成审计报告 generate_audit_report() { show_section "审计报告生成" # 计算安全评分 local total_possible=$((CHECK_COUNT * 2)) local security_score=0 if [ $total_possible -gt 0 ]; then security_score=$((AUDIT_SCORE * 100 / total_possible)) fi print_color "$CYAN" "正在生成详细审计报告..." # 报告摘要 { echo "安全审计报告摘要" echo "==================" echo "审计时间: $(date)" echo "主机名: $(hostname)" echo "总检查项: $CHECK_COUNT" echo "通过: $PASS_COUNT" echo "失败: $FAIL_COUNT" echo "警告: $WARN_COUNT" echo "安全评分: ${security_score}/100" echo "" if [ "$security_score" -ge 80 ]; then echo "安全评级: 优秀" elif [ "$security_score" -ge 60 ]; then echo "安全评级: 良好" elif [ "$security_score" -ge 40 ]; then echo "安全评级: 一般" else echo "安全评级: 危险" fi echo "" echo "关键问题汇总:" echo "-------------" grep "FAIL" "$LOG_FILE" | tail -10 echo "" echo "加固建议:" echo "---------" echo "1. 立即修复所有FAIL级别的问题" echo "2. 审查并处理WARN级别的问题" echo "3. 定期运行此审计脚本" echo "4. 启用系统自动更新" echo "5. 配置定期安全扫描" } > "$REPORT_FILE"
    
    print_result "INFO" "报告生成" "PASS" "审计报告已保存到: $REPORT_FILE"
    print_result "INFO" "备份文件" "INFO" "配置文件备份在: $BACKUP_DIR"
    
    # 显示报告位置
    echo ""
    print_color "$PURPLE" "================================================"
    print_color "$GREEN" "✅ 安全审计完成!"
    print_color "$CYAN" "📋 详细报告: $REPORT_FILE"
    print_color "$CYAN" "📝 操作日志: $LOG_FILE"
    print_color "$CYAN" "💾 配置备份: $BACKUP_DIR"
    print_color "$CYAN" "📊 安全评分: $security_score/100"
    
    if [ "$security_score" -lt 60 ]; then
        print_color "$RED" "⚠️  警告:系统存在严重安全问题,建议立即修复!"
    fi
    
    print_color "$PURPLE" "================================================"
}

# 主函数:运行所有安全检查
run_security_audit() {
    print_color "$BLUE" "🔒 Linux系统安全审计与加固脚本"
    print_color "$CYAN" "开始时间: $(date)"
    print_color "$PURPLE" "================================================"
    
    # 检查root权限
    check_root
    
    # 创建日志和报告文件
    touch "$LOG_FILE"
    report_header
    
    # 备份重要配置文件
    mkdir -p "$BACKUP_DIR"
    
    # 执行所有安全检查
    local checks=(
        check_ssh_security
        check_firewall
        check_password_policy
        check_users_permissions
        check_filesystem_security
        check_services_security
        check_logging_config
        check_kernel_security
        check_network_security
        check_malware_rootkit
        check_docker_security
        check_cron_jobs
    )
    
    local total_checks=${#checks[@]}
    local current_check=0
    
    for check_func in "${checks[@]}"; do
        current_check=$((current_check + 1))
        show_progress $current_check $total_checks
        $check_func
        sleep 0.5  # 短暂延迟,让用户看到进度
    done
    
    echo ""  # 换行
    
    # 生成建议和报告
    generate_hardening_suggestions
    generate_audit_report
    
    log_message "审计完成 - 总检查: $CHECK_COUNT, 通过: $PASS_COUNT, 失败: $FAIL_COUNT, 警告: $WARN_COUNT"
}

# 交互式菜单
show_menu() {
    while true; do
        clear
        print_color "$PURPLE" "┌──────────────────────────────────────────────────────┐"
        print_color "$PURPLE" "│           Linux安全审计与加固工具                     │"
        print_color "$PURPLE" "└──────────────────────────────────────────────────────┘"
        echo ""
        print_color "$CYAN" "1. 运行完整安全审计"
        print_color "$CYAN" "2. 仅运行SSH安全配置检查"
        print_color "$CYAN" "3. 仅运行防火墙配置检查"
        print_color "$CYAN" "4. 仅运行密码策略检查"
        print_color "$CYAN" "5. 仅运行文件权限检查"
        print_color "$CYAN" "6. 查看最近审计报告"
        print_color "$CYAN" "7. 应用基本安全加固"
        print_color "$CYAN" "8. 查看系统安全状态"
        print_color "$CYAN" "9. 退出"
        echo ""
        print_color "$YELLOW" "选择操作 [1-9]: "
        
        read -n 1 choice
        echo ""
        
        case $choice in
            1)
                run_security_audit
                pause
                ;;
            2)
                show_section "SSH安全配置检查"
                check_ssh_security
                pause
                ;;
            3)
                show_section "防火墙配置检查"
                check_firewall
                pause
                ;;
            4)
                show_section "密码策略检查"
                check_password_policy
                pause
                ;;
            5)
                show_section "文件权限检查"
                check_filesystem_security
                pause
                ;;
            6)
                if [ -f "$REPORT_FILE" ]; then
                    less "$REPORT_FILE"
                else
                    print_color "$RED" "未找到审计报告"
                    sleep 2
                fi
                ;;
            7)
                apply_basic_hardening
                ;;
            8)
                show_system_security_status
                ;;
            9)
                print_color "$GREEN" "感谢使用安全审计工具!"
                exit 0
                ;;
            *)
                print_color "$RED" "无效选择"
                sleep 1
                ;;
        esac
    done
}

# 应用基本安全加固
apply_basic_hardening() {
    show_section "应用基本安全加固"
    
    print_color "$RED" "警告:此操作将修改系统配置!"
    read -p "确定要继续吗?(y/N): " confirm
    
    if [[ ! $confirm =~ ^[Yy]$ ]]; then
        print_color "$YELLOW" "操作已取消"
        return
    fi
    
    # 备份当前配置
    mkdir -p "$BACKUP_DIR"
    
    # 1. 加固SSH
    print_color "$CYAN" "加固SSH配置..."
    backup_config "/etc/ssh/sshd_config"
    
    # 禁用root登录
    sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config 2>/dev/null
    # 禁用密码认证
    sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config 2>/dev/null
    # 使用SSH协议2
    sed -i 's/^#*Protocol.*/Protocol 2/' /etc/ssh/sshd_config 2>/dev/null
    
    # 2. 配置防火墙基本规则
    print_color "$CYAN" "配置防火墙..."
    
    if command -v ufw &> /dev/null; then
        ufw --force enable
        ufw default deny incoming
        ufw default allow outgoing
        ufw allow ssh
        ufw reload
    fi
    
    # 3. 配置密码策略
    print_color "$CYAN" "配置密码策略..."
    backup_config "/etc/login.defs"
    
    sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs 2>/dev/null
    sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' /etc/login.defs 2>/dev/null
    sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 7/' /etc/login.defs 2>/dev/null
    
    # 4. 配置内核参数
    print_color "$CYAN" "配置内核安全参数..."
    backup_config "/etc/sysctl.conf"
    
    cat >> /etc/sysctl.conf << EOF # 安全加固配置 net.ipv4.ip_forward = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.all.rp_filter = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.tcp_syncookies = 1 kernel.randomize_va_space = 2 fs.suid_dumpable = 0 EOF sysctl -p print_color "$GREEN" "✅ 基本安全加固完成!" print_color "$CYAN" "建议重启系统以使所有更改生效" log_message "应用基本安全加固" } # 显示系统安全状态 show_system_security_status() { clear print_color "$PURPLE" "系统安全状态概览" echo "========================================" echo "" print_color "$CYAN" "🔐 认证安全:" echo " - 空密码用户: $(awk -F: '($2 == "" ) {print $1}' /etc/shadow 2>/dev/null | wc -l)"
    echo "  - UID 0用户: $(awk -F: '($3 == 0) {print $1}' /etc/passwd | wc -l)"
    
    echo ""
    print_color "$CYAN" "🛡️  网络安全:"
    echo "  - 开放端口: $(netstat -tulpn 2>/dev/null | grep LISTEN | wc -l)"
    echo "  - SSH连接: $(ss -tun 2>/dev/null | grep :22 | wc -l)"
    
    echo ""
    print_color "$CYAN" "📁 文件安全:"
    echo "  - SUID文件: $(find / -type f -perm -4000 2>/dev/null | wc -l)"
    echo "  - 全局可写文件: $(find / -type f -perm -0002 ! -path "/proc/*" ! -path "/sys/*" 2>/dev/null | wc -l)"
    
    echo ""
    print_color "$CYAN" "⚙️  服务安全:"
    echo "  - 运行中服务: $(systemctl list-units --state=running 2>/dev/null | grep service | wc -l)"
    echo "  - 失败的服务: $(systemctl list-units --state=failed 2>/dev/null | wc -l)"
    
    echo ""
    print_color "$CYAN" "📊 资源监控:"
    echo "  - 内存使用: $(free -h | awk '/^Mem:/ {print $3 "/" $2}')"
    echo "  - 磁盘使用: $(df -h / | awk 'NR==2 {print $5}')"
    
    echo "========================================"
    print_color "$CYAN" "按回车键继续..."
    read -n 1
}

# 启动脚本
if [ "$#" -eq 0 ]; then
    show_menu
else
    case $1 in
        "--audit"|"-a")
            run_security_audit
            ;;
        "--harden"|"-h")
            apply_basic_hardening
            ;;
        "--status"|"-s")
            show_system_security_status
            ;;
        "--help"|"-?")
            print_color "$CYAN" "使用说明:"
            echo "  $0                   显示交互式菜单"
            echo "  $0 --audit | -a     运行完整安全审计"
            echo "  $0 --harden | -h    应用基本安全加固"
            echo "  $0 --status | -s    显示系统安全状态"
            echo "  $0 --help | -?      显示帮助信息"
            ;;
        *)
            print_color "$RED" "未知选项: $1"
            print_color "$CYAN" "使用 $0 --help 查看帮助"
            ;;
    esac
fi

文档仓库 » Linux系统安全加固与审计脚本