把一台刚装好Debian的服务器直接扔到生产环境,等于把家门钥匙放在门垫下面。默认安装的Debian虽然不像某些系统那样漏洞百出,但离真正的生产级安全标准还有很大距离。我见过太多运维人员装完系统就开始跑业务,SSH开着密码登录,root账户随便连,防火墙规则一片空白,内核参数从未调整,这种机器上线不到一周就会被自动扫描器攻破。安全基线配置不是可有可无的加固项,而是系统上线前必须完成的硬性要求。

真正有效的安全基线配置,核心思路就一条:最小化攻击面。凡是业务不需要的功能、服务、端口、权限,统统关闭或移除。然后在此基础上,对必须保留的服务进行层层加固。下面我会按照从系统底层到应用层的顺序,逐一拆解每个需要配置的检查项,并给出具体的核查脚本思路,你可以直接把这些逻辑集成到自动化巡检工具里。

系统更新与软件包管理

系统更新是安全基线的第一道防线。Debian的安全更新源必须配置正确,/etc/apt/sources.list里至少要包含security.debian.org的条目。很多人在安装时选了国内镜像,却忘了把安全更新源也加上,导致关键安全补丁迟迟打不上。核查脚本的思路很简单:用apt update先刷新源,然后执行apt list --upgradable 2>/dev/null | grep -i security,看有没有未安装的安全更新。如果有输出,就标记为不合规。同时还要检查系统中是否残留了不必要的软件包,用dpkg -l列出所有已安装包,对照业务白名单逐一排查。编译工具链、X11相关库、游戏程序这些在生产服务器上没有任何存在理由,直接用apt purge清理掉。

账户与认证安全

root账户的处置是争议最多的地方。我的建议是:禁止root直接通过SSH登录,但保留本地root访问能力。修改/etc/ssh/sshd_config中的PermitRootLogin为no,同时确保/etc/passwd中root的shell不是/sbin/nologin,否则紧急情况下你连控制台都进不去。普通用户必须使用sudo提权,sudoers文件要用visudo编辑,确保没有NOPASSWD的例外规则,除非你非常清楚自己在干什么。

密码策略必须强制。/etc/login.defs里设置PASS_MAX_DAYS 90、PASS_MIN_DAYS 1、PASS_WARN_AGE 7,密码长度和复杂度通过/etc/pam.d/common-password来控制,加入minlen=12和remember=5防止用户循环使用旧密码。核查脚本可以用chage -l 用户名来检查每个账户的密码过期策略是否生效,用passwd -S检查账户状态是否被锁定。系统里所有UID为0的账户必须逐一核实,除了root之外不应该有其他特权账户存在,用awk -F: '($3==0){print $1}' /etc/passwd就能查出来。

SSH服务加固

SSH是服务器最常被攻击的入口,没有之一。除了前面说的禁止root登录,还有几项配置必须改。Port 22改成非标准端口虽然不能显著提升安全性,但能过滤掉90%的自动扫描流量,减少日志噪音。Protocol 2是默认值,但最好显式声明,确保旧版协议彻底关闭。MaxAuthTries设为3,超过次数直接断开,ClientAliveInterval和ClientAliveCountMax配合使用,让空闲连接自动超时。

密钥认证比密码认证安全得多,PasswordAuthentication要设为no,前提是你已经配置好了密钥对并且测试通过。AllowUsers或AllowGroups白名单是最容易被忽略的强力防护手段,只允许特定用户或组通过SSH登录,其他账户即使密码泄露也连不进来。核查脚本需要读取/etc/ssh/sshd_config,逐项检查这些参数是否符合基线值,注意要处理参数被注释掉的情况,很多配置项默认值不等于显式配置值。

防火墙与网络访问控制

Debian默认没有启用任何防火墙,iptables规则表是空的。生产环境必须启用netfilter,可以用iptables或nftables来管理。最小化原则在这里体现为白名单模式:默认DROP所有入站流量,只放行业务必需的端口。比如只开放80、443和SSH端口,其他一律拒绝。出站流量一般可以放行,但如果对数据外泄有严格要求,出站也要做限制,只允许服务器主动向特定的日志收集器、监控平台、软件源建立连接。

核查脚本的思路是检查iptables-save或nft list ruleset的输出,确认默认策略是DROP,确认已开放的端口列表与业务白名单一致。同时还要检查ip6tables的配置,很多运维只配了IPv4的防火墙,IPv6完全裸奔,攻击者通过IPv6地址可以绕过所有限制。如果业务不需要IPv6,直接在/etc/sysctl.conf里设置net.ipv6.conf.all.disable_ipv6=1,从内核层面关掉。

