简介面向运维安全从业者与系统管理员的一套 Windows 与 Linux 基线核查工具集聚焦主机安全加固前的合规自查场景帮助读者快速定位系统配置与安全基线之间的差距。压缩包共 8 个文件约 182KB以 docx 基线配置文档、ps1 与 sh 核查脚本、cfg 配置项、csv 主机清单及 md 说明为主兼顾文档规范与自动化执行另附报错处理说明便于排查运行异常。已有 1175 人学习下载具备一定实践参考价值。读者可据此对照 Windows 与 Linux 两套基线文档逐项核验账户、权限、服务等配置借助脚本批量采集结果并输出清单配合配置文件适配不同主机环境同时从报错处理记录中获取常见问题的解决思路适合作为日常安全巡检与等保自查的辅助材料。1. 基线核查脚本把 Windows 和 Linux 的安全底子一次摸清接手一台新服务器第一件事不是装环境而是先看它“底子”干不干净账号策略是不是默认的、有没有多余的管理员、日志服务开没开、SSH 是不是还允许空密码登录。这些检查项在运维里有个统一叫法——安全基线。手工一条条敲命令、翻注册表、对配置文件几十台机器下来人直接麻了。这份「运维安全 Windows Linux 基线核查脚本」干的就是把这类重复劳动收进脚本里在目标机器上跑一遍输出一份可读的核查结果告诉你哪些项达标、哪些项有风险。它适合两类人一是刚接手一批机器、想快速摸清安全现状的运维新手二是要给自己项目补一套轻量合规自检能力的开发者。脚本不依赖重型平台落地成本低这也是我愿意花时间拆它的原因。2. 脚本架构与目录结构一份脚本怎么同时管两个系统2.1 整体设计思路入口分发 平台分支 检查项集合这类基线核查脚本合格的做法通常不是写成一个几百行的巨型文件而是拆成三层入口层负责判断当前系统、解析参数、决定跑哪套检查平台层分别放 Windows 和 Linux 的检查逻辑公共层放结果输出、日志、颜色提示这些与平台无关的东西。这样拆的好处很直接——加一条新检查项只需要在对应平台目录里加一个函数并注册不用动主流程。从项目标题能确定的核心事实是它同时覆盖 Windows 和 Linux。Windows 侧靠 PowerShell 读注册表、查本地安全策略、看服务状态Linux 侧靠 Shell 读/etc下的配置文件、查systemctl服务、看文件权限。两套语言不同但输出格式要统一否则结果没法横向对比。常见做法是定义一个统一的结果结构每条检查产出「检查项 ID、名称、期望值、实际值、结论」五个字段最后汇总成表格或 JSON。下面是我按这个思路还原的入口骨架语言用 bash负责在 Linux 上分发Windows 侧对应一个.ps1入口逻辑对称#!/usr/bin/env bash # 入口脚本判断平台并分发到对应检查集 set -uo pipefail PLATFORM$(uname -s) RESULT_DIR${RESULT_DIR:-./reports} mkdir -p $RESULT_DIR case $PLATFORM in Linux) # 加载 Linux 检查项并逐条执行 source ./checks/linux/loader.sh run_linux_checks $RESULT_DIR ;; *) echo 当前平台 $PLATFORM 不在支持范围Windows 请使用 run_checks.ps1 exit 1 ;; esac这段代码的关键点有三个set -uo pipefail让脚本在变量未定义、管道中途失败时及时暴露问题避免“看起来跑完了其实一半没执行”RESULT_DIR用环境变量覆盖方便把报告统一落到指定目录source加载检查项而不是把所有逻辑塞进入口保证入口永远只有几十行。参数上RESULT_DIR是最值得改的一个——批量跑的时候我会把它指向一个按主机名命名的子目录避免多台机器的报告互相覆盖。2.2 目录结构检查项、公共库、报告三块分开按上面的分层目录大致长这样。这不是官方结构而是这类脚本最稳妥的组织方式你拿到源码后可以对照着看自己那份是不是也这么分的baseline-check/ ├── run_checks.sh # Linux 入口 ├── run_checks.ps1 # Windows 入口 ├── checks/ │ ├── linux/ │ │ ├── loader.sh # 注册并调度 Linux 检查项 │ │ ├── account.sh # 账号与口令策略 │ │ ├── ssh.sh # SSH 服务配置 │ │ └── audit.sh # 日志与审计 │ └── windows/ │ ├── loader.ps1 │ ├── account.ps1 # 本地用户与组策略 │ ├── service.ps1 # 高危服务与端口 │ └── audit.ps1 # 审核策略与日志 ├── lib/ │ ├── output.sh # 统一结果输出表格/JSON │ └── utils.sh # 通用函数读配置、判权限 └── reports/ # 运行后生成的报告目录checks下按平台再按主题分文件是这套结构最值钱的地方。主题划分对应真实运维的关注点账号、远程访问、日志审计、服务暴露面。你二次开发时新增检查项先想清楚它属于哪个主题再决定放哪个文件别一股脑往 loader 里堆。lib放跨检查项复用的函数比如“读取某个配置文件的某个键值”这种在 SSH、账号、审计里都会用到的操作抽出来只写一次。2.3 统一结果结构让两个系统的报告能放在一张表里Windows 和 Linux 检查逻辑完全不同但如果输出结构不统一最后你拿到的是两份风格迥异的文本没法做汇总统计。我一般会定义一个结果写入函数所有检查项都通过它落结果格式固定为五列。Linux 侧的实现大概是这样# lib/output.sh # 写入一条检查结果 # 参数$1 检查项ID $2 名称 $3 期望值 $4 实际值 $5 结论(PASS/FAIL/WARN) write_result() { local id$1 name$2 expect$3 actual$4 status$5 printf %s\t%s\t%s\t%s\t%s\n \ $id $name $expect $actual $status \ ${RESULT_DIR}/result.tsv }五个参数的含义要记牢id是稳定标识后续做趋势对比靠它expect写合规要求比如“最大密码有效期 ≤ 90 天”actual写机器上的真实值status只允许 PASS、FAIL、WARN 三种WARN 用于“不直接违规但建议关注”的项比如某个非必需服务在跑。用 TSV 而不是直接打印是为了后面能一键转成表格或导入表格软件。Windows 侧用 PowerShell 写同样的五列只是分隔符和编码要注意这个坑后面单独讲。3. 核心检查项拆解账号、远程访问、日志三条主线3.1 Linux 账号与口令策略检查账号策略是基线里最容易被忽视、又最容易被利用的一块。检查点通常包括是否存在 UID 为 0 的非 root 账号、空口令账号、口令最长有效期、口令最小长度、登录失败锁定策略。这些值大多在/etc/login.defs和/etc/pam.d/下。下面这段是读取并判断口令有效期的典型写法# checks/linux/account.sh check_password_max_age() { local max_age # 从 login.defs 读取 PASS_MAX_DAYS缺省视为未配置 max_age$(grep -E ^\s*PASS_MAX_DAYS /etc/login.defs | awk {print $2}) if [[ -z $max_age ]]; then write_result ACC-001 口令最大有效期 90 未配置 FAIL elif (( max_age 90 )); then write_result ACC-001 口令最大有效期 90 ${max_age}天 FAIL else write_result ACC-001 口令最大有效期 90 ${max_age}天 PASS fi }逻辑很直白先取值取不到就判 FAIL未配置本身就是风险取到了和阈值比。这里有两个参数值得你按实际环境改阈值 90 天是常见合规要求有些环境放宽到 180grep用了^\s*容忍行首空格因为不同发行版的login.defs格式略有差异。再补一个查空口令账号的检查思路是读/etc/shadow第二字段为空则风险# 查找 shadow 中口令字段为空的账号排除锁定标记 awk -F: ($2 ) {print $1} /etc/shadow | while read -r user; do write_result ACC-002 空口令账号 无 $user FAIL done注意/etc/shadow只有 root 可读脚本必须以 root 或 sudo 运行否则这一项会静默跳过——这是新手最容易翻车的地方后面避坑章节会展开。3.2 SSH 服务配置核查远程访问是攻击面最大的入口SSH 配置检查几乎是每份基线的必选项。核心看几个键PermitRootLogin、PasswordAuthentication、PermitEmptyPasswords、Protocol、MaxAuthTries。配置文件在/etc/ssh/sshd_config但要注意它可能被Include指令引入的子配置覆盖直接 grep 主文件可能读到的是“名义值”而非“生效值”。# checks/linux/ssh.sh check_ssh_root_login() { local cfg/etc/ssh/sshd_config local val # 取最后一个生效配置sshd 以最后出现者为准 val$(grep -Ei ^\s*PermitRootLogin $cfg | tail -n1 | awk {print tolower($2)}) case $val in no) write_result SSH-001 禁止root直接登录 no no PASS ;; ) write_result SSH-001 禁止root直接登录 no 未显式配置 WARN ;; *) write_result SSH-001 禁止root直接登录 no $val FAIL ;; esac }这里用tail -n1取最后一次出现是因为 sshd 对重复指令以最后一条为准这个细节决定了你读到的是不是真正生效的值。tolower是为了兼容No、NO这类大小写写法。未显式配置判 WARN 而不是 FAIL是因为不同发行版默认值不同直接判死容易误报。想更严谨的话可以用sshd -T让 sshd 自己输出生效配置这是熟手会用的做法代价是需要 sshd 可执行且配置语法正确。3.3 Windows 本地账号与审核策略Windows 侧用 PowerShell检查本地用户、管理员组成员、密码策略、审核策略。密码策略不在注册表里直接读而是通过net accounts或安全策略接口。下面这段查管理员组成员是判断“有没有多余的高权限账号”的核心# checks/windows/account.ps1 function Check-AdminGroup { # 取本地管理员组成员排除系统内置 Administrator 与 Domain Admins $members Get-LocalGroupMember -Group Administrators | Where-Object { $_.Name -notmatch Administrator$|Domain Admins } if ($members.Count -eq 0) { Write-Result WIN-ACC-001 管理员组多余成员 无 无 PASS } else { $names ($members | ForEach-Object { $_.Name }) -join , Write-Result WIN-ACC-001 管理员组多余成员 无 $names FAIL } }Get-LocalGroupMember在较新的 Windows 上可用老系统要退回net localgroup administrators解析文本这是兼容性上要留意的点。Where-Object的过滤正则把内置 Administrator 和域管理员排除掉避免把正常项误报成风险。审核策略则用auditpol /get /category:*读取判断登录、账号管理、策略更改这几类是否开启成功和失败审计。Windows 侧的输出函数要和 Linux 的五列结构对齐编码统一用 UTF-8否则中文报告在跨平台汇总时会乱码。3.4 日志与审计服务状态日志是事后追溯的唯一依据基线里通常要求审计服务运行、日志文件权限合理、关键日志不被随意清空。Linux 侧查auditd、rsyslog是否 active以及/var/log下关键文件的权限Windows 侧查事件日志服务和安全日志大小上限。这类检查逻辑简单但数量多适合做成“服务名列表 循环判断”的形式# checks/linux/audit.sh for svc in auditd rsyslog; do state$(systemctl is-active $svc 2/dev/null) if [[ $state active ]]; then write_result LOG-${svc} ${svc}服务状态 active $state PASS else write_result LOG-${svc} ${svc}服务状态 active ${state:-未安装} FAIL fi donesystemctl is-active对未安装的服务会返回非零并输出空所以用${state:-未安装}兜底让报告里能区分“装了没开”和“压根没装”。这个区分在整改时很有用前者去systemctl enable --now后者要先装包。日志文件权限检查则用stat -c %a %U取权限和属主和期望值比对思路和上面一致不再重复贴。4. 跑起来从下载到出第一份报告4.1 Linux 侧执行步骤与参数拿到脚本后Linux 侧的标准流程是赋执行权限、以 root 运行、指定报告目录。建议先在测试机上跑确认没有误报再上生产。命令如下# 1. 赋权 chmod x run_checks.sh checks/linux/*.sh lib/*.sh # 2. 以 root 运行报告落到指定目录 sudo RESULT_DIR/tmp/baseline-report ./run_checks.sh # 3. 查看结果TSV 五列 column -t -s $\t /tmp/baseline-report/result.tsvsudo不能省原因前面说过/etc/shadow、部分日志文件只有 root 可读非 root 跑会漏检且不报错。RESULT_DIR建议每台机器一个子目录批量场景下用主机名拼接。column -t只是把 TSV 对齐显示方便肉眼扫原始文件保留给后续处理。如果你的发行版没有column直接cat也能看只是列不对齐。4.2 Windows 侧执行步骤与执行策略Windows 侧要先解决 PowerShell 执行策略否则.ps1直接被拦。推荐用-ExecutionPolicy Bypass只对本次进程生效不要全局改策略这是血泪经验——全局改成 Unrestricted 会留下长期风险。命令如下# 以管理员身份打开 PowerShell单次绕过执行策略运行 powershell -ExecutionPolicy Bypass -File .\run_checks.ps1 -ResultDir C:\baseline-report # 查看结果 Import-Csv C:\baseline-report\result.csv | Format-Table -AutoSize-ResultDir是脚本自定义参数对应 Linux 的RESULT_DIR两边语义保持一致方便统一调度。Windows 侧输出用 CSV 而不是 TSV是因为 PowerShell 的Export-Csv原生支持且 Excel 直接能开。注意必须以管理员身份运行否则读本地组、审核策略会权限不足。4.3 报告怎么读先看 FAIL 再看 WARN报告出来后不要从头读到尾先按结论列筛 FAIL这些是明确不合规项优先整改再筛 WARN这些是“没配但可能有默认值兜底”的项结合环境判断。我一般会按检查项 ID 前缀分组看ACC 类账号风险最高SSH 类次之LOG 类用于确认追溯能力。同一台机器多次跑用 ID 做 diff 就能看出整改有没有生效这也是为什么前面强调 ID 要稳定、不能随版本乱改。5. 避坑与常见问题那些让报告“看起来对其实错”的细节5.1 非 root 运行导致静默漏检现象报告里账号类检查全是 PASS但明明有台机器口令策略没配。原因非 root 读不到/etc/shadowawk输出为空循环体一次没进结果就是“没有 FAIL 等于全过”。解决脚本开头加一段权限自检非 root 直接退出并提示别让它带着残缺结果往下跑。if [[ $EUID -ne 0 ]]; then echo 请以 root 运行否则部分检查项会被跳过 2 exit 1 fi5.2 SSH 配置读的是主文件而非生效值现象sshd_config里写着PermitRootLogin no脚本判 PASS但实际还能 root 登录。原因配置被Include的子文件覆盖或存在重复指令且最后一条是 yes。解决优先用sshd -T读生效配置退而求其次grep 时取最后一条并检查Include引入的文件。这个坑不踩一次很难想到。5.3 Windows 报告中文乱码现象Linux 汇总 Windows 的 CSV 时中文变问号。原因PowerShell 默认导出编码与目标环境不一致。解决导出时显式指定 UTF-8Export-Csv -Encoding UTF8并在汇总脚本里按 UTF-8 读取。跨平台汇总的项目编码问题几乎必现提前统一能省很多时间。5.4 把 WARN 当 FAIL 批量整改引发故障现象按报告把所有 WARN 都改了结果某服务起不来。原因WARN 项里有些是“未显式配置但有安全默认值”强行改反而破坏兼容。解决整改前逐条确认 WARN 的实际生效值只对确认有风险的项动手。基线核查是“发现问题”不是“无脑全改”。5.5 批量执行时报告互相覆盖现象几十台机器跑完只剩最后一台的报告。原因RESULT_DIR用了固定路径后跑的覆盖先跑的。解决调度时按主机名或时间戳拼子目录RESULT_DIR./reports/$(hostname)-$(date %F)一行改动解决。6. 二次开发加检查项、接调度、做趋势对比拿到这套脚本真正有价值的是把它改成贴合自己环境的版本。加一条检查项的标准动作是三步在对应主题文件里写检查函数、在 loader 里注册、跑一遍确认输出五列齐全。以“检查是否存在过期账号”为例Linux 侧读/etc/shadow的过期字段逻辑和前面的空口令检查同构照着改字段位置即可不用重写框架。这是分层设计带来的直接收益——新增成本被压到最低。接调度是第二个改造方向。单机跑没意思批量场景下我一般用两种方式一是把脚本推到各机器本地执行回收报告目录二是通过配置管理工具下发执行、收集结果。前者简单、对网络要求低后者适合机器多且已有统一管理通道的环境。不管哪种报告文件名带上主机名和时间戳是硬要求否则汇总阶段又要踩覆盖的坑。第三个方向是做趋势对比。基线核查单次结果只能说明“此刻状态”把多次结果按检查项 ID 对齐才能看出“整改有没有落地、有没有回退”。做法很简单把每次的result.tsv按 ID 建索引对比同一 ID 的 status 变化输出“新出现 FAIL”和“由 FAIL 转 PASS”两个列表。下面这段是思路示意# 对比两次报告输出状态变化的检查项 join -t $\t -1 1 -2 1 \ (sort -t $\t -k1,1 old/result.tsv) \ (sort -t $\t -k1,1 new/result.tsv) | awk -F\t $5 ! $10 {print $1, $5-$10}join按第一列检查项 ID对齐两份报告awk比较新旧结论列只打印发生变化的项。参数上要注意两份文件都必须先按 ID 排序join才正确列号$5和$10取决于 join 后的字段拼接顺序实际用时先head看一眼再定。这个对比脚本不长但能把“一次性核查”变成“持续合规”是这套东西从工具升级成能力的关键一步。最后说个我自己的习惯每次改完检查逻辑我都会先在一台已知状态的测试机上跑人工核对至少三条 PASS 和三条 FAIL 是否和预期一致再推到批量环境。基线脚本最怕的不是漏检是“判错了还自信地输出 PASS”那种错误比不跑更危险。从那以后我每次动检查项都强制走一遍这个对照流程宁可慢一点也不让报告骗自己。希望帮到你。本文还有配套的精品资源点击获取