InfraGuard

安全诊断

一次性对多台服务器运行 rootkit 扫描和系统加固扫描,为每台主机打出 A–F 等级,并把严重失败项自动升级为问题处理工单。

概览 — 这个页面做什么

安全诊断 会对已注册的服务器运行 rootkit 痕迹扫描以及系统加固扫描,把每次结果换算为 0–100 分及 A–F 等级,在一个页面上呈现整个服务器群的安全态势。每一项检查都带有「检查什么/为什么/如何修复」的说明;凡是 状态为失败且严重度为致命(fail + crit) 的发现项,都会 自动升级为 问题处理 工单(会去重,并在后续检查通过后自动关闭)。

核心组成:

以 root 身份 运行探针可启用 /etc/shadow、sudoers、sshd -T 等特权检查。非 root 时,特权检查会降级为 skip,并从评分的分母中排除(不扣分)。

何时使用

页面构成

区域 内容
KPI 条 群体平均分 · 已扫描主机数 · 未关闭的致命工单 · 失败检查总数 · 最近一次扫描时间
等级分布 A/B/C/D/F 主机数量的直方图
类别得分 9 个类别的汇总(群体平均、目录顺序、色调)
分数趋势 按时间顺序排列的最近 60 次扫描
按等级排列的主机 主机卡片网格 — 按分数从低(风险最高)到高排序。每张卡片显示等级,pass/warn/fail/crit 数量,接入来源,扫描时间。点击 → 报告弹窗
报告弹窗 总分与等级 + 摘要(OS/内核)+ 每项检查的详情(检查什么/为什么/如何修复/证据)+ 已升级工单的链接

顶部工具栏: [运行扫描](多服务器远程运行)· [粘贴结果] · 刷新 · 自动刷新开关 · 全屏。

如何使用

四条接入通道

infr_sec_scan.source 用来区分一次扫描是怎么接收进来的:

source 入口 方式
emulate [运行扫描] 弹窗 选择实例 → 远程部署并运行探针。带有 instance_id 的粘贴或远程仿真应用同样记为 emulate
paste [粘贴结果] 弹窗 粘贴直接在目标主机上生成的探针 JSON,不绑定实例
api POST /api/v1/security/scan 每台主机的 cron 运行探针 → 通过 API 密钥推送(上报器 通道)
manual 兜底 source 取值无效时的默认值

1) 多服务器远程运行 — 异步轮询

同步地跑完全部主机会超出 PHP 的执行时间,因此流程是 登记 → 发射后不管的 worker → 轮询。

  1. [运行扫描] → 勾选目标实例(每台都会标出可达/不可达)。
  2. POST /security/diagnostics/run → 创建一条进度记录并立即返回 run_id(Cache,TTL 30 分钟)。
  3. 前端 不等待 地调用 POST /security/diagnostics/run-worker/{runId} → worker 在 set_time_limit(0) 和 ignore_user_abort 下依次跑完所有主机(某一台失败不会中断其余)。
  4. 前端轮询 GET /security/diagnostics/run-status/{runId} 来刷新进度(done/total/ok)。

标记为 不可达 的实例没有匹配到 run_report 采集器节点,因此无法远程运行。请直接在主机上运行探针,然后通过 [粘贴结果] 提交。远程运行的接线方式参见 远程仿真。

2) 粘贴/JSON 摄入

在目标主机上运行探针,并粘贴它输出的 JSON。

# 在目标主机上(推荐: root)
node infr_seccheck_probe.cjs --min

--min 输出单行 JSON。输出结构:

{
  "host": "web-01",
  "generated_at": "2026-08-13T03:30:00Z",
  "os": "Rocky Linux 9.3",
  "kernel": "5.14.0-362.el9",
  "root": true,
  "checks": [
    { "key": "ssh_root_login", "category": "ssh", "status": "fail", "severity": "crit", "detail": "PermitRootLogin yes", "evidence": "/etc/ssh/sshd_config:32" },
    { "key": "svc_firewall",   "category": "services", "status": "pass", "severity": "crit", "detail": "firewalld active" }
  ],
  "summary": {}
}

由 POST /security/diagnostics/ingest 接收。绑定实例会记为 source=emulate;不绑定则记为 source=paste。

3) API 上报器(自动化)

每台主机的 cron 运行探针,并把结果推送进 InfraGuard。API 密钥在 管理 ▸ API 密钥 中签发;该密钥必须带有 scan.write 权限范围。

30 3 * * *  node /opt/infr/infr_seccheck_probe.cjs --min | curl -sS -X POST https://infraguard.example.com/api/v1/security/scan -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" --data @-

响应(201):

{ "ok": true, "scan_id": 812, "host": "web-01", "score": 74, "grade": "C" }