内核参数安全调优

/etc/sysctl.conf里的网络相关参数直接影响系统抵御攻击的能力。net.ipv4.tcp_syncookies=1是必开的,防止SYN Flood攻击。net.ipv4.conf.all.rp_filter=1开启反向路径过滤,防止IP地址欺骗。net.ipv4.conf.all.accept_source_route=0和net.ipv4.conf.all.accept_redirects=0关闭源路由和ICMP重定向,这些功能在公网环境下弊大于利。net.ipv4.icmp_echo_ignore_broadcasts=1防止被用于Smurf放大攻击,net.ipv4.conf.all.log_martians=1记录可疑数据包到内核日志。

内核参数核查脚本可以直接sysctl -a输出当前值,与基线列表做比对。注意有些参数需要配合网络接口名称来设置,比如rp_filter在all和default接口之外,还要在每个实际接口上单独设置。执行sysctl -p让配置生效后,用sysctl 参数名逐个验证是否真的加载成功。

文件系统与权限控制

世界可写的文件和目录是权限管理的头号隐患。find / -type f -perm -o+w 2>/dev/null这条命令能找出所有其他用户可写的文件,结果中的每一项都需要确认是否合理。/tmp目录必须设置粘滞位,权限应为1777,同时建议挂载为noexec、nosuid、nodev,防止攻击者在临时目录存放和执行恶意程序。/etc/fstab里检查/tmp分区的挂载选项,如果/tmp是独立分区就直接加参数,如果不是独立分区,可以考虑用tmpfs挂载到/tmp。

SUID和SGID文件是另一个风险点,攻击者经常利用设置了SUID位的程序进行提权。用find / -perm -4000 -o -perm -2000 2>/dev/null列出所有SUID和SGID文件,与已知的安全基线清单对比。像/usr/bin/passwd这样的程序必须有SUID位才能正常工作,但/usr/bin/chsh、/usr/bin/chfn这些如果业务不需要用户修改自己的Shell和finger信息,就可以去掉SUID位。核查脚本生成SUID文件列表后,与白名单做差集运算,多出来的就是需要调查的异常项。

日志与审计

没有日志,出了事你连怎么被攻破的都不知道。rsyslog或systemd-journald必须正常运行,/var/log目录下的auth.log、syslog、kern.log等核心日志文件要有合理的轮转策略。logrotate配置里保留至少30天的历史日志,压缩存储以节省空间。更重要的是,日志要发送到远程集中日志服务器,否则攻击者入侵后第一件事就是清理本地日志。核查脚本检查rsyslog服务状态,检查/etc/rsyslog.conf或/etc/rsyslog.d/下是否有远程转发的配置,检查关键日志文件是否存在且近期有写入。

auditd是更精细的审计工具,可以记录特定文件、系统调用的访问情况。对于存放敏感数据的目录,比如数据库数据文件、SSL私钥目录,应该配置audit规则监控所有读写操作。/etc/audit/rules.d/下的规则文件需要用auditctl -l来验证是否已加载。核查脚本可以解析auditctl -l的输出,确认关键路径的监控规则是否存在。

服务与进程最小化

生产服务器上跑的服务越少,攻击面就越小。systemctl list-units --type=service --state=running列出所有正在运行的服务,每一项都要能说出它为什么必须存在。cron、sshd、systemd-journald这些是合理的,但bluetooth、avahi-daemon、cups这类桌面相关的服务在服务器上完全没有存在的必要。用systemctl disable和mask把它们彻底禁用,mask比disable更强,能防止服务被其他依赖项意外拉起来。

网络监听端口也要做一次全面审计。ss -tlnp和ss -ulnp分别列出TCP和UDP的监听端口,127.0.0.1开头的地址只监听本地回环,0.0.0.0或:::开头的地址监听所有网络接口。核查脚本需要把监听地址和端口与业务白名单比对,发现任何不在白名单中的监听进程都要告警。特别注意那些监听在0.0.0.0上但业务只需要本地访问的服务,比如Redis、MySQL,它们应该绑定到127.0.0.1或内网IP。

核查脚本的整体设计思路

