feat(host-sec-check): add staged hardening and remediation

This commit is contained in:
2026-10-10 18:59:38 +08:00
parent 86ce602b28
commit cf78d0cab2
7 changed files with 545 additions and 9 deletions
+12 -1
View File
@@ -14,6 +14,7 @@
## B. 身份与入口
- [ ] sshd 配置:端口、root、密码、Pubkey
- [ ] `sshd -t` 语法与 `sshd -T` 生效值;确认 `ssh.service` / `sshd.service` / `ssh.socket` 实际监听关系
- [ ] 所有 `authorized_keys`(含 `/root`、`/home/*`、git 用户)
- [ ] `last` / `lastb` / ssh journal
- [ ] `/etc/passwd` 里 UID 0、系统外用户、登录 shell
@@ -53,6 +54,7 @@
- [ ] rpcbind :111、DB、Redis、应用明文端口
- [ ] 管理 SSH 是否可收到 VPN
- [ ] 证书/域名入口是否可被 IP:端口绕过
- [ ] 实际防火墙后端与 IPv4/IPv6 规则:UFW、nftables、iptables、firewalld
## G. 补漏(容易漏)
@@ -68,7 +70,16 @@
- [ ] `.git` 或备份文件可下载
- [ ] 云 metadata 是否对容器开放
## H. 报告与处置确认
## H. 加固专用
- [ ] 变更前有带外控制台、配置备份和第二条 SSH 会话
- [ ] 非 root 管理账号的密钥登录已验证,再收紧 root/密码登录
- [ ] Fail2ban 日志后端、`ignoreip`、jail 状态和误封回滚路径
- [ ] 补丁状态与是否需要重启已记录
- [ ] Docker 端口绑定、特权、`docker.sock`/宿主机挂载已审查;没有无条件改 `daemon.json`
- [ ] 变更后从外部验证 IPv4/IPv6 端口和业务域名;未验证项已列出
## I. 报告与处置确认
- [ ] 结论表
- [ ] 用户确认后再改
@@ -0,0 +1,73 @@
# 分阶段加固
本参考只在用户明确同意「加固 / 配置 / 修复」后使用。默认先做检查,不改变远端主机。
## 0. 变更前闸门
1. 确认目标主机、系统、维护窗口、业务端口和允许的管理来源。
2. 确认有云控制台/VNC/串口等带外回滚入口;没有就不要同时改 SSH 和防火墙。
3. 保留当前 SSH 会话,再准备第二条测试会话。先验证新会话成功,再关闭旧入口。
4. 记录 `sshd -T`、`ss -lntup`、`ufw status verbose` 或 nftables/iptables 状态、Docker/k8s 入口。
5. 将待改配置复制到受限的备份目录,记录时间和校验值;备份中可能有敏感信息,不要贴进聊天。
不要要求用户把 root 密码发给 Agent,也不要使用 `sshpass`、命令行密码、`SSHPASS` 或关闭 host-key 校验。优先复用现有 SSH key;必须创建临时密码时,用控制台或交互式 `passwd` 设置,并在密钥验证后删除/锁定。
## 1. 账号和 SSH
先创建并验证一个非 root 管理账号,再收紧 root 和密码登录:
- 用户加入发行版正确的管理组(Debian/Ubuntu 通常是 `sudo`,RHEL 系通常是 `wheel`)。
- 用户的 `~/.ssh` 为 `0700`,`authorized_keys` 为 `0600`,目录和文件归属正确;只记录 key 指纹,不复制私钥或完整 key 内容。
- 配置应放在发行版支持的 drop-in 中,例如 `/etc/ssh/sshd_config.d/60-host-sec-check.conf`,不要删除或改名所有现有 drop-in。
- 基线建议:`PermitRootLogin no`、`PasswordAuthentication no`、`KbdInteractiveAuthentication no`(启用 MFA/键盘交互时例外)、`PermitEmptyPasswords no`、`PubkeyAuthentication yes`、`MaxAuthTries 3`。
- `X11Forwarding`、TCP 转发、Agent 转发和 `AllowGroups` 要按业务需要逐项确认,不能为了“看起来更安全”无条件关闭。
- 改端口不是必需项;如果确实改,先放行云安全组和本机防火墙,再在第二条会话中用新端口测试。IPv4 和 IPv6 规则都要确认。
每次变更都按这个顺序:
```bash
sshd -t
sshd -T | grep -iE '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitempty passwords|maxauthtries) '
systemctl reload ssh 2>/dev/null || systemctl reload sshd
# 从另一条终端测试新用户/新端口,成功后才关闭旧登录路径
```
不要按 Ubuntu 版本号猜 SSH socket 模式。先看 `systemctl is-enabled ssh.socket`、`systemctl status ssh.socket ssh.service` 和 `ss -lntp`,按实际监听单元配置;改动后再次用 `sshd -T` 和 `ss` 验证。
## 2. 防火墙和暴露面
- 先识别实际后端:UFW、nftables、firewalld 和 Docker 规则可能同时存在;不能在不了解现有规则时直接 `default deny incoming` 或 reset。
- 保留已有业务白名单,最小化放行管理来源;明确处理 IPv6,避免只收紧 IPv4。
- 防火墙只解决入站路径,不代表云安全组已收紧。云平台规则查不到就写「未验证」。
- Docker 默认会安装自己的转发规则,不能仅凭 `daemon.json` 中没有 `iptables` 就认定需要写入 `"iptables": false`。先检查现有规则、发布端口和业务依赖;优先修正 compose 的绑定(内部服务不发布、回源绑 loopback/VPN),必要时在 `DOCKER-USER`/nftables 设计明确策略。
- 关闭 `rpcbind`、数据库、Docker API、Portainer、Cockpit、k8s API 等入口前,先确认没有业务或编排依赖,并从外部验证端口确实消失。
## 3. Fail2ban、补丁和日志
- 安装/启用 Fail2ban 前确认日志后端(`systemd` journal 或 `/var/log/auth.log`/`secure`),配置 `ignoreip` 为可信管理网段/VPN,而不是只写 loopback。
- 变更后检查 `fail2ban-client status`、对应 jail、过滤器命中和当前封禁;避免把维护网段封掉。
- 按发行版包管理器修补安全更新,并记录是否需要重启;不要把“执行了 apt upgrade”当成补丁覆盖证明。
- 检查时间同步、journal 持久化和日志轮转;日志缺失本身是审计结果,不要静默当作正常。
- `auditd`、自动安全更新和登录通知属于按业务选择的增强项,不是所有机器都应无条件启用。通知脚本必须避免泄露 token、密码和完整环境变量。
## 4. 验证和回滚
加固完成后至少验证:
1. 第二条会话可用:新用户、密钥、预期端口、预期来源均成功。
2. `sshd -t`、`sshd -T`、服务状态、监听端口和防火墙规则与目标一致。
3. 从外部探测 IPv4/IPv6:应关闭的端口不可达,业务域名和必要入口仍可用。
4. Fail2ban jail 正常;Docker/k8s 业务仍能启动,关键容器没有意外变成公网监听。
5. 记录未验证项、重启需求和回滚命令。SSH 或防火墙失败时,优先从带外控制台恢复备份,不要在失联状态下反复猜配置。
## 与“一键加固脚本”的边界
一键脚本可以提供检查清单,但以下做法不能作为本 skill 的默认行为:
- 在命令行、环境变量或最终报告打印密码;
- `StrictHostKeyChecking=no` / `UserKnownHostsFile=/dev/null`;
- 先要求开启 root 密码登录,再自动收紧;
- 删除/改名整个 `sshd_config.d`;
- 无条件开启密码认证或把 root 保留为 `prohibit-password`;
- 用 `sed` 拼接 JSON,或无条件把 Docker `iptables` 设为 `false`;
- 只 `sleep` 几秒后重启 SSH/防火墙,未通过第二会话和外部探测验证。
@@ -0,0 +1,53 @@
# 受控自动修复
自动修复必须建立在已完成取证、用户明确批准具体修复项、且 SSH key 和带外控制台可用的前提上。默认只生成计划,不修改远端。
## 支持的入口
```bash
# 只读计划
bash <skill-dir>/scripts/remediate.sh user@host --plan --fix ssh-perms --fix fail2ban --ignore-ip 203.0.113.10/32
# 应用单项低风险修复
bash <skill-dir>/scripts/remediate.sh user@host \
--apply --fix ssh-perms --confirm 'APPLY user@host'
# 应用 Fail2ban;必须提供维护来源白名单
bash <skill-dir>/scripts/remediate.sh user@host \
--apply --fix fail2ban --ignore-ip 203.0.113.10/32 \
--confirm 'APPLY user@host'
# 使用脚本输出的 BACKUP_ID 回滚
bash <skill-dir>/scripts/remediate.sh user@host \
--rollback 20261010T120000Z --confirm 'ROLLBACK 20261010T120000Z'
```
脚本使用现有 SSH key、BatchMode 和 host-key 默认校验,不接受密码参数,不使用 `sshpass`,不自动安装系统包。
## 修复边界
### 可以自动执行(仍需 `--apply`)
- `ssh-perms`:只修正当前 SSH 用户的 `~/.ssh` 为 `0700`、`authorized_keys` 为 `0600`,不改 key 内容和 owner。
- `fail2ban`:在独立的 `jail.d/host-sec-check.local` 写入 sshd jail;先备份、测试配置,再对已运行的服务 reload 并验证 jail。不自动启动或启用服务。
### 只生成计划,不由脚本执行
- SSH 端口、root 登录、密码认证、MFA、转发策略。
- UFW/nftables/firewalld 规则和云安全组。
- Docker/k8s 端口绑定、特权、socket/宿主机挂载。
- 删除或隔离恶意文件、容器、镜像、服务和 `/etc/ld.so.preload`。
- 重启、密钥/token 轮换和系统包安装。
## 备份与回滚
应用前在远端创建 `/var/backups/host-sec-check/<BACKUP_ID>/`,保存受影响文件和权限元数据。Fail2ban 文件恢复、SSH 权限恢复和本次新建文件删除都只针对该备份 manifest 中的精确路径。
如果应用中途失败,报告已完成项、备份目录和回滚命令;不能把“备份已创建”当成“回滚已成功”。回滚后仍需重新检查 SSH、Fail2ban 和业务状态。
## Agent 行为
1. 先跑 `--plan`,把修复项、影响、备份目录和验证方式列给用户。
2. 用户只批准某些项时,只传对应的 `--fix`,不能顺手扩大范围。
3. 应用后读取脚本输出,逐项确认 `[已修复]`、验证结果和未执行项。
4. SSH/防火墙/业务中断风险出现时停在计划阶段,转为人工确认,不用脚本猜测或重试。