--- name: host-sec-check description: >- Linux/VPS 主机安全检查、入侵取证与用户确认后的分阶段加固:覆盖矿机、隐藏进程/ld.so.preload、对外端口、SSH/Docker/k8s、cron/systemd、hugepage/OOM、SSH/防火墙/Fail2ban。触发:安全检查、安全加固、新机初始化、被黑、入侵、矿机、排查端口、对公网放开、host security、hardening、compromise、/host-sec-check。 --- # host-sec-check 对一台 Linux 主机做安全检查。默认 **只读取证 + 出报告**。清理、关端口、重启、轮换密钥和安全加固必须用户明确同意后再做。 需要:SSH 目标(`user@host` 或 ssh config 别名)、哪张网卡/IP 算公网。没有公网 IP 就只报「监听面」,并标明未做外部探测。 ## 不适用 - Web 应用代码审计、依赖 CVE 扫描(可在报告里列为后续) - 云厂商安全组控制台(能查就查,查不到就写「未验证」) - 未获准的破坏性操作(`rm -rf`、整盘 wipe、强制重启) ## 铁律 1. **先取证,后动手。** 重启、删文件、停服务会毁掉证据。 2. **不要把密钥打进对话。** token、密码、authorized_keys、kubeconfig 只写「存在/已轮换/路径」,不贴原文。 3. **sudo 失败不要停。** 用户在 `docker` 组时,用特权容器 `nsenter` 看宿主机(见 [references/access.md](references/access.md))。 4. **怀疑进程隐藏时,不要只信宿主机 `ps`。** 从**不加载宿主机 `ld.so.preload` 的容器**读 `/proc` 和文件。 5. 宿主机 `chattr` 可能被隐藏库挡住;改 immutable 文件用 **Ubuntu/Alpine 自带 chattr** 操作 bind-mount 的 `/host/...`。 6. 不要把 root 密码、临时密码、私钥、webhook 或 token 作为命令行参数、环境变量或聊天内容传递;优先使用现有密钥、控制台或交互式 `passwd`。 7. 加固不是“改 SSH 端口”竞赛:端口更改只减少噪声,必须先确认云安全组、主机防火墙和第二条登录路径。 收集脚本(可选,先跑再解读): ```bash bash /scripts/collect.sh USER@HOST ``` `` 是本 skill 目录。脚本只采集,不修改系统。 自动修复默认先生成计划: ```bash bash /scripts/remediate.sh USER@HOST --plan --fix ssh-perms --fix fail2ban --ignore-ip ADMIN_CIDR ``` 只有用户明确批准具体修复项后,才可使用 `--apply` 和精确确认串。每次应用前必须备份,应用后必须验证;脚本输出的 `BACKUP_ID` 可用于精确回滚。防火墙、SSH 登录策略、Docker/k8s 绑定、恶意文件、重启和密钥/token 轮换只生成计划,不由此入口自动执行。详细边界见自动修复资料。 ## 流程 按顺序做。每步记下「正常 / 可疑 / 未查到」。需要逐项展开时再读检查清单。 如果用户明确要求「加固」「新机初始化」「配置 SSH/防火墙」,先完成只读快照,再阅读并执行加固资料。加固采用分阶段、可验证、可回滚流程;不能把远程一键脚本直接当作安全默认值。 ### 1. 快照 - 主机名、发行版、uptime、负载、`free -h`、`df -h`、公网/内网/Tailscale IP - `ss -lntup`(最好 root) - `docker ps -a`、compose 项目 - 若有 k3s/k8s:agent 是否在跑、能否 `kubectl get pods -A` ### 2. 入口 - sshd:`Port`、`PermitRootLogin`、`PasswordAuthentication`、`authorized_keys`(root 与所有 UID≥1000) - `last` / `lastb` / `journalctl -u ssh`:异常登录;**无交互登录却出现 docker run 也是入口信号** - sudoers、`docker` 组(= root)、NOPASSWD - 对公网的管理面:Portainer / Cockpit / 面板 / k8s apiserver / Docker 端口,尤其 `-p 0.0.0.0:…` 且挂了 `docker.sock` 或 `/:/host` - 单元文件或 compose 里的明文 token(k3s `--token`、agent secret) ### 3. 持久化 发现持久化信号或需要深查时,再读持久化资料。至少查: - crontab(root 与所有用户)、`/etc/cron.*` - `/etc/systemd/system/*.service`、timers - `/etc/ld.so.preload`(用**外部** cat/hexdump,宿主机 cat 可能被藏空) - `chattr +i` 的异常二进制(`lsattr`) - 隐藏 Docker 数据根(非 `/var/lib/docker` 的 dockerd `--data-root`) ### 4. 进程与隐藏库 - 从特权容器列 `/proc/*/cmdline`(绕过 preload) - 对照宿主机 `ps`:只在容器视角出现的进程 = 隐藏 - `/proc/*/maps` 是否映射陌生 `.so`(`libgcwrap`、`libprocesshider`、随机名) - 矿机常见名:`perfcc`、`xmrig`、`kdevtmpfsi`、`javavm64`;同体积多副本(例如都是 11MB) - hugepage:`grep Huge /proc/meminfo`。`nr_hugepages>0` 且进程列表对不上,多半是矿机预留 ### 5. 容器与编排 - `docker ps -a`:镜像名含 earnfm / packetstream / repocket / traffmonetizer / honeygain / proxyrack / bitping / antgain 等挂机池 - inspect:`Privileged`、Binds(`docker.sock`、`/:/host`)、Ports 是否 `0.0.0.0` - 第二套 dockerd / 异常 `--bridge` - k8s:节点上是否有与 Git 仓库无关的 workload;控制面不健康时从 kubelet `pods/*/etc-hosts` 读 Pod 名 ### 6. 对外暴露 - 本机 `0.0.0.0` / `[::]` 监听 - **从另一台机器探测公网 IP**(含 IPv6)。只看 `ss` 会漏安全组,只看安全组会漏「监听但没防火墙」 - 高危:`rpcbind :111`、明文 HTTP 应用端口、Docker/Portainer、k8s 6443、数据库 - 管理 SSH 若可走 Tailscale,公网 SSH 算多余攻击面 ### 7. 资源与日志 - 无 swap + 高 hugepage → 应用易 OOM - `journalctl` / `auth.log` 在处置时间点附近;日志空也要记 - 出站连接:`ss -tp` 看陌生 IP(不要把结果里的密钥打出来) ### 8. 补漏(本次检查没做或做得浅的) 见 checklist 后半:补丁、SUID、世界可写、意外用户、IPv6、定时器、内核模块、出站 C2、云安全组、备份是否可用。 加固模式还要额外记录: - SSH 的 `sshd -t` 与 `sshd -T` 生效结果,不只看配置文件中的 grep - 防火墙实际后端(nftables/iptables/ufw)及 IPv4/IPv6 规则 - Fail2ban jail、日志后端、白名单和当前封禁状态 - 云安全组/平台防火墙是否已放行管理入口(查不到必须标未验证) - 新管理员的密钥登录是否能在第二条会话中成功,再关闭 root/密码登录 - Docker 的端口绑定、特权、socket/宿主机挂载;不要无条件改 `daemon.json` - 变更前备份、变更后验证、失败时的控制台回滚路径 ## 检查清单(按需) 需要逐项展开时,读取 [references/checklist.md](references/checklist.md)。 ## 持久化资料(按需) 发现持久化信号或需要深查时,读取 [references/persistence.md](references/persistence.md)。 ## 加固资料(按需) 用户明确要求加固时,读取 [references/hardening.md](references/hardening.md)。 ## 自动修复资料(按需) 用户批准执行修复时,读取 [references/remediation.md](references/remediation.md)。 ## 报告格式 先给结论(是否沦陷、最可能入口、对外还开着什么),再给证据表,最后给 **需用户点头的处置清单**。不要默默清理。 ``` 结论: 沦陷 / 可疑 / 未见异常 最可能入口: … 对外端口: … 持久化: … 隐藏/矿机: … 容器/编排: … 未查项: … 建议处置: (等确认) ``` ## 处置(仅在用户同意后) 原则见 [references/cleanup.md](references/cleanup.md)。 - 停并删除未授权容器;镜像也删 - 矿机二进制:删掉后用 **空文件 + `chattr +i` 占路径** - 清空并锁定 `/etc/ld.so.preload` - 关公网绑定:改成 `127.0.0.1` 或 Tailscale IP(若回源走 Tailscale 主机名,必须绑 TS IP,不能只绑 loopback) - 停无用服务(如 `rpcbind.socket` + `rpcbind.service`,必要时 mask) - 重启才能卸掉仍映射已删 `.so` 的老进程(sshd/dockerd)。先取证再重启 - 轮换:暴露过的 token、面板、k3s bootstrap。控制面 API 不通就不要假轮换,写阻塞原因 ## 验证 - 公网探测:已关端口失败;该留的业务域名仍 200 - `ss`:无 `0.0.0.0:已关端口` - `grep -l libgcwrap /proc/*/maps`(或同类库)重启后为 0 - 占位文件仍在且 immutable - 业务容器 `restart: always` 仍在