以上所有检查项整合成一个完整的核查脚本,我推荐采用模块化设计。每个检查项写成独立的函数,返回0表示合规,返回非0表示不合规,并输出具体的违规详情。主函数依次调用各检查模块,最后汇总输出一份合规报告。脚本输出格式建议采用JSON,方便对接监控系统或CMDB。下面是一个框架示例:

#!/bin/bash
# Debian安全基线核查脚本框架

RESULT_FILE="/tmp/security_baseline_check.json"
echo "{" > $RESULT_FILE
echo '  "check_time": "'$(date -Iseconds)'",' >> $RESULT_FILE
echo '  "hostname": "'$(hostname)'",' >> $RESULT_FILE
echo '  "results": [' >> $RESULT_FILE

check_security_updates() {
    apt update -qq 2>/dev/null
    local count=$(apt list --upgradable 2>/dev/null | grep -i security | wc -l)
    if [ $count -gt 0 ]; then
        echo '    {"check": "security_updates", "status": "FAIL", "detail": "Found '$count' pending security updates"},' >> $RESULT_FILE
        return 1
    else
        echo '    {"check": "security_updates", "status": "PASS", "detail": "All security patches applied"},' >> $RESULT_FILE
        return 0
    fi
}

check_root_ssh() {
    local permit_root=$(grep -E "^PermitRootLogin\s+" /etc/ssh/sshd_config | awk '{print $2}')
    if [ "$permit_root" = "no" ]; then
        echo '    {"check": "root_ssh_login", "status": "PASS", "detail": "Root SSH login disabled"},' >> $RESULT_FILE
        return 0
    else
        echo '    {"check": "root_ssh_login", "status": "FAIL", "detail": "Root SSH login is permitted"},' >> $RESULT_FILE
        return 1
    fi
}

check_suid_files() {
    local baseline_file="/etc/security/suid_baseline.txt"
    local current_suid=$(mktemp)
    find / -perm -4000 -o -perm -2000 2>/dev/null > $current_suid
    if [ -f "$baseline_file" ]; then
        local diff_result=$(diff $baseline_file $current_suid | grep "^>" | wc -l)
        if [ $diff_result -gt 0 ]; then
            echo '    {"check": "suid_files", "status": "FAIL", "detail": "New SUID/SGID files detected"},' >> $RESULT_FILE
            rm -f $current_suid
            return 1
        fi
    fi
    echo '    {"check": "suid_files", "status": "PASS", "detail": "SUID/SGID files match baseline"},' >> $RESULT_FILE
    rm -f $current_suid
    return 0
}

# 依次调用各检查函数
check_security_updates
check_root_ssh
check_suid_files
# ... 更多检查函数

# 移除最后一个逗号并闭合JSON
sed -i '$ s/,$//' $RESULT_FILE
echo '  ]' >> $RESULT_FILE
echo '}' >> $RESULT_FILE

cat $RESULT_FILE

这个脚本框架可以持续扩展,每新增一个检查项就加一个函数。第一次运行时,SUID文件基线还不存在,可以先运行一次生成基线文件,后续每次核查都与基线对比,这样就能发现新增的可疑SUID文件。同样的思路也适用于已安装软件包列表、监听端口列表、系统服务列表的基线对比。

持续合规与自动化集成

安全基线不是一次配置完就万事大吉。系统在运行过程中会安装新软件、修改配置、新增用户,任何变更都可能破坏原有的安全状态。核查脚本应该纳入日常巡检,通过cron定时执行,结果推送到监控平台。我建议每周至少执行一次全量检查,核心检查项如安全更新、SSH配置可以每天执行。当发现不合规项时,除了告警之外,最好能自动修复,比如自动安装安全更新、自动修正文件权限。但自动修复要谨慎,有些修复操作可能影响业务连续性,建议先在测试环境验证。

对于管理多台服务器的团队,可以用Ansible或SaltStack把安全基线配置写成Playbook或State文件,新机器上线时自动执行加固。核查脚本则作为事后验证手段,确保配置没有被意外修改。两者结合,形成配置-核查-修复的闭环。基线标准可以参考CIS Debian Linux Benchmark,这是业界公认的权威基线文档,里面每一项都有详细的原理说明和验证方法,你可以根据自身业务需求从中选取适用的检查项。

安全基线配置的本质是用工程化的手段把安全最佳实践固化为可执行、可验证的规则。花一个下午把这些检查项和脚本搭好,换来的是未来每一台Debian服务器都能以一致的安全状态运行,这笔时间投资绝对值得。