简介:本资源是面向网络安全工程师、系统运维人员及等保合规实施者的Linux操作系统安全基线检查实操指南,聚焦主机层面的身份鉴别、访问控制与安全审计三大核心要求。文档依据启明信息安全中心标准编制,覆盖管理员口令策略配置、SSH加密远程管理、默认账户重命名与禁用、用户权限分离、敏感标记设置及auditd日志审计规则等20余项关键检查项,每项均含手工检查命令、预期结果与符合性判定方法。资源为单个PDF文件,大小402KB,内容结构清晰,适合作为现场检查清单或等保2.0三级系统整改参考。目前已有289人学习下载,可直接用于企业安全加固自查、渗透测试前基线核查或安全运维岗培训材料。
1. 为什么一份《Linux操作系统基线检查指导书》比十个漏洞扫描器更管用?
你有没有遇到过这样的场景:安全团队凌晨三点发来告警,说某台生产数据库服务器“SSH允许root远程登录”“密码策略未启用”“关键日志被轮转覆盖”,运维同事回一句“刚扫完,没高危漏洞啊”,然后继续睡觉——结果三天后这台机器成了横向渗透跳板。问题不在工具没扫出问题,而在于——基线不是“有没有漏洞”,而是“系统是否按最小安全原则运行”。这份《主机安全 - Linux操作系统基线检查指导书1.0版.pdf》不是又一个合规文档模板,它是一份可直接落地的“操作系统健康体检清单”:从内核参数、账户权限、服务配置、日志审计到文件权限,每一项都对应真实攻防链路上的薄弱点。它不依赖第三方Agent,不强制上云平台,纯Shell+文本解析就能跑;它适配CentOS 7/8、Ubuntu 20.04/22.04、银河麒麟V10、统信UOS 20等主流发行版;它能让你在30分钟内完成一次全量检查,并生成带修复建议的HTML报告。适合一线运维、安全加固工程师、等保测评实施人员——尤其当你手头没有商业堡垒机或SOC平台,或者需要快速验证国产化环境(如麒麟、欧拉、统信)是否真正满足等保2.0三级“安全计算环境”要求时,这份指导书就是你的第一道防线。
2. 基线检查不是脚本执行,而是安全策略的逐条映射与裁剪
基线检查的本质,是把抽象的安全策略(如“禁止SSH空密码登录”“日志保留不少于180天”)翻译成操作系统可验证的具体状态。市面上很多工具只做“有无检测”,比如grep -q "PermitEmptyPasswords no" /etc/ssh/sshd_config,但忽略了策略生效的前提条件:配置文件是否被Include引用?sshd服务是否重载?SELinux是否阻止了配置生效?因此,真正的基线检查必须包含三层验证:配置存在性 → 配置有效性 → 运行时符合性。本指导书采用“策略驱动+发行版适配”的双轨设计:主干策略基于CIS Linux Benchmark v2.0.0和等保2.0三级要求,再针对不同发行版做差异化裁剪——例如,CentOS默认使用rsyslog,而麒麟V10默认启用journalctl+rsyslog双日志体系,其日志轮转策略就必须同时检查/etc/logrotate.d/rsyslog和/etc/systemd/journald.conf。这种裁剪不是简单删减条目,而是建立“策略-发行版-验证方法”三元组映射表。比如“禁用IPv6”这一条,在Ubuntu上需检查/etc/sysctl.conf中net.ipv6.conf.all.disable_ipv6 = 1,而在麒麟V10上还需额外验证/etc/default/grub中ipv6.disable=1是否写入内核启动参数——因为该系统默认启用IPv6且内核级禁用优先级更高。这种细粒度适配,正是手工编写检查脚本无法替代的核心价值。
2.1 基线条目如何从标准转化为可执行命令?
每一条基线在指导书中都对应一个最小可验证单元(MVEU),包含:策略原文、适用范围、检查命令、预期输出、修复命令、验证方式。以“确保SSH服务仅监听IPv4地址”为例:
策略原文(CIS 5.2.1):Configure SSH server to listen only on IPv4 addresses
适用范围:所有启用SSH服务的Linux主机(排除仅内网管理且明确要求IPv6的场景)
检查命令:
# 检查sshd监听地址(注意:netstat已被废弃,改用ss) ss -tlnp | grep ':22' | grep -v '\[::\]:22' | grep -v '\*\.22'预期输出:应返回类似
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1234/sshd,且无[::]:22行
修复命令:
# 修改/etc/ssh/sshd_config echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config systemctl restart sshd验证方式:重启后再次执行
ss -tlnp | grep ':22',确认无IPv6监听
这个MVEU的关键在于:不依赖sshd -T语法校验(因部分老版本不支持),不假设netstat存在,不忽略systemctl restart后服务实际状态。我们实测发现,某些麒麟V10镜像中sshd服务重启后仍残留IPv6监听,必须配合kill -HUP $(pgrep sshd)强制重载配置。这就是为什么指导书里每条命令都附带“失败兜底方案”。
2.2 发行版适配不是if-else,而是策略权重分级
不同发行版对同一策略的实现路径差异极大。指导书采用“策略权重分级”机制:将基线条目分为三级——
- 强制级(Level 1):所有发行版必须满足,如“root密码哈希强度≥SHA512”“关键目录权限≤750”;
- 适配级(Level 2):按发行版提供多套验证逻辑,如“日志轮转周期”,在CentOS检查
/etc/logrotate.conf,在Ubuntu检查/etc/logrotate.d/rsyslog,在麒麟V10则需同时检查/etc/logrotate.d/syslog和journalctl --disk-usage输出; - 豁免级(Level 3):特定场景可豁免,如“禁用telnet服务”在纯容器化环境可豁免,但需在报告中标注豁免理由及风险说明。
这种分级不是拍脑袋定的,而是基于近三年我们参与的47个等保测评项目数据统计:Level 1条目违规率超63%,Level 2条目中发行版差异导致误报率达29%,Level 3豁免申请通过率仅12%(多数因未提供技术佐证)。因此,指导书中的每一条豁免条款都要求填写《豁免申请单》,包含“技术依据”“替代控制措施”“风险补偿方案”三栏——这不是走形式,而是把安全决策权交还给实施者。
3. 用Shell+awk构建轻量级基线检查引擎:不依赖Python,不安装额外包
指导书配套的检查脚本(linux_baseline_check.sh)严格遵循POSIX Shell规范,仅依赖bash(v4.0+)、awk、sed、grep、ss、systemctl等系统自带工具。这意味着它能在最小化安装的麒麟V10、欧拉22.03、甚至某些定制嵌入式Linux上直接运行——无需pip install、无需apt-get install、无需Docker环境。整个脚本仅2187行,核心逻辑分三层:初始化→逐条检查→报告生成。其中最关键的“逐条检查”模块采用“策略ID+函数名”映射机制,例如CIS_5.2.1对应函数check_cis_5_2_1(),函数体内封装完整的验证逻辑。这种设计让新增条目只需编写新函数并注册ID,无需修改主流程。
3.1 最小化检查脚本的结构设计
脚本主体结构如下(已简化):
#!/bin/bash # linux_baseline_check.sh v1.0 # 依赖:bash>=4.0, awk, sed, grep, ss, systemctl, journalctl # === 初始化 === declare -A CHECK_RESULT # 存储每条结果:PASS/FAIL/ERROR/NOT_APPLICABLE declare -a CHECK_LIST # 待检查条目数组 source ./config.sh # 加载发行版识别、路径映射等配置 # === 主检查循环 === for check_id in "${CHECK_LIST[@]}"; do case "$check_id" in "CIS_5.2.1") check_cis_5_2_1 ;; "CIS_6.1.2") check_cis_6_1_2 ;; "GB_T22239_8_2_3") check_gb_t22239_8_2_3 ;; *) echo "Unknown check ID: $check_id" >&2; continue ;; esac done # === 报告生成 === generate_html_report每个检查函数都遵循统一模板:
check_cis_5_2_1() { local result="PASS" local reason="" # 步骤1:检查sshd配置文件是否存在且可读 if [[ ! -r "/etc/ssh/sshd_config" ]]; then result="ERROR" reason="sshd_config not readable" CHECK_RESULT["CIS_5.2.1"]="$result:$reason" return fi # 步骤2:检查ListenAddress配置(兼容多种写法) if ! grep -E '^[[:space:]]*ListenAddress[[:space:]]+0\.0\.0\.0' /etc/ssh/sshd_config >/dev/null 2>&1; then result="FAIL" reason="ListenAddress not set to 0.0.0.0" fi # 步骤3:检查运行时监听状态(关键!配置正确≠生效) if ss -tlnp 2>/dev/null | grep ':22' | grep -q '\[::\]:22'; then result="FAIL" reason="sshd still listening on IPv6" fi CHECK_RESULT["CIS_5.2.1"]="$result:$reason" }提示:函数内所有路径均使用绝对路径,避免
cd切换导致的相对路径错误;所有grep均加-E启用扩展正则,兼容老版本;ss命令加2>/dev/null屏蔽权限不足警告,但保留stdout供判断。
3.2 发行版自动识别与路径映射
脚本启动时自动探测发行版并加载对应配置:
detect_distro() { if [[ -f "/etc/os-release" ]]; then . /etc/os-release DISTRO_ID="$ID" DISTRO_VERSION="$VERSION_ID" elif [[ -f "/etc/redhat-release" ]]; then DISTRO_ID="centos" DISTRO_VERSION=$(awk '{print $4}' /etc/redhat-release | cut -d"." -f1) else DISTRO_ID="unknown" fi # 加载发行版专属配置 case "$DISTRO_ID" in "kylin") source ./distro/kylin_v10.sh ;; "uos") source ./distro/uos_v20.sh ;; "centos") source ./distro/centos_7.sh ;; "ubuntu") source ./distro/ubuntu_20.sh ;; *) echo "Unsupported distro: $DISTRO_ID" >&2; exit 1 ;; esac }每个发行版配置文件(如distro/kylin_v10.sh)定义:
LOG_ROTATE_CONF="/etc/logrotate.d/syslog"JOURNAL_MAX_USE="1G"SSH_CONFIG_PATH="/etc/ssh/sshd_config"CRON_ALLOW_PATH="/etc/cron.allow"
这种解耦设计让新增发行版只需新建一个.sh文件,无需改动主脚本——我们在为某银行信创项目适配欧拉22.03时,仅用2小时就完成了全部路径和命令适配。
4. 常见问题排查:那些让基线检查“看起来通过实则失效”的玄学坑
基线检查最危险的状态不是“FAIL”,而是“PASS”却掩盖真实风险。以下是我们在47个项目中踩过的5类高频坑,每条都附带现场取证方法和修复逻辑:
4.1 现象:ss -tlnp | grep ':22'显示PASS,但nmap扫描仍能探测到IPv6 SSH端口
原因:sshd进程启动时读取了/etc/ssh/sshd_config,但配置中ListenAddress ::被注释,而AddressFamily inet未设置,导致sshd默认启用IPv4+IPv6双栈。ss命令只显示当前监听socket,但nmap -6能探测到IPv6地址上的22端口。
解决:在/etc/ssh/sshd_config中显式添加AddressFamily inet,并执行systemctl reload sshd(非restart,避免连接中断)。验证命令改为:ss -tlnp | grep ':22' | grep -v '\[::\]' && ssh -6 -o ConnectTimeout=2 localhost echo ok 2>/dev/null || echo "IPv6 SSH blocked"
4.2 现象:日志轮转检查显示“保留180天”,但journalctl --disk-usage显示日志占用超20G
原因:logrotate只管理/var/log/下文件,而journald日志独立存储在/var/log/journal/,其大小由/etc/systemd/journald.conf中SystemMaxUse控制。两者策略未同步。
解决:检查/etc/systemd/journald.conf,设置SystemMaxUse=1G和MaxRetentionSec=180d,然后执行systemctl restart systemd-journald。验证命令:journalctl --disk-usage | awk '{print $3}' | grep -E '^[0-9]+[KMG]$'
4.3 现象:grep "password requisite pam_pwquality.so" /etc/pam.d/system-auth返回匹配,但passwd testuser仍能设弱密码
原因:PAM配置中pam_pwquality.so被放在[success=ok]之后,导致即使校验失败也跳过。真实生效顺序需看/etc/pam.d/system-auth中password [default=ignore] pam_pwquality.so是否位于password [default=bad] pam_pwquality.so之前。
解决:用awk '/^password.*pam_pwquality/ {print NR ": " $0}' /etc/pam.d/system-auth定位行号,确保pam_pwquality.so出现在pam_unix.so之前,且default=bad。修复后用pwscore命令测试:echo "123456" | pwscore应返回0。
4.4 现象:ls -ld /etc/shadow显示权限640,但检查结果为FAIL
原因:/etc/shadow所在目录/etc/权限为777,违反“父目录权限不能比子文件宽松”原则(CIS 1.2.2)。基线检查不仅验文件,更验路径链。
解决:执行chmod 755 /etc,再验证namei -l /etc/shadow输出中每级目录权限均≤755。注意:namei命令在最小化系统中可能不存在,此时用stat -c "%a %n" / /etc /etc/shadow逐级检查。
4.5 现象:脚本在麒麟V10上运行报错/bin/bash: line 123: syntax error near unexpected token 'elif'
原因:麒麟V10默认/bin/sh指向dash而非bash,而脚本shebang为#!/bin/bash,但某些调用场景(如sudo sh script.sh)会忽略shebang,强制用sh解释。
解决:统一用bash script.sh执行,或在脚本开头添加防护:
if [[ -z "$BASH_VERSION" ]]; then echo "This script must be run with bash, not sh." >&2 exit 1 fi注意:所有修复操作必须在变更前备份原文件(如
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%s)),且修复后立即验证服务可用性——我们曾因未验证sshd reload导致批量主机SSH失联,血泪教训。
5. 把基线检查变成日常运维习惯:自动化集成与可信报告生成
基线检查的价值不在“一次性通过”,而在成为持续验证的肌肉记忆。我们不推荐把它做成每天凌晨自动执行的Cron Job(容易沦为无人关注的日志),而是嵌入三个关键节点:上线前预检、变更后验证、等保迎检前快照。为此,指导书配套提供了三套即用型工作流。
5.1 上线前预检:Ansible Playbook一键注入基线检查
将检查脚本打包为Ansible Role,部署时自动执行:
# roles/linux_baseline/tasks/main.yml - name: Copy baseline check script copy: src: "files/linux_baseline_check.sh" dest: "/usr/local/bin/linux_baseline_check.sh" mode: "0755" - name: Run baseline check and save report shell: "/usr/local/bin/linux_baseline_check.sh --output /tmp/baseline_{{ ansible_date_time.date }}.html" args: executable: /bin/bash register: baseline_result - name: Fail if critical checks fail fail: msg: "Baseline check failed: {{ baseline_result.stdout }}" when: baseline_result.stdout.find('CRITICAL') != -1关键设计点:
--output参数指定HTML报告路径,便于存档;fail模块仅在报告中含CRITICAL字眼时中断部署(如root密码为空、SSH PermitRootLogin yes);- 所有检查在
no_log: true下执行,避免敏感信息(如/etc/shadow路径)泄露到Ansible日志。
5.2 变更后验证:Git Hook驱动的配置变更审计
在/etc/目录下部署git init,将所有配置文件纳入版本控制。利用pre-commit钩子,在每次git commit前自动触发基线检查:
#!/bin/bash # .git/hooks/pre-commit BASELINE_SCRIPT="/usr/local/bin/linux_baseline_check.sh" REPORT_DIR="/var/log/baseline" # 检查本次commit修改的配置文件是否影响基线 CHANGED_FILES=$(git diff --cached --name-only | grep -E "\.(conf|cfg|sh|env)$") if [[ -n "$CHANGED_FILES" ]]; then $BASELINE_SCRIPT --files "$CHANGED_FILES" --output "$REPORT_DIR/$(date +%s).html" if grep -q "FAIL\|ERROR" "$REPORT_DIR/$(date +%s).html"; then echo "⚠️ Baseline check failed for changed files. See report." exit 1 fi fi这样,当运维修改/etc/ssh/sshd_config后执行git commit,钩子会自动检查该文件关联的所有基线条目(如CIS_5.2.1、CIS_5.3.1),失败则拒绝提交——把安全左移真正落到代码层面。
5.3 等保迎检前快照:生成带数字签名的PDF报告
HTML报告虽便于浏览,但等保测评要求“不可篡改”。我们用wkhtmltopdf生成PDF,并用私钥签名:
# 生成PDF wkhtmltopdf --enable-local-file-access \ --footer-right "[page]/[toPage]" \ /tmp/baseline_report.html \ /tmp/baseline_report.pdf # 签名(需提前配置GPG密钥) gpg --detach-sign --armor /tmp/baseline_report.pdf # 输出:baseline_report.pdf.asc最终交付物为三件套:
baseline_report.pdf(内容完整)baseline_report.pdf.asc(GPG签名,验证完整性)baseline_report.json(结构化数据,含每条检查的status、command、output、timestamp,供SOC平台对接)
我的习惯:每次做完基线加固,我都会在服务器
/root/.bash_history末尾追加一行# BASELINE_CHECK_$(date +%Y%m%d_%H%M%S),并在/etc/motd里写上最近一次检查时间。这不是形式主义——当某天发现异常进程时,第一反应是last | head -5看谁登录过,再grep BASELINE_CHECK /root/.bash_history确认上次加固时间,就能快速判断是配置漂移还是外部入侵。安全不是一劳永逸,而是把每一次检查都变成下一次攻击的“后悔药”。希望帮到你。
本文还有配套的精品资源,点击获取