评分 — SecurityCheckbook

分数是每项检查的 权重与状态 的加权比值。

状态系数(STATUS_FACTOR) — 只有这些计入;skip/info 会被排除在分母之外:

状态 系数
pass 1.0
warn 0.6
fail 0.0
skip · info 排除

严重度乘数(SEV_MUL) — 一次失败所隐含的后果越重,对分数的影响越大:

fail_severity 乘数
crit 1.6
warn 1.0
info 0.5

单项检查的权重 w = check_weight(1–3) × severity_multiplier;总分 = round(100 × Σ(w × status_factor) / Σw)。类别得分在各自类别内使用同一个公式。

等级与色调:

分数 等级 色调
≥ 90 A good
≥ 80 B good
≥ 70 C warn
≥ 60 D warn
< 60 F crit

(等级为 A/B/C/D/F;色调是 good ≥ 80 · warn ≥ 60 · crit < 60。)

9 个类别 · 36 项检查

每个类别都有用于展示的权重和图标;实际评分使用的是上面的 单项检查权重 × 严重度乘数。

类别 标签 检查数 重点检查(失败时的含义)
rootkit Rootkit/恶意软件 6 rk_known_files·rk_ld_preload·rk_hidden_proc(crit)
patch 补丁/更新 3 pt_security_updates·pt_eol_os(crit)
ssh SSH 配置 5 ssh_root_login·ssh_empty_pass(crit)
accounts 账号/权限 5 acc_uid0·acc_empty_pass(crit)
network 网络暴露面 3 net_legacy_services·net_db_exposed(crit)
filesystem 文件系统权限 5 fs_shadow_perm(crit)·fs_world_writable·fs_suid(warn)
hardening 系统加固 5 hd_selinux·hd_aslr·hd_syncookies(warn)
services 服务/防火墙 2 svc_firewall(crit)·svc_unnecessary(info)
logging 审计/日志 2 log_auditd·log_syslog(warn)

未知的键会被并入 other 类别。每项检查的「检查什么/为什么/如何修复」文本由 seccheck.check.* 渲染到报告弹窗中。

致命发现项 → 升级为问题处理

每次摄入时,reconcileCases() 都会核对该主机的工单。

已升级的检查会在报告弹窗中带有「已升级为工单」徽章和一个 [查看工单] 链接。工单在 安全 ▸ 问题处理 看板上处理。

删除一次扫描(POST /security/diagnostics/delete)不会 删除由它升级出的工单 — 两者相互独立。

数据来源

来源 作用
infr_sec_scan(000766) 扫描结果(每台主机一条)。score·grade·pass/warn/fail/crit/total_count·category_scores·summary·findings JSON。索引 (host,scanned_at)·(instance_id,scanned_at)·grade·score
infr_instance 目标实例(按 alias/hostname 把 host 解析为 instance_id)
infr_incident / infr_incident_event 升级出的致命工单及其时间线
infr_api_key API 上报器的认证(权限范围 scan.write)
Cache 仪表盘聚合缓存 20 秒(infr:sec:diag:build)· 异步运行的进度记录(TTL 1800 秒)
infr_seccheck_probe.cjs 数据来源探针(本地、只读)

访问权限

关于总体的级别模型(infr.level:N 中间件),参见 RBAC 级别。

排障与 FAQ

Q. 实例在 [运行扫描] 中显示「不可达」。 它没有匹配到 run_report 采集器节点,因此没有远程运行的接线。请直接在主机上运行探针,并通过 [粘贴结果] 提交。

Q. 出现「No checks found」错误。 粘贴的 JSON 没有 checks(或 items)数组,或者其中的条目缺少 key。请原样粘贴 infr_seccheck_probe.cjs --min 的输出。

Q. 失败数量不多,分数却很低。 这是严重度乘数造成的。一次 crit 失败的权重是 warn 的 1.6 倍。用类别得分和报告弹窗查看是哪条发现项在拉低分数。

Q. 所有特权检查都是 skip。 探针是以非 root 账号运行的。/etc/shadow、sudoers、sshd -T 这类检查需要 root。skip 会被排除在分母之外(不扣分),但要得到完整诊断,请以 root(或通过网关)运行。

Q. 一次运行要花些时间 — 可以关掉浏览器吗? 可以。worker 是发射后不管的,即使请求中断,也会在 ignore_user_abort 下继续运行。重新打开后轮询 run-status,或者等等级出现在仪表盘上再看即可。进度记录会保留 30 分钟。

Q. 一条工单自己消失了。 如果后续扫描不再把该发现项报告为 fail+crit(已修复或未检出),引擎会自动关闭该工单。这是预期行为。

相关内容

보안 진단 — 점검 결과와 등급
보안 진단 — 점검 결과와 등급