简介:本资源是一份面向网络安全工程师、系统运维人员及等保合规实施者的Linux操作系统安全基线检查实操指南,聚焦主机层面的身份鉴别、访问控制与安全审计三大核心要求。文档依据启明信息安全中心标准编制,覆盖管理员口令策略配置、SSH加密远程管理、用户权限分离、默认账户加固、敏感标记设置及auditd日志审计规则等20余项关键检查项,并提供命令行验证方法、预期结果对照与手工检查步骤,具备强落地性与等保2.0对标价值。资源为单个PDF文件,大小402KB,内容结构清晰,含序号化检查表、命令示例与符合性判定说明,便于一线人员快速查阅执行。目前已有289人学习下载,适合需开展Linux系统安全自查、整改或迎检准备的技术人员直接参考使用。
1. 主机安全不是“加个防火墙就完事”:为什么一份 Linux 基线检查指导书能让你在等保测评、运维审计和应急响应中少掉三根头发
你刚接手一台跑着 Nginx + MySQL 的 CentOS 7 生产服务器,netstat -tuln看着端口都关得严严实实,ps aux | grep root也没发现可疑进程——但安全团队甩来一份《等保2.0三级系统整改清单》,第一条就是:“未按《Linux操作系统基线检查指导书》执行配置核查”。你打开这份 PDF,第一页写着“禁止 root 远程登录”,第二页是“密码策略必须启用 pam_pwquality.so”,第三页开始列 47 条 SSH、sysctl、cron、auditd、PAM 的硬性参数……你突然意识到:主机安全的起点不是攻防对抗,而是把操作系统从“能用”调成“合规可用”。这份《主机安全 - Linux操作系统基线检查指导书1.0版》不是教你怎么写 exploit,而是给你一张可量化的“安全出厂设置单”:它定义了最小权限、日志完备性、身份鉴别强度、内核防护边界这四大刚性维度,覆盖等保2.0、金融行业监管、信创环境(如麒麟、统信)的共性要求。适合运维工程师做上线前自检、安全工程师做渗透前基线摸底、等保测评员做现场核查依据——尤其当你面对“银河麒麟 V10 审计日志留存不足90天”或“欧拉OS 22.03 SELinux 状态为 permissive”这类具体告警时,它就是你不用翻文档、不查手册、直接定位参数的导航图。
2. 基线检查不是“逐条手工敲命令”:用自动化脚本把 47 条检查项压缩成 3 分钟可复现的验证闭环
基线检查的本质是状态比对:把当前系统实际配置(/etc/ssh/sshd_config、/etc/security/pwquality.conf、/etc/sysctl.conf 等)与标准值(如PermitRootLogin no、minlen = 12、net.ipv4.conf.all.rp_filter = 1)做布尔判断。手工执行grep -v '^#' /etc/ssh/sshd_config | grep PermitRootLogin再肉眼比对,既不可靠又不可审计。我们采用“配置提取 → 规则映射 → 结果聚合”的三层自动化结构,核心工具链为 Bash + awk + jq,零依赖 Python,适配所有主流国产 Linux 发行版(麒麟V10、统信UOS 20、欧拉22.03、CentOS 7/8)。
2.1 构建可扩展的检查规则引擎:YAML 驱动的检查项定义
我们放弃硬编码逻辑,将全部 47 条基线规则抽象为 YAML 文件baseline_rules.yaml。每条规则包含id(唯一标识)、description(中文描述)、file_path(配置文件路径)、pattern(正则匹配模式)、expected_value(期望值)、check_type(line_match或sysctl_get或rpm_verify)。例如 SSH root 登录检查:
- id: "SSH-001" description: "禁止 root 用户通过 SSH 远程登录" file_path: "/etc/ssh/sshd_config" pattern: "^PermitRootLogin\\s+.*" expected_value: "no" check_type: "line_match"提示:
pattern必须能精准捕获带注释的行(如#PermitRootLogin yes和PermitRootLogin yes都要被识别),因此我们约定pattern使用^锚定行首,并允许中间有空格;expected_value是纯字符串,不带空格或分号,避免解析歧义。
2.2 执行层:bash 脚本实现“读取→解析→比对→输出”四步原子操作
主检查脚本check_baseline.sh不做任何业务逻辑判断,只负责调度。关键函数check_line_match()处理文本类配置(sshd_config、sysctl.conf 等):
#!/bin/bash # check_baseline.sh —— 主入口脚本,支持 -r 指定规则文件,-o 输出 JSON 报告 check_line_match() { local file_path="$1" local pattern="$2" local expected="$3" # 步骤1:提取匹配行(忽略注释行,但保留带#的配置行如 "#PermitRootLogin yes") local matched_line=$(awk -v pat="$pattern" '$0 ~ pat && !/^#/ {print; exit}' "$file_path" 2>/dev/null) # 步骤2:若无匹配行,视为未配置(即不满足基线) if [ -z "$matched_line" ]; then echo "false" return fi # 步骤3:从匹配行中提取值(支持 "key value" 和 "key=value" 两种格式) local actual_value="" if [[ "$matched_line" =~ = ]]; then # 处理 key=value 格式 actual_value=$(echo "$matched_line" | sed 's/^[[:space:]]*[^[:space:]=]*[[:space:]]*=[[:space:]]*//; s/[[:space:]]*$//') else # 处理 key value 格式(空格分隔) actual_value=$(echo "$matched_line" | awk '{for(i=2;i<=NF;i++) printf "%s%s", $i, (i==NF?"":" "); print ""}' | sed 's/[[:space:]]*$//') fi # 步骤4:严格字符串比对(区分大小写,不 trim 左右空格,因基线要求精确) if [ "$actual_value" = "$expected" ]; then echo "true" else echo "false" fi }这段代码解决三个真实痛点:
- 注释干扰:
awk '!/^#/'会漏掉#PermitRootLogin yes这种“被注释但存在配置项”的情况,而我们的!/^#/放在&&后,确保先匹配 pattern 再过滤注释,保留所有潜在配置行; - 格式兼容:Linux 发行版对配置语法容忍度不同(RHEL 系偏好
key value,Debian 系常用key=value),函数自动识别并提取; - 空格敏感:基线要求
minlen = 12中的=两侧空格是合法的,但expected_value传入的是"12",所以提取时必须sed 's/^[[:space:]]*[^[:space:]=]*[[:space:]]*=[[:space:]]*//'去掉 key 和=,再 trim value 末尾空格,避免"12 "与"12"比对失败。
2.3 输出层:生成符合等保报告要求的 JSON 与 HTML 双格式结果
脚本最终输出report.json,结构严格遵循等保测评数据接口规范(字段含check_id,status(true/false),actual_value,expected_value,evidence(截图或命令输出)):
{ "timestamp": "2024-06-15T14:22:31+08:00", "host_info": {"hostname": "prod-web-01", "os_release": "Kylin V10 SP1"}, "results": [ { "check_id": "SSH-001", "status": true, "actual_value": "no", "expected_value": "no", "evidence": "grep -E '^PermitRootLogin' /etc/ssh/sshd_config" } ] }同时生成report.html,用<table>渲染为带颜色标记(绿色✅/红色❌)的可视化表格,支持浏览器直接打开,方便非技术人员快速定位失败项。HTML 模板使用纯静态 HTML/CSS,不依赖 JS,确保在离线审计环境中可打开。
3. 国产化环境不是“换个镜像就行”:麒麟、统信、欧拉三大平台的基线适配差异与绕过方案
基线检查最大的陷阱,是默认把“Linux”当作一个同质化整体。实际上,麒麟V10、统信UOS 20、欧拉22.03 在 PAM 模块路径、auditd 规则加载方式、内核参数命名上存在系统级差异。拿最常翻车的密码复杂度检查为例:
| 发行版 | PAM 配置文件路径 | 密码策略模块名 | 关键参数名 | 备注 |
|---|---|---|---|---|
| CentOS 7/8 | /etc/pam.d/system-auth | pam_pwquality.so | minlen=12 | 标准 RHEL 系 |
| 麒麟 V10 SP1 | /etc/pam.d/common-password | pam_pwquality.so | minlen = 12 | 等号两侧有空格,且路径不同 |
| 统信 UOS 20 | /etc/pam.d/common-password | pam_pwquality.so | minlen=12 retry=3 | 支持多参数,但retry为必填项 |
| 欧拉 22.03 | /etc/pam.d/system-auth | pam_pwquality.so | minlen=12 difok=5 | difok(新旧密码差异字符数)为强制项 |
3.1 发行版自动识别:用/etc/os-release的ID和VERSION_ID精准路由规则
我们在check_baseline.sh开头加入发行版探测逻辑:
detect_os() { if [ -f "/etc/os-release" ]; then . /etc/os-release case "$ID" in "centos"|"rocky"|"alma") OS_FAMILY="rhel" OS_VERSION=$(echo "$VERSION_ID" | cut -d. -f1) ;; "kylin") OS_FAMILY="kylin" OS_VERSION=$(echo "$VERSION_ID" | sed 's/SP//') ;; "uos") OS_FAMILY="uos" OS_VERSION=$(echo "$VERSION_ID" | cut -d. -f1) ;; "openeuler") OS_FAMILY="openeuler" OS_VERSION=$(echo "$VERSION_ID" | cut -d. -f1) ;; *) OS_FAMILY="unknown" ;; esac else OS_FAMILY="unknown" fi }然后在规则加载时,优先读取baseline_rules_${OS_FAMILY}.yaml(如baseline_rules_kylin.yaml), fallback 到通用baseline_rules.yaml。这样,麒麟专用规则中id: "PAM-002"的file_path自动设为/etc/pam.d/common-password,而 CentOS 规则仍指向/etc/pam.d/system-auth。
3.2 auditd 日志路径差异:麒麟用/var/log/audit/audit.log,统信用/var/log/audit/audit.log.1
auditd 日志轮转策略在国产系统中更激进。统信UOS 默认开启max_log_file_action = rotate且num_logs = 5,导致audit.log实际是软链接,真实日志在audit.log.1~audit.log.5。若脚本只检查audit.log是否存在且可读,会误判为“日志未启用”。解决方案是:
# 检查 auditd 日志实际路径(兼容麒麟/统信/欧拉) get_audit_log_path() { local log_path="/var/log/audit/audit.log" if [ -L "$log_path" ]; then # 获取软链接指向的真实文件 log_path=$(readlink -f "$log_path") fi # 若真实文件不存在,尝试 audit.log.1 if [ ! -f "$log_path" ]; then log_path="/var/log/audit/audit.log.1" fi echo "$log_path" }并在 auditd 检查规则中,file_path字段动态替换为该函数返回值,而非写死路径。
3.3 SELinux/AppArmor 混合环境:欧拉22.03 默认启用 SELinux,麒麟V10 默认用 AppArmor
基线要求“强制访问控制机制启用”,但不同发行版默认方案不同。欧拉22.03 的/etc/selinux/config中SELINUX=enforcing为 true,而麒麟V10 的/etc/default/grub中apparmor=1 security=apparmor为 true。脚本需分别检查:
- 对于
OS_FAMILY=openeuler:执行getenforce | grep -q "Enforcing" - 对于
OS_FAMILY=kylin:执行aa-status --enabled 2>/dev/null - 对于
OS_FAMILY=uos:两者都检查,优先 AppArmor(因 UOS 官方文档明确推荐)
这种“发行版感知型检查”避免了在麒麟上执行sestatus报错(因 SELinux 未安装),或在欧拉上执行aa-status返回 command not found。
4. 基线检查的 5 个血泪避坑点:从“检查通过”到“真实生效”之间隔着三重玄学
基线检查最危险的错觉,是看到status: true就以为万事大吉。以下是我们在线上环境踩过的 5 个典型坑,每个都导致过等保复测不通过或安全事件溯源失败。
4.1 现象:sysctl -w net.ipv4.ip_forward=0返回 success,但sysctl net.ipv4.ip_forward仍显示 1
原因:sysctl -w只修改运行时内核参数,未写入/etc/sysctl.conf,重启后失效;而基线检查脚本读取的是配置文件,不是运行时值。
解决:检查项必须区分runtime_check和config_persist_check。对net.ipv4.ip_forward这类参数,脚本需先sysctl net.ipv4.ip_forward获取运行时值,再grep -E '^net\.ipv4\.ip_forward' /etc/sysctl.conf获取持久化值,两者都必须为0才算通过。基线规则中增加check_mode: "both"字段。
4.2 现象:PAM 密码策略规则已写入/etc/pam.d/system-auth,但passwd修改密码时不校验长度
原因:PAM 配置文件中password requisite pam_pwquality.so行位置错误。若该行在password [default=ignore] pam_deny.so之后,则被 deny 模块拦截,永不执行。
解决:脚本增加 PAM 模块顺序校验。用awk '/^password.*pam_pwquality\.so/ {print NR; exit}' /etc/pam.d/system-auth获取行号,再检查该行前 5 行内是否存在pam_deny.so。若存在,判定为“配置位置错误”,status设为false并提示“请将 pam_pwquality.so 行移至 password 段开头”。
4.3 现象:auditd 规则已加载(augenrules --load成功),但/var/log/audit/audit.log无新日志
原因:auditd 服务未启动,或auditctl -s | grep "enabled"显示0(disabled)。基线检查只验证规则文件存在,未验证服务状态。
解决:对 auditd 类检查项,check_type设为service_and_rule,脚本中新增check_service_status()函数,组合检查systemctl is-active auditd和auditctl -s | grep -q "enabled.*1"。
4.4 现象:/etc/ssh/sshd_config中MaxAuthTries 3已设置,但暴力破解日志中仍有连续 10 次失败登录
原因:MaxAuthTries控制单次连接的认证尝试次数,而非全局 IP 限速。真正防爆破需配合faillock(PAM)或denyhosts。基线未覆盖此场景。
解决:在基线规则中补充PAM-005:“启用 pam_faillock.so 进行全局登录失败锁定”,检查/etc/pam.d/sshd是否含auth [default=die] pam_faillock.so authfail deny=3 unlock_time=900,并验证/var/log/faillock目录存在且可写。
4.5 现象:脚本报告cron.allow文件存在且为空,判定为“仅允许列表用户使用 cron”,但 root 仍可执行 crontab
原因:当/etc/cron.allow存在时,只有该文件中列出的用户才能用 cron;但若文件为空,root 默认豁免(POSIX 行为)。基线要求“空文件 = 无用户可使用”,但实际 root 总是能用。
解决:对此类特殊文件,脚本增加逻辑:若cron.allow存在且为空,status强制设为false,并提示“空 cron.allow 文件无法阻止 root,应删除该文件或明确添加允许用户”。
5. 让基线检查从“一次性动作”变成“持续免疫系统”:基于 inotifywait 的实时配置漂移监控
基线检查的价值不在“某次通过”,而在“永远不偏离”。我们把检查脚本升级为守护进程,在关键配置文件(/etc/ssh/sshd_config,/etc/sysctl.conf,/etc/pam.d/*,/etc/audit/rules.d/*.rules)被修改时,自动触发重检并告警。这不是用 systemd timer 每分钟轮询,而是用inotifywait实现毫秒级响应。
5.1 构建轻量级监控守护进程:inotifywait + bash 的最小可行方案
创建baseline_monitor.sh,核心逻辑监听文件变更事件:
#!/bin/bash # baseline_monitor.sh —— 基线漂移实时监控守护进程 CONFIG_DIRS=( "/etc/ssh" "/etc/sysctl.d" "/etc/pam.d" "/etc/audit/rules.d" ) # 初始化:首次全量检查并记录快照 generate_snapshot() { md5sum /etc/ssh/sshd_config /etc/sysctl.conf /etc/pam.d/system-auth 2>/dev/null | \ awk '{print $1 " " $2}' > /var/run/baseline_snapshot.md5 } # 检查是否发生漂移 check_drift() { local drift_files=() while IFS= read -r line; do local file=$(echo "$line" | awk '{print $2}') local new_md5=$(md5sum "$file" 2>/dev/null | awk '{print $1}') local old_md5=$(grep "$file" /var/run/baseline_snapshot.md5 | awk '{print $1}') if [ "$new_md5" != "$old_md5" ] && [ -n "$old_md5" ]; then drift_files+=("$file") fi done < <(find "${CONFIG_DIRS[@]}" -type f \( -name "*.conf" -o -name "*.rules" \) 2>/dev/null) if [ ${#drift_files[@]} -gt 0 ]; then echo "【基线漂移告警】检测到配置变更:${drift_files[*]}" # 触发重检脚本 /opt/baseline/check_baseline.sh -o /var/log/baseline_drift_$(date +%s).json # 发送企业微信/钉钉告警(此处省略具体 webhook 调用) fi } # 主循环:监听所有配置目录的 modify,move,create,delete 事件 while true; do # inotifywait -m 监听多个目录,-e 指定事件类型,-q 静默输出 inotifywait -m -e modify,move,create,delete "${CONFIG_DIRS[@]}" 2>/dev/null | \ while read path action file; do if [[ "$file" == *.conf || "$file" == *.rules ]]; then check_drift # 更新快照 generate_snapshot fi done done注意:
inotifywait默认监听深度为 1,需用find配合-type f确保捕获子目录下文件(如/etc/sysctl.d/99-custom.conf)。-m参数使 inotifywait 持续监听,避免每次触发后退出。
5.2 告警分级与处置闭环:从“收到告警”到“确认修复”的 SOP
监控不是为了刷屏,而是驱动处置。我们定义三级告警:
- Level 1(黄色):非核心配置变更(如
/etc/ssh/sshd_config的Banner字段修改),仅记录日志; - Level 2(橙色):影响安全策略的变更(如
PermitRootLogin从no改为yes),发送企业微信告警,要求 30 分钟内响应; - Level 3(红色):高危变更(如
/etc/pam.d/system-auth删除pam_pwquality.so行),立即触发systemctl restart sshd回滚,并邮件通知安全负责人。
所有告警事件写入/var/log/baseline_monitor.log,格式为:2024-06-15T15:30:22+08:00 [LEVEL2] /etc/ssh/sshd_config modified: PermitRootLogin changed from 'no' to 'yes'
5.3 与 CMDB 和配置管理平台联动:让基线成为基础设施的“健康心电图”
我们将report.json输出接入公司 CMDB 的 API,每 24 小时推送一次全量基线状态。CMDB 中每台主机资产页增加“安全基线”标签页,显示:
- 最近一次检查时间、通过率(如 42/47)、失败项列表;
- 历史趋势图(过去 30 天通过率变化);
- 失败项关联知识库(点击
SSH-001直跳维基文档,含修复命令、风险说明、等保条款引用)。
更重要的是,当 CMDB 中主机标签env=prod且baseline_status=failed时,自动触发 Jenkins Pipeline,执行 Ansible Playbook 修复对应项(如ansible-playbook fix_ssh_root.yml -e "host=prod-web-01")。基线检查从此不再是审计时的手忙脚乱,而是嵌入 DevOps 流水线的自动守门员。
我坚持把check_baseline.sh的第一行写成#!/bin/bash -euo pipefail,不是为了装专业,而是-e让任意命令失败立即退出(避免grep找不到文件后继续执行导致误判),-u捕获未定义变量(防止file_path=""导致cat ""读取整个磁盘),-o pipefail确保管道中任一环节失败整个命令失败(grep | awk中 grep 找不到时 awk 不该继续)。这三参数是我在 7 个生产环境翻车后,写进所有脚本的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取