服务器安全加固

SKILL运维和基础设施
运维和基础设施 未标注权限 许可证不明确
安装这个技能

服务器安全加固 + 网络安全等级保护(等保 2.0 / GB/T 22239-2019)合规检查,默认按等保一级(第一级安全要求)检查;用户明确指定二级时按「一级全量 + 二级增量」逐条落(口令策略与更换、登录失败锁定、会话超时、远程管理防窃听、安全审计范围与留存、系统/审计管理分权、备份恢复演练、补丁与漏洞、变更管理、备案测评),三级及以上只给增量提示不假装查过——暴露面勘察、SSH 端口 IP 白名单、全端口收口(除 SSH/nginx 外一律不对外,远程改走 SSH 隧道)、公钥免密登录与关闭口令登录(带「无人登录成功即自动回滚」守卫)、docker 发布端口绕过防火墙的处置、爆破日志审计,以及等保十个层面(安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安全建设管理、安全运维管理)的逐项自查、判定口径与证据留痕,自查材料按实际命令输出生成(未验证的一律标「待人工确认」,不凑「符合」)。当用户要求「做等保 / 过等保 / 等保合规 / 等保自查 / 等保整改 / 等保一级 / 等保二级 / 等保三级 / 等级保护 / 网络安全等级保护 / GB/T 22239 / 等保测评 / 定级备案 / 等保差距分析 / 出等保材料 / 等保自查报告 / 给服务器做安全加固 / 只允许某几台机器访问 SSH / 给端口加 IP 白名单 / 分析还有哪些端口暴露到公网 / 把 redis、ClickHouse 关掉只留 SSH 和 80 / docker 端口为什么绕过防火墙 / 应用监听 0.0.0.0 能不能让外面连不上 / 配置免密登录 / 关闭密码登录只允许公钥 / 改 sshd 只留密钥登录」时使用。含三层清单分析法(监听 × 放行 × 外部实测)、默认拒绝机制与失效例外、绑环回 vs firewalld 的取舍、等保一级逐项自查表(技术 5 层面 + 管理 5 层面)、二级增量检查表与一级→二级 gap 清单、二级报告模板、等级差异与升级提示、只读自查脚本(--level 1/2、--report、--strict)、必须人工确认才执行的闸门、验证清单与锁死自救。

一句话选型结论

按资料字段整理

服务器安全加固是运维和基础设施类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类运维和基础设施 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_b9442967/server-security-hardening
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

运维和基础设施
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_b9442967仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量57.9 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
查服务与日志

按现象定位服务状态与日志线索。

写部署与巡检脚本

把重复的部署、巡检写成脚本。

故障排查

按步骤排查网络、依赖与配置。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在运维和基础设施场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
服务器安全加固当前 未标注权限 未标注 未标注 未标注 有
Azure 部署 调用外部服务 GitHub 仓库 664.2 万 Codex、Claude、Cursor 有
Azure diagnostics 问题管理 调用外部服务 GitHub 仓库 657.6 万 Codex、Claude、Cursor 有
Azure aigateway 部署 调用外部服务 GitHub 仓库 655.3 万 Codex、Claude、Cursor 有
Azure AI 搜索 调用外部服务 GitHub 仓库 651.4 万 Codex、Claude、Cursor 有
Azure Cloud migrate 部署 调用外部服务 GitHub 仓库 650.7 万 Codex、Claude、Cursor 有

SKILL.md 全文

57.9 KB
模型实际读到的说明书原文,来自技能仓库

服务器安全加固

设计原则:最小开放 —— 能不开就不开,能只给一台就不给一个网段,能只给公钥就不留口令。

合规基线:默认等保一级 —— 用户没指定等级时,一律按《GB/T 22239-2019 信息安全技术 网络安全等级保护基本要求》的
第一级安全要求(自主保护级) 做检查与留证(§2);用户明确说「二级 / 三级 / 四级」才按对应等级叠加增量控制点(§2.3)。

一句话:先查清谁要访问、从哪个地址来、到底哪些端口真能从公网连上 → 备份防火墙并挂自动回滚保险 → 只按 /32 精确放行 + 兜底 drop → 验证 → 固化并撤保险 → 按等保口径逐项留证据。
全端口收口更激进:除 SSH 与 nginx 的 80/443 外一律不对外,远程访问改走 SSH 隧道。

> 本技能管主机的网络暴露面(谁能连哪个端口)、登录方式(口令 vs 公钥)与等保通用要求的自查留证。
> 不管应用层鉴权、证书、WAF,也不替代测评机构的测评结论。
> 可直接跑的脚本见 §11;等保逐条检查项与命令见 references/mlps-l1-checklist.md。

约定:本文示例里的 x.x.x.x / y.y.y.y 代表任意真实地址,1234 代表任意端口,
<主机A> / <主机B> 代表任意目标机,<构建机> 代表发起运维的跳板机。
落地时替换成你自己的值即可 —— 技能本身不含任何具体环境信息。

0. 五条原则

原则 含义 反例
最小开放 白名单精确到 /32,能写单机就不写网段 为了「省事」放 10.0.0.0/8、x.x.x.x/25
先勘察再动手 不知道有谁在连,就别急着关 直接 --add-port / 直接删服务
可回滚 任何远程改防火墙或登录方式 的动作都要能自己退回去 只 --permanent 后 reload,错了只能进机房
人工确认后才执行 收口会断别人的访问,清单必须先给人看 脚本一把梭把 8080 关了,业务方第二天来问
合规可举证 每做一项都留下「做了什么 + 怎么验证 + 输出是什么」,按等保条目归档(§2) 改完就完,被问「凭啥说符合等保」时只剩口头解释

1. 优化项清单

# 优化项 一句话做法
1 SSH 端口 IP 白名单 rich rule priority=-10 accept + priority=-5 drop,见 §4
2 端口分层管控(80/443 全开 · 其余走 IP 白名单 · 不该跨机的绑环回) 先分档再动手,见 §5
3 docker 发布端口绑环回 -p 127.0.0.1:宿:容,见 §5.6
4 业务端口收进 nginx 反代 proxy_pass 不带 URI + location = 301,见 §5.7
5 公钥免密登录 + 关闭口令登录 先备齐/实测公钥 → 带自动回滚守卫关口令,见 §6
6 sshd 抗爆破参数 MaxAuthTries 3、LoginGraceTime 20、MaxStartups 10:30:60
7 fail2ban / sshguard 白名单已挡绝大多数,仍需拦白名单内的异常高频尝试
8 authorized_keys 审计 逐条对指纹与来源,删无效行(如前缀拼错的无效行)
9 等保自查(默认一级) 按十个层面逐项打 ✓/✗ 并留证据,见 §2
10 账户与口令基线 UID 0 唯一、无空口令、默认账户锁定、口令策略落地,见 §2 与 references/mlps-l1-checklist.md
11 入侵防范与恶意代码 最小服务、内核参数、防病毒/EDR 在位且病毒库在更新
12 备份与恢复演练 有定时备份任务 + 至少一次真实恢复验证(只有备份不算,能恢复才算)

> 本表只是待办列表,不带「已做/未做」状态 —— 每台机器做到哪一步、什么时候做的,
> 写进你自己的运维报告,不要回填进本技能。
> 顺序建议:1(白名单)→ 5(免密+关口令)→ 6(抗爆破)→ 7/8 → 2/3/4(收口)→ 9(等保自查出报告)。
> 白名单先做——它把绝大多数扫描挡在门外,之后的爆破审计才有意义。

默认交付流程(用户只说「做安全加固」也按这个走):

步 动作 读哪节
① 等保自查(只读):先跑 scripts/mlps-l1-check.sh 出基线报告,知道差距在哪 §2、§11.4
② 勘察:谁在连、有没有内网、哪些是 docker 发布 §3
③ 收口:SSH 白名单 → 端口分档 → docker 绑环回 → 反代/隧道 §4、§5
④ 登录方式:公钥免密 → 带守卫关口令 §6
⑤ 复验 + 等保回归:逐条打勾,差距项写明「已整改/待整改」 §7
⑥ 出报告:加固项 + 等保条目 + 证据,归档 §10

2. 等保合规基线(默认等保一级)

依据 GB/T 22239-2019《信息安全技术 网络安全等级保护基本要求》(业界俗称「等保 2.0」)。
第一级即「自主保护级」:系统受破坏后仅损害公民、法人和其他组织的合法权益,不损害社会秩序和公共利益、不损害国家安全。
三个必须先对齐的认知:

  • 无需备案、无需测评 ≠ 不用管:安全建设仍要按标准落实,且公安机关将其纳入监督检查——查的就是「有没有做、有没有记录」。
  • 技术只到「安全计算环境」:集中管控/集中审计所在的安全管理中心是二级及以上才出现的控制点,一级明确记为不适用。
  • 管理五项齐全但要求是「基本」:有制度、有人负责、有培训、有建设与运维管理即可;
    背景审查、外部人员访问权限清理属二级及以上要求,一级不因缺这两项判不符合。

2.1 等级怎么定:默认一级,除非用户指定

用户怎么说 怎么做
只说「做安全加固 / 做等保 / 过等保 / 等保合规 / 等保自查 / 等保整改 / 等级保护 / 按等保检查」 按一级跑完整自查(§2.2 + references/mlps-l1-checklist.md),报告首行注明「基线:等保一级(默认)」
明确说「一级」 按一级跑
明确说「二级 / 三级 / 四级」 先声明「内置表以一级为基线,二级及以上需叠加 §2.3 增量项,最终结论须由测评机构出具」→ 再按 §2.3 补查;二级直接照 references/mlps-l2-delta.md 逐条落,脚本用 --level 2
已定级备案、给出等级 以用户/备案材料的等级为准,不要自行判定
反问「我这算几级」 只给判定依据(受破坏后损害谁的利益),不替用户定级;定级是运营者责任

🔴 三条纪律:

  1. 默认就是一级,不要因为「看着像生产环境」自行升到二级/三级,也不要因为「才一级」就把检查做浅;
  2. 不许把没查的写成「符合」 —— 一级允许「部分符合 + 待整改」,如实记录比凑表更有用;
  3. 报告里分清楚「主机可验证」与「需人工确认」,后者不要替人签字。

2.2 一级检查总表(技术 5 层面 + 管理 5 层面)

技术层面——主机上基本都能查到,逐条命令见 references/mlps-l1-checklist.md:

层面 一级控制点要点 主机上的证据 对应小节
安全物理环境 机房选址能防震/防风/防雨;基本物理访问控制;防盗窃防破坏;防雷击;防火;防水防潮;温湿度控制;电力供应(UPS/双路) 现场照片 + 机房巡检记录;云主机记「不适用(云服务商责任,见共享责任边界)」 本技能不覆盖,仅登记
安全通信网络 关键网络设备处理能力与带宽满足业务高峰;有与实际相符的网络拓扑图;通信传输用校验技术保证完整性;可信验证(一级为「可」,非强制) 拓扑图版本与更新日期;nginx -T \| grep -E 'ssl_protocols\|ssl_ciphers';openssl s_client 看协议/套件;sha256sum 传输比对 §5.7、§6
安全区域边界 跨边界访问与数据流经边界设备受控接口通信;访问控制策略默认除允许外拒绝;删除多余/无效规则;规则要检查源/目的地址、端口、协议;可信验证(可) firewall-cmd --list-all / nft list ruleset / ufw status verbose;默认策略 drop/reject;--list-rich-rules 无 /8、/25 这类宽放行;安全组截图 §4、§5、§11.2
安全计算环境 登录用户分配账户和权限;重命名或删除默认账户、修改默认口令;及时删除/停用多余、过期账户;避免共享账户;身份鉴别、访问控制、入侵防范、恶意代码防范、数据完整性/保密性/备份恢复 账户清单(UID 0 唯一、无空口令、无 test 类账户)、口令策略、文件权限、监听端口、AV/AIDE、备份任务与服务端日志 §6、§11.4 + references/mlps-l1-checklist.md
安全管理中心 一级不要求(集中管控、集中审计、集中恶意代码/补丁管理均为二级+) 无需取证,报告写「不适用(一级无此控制点)」 —

管理层面——非主机可验证,靠文档 + 访谈,本技能只给清单与判定口径:

层面 一级控制点要点 要收的证据
安全管理制度 有基本网络安全管理制度与操作规程;明确安全策略、管理制度、制度制定与发布流程 制度文件(含版本/生效日期)、发布审批记录
安全管理机构 指定或授权专门部门/人员负责网络安全;落实安全保护责任;配备安全管理人员 任命或授权文件、岗位职责说明、人员名单
安全管理人员 对各类人员做安全意识教育与岗位技能培训;告知安全责任与惩戒措施;人员录用由专人负责 培训签到与考核记录、责任告知书、录用流程记录
安全建设管理 定级和备案、安全方案设计、产品采购、软件开发、工程实施、测试验收、系统交付 定级报告、安全方案、采购与验收记录、交付清单
安全运维管理 环境管理、资产管理、介质管理、设备维护、漏洞和风险管理、网络和系统安全管理、恶意代码防范管理、备份与恢复管理、安全事件处置 资产台账、巡检记录、漏洞与补丁记录、备份记录、事件处置工单

> 一级无需备案:安全建设管理里的「定级和备案」按「已完成自主定级、留存定级过程材料」把握即可,不要去催备案回执。

2.3 等级差异:什么时候需要升级(要点,非逐条对照)

用户提到更高等级时,按这张表把增量补上;二级的逐条落法见 references/mlps-l2-delta.md(编号 L2-01…L2-17,
与 --level 2 的脚本输出一一对应)。正式定级/测评以标准原文与测评机构意见为准。

控制点 一级(默认) 二级增量 三级增量
备案 / 测评 无需备案、无需测评 需备案,按监管要求测评 需备案,每年测评
安全管理中心 不适用 要求:系统管理 + 审计管理(管理员分权、操作审计)。⚠️ 集中管控是三级,别在二级强推 增加安全管理 + 集中管控(统一策略、集中日志/事件/补丁/病毒管理)
身份鉴别 账户唯一 + 基本口令强度 口令复杂度与定期更换、登录失败处理、远程管理防窃听、会话超时 两种以上组合鉴别(双因素)、鉴别信息加密存储
访问控制 分配账户与权限 最小权限、默认拒绝、授权粒度更细、由授权主体配置策略 更细粒度 + 重要主体/客体的安全标记与强制访问控制
安全审计 一级无独立审计控制点,按「关键操作留有记录」把握 要求:审计范围、记录内容、记录保护、留存 增加审计记录备份、防未授权删改、集中审计
可信验证 「可」(非强制) 「可」 「应」:基于可信根验证引导/系统程序,动态度量、异常报警
数据备份恢复 基本备份措施 本地备份 + 恢复演练(要能证明可恢复) 异地/热备、重要数据定期备份与恢复演练
恶意代码防范 基本措施 统一管理、及时更新病毒库 更严 + 集中管理
数据保密性 基本措施 重要数据传输/存储加密 密码技术保护,涉及商用密码合规
人员管理 培训 + 告知责任 增加背景审查、外部人员访问权限审批与清理、离岗权限回收 关键岗位定期审查、签署保密协议
运维管理 基本运维活动 增加漏洞扫描与补丁管理、变更管理、审计管理规范、日志留存 集中日志审计、漏洞与补丁闭环

2.4 判定口径与报告怎么出

结论 什么情况用 必须写清
符合 已落实,且证据能复现 命令/配置文件 + 输出摘要
部分符合 做了一部分,或只覆盖部分主机/账户 缺口是什么(哪台、哪个账户、哪条规则)
不符合 未落实 整改建议 + 优先级
不适用 一级无此控制点 / 云上由云服务商负责 / 业务确实不涉及 理由(一句话)
待人工确认 文档、访谈、现场照片类 交给谁确认、要什么材料

证据三件套:命令或文件 + 输出摘要(别贴几千行) + 主机标识与时间。
同一台机器重复排查时,用上次的证据做对比,才能证明「真的改了」。

2.5 加固动作 ↔ 等保控制点映射(做过的活别浪费)

等保控制点 本技能里对应的动作
安全区域边界 › 访问控制(默认拒绝、五元组、删冗余) SSH 白名单(§4)、端口三档收口(§5)、端口段单独 drop(§5.6)
安全区域边界 › 边界受控接口 只留 nginx 80/443 与 SSH,其余走隧道(§5.7、§5.8)
安全计算环境 › 身份鉴别 公钥免密 + 关闭口令登录 + MaxAuthTries(§6)
安全计算环境 › 访问控制 账户与权限梳理、文件权限、sudo 最小化(§2.2 + 检查表)
安全计算环境 › 入侵防范 最小服务与端口、fail2ban/sshguard、内核参数(§5、§11.2)
安全计算环境 › 恶意代码防范 防病毒/EDR 在位 + 病毒库更新
安全计算环境 › 数据完整性 AIDE/rpm -Va、关键文件 sha256 基线
安全计算环境 › 数据备份恢复 定时备份任务 + 真实恢复演练
安全通信网络 › 通信传输完整性 TLS 版本/套件、传输校验
安全运维管理 §10 落地记录 + 巡检/漏洞/备份/事件台账

2.6 逐条检查项

  • 完整检查表(每条:检查内容 / 命令 / 符合判据 / 证据 / 常见不符)
  • 一级 → references/mlps-l1-checklist.md
  • 二级增量 → references/mlps-l2-delta.md(L2-01…L2-17,含「一级→二级 gap 清单」与二级报告模板)
  • 一键只读自查(输出 符合/部分符合/不符合/不适用/待人工确认 + 证据行 + 汇总)→
bash scripts/mlps-l1-check.sh              # 只读,默认按一级
bash scripts/mlps-l1-check.sh --level 2    # 一级全量 + 二级增量,分别计分
bash scripts/mlps-l1-check.sh --strict     # 有「不符合」时退出码 2(可进 CI)

> 脚本只读(不改任何配置、不重启服务),覆盖主机可验证项;文档/访谈/现场类会标成「待人工确认」,不会替你判符合。
> 三级/四级只会列出尚未覆盖的增量项,不会假装查过。

3. 第一步:勘察(跳过这步必踩坑)

3.1 谁在连这个端口?——从日志反查,不要问「应该是谁」

lastb -a | head -20
lastb -a | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | sort -rn | head
lastb -a | awk 'NF>2{print $1}' | sort | uniq -c | sort -rn | head   # 被爆破的用户名

journalctl -u sshd --no-pager | grep "Accepted" | tail -50          # 合法来源只有这里
journalctl -u sshd --no-pager | grep -c "Accepted password"          # >0 = 口令登录还开着
  • lastb 读 /var/log/btmp,只保留当月;「最早一行」= 重装/轮转时间,不是攻击起点。
  • 取 IP 用 grep -Eo 正则比 awk $N 稳(短行会报 field -2)。
  • Accepted 的来源与 lastb 的攻击 IP 交集为空,才说明「被爆破但没破」。
  • grep -c "Accepted password" > 0 就是关闭口令登录的收益点(§6)。

3.2 这台机器到底有没有「内网地址」?

ip -4 -o addr show; ip route
ip -6 -o addr show scope global; ip -6 route | head -3

🔴 「公网地址直配网卡」是最常见的误判:IDC 把公网段直接配在 eno1 上(如 x.x.x.x/25),
两台同段二层互通(0.1ms),看着像内网,其实没有任何私有地址。此时「只开放内网 IP」做不到:

方案 做法 评价
精确 /32(推荐) 只放对端公网 IP 效果等同「只允许那一台」,无额外组件
隧道叠加 GRE / WireGuard 建 10.99.0.0/30 隧道仍需对端公网放行某协议,暴露面没减少
跳板中转 只放跳板机,对端 ssh -L / ProxyJump 真正不信任对端网络,代价是多一跳

IPv6 别想当然当替代通路:两端实测(ping -6 + /dev/tcp),常见结果是「有地址没路由」。

3.3 端口是「主机进程」还是「docker 发布」?

ss -tlnp | grep <端口>            # process 是 docker-proxy 即为发布端口
docker ps --format '{{.Names}}\t{{.Ports}}'

🔴 docker -p 0.0.0.0:1234:1234 完全绕过 firewalld(§5.6 有实测证据)。这类端口只能靠
-p 127.0.0.1:1234:1234 收口,zone 里的白名单/drop 对它无效。

3.4 防火墙栈确认

systemctl is-active firewalld ufw 2>/dev/null
firewall-cmd --version                              # ≥0.6 才支持 rich rule 的 priority
grep -i FirewallBackend /etc/firewalld/firewalld.conf
firewall-cmd --list-all                             # ports / services / rich rules

4. SSH 端口 IP 白名单

4.1 规则结构(rich rule + 优先级,不要 source-based zone)

priority=-10  源在白名单 → 该端口 accept
priority=-5   其余所有源 → 该端口 drop
(zone 的 ports/services 隐含 priority 0,永远排在上面两条之后)
  • 负优先级保证白名单判定先于 zone 自身 ports/services;同优先级按添加顺序。
  • 🔴 绝对不要用 source-based zone:--add-source 会把该源全部流量切进新 zone,
    连带 80 与其它已放行端口一起断。必须同 zone 内用「只针对该端口的 rich rule」。

4.2 实施

TS=$(date +%Y%m%d-%H%M%S); cp -a /etc/firewalld /root/firewalld.bak.$TS
systemd-run --on-active=600 --unit=sshwl-rollback firewall-cmd --reload   # 回滚保险

for ip in <本机出口>/32 <构建机出口>/32 <对端IP>/32 <自身IP>/32; do
  firewall-cmd --add-rich-rule="rule family=\"ipv4\" priority=-10 source address=\"$ip\" port port=\"<端口>\" protocol=\"tcp\" accept"
done
firewall-cmd --add-rich-rule="rule family=\"ipv4\" priority=-5 port port=\"<端口>\" protocol=\"tcp\" drop"
firewall-cmd --remove-port=<端口>/tcp      # 摘掉「全开」声明,纵深防御

4.3 IPv6 必须同步

ss 显示 [::]:<端口> 或 *:<端口>(双栈)时,只写 family="ipv4" 的 drop,v6 侧仍全开:

firewall-cmd --add-rich-rule='rule family="ipv6" priority=-10 source address="<v6对端>/128" port port="<端口>" protocol="tcp" accept'
firewall-cmd --add-rich-rule='rule family="ipv6" priority=-5 port port="<端口>" protocol="tcp" drop'

4.4 固化

systemctl stop sshwl-rollback.timer sshwl-rollback.service
firewall-cmd --runtime-to-permanent
# ❌ 不要 --permanent 逐条重加(易漂移);❌ 不要 --reload(flush docker DNAT)

5. 端口分层管控(全开 / 白名单 / 仅环回)

目标态:三档并存,按需定档,不要一刀切全关。

档 典型端口 策略 谁能连
① 全开放 80、443(nginx) 对全网开放,靠应用自身鉴权兜住 任何人
② IP 白名单 SSH 端口,以及所有需要远程使用的业务端口(管理后台、API、监控口) 只放白名单 /32,其余 drop —— 与 §4 完全同一套规则结构 白名单内的机器
③ 仅环回 数据库、缓存、监控采集端等「不该跨机直连」的 服务绑 127.0.0.1,公网地址上根本没人监听;远程要连就开 SSH 隧道 仅本机(+ 隧道)

> ②是主力档:不是「关掉」,而是「只给固定几台机器开」——运维不用天天开隧道,外网扫描也进不来。
> 一刀切全禁只在「这个端口没人远程用」时才成立,否则第二天就有人要求重新放行。

5.1 先定「每个端口属于哪一档」(唯一需要人拍板的事)

档 判定依据
① 全开放 面向终端用户浏览器的入口,通常只有 80/443
② 白名单 内部系统间调用、管理后台、API —— 需要跨机,但不该对全网
③ 仅环回 单机自用(数据库、缓存、监控采集端),或「连白名单机器都不该直连」的
  • 定档原则:能进③就别进②,能进②就别进①。同一台机三档并存是常态,但每个端口必须明确归档。
  • 白名单地址通常复用 SSH 那几个 /32(本机出口、构建机、对端服务器、自身);新增对端时两处一起加。
  • 定完档再动手,别边改边想——改错档位等于把该锁的打开、或把该开的锁死。

5.2 为什么「应用在 0.0.0.0 监听」≠「外面能连」——默认拒绝机制

firewalld 是默认拒绝模型:流量进入 filter_IN_public 链后,只有命中
ports / services / rich rule accept 才放行,其余一律被 zone 的兜底动作丢掉(实测表现为 Timeout,
说明包根本没被接受,不是被应用拒绝)。

所以只要不 --add-port,应用绑 0.0.0.0 也是外面连不上的。实测:

# ① 临时起一个 0.0.0.0:11111 的监听,且 firewalld 不放行
python3 -m http.server 11111 --bind 0.0.0.0 &
ss -tlnp | grep 11111            # LISTEN 0.0.0.0:11111  ← 确实全接口监听
firewall-cmd --list-ports        # 里面没有 11111

# ② 从外部探测 → TimeoutError(防火墙挡住了,应用毫不知情)
# ③ firewall-cmd --add-port=11111/tcp --timeout=60 之后再探测 → OPEN
# ④ --remove-port=11111/tcp 后探测又回到 Timeout

这条结论的价值:收口的主力是「不放行」,不是「改应用」。应用绑不绑 0.0.0.0 只影响
「同机容器/进程能不能连」,不影响公网可达性。

🔴 但这个「默认拒绝」有 6 个失效例外,必须逐一排除:

# 例外 为什么会穿透 处置
1 docker 发布 -p 0.0.0.0:宿:容 nat PREROUTING 先 DNAT,包直接进容器,filter_IN_public 轮不到 改 -p 127.0.0.1:,见 §5.6
2 有人 --add-port / --add-service / 另一 zone 放行 配置本身放开了 审计 --list-ports、--list-all-zones
3 source-based zone(--add-source) 该源全部流量进新 zone,若新 zone 宽松就全开 禁用此法
4 IPv6 侧漏配 *:端口 是双栈,只 drop 了 v4 补 family="ipv6"
5 firewalld 未启动 / 被 stop / reload 后规则丢失 没有 netfilter 规则 = 全开 systemctl is-enabled firewalld + 固化
6 手工 iptables/nft 规则、docker run --net=host host 网络不经 DNAT,但也受 filter 约束;手工规则可能 ACCEPT 审计 nft list ruleset

5.3 三层清单分析法(监听 × 放行 × 外部实测)

只看 ss 会误判,只看 firewall-cmd 也会误判(docker DNAT、v6、绑特定 IP 都藏在缝里):

# ① 监听层
ss -tlnp
#   0.0.0.0:X / *:X / [::]:X → 全接口(* 为双栈)
#   x.x.x.x:X               → 只绑公网单地址(同样对外)
#   127.0.0.1:X / [::1]:X   → 仅本机(合规)

# ② 放行层
firewall-cmd --list-ports; firewall-cmd --list-services
docker ps --format '{{.Names}}\t{{.Ports}}'    # 0.0.0.0 发布的容器端口

# ③ 实测层(从另一台机器,别看配置看连通性)
python -c "
import socket
h='<目标IP>'; ports=[<端口列表>]
for p in ports:
    s=socket.socket(); s.settimeout(4)
    try: s.connect((h,p)); print(p,'OPEN'); s.close()
    except Exception as e: print(p,type(e).__name__)
"

结论分三类:

类 判据 处置
A 公网可达 全接口监听 且(firewalld 放行 或 docker 0.0.0.0 发布)且 实测 OPEN 收口(不在保留清单就关)
B 纵深待办 全接口监听,但未放行(实测 Timeout) 不紧急但要补:现在靠一条 --add-port 挡着
C 已合规 只绑 127.0.0.1/[::1] 无需动

> ⚠️ B 类别漏:「今天没暴露」只说明防火墙恰好没开,不代表配置到位。
> 收口价值一半在 A(止血),一半在 B(消除「误放行即裸奔」)。

5.4 收口手段优先级

优先级 手段 适用 代价
① 绑环回(根治) 服务只监听 127.0.0.1 能改配置的服务 / docker 发布 重启服务
② firewalld 收口(缓解) 摘 --add-port + 加 priority=-5 drop 第三方进程、改不了配置 无(即时)
③ SSH 隧道(访问方式) ssh -N -L 转发 收口后的远程访问 需 AllowTcpForwarding local

各服务绑环回写法:

服务 做法
ClickHouse config.xml 的 <listen_host> 改 127.0.0.1(全局:http/tcp/其它端口一起变),systemctl restart clickhouse-server
Redis bind 127.0.0.1 -::1 + protected-mode yes(见 install-redis 技能)
Spring Boot / TongWeb server.address=127.0.0.1(或 SERVER_ADDRESS)
docker -p 127.0.0.1:宿主:容器
无此配置项的第三方二进制 只能走 ②

5.5 🔴 人工确认闸门(必须)

① 跑审计(只读)→ 输出 A/B/C 三层清单
② 把 A 类清单交给用户/业务方确认:哪些端口有人在用?保留哪些?
③ 用户明确回复「关哪些、留哪些」→ 才执行
④ 执行前挂回滚保险;执行后逐端口从外部复验
  • 审计与执行分成两个动作,脚本也分两个模式(审计只读,--close 还需 --confirm)。
  • 确认时逐端口问「谁在用」,不要一次「全关」。典型踩坑:CI 从外网调数据库 HTTP 口灌数、
    监控探针调采集口、同事浏览器直连调度后台。
  • 关闭前查连接来源:
ss -tn state established '( sport = :<端口> )' | head
journalctl --no-pager --since -24h | grep -c ':<端口>'

5.6 docker 端口绕过 firewalld —— 现象、证据与四种应对

现象:容器用 -p 1234:1234 发布,firewalld 明明没放行,外面照样能连。

原因:包进入主机后先走 nat PREROUTING,docker 的规则把目的地址 DNAT 成容器 IP,
之后才进 filter。filter_IN_public 只对「目的是本机地址」的包生效,被 DNAT 的包早已不是本机目的地址,
于是 zone 的 accept/drop 全部轮不到。在 filter 层做白名单对 docker 发布端口天然无效。

实测(docker 25.0.3 / nftables 后端):

实验 发布方式 firewalld 是否放行 外部探测结果 含义
A -p 0.0.0.0:11112:1234 否 ConnectionRefusedError 包穿过了防火墙进到容器(容器内无监听才回 RST);若被拦会是 Timeout
B -p 127.0.0.1:11113:1234 否 TimeoutError 外部根本到不了

> 判读要点:Timeout = 被防火墙丢掉;Connection refused = 包已经到达了目标协议栈。
> 看到 refused 就说明「外层防护没生效」,这是识别 docker 绕过的关键信号。

四种应对(按推荐度):

# 方案 做法 评价
1 发布绑环回(首选) -p 127.0.0.1:1234:1234;远程用 ssh -L 11234:127.0.0.1:1234 根治、零额外组件;实测外部 Timeout、宿主 127.0.0.1 正常
2 host 网络 + 应用绑环回 --net=host + 服务配置 127.0.0.1 适用于容器内服务能改配置;没有再套一层 DNAT
3 DOCKER-USER 链限制源 iptables -I DOCKER-USER -p tcp --dport 1234 ! -s <白名单> -j DROP 在 FORWARD 最前,能拦住已 DNAT 的转发流;⚠️ docker 重启会重建链,需持久化(systemd unit 或 iptables-save)
4 raw 表在 DNAT 之前丢 iptables -t raw -I PREROUTING -p tcp --dport 1234 -s ! <白名单> -j DROP 唯一在 conntrack 之前生效的位置;同样需持久化
✗ 关掉 docker 的 iptables daemon.json 设 "iptables": false 不要:容器网络/出网/端口发布连带失效,排障成本极高

> 检查清单:docker ps --format '{{.Names}}\t{{.Ports}}' 里凡是以 0.0.0.0: 开头的一律视为
> 「firewalld 管不到」,必须按上面 1~4 处理,别指望 zone 规则。

5.7 收口前先把业务端口收进 nginx 反代

收口的正确姿势不是「关掉就不管」,而是先给它留正规入口(nginx 的 80/443),再关掉直连端口。
否则页面打不开、API 调不通,业务方第二天就来要求重新放行。

典型:调度中心类后台(监听某个业务端口),要能从浏览器访问,但不该暴露那个端口。

# nginx.conf  ->  server { listen 80; ... }
location = /admin {                          # 无尾斜杠必须 301,否则静态资源相对路径算错
    return 301 /admin/;
}
location /admin/ {
    proxy_pass http://127.0.0.1:1234;        # ⚠️ 不带 URI 部分,保留 /admin 前缀
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 300s;                 # 后台常有长请求(触发任务、拉日志)
}

三条硬规矩:

规矩 原因
proxy_pass 不带 URI 部分(写 http://127.0.0.1:1234,不要 .../admin) 带 URI 时 nginx 会用 URI 替换掉匹配到的 location 前缀;而 Spring 应用自身的 context-path 已是 /admin,会变成 /admin/admin/...
单独写 location = /admin 做 301 少了尾斜杠时首页能开、但静态资源 404
后台里配的地址一律改成 nginx 入口 执行器地址 / 回调地址若还写 http://host:1234/...,收口后回调直接失败

验证(从外部、不经直连):

curl -sI http://<host>/admin          # 302 → /admin/
curl -sI http://<host>/admin/         # 302 → 登录页
curl -s  http://<host>/admin/login | head -3    # 应返回登录页 HTML

🔴 改 nginx 前必须 nginx -t,但它常常「本来就失败」。实测报
cannot load certificate "..."(有人在配 443,证书还没放上去)。处置顺序:

  1. 先修前提(补证书,或把 listen 443 ssl 的 server 块注释掉)→ 再 -t;
  2. -t 通过后才 -s reload;-t 不通过绝不能 reload(reload 会加载失败,旧 worker 还在跑,
    你无法确认新配置到底生效没有);
  3. nginx 可能是手工启动(源码安装、无 systemd 单元)——
    systemctl is-active nginx 会说 inactive,别被误导;reload 用 <nginx前缀>/sbin/nginx -s reload。

反代确认可用后再收口直连端口。若该端口落在端口段放行内(如 8080-8090/tcp),段是摘不掉的,
用 priority=-5 的 drop 单独挡住它即可(drop 不受段放行影响,段内其他端口不受影响)。

5.8 远程访问改用 SSH 隧道

ssh -N -L 11234:127.0.0.1:1234 <目标主机>                      # 单端口
ssh -N -L 11234:127.0.0.1:1234 -L 15678:127.0.0.1:5678 <目标主机>   # 多端口
ssh -N -J <跳板> -L 11234:127.0.0.1:1234 <user>@<目标>            # 走跳板
# 之后本地访问 127.0.0.1:11234 即为远端 1234

前置:sshd AllowTcpForwarding local(重装系统默认常为 no,报 ChannelException: Administratively prohibited 就是它);
GatewayPorts no 保持默认,别把隧道对外开。

5.9 执行(按档位二选一)

② 白名单档(主力,等同 SSH 的规则结构):

firewall-cmd --list-ports                          # 记下原始状态
firewall-cmd --remove-port=<端口>/tcp              # 先摘掉「对全网放行」的声明
for ip in <白名单1> <白名单2> <白名单3> <自身IP>; do
  firewall-cmd --add-rich-rule="rule family=\"ipv4\" priority=-10 source address=\"$ip/32\" port port=\"<端口>\" protocol=\"tcp\" accept"
done
firewall-cmd --add-rich-rule="rule family=\"ipv4\" priority=-5 port port=\"<端口>\" protocol=\"tcp\" drop"
firewall-cmd --add-rich-rule="rule family=\"ipv6\" priority=-5 port port=\"<端口>\" protocol=\"tcp\" drop"
firewall-cmd --runtime-to-permanent                # 验完再固化

③ 仅环回档:连 firewalld 都可以不加——把服务绑 127.0.0.1 才是根治(§5.4)。若要防「配置被改回去」,
再补一条 drop 兜底即可(此时白名单 accept 没有意义,因为进程不听公网地址)。

> 为什么摘了 --add-port 还要加 drop?前者只是「不再放行」,后者是不受其他放行影响的一道独立护栏
> (防手抖、防别的 zone/service/端口段再放开)。端口段放行(如 8080-8090/tcp)尤其只能靠 drop 单独挡。
> 但对 docker 发布端口这套无效,必须按 §5.6 处理。

5.10 ②+③ 叠加 = 双重保护(可选,取决于要不要让白名单机器直连)

同一端口可以同时吃两道防线:firewalld 只放白名单 + 服务只听 127.0.0.1。
叠加后的实测特征:

来源 结果 原因
白名单内 Connection refused 防火墙放行了,但服务在公网地址上没监听 → 内核回 RST
白名单外 Timeout 被 firewalld drop
本机 127.0.0.1 正常 走 lo,不受 zone 限制

怎么选:

场景 选档
白名单机器需要直连(灌数、BI 拉数、管理后台) 只上 ②,服务听 0.0.0.0
谁都不许直连,只能通过隧道 ②+③ 叠加(或直接只上 ③)

> 判读口诀:白名单内 refused = 双重保护生效(服务没听公网),不是配置错了。

6. 公钥免密登录 + 关闭口令登录(带自动回滚守卫)

目标态:PasswordAuthentication no + KbdInteractiveAuthentication no + PermitRootLogin prohibit-password,
只允许公钥进来。爆破在口令层面直接失效(口令不再是一把可猜的钥匙)。

🔴 这是本技能里唯一「关错了就再也进不去」的动作 —— 防火墙关错了还能等 timer 回滚/走控制台,
口令关错了且公钥有问题,远端机器就只能进机房。所以必须同时满足两件事:

  1. 关之前:所有需要进来的人的公钥都已装好,且每条链路都用 BatchMode 实测通过(§6.1 准入清单);
  2. 关的时候:挂一个跑在服务器本地的回滚守卫 —— 窗口内没人成功登录就自动把口令登录打开(§6.3)。

> 顺序不可颠倒:先配免密实测通过 → 再关口令。反了就是自锁。

6.1 前置:免密公钥部署与准入清单

bash scripts/ssh-key-deploy.sh --host <主机A> --user root          # 预演:看将做什么 + 现有指纹
bash scripts/ssh-key-deploy.sh --host <主机A> --user root --apply  # 执行:备份 → 追加 → BatchMode 实测

为什么不用 ssh-copy-id(踩过):目标机常带 MaxAuthTries 3(§6.4 抗爆破),
而来源机往往有多把私钥(id_rsa + id_ed25519 + agent),ssh 握手会先试无效的钥匙,
3 次即被踢 → 报 Too many authentication failures。看着像密码错,其实是认证次数被卡住。
对策:直接从已通的链路 append 公钥,或强制 -o IdentitiesOnly=yes -i <指定私钥>。

关口令前的准入清单(逐条确认,缺一不可):

# 检查 命令
① 目标机全部授权指纹都认识 ssh <host> 'ssh-keygen -lf ~/.ssh/authorized_keys'
② 每条链路都 BatchMode 实测通过 ssh -o BatchMode=yes <host> hostname(本机 / 跳板 / CI 各来一次)
③ 所有可登录账号都覆盖 ssh <host> "awk -F: '\$7!~/(nologin\|false)/{print \$1}' /etc/passwd"
④ 防火墙白名单含你的出口 IP firewall-cmd --list-rich-rules \| grep <SSH端口>
  • 🔴 authorized_keys 里他人的钥匙别动(同事、备份机的)。逐条对指纹,删的只有拼错的无效行。
  • ⚠️ authorized_keys 是「每账号一份」:非 root 账号有自己的
    /home/<user>/.ssh/authorized_keys,只顾 root 会让该账号直接失联。
    但也要注意:那份钥匙的私钥可能就在目标机自己身上(自生成),外部拿不到 = 等于没配。
  • ⚠️ 出口 IP 一变全体进不去:白名单只有 /32 时,办公网换出口即全体失联,确认有控制台/VNC 兜底。

6.2 关闭动作

bash scripts/nopasswd-login.sh                                  # 预演
bash scripts/nopasswd-login.sh --apply --window 300             # 执行:备份 → 先起守卫 → 写 drop-in → reload

写下的 drop-in(/etc/ssh/sshd_config.d/99-no-password.conf):

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
  • drop-in 优先,不改主文件:现代发行版的 sshd_config 多有 Include /etc/ssh/sshd_config.d/*.conf,
    且若无 Match 块 → 一条 drop-in 全局覆盖。先确认这两个前提(脚本会自动探测);
    无 Include 的老机器脚本会自动降级为改主文件 + 注释原位 yes。
  • PermitRootLogin 生效值可能显示 without-password —— 那是 prohibit-password 的等价写法,非笔误。
  • 只 reload 不 restart:不中断已建立的会话(当前会话是你最后的退路,别关)。
  • 改完必看生效值,不看文件:sshd -T | grep -iE '^passwordauthentication'。
    只改文件不够 —— 主文件里残留的 PasswordAuthentication yes 会抢先命中。

6.3 回滚守卫(本技能的核心设计)

关之前 ──▶ systemd-run 起守卫(transient 单元,跑在服务器本地,脱离一切 SSH 会话)
              │
              └─ ① 备份 sshd 配置        ② 【先】起守卫        ③ 再写 drop-in 并 reload
                                                                    │
                    N 分钟窗口后 ◀──────────────────────────────────┘
                        │
              <marker> 在窗口内被创建?
                 ├─ 是 → verdict=keep    ,保持关闭(= 固化成功)
                 └─ 否 → verdict=rollback,自动删 drop-in + reload → 口令登录恢复
要点 说明
守卫必须跑在服务器本地 systemd-run --unit=... --collect(/run/systemd/transient/)。发起方(agent/SSH)断开也照样判定 —— 否则「免密坏了」时发起方自己也连不上,守卫形同虚设
守卫先于关闭启动 顺序是「起守卫 → 再关」。反过来若关闭后守卫没起来,就是无守卫裸奔
成功判据只有一个 <marker>(默认 /root/i_am_here)在窗口内被新建(mtime ≥ 起点)
窗口内人工动作 从另一条链路免密登录并 touch /root/i_am_here(这就是「证明免密可用」的凭据)
幂等 已在「关闭+守卫运行中」时重复执行直接短路(避免抹掉标记、重启窗口)

🔴 绝对不要用「sshd 日志出现 Accepted publickey」当判据 —— 这是实测踩到的致命缺陷:
关闭动作本身就是经 ssh 触发的,那次登录必然落在窗口内,判据永远命中 → 守卫永远判 keep、永不回滚。
(实测:窗口内明明只有发起关闭的 1 次连接,计数 =1,守卫却判了 keep。)
> 通用教训:兜底判据若与「触发动作自身」耦合,必须剔除,否则守卫等于没装。

6.4 验证(关口令后必做)

# 验证 命令 / 期望
① 生效值已关 sshd -T \| grep -iE '^passwordauthentication' → no
② 免密仍可用 ssh -o BatchMode=yes <host> hostname → 成功
③ 口令确被拒 ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no <host> true → Permission denied,且拒绝列表里已无 password
④ 守卫判定 cat /root/sshd-nopasswd/$(cat /root/sshd-nopasswd/latest)/verdict → verdict=keep
⑤ 无残留 drop-in 仍在(keep)或已删(rollback);sshd -t OK

手工回滚(任何时候):rm -f /etc/ssh/sshd_config.d/99-no-password.conf && systemctl reload sshd

6.5 定时执行(可选)

想「约定时间自动关」,别用外部调度器(agents 掉线就没人做窗口内登录):把关闭动作交给服务器本地 systemd timer。

# /etc/systemd/system/sshd-nopasswd-close.service
[Service]
Type=oneshot
ExecStart=/root/bin/nopasswd-login.sh --apply --window 300
RemainAfterExit=yes

# /etc/systemd/system/sshd-nopasswd-close.timer
[Timer]
OnCalendar=YYYY-MM-DD HH:MM:SS
Persistent=false
AccuracySec=1s
Unit=sshd-nopasswd-close.service
  • OnCalendar 到点即触发,不依赖发起方在线;守卫同样跑在本地,闭环自洽。
  • ⚠️ 窗口内仍需有人登录建标记 —— timer 不会替你登录。若完全无人值守,
    守卫会在窗口后回滚(这是设计的正确行为:没证据就认为免密不可靠)。
  • 取消:systemctl disable --now sshd-nopasswd-close.timer。

7. 验证(改完必验)

# 验证 命令 / 期望
① 入口端口仍通 80/443 站点正常;SSH 从白名单主机新开连接可登录
② 档位效果符合预期 白名单档:白名单内 OPEN、白名单外 Timeout;仅环回档:谁都连不上(白名单内为 refused)。出现「该断的却 OPEN」→ 查 docker 发布(§5.6)与端口段放行
③ 规则顺序正确 nft list ruleset \| grep -n <端口>:accept 行在 drop 行之前
④ 无网段/全开残留 --list-rich-rules \| grep /25 无输出;--list-ports 里不该再有该端口
⑤ 隧道能干活 ssh -N -L 后本地 curl 127.0.0.1:<本地端口> 与原来响应一致
⑥ 登录方式 若做了 §6:口令被拒、免密可用、守卫 verdict=keep(§6.4)
⑦ 等保回归 bash scripts/mlps-l1-check.sh 再跑一遍,差距项对比上一次报告是否收敛;报告首行写明「基线:等保一级(默认)」

> ①②③④ 证明「关掉了」,⑤ 证明「关掉之后还能干活」,⑦ 证明「改完确实更合规了」——
> 只做前四项,第二天业务就会来找你;漏掉 ⑦,检查时还是拿不出证据。

8. 锁死后自救

  1. 永远先挂自动回滚:systemd-run --on-active=600 --unit=<名> firewall-cmd --reload(§6 的守卫同类思路)。
  2. 别用 --reload 主动应用改动:它会 flush docker 的 nat 链(见 §5.6)。
  3. 固化后再撤 timer,否则 10 分钟后被打回原形。
  4. 被锁死的兜底顺序:控制台/VNC → 云商安全组/串口 → 同网段已放行的机器跳进来。
    所以放行清单必须包含自己当前出口 IP,最好再加一台对端/跳板。
  5. 收口前先确认 nginx 真在跑并能反代——把 80 关了才发现 nginx 没起,等于全站下线。
  6. 若锁死原因是关了口令登录(§6):等守卫窗口自动回滚;或从仍持有公钥且在白名单内的机器进来
    执行 §6.4 的手工回滚命令。

9. 常见坑速查

现象 原因 / 处理
白名单外探测得到 Connection refused 包已到达目标协议栈 = 外层没拦住:多半是 docker 0.0.0.0 发布(§5.6),或命中端口段放行
白名单内探测得到 Connection refused 防火墙已放行,是服务自己没监听公网地址(绑了 127.0.0.1)→ 双重保护生效,属预期;要直连就开隧道,别把服务改回 0.0.0.0
加完白名单,docker 端口仍能连 DNAT 绕过 filter;只能改 -p 127.0.0.1:(zone 规则无效)
加完白名单,自己也连不上 白名单漏了当前出口 IP;等回滚 timer(§8),或走控制台
--add-source 后入口端口全断 source-based zone 把该源全部流量切走;改同 zone rich rule
reload 之后数据库端口又对公网开放 reload flush 了 docker nat 链;改规则期间禁用 reload
只 drop v4,扫描仍从 v6 进来 *:端口 是双栈,补 family="ipv6"
应用明明绑了 0.0.0.0,外部却连不上,怀疑防火墙没生效 这是正常的(默认拒绝);确认 --list-ports 里没有它即可,见 §5.2
改了 ClickHouse listen_host 没生效 listen_host 全局生效,多个端口共用,改完要重启
systemctl is-active nginx 说 inactive,站点却是通的 nginx 是手工启动(源码安装),没接 systemd;查 ps -ef \| grep nginx
收口后调度后台打不开 应走 nginx 反代,别直连应用端口(§5.7)
收口后执行器掉线 执行器主动注册(出向),入向收口不影响;查注册地址配置
rich rule 里 priority 报错 firewalld < 0.6 不支持
审计说「未放行」,外面却连得上 大概率是 端口段放行(如 8080-8090/tcp):只匹配精确端口会漏判。用 public-ports-guard.sh(已支持范围)或人工展开段内每个监听端口
收口报告成功,但外部仍 OPEN 该端口在端口段放行里,--remove-port=1234/tcp 无效(段不是精确端口)→ 必须加 priority=-5 的 drop 单独挡
nginx 反代后页面 404 / 资源加载失败 proxy_pass 带了 URI 部分把前缀替换掉了;改成只写 http://127.0.0.1:&lt;端口&gt;,并补 location = /path 的 301
nginx -t 报 cannot load certificate 证书没放上去(有人在配 443)→ 先补证书或注释掉 ssl server 块,-t 不过绝不 reload
firewall-cmd --permanent --list-* 夹 --zone=docker 警告 只是提示(默认 zone 与活动 zone 不一致);显式加 --zone= 可静音
ssh-copy-id 报 Too many authentication failures 目标机 MaxAuthTries 小 + 来源机私钥多,握手先试无效钥匙被踢 → 用 -o IdentitiesOnly=yes -i,或直接从已通链路 append(§6.1)
关口令后仍能用口令登录 主文件里的 PasswordAuthentication yes 抢先命中(drop-in 没生效)→ sshd -T \| grep passwordauthentication 看生效值,别只看文件
改了 drop-in 却「什么都没变」 sshd_config 没有 Include sshd_config.d/*.conf,或存在 Match 块把它挡在外面 → 先确认这两个前提(§6.2)
守卫从不回滚(永远 keep) 判据与「触发动作自身」耦合(用了 sshd 日志的 Accepted publickey)→ 唯一判据必须是标记文件(§6.3)
窗口内忘了建标记,被回滚了 这是预期行为(无证据即认为免密不可靠)→ 重新执行并确保窗口内登录(§6.3)
自查表全「符合」,一复现就露 把「没查」当「符合」写的 → 每条必须有命令输出;一级允许「部分符合 + 待整改」,别凑表(§2.4)
拿「防火墙开了」当等保边界访问控制符合 默认拒绝 + 五元组 + 删冗余规则才是判据;--list-ports 里挂着宽放行就不算(§2.2)
「有备份」但说不清能不能恢复 备份任务只证明「跑了」;要有一条恢复演练记录才算数据备份恢复符合
用户说「按等保检查」却自己升成三级 默认一级;用户没明确指定就不要加码,等级以用户/备案材料为准(§2.1)
管理类条目被当成「不适用」跳过 制度/机构/人员/建设/运维是待人工确认,不是不适用;把清单交给负责人落实(§2.4)

10. 落地记录(模板)

> ⚠️ 本技能不记录任何具体环境信息(真实 IP / 端口 / 主机名 / 时间 / 业务名)。
> 每台机器的实际执行状态,写进你自己的运维报告。下面这份表是报告里该怎么记的格式:

项 记什么
目标机 标识(主机名 / 编号 / 云实例 ID)
网络 是否有私网地址;公网是否直配网卡;IPv6 是否可用
白名单 放行了哪些 /32、各代表什么角色(本机出口 / 跳板 / 对端 / 自身)
备份 防火墙与 sshd 配置的备份路径与时间戳
审计结论 A/B/C 三层清单(哪些公网可达、哪些待办、哪些已合规),及其逐项处置
登录方式 免密公钥是否全链路实测通过;守卫判定结果;最终 sshd 生效值
验证 逐条打勾:入口仍通 / 档位符合预期 / 规则顺序 / 无残留 / 隧道可用 / 口令被拒
等保基线 本次按哪一级(默认一级);用户是否指定了等级
等保结论 各层面 符合 / 部分符合 / 不符合 / 不适用 / 待人工确认 的条数,以及不符合项与整改建议
等保证据 每条结论对应的命令输出摘要(含主机标识与时间),mlps-l1-check.sh 的报告路径

11. 脚本用法

11.1 SSH 端口白名单

bash scripts/ssh-port-whitelist.sh --port 1234 \
  --allow &lt;ip,ip,...&gt;                      # 预演
bash scripts/ssh-port-whitelist.sh --port 1234 --allow &lt;ip,ip,...&gt; --apply
firewall-cmd --runtime-to-permanent &amp;&amp; systemctl stop sshwl-rollback.timer

11.2 暴露面审计 / 档位管控

# ① 审计(只读,任何时候都能跑):A/B/C 三层清单 + docker 告警 + 端口段放行告警
bash scripts/public-ports-guard.sh --keep 1234,80,443

# ② 白名单档(推荐):这些端口只放列出的源(缺 --confirm 会拒绝)
bash scripts/public-ports-guard.sh --keep 1234,80,443 \
     --open 1235,1236,1237 \
     --allow &lt;ip,ip,...&gt; --confirm

# ③ 全禁档(该端口确实无人远程用)
bash scripts/public-ports-guard.sh --keep 1234,80,443 --close 5678 --confirm

# ④ 验完固化
firewall-cmd --runtime-to-permanent &amp;&amp; systemctl stop portguard-rollback.timer

脚本刻意不做:不改服务配置(绑环回要人手动改 + 重启)、不 --reload、不自动固化、
不在没有 --confirm 时改动任何端口。审计里会显式标出「docker 0.0.0.0 发布」和
「端口段放行」两类高危项——前者 firewalld 关不掉,后者人工审计最容易漏。

11.3 免密登录 / 关闭口令登录

# ① 部署公钥免密(先预演看现有指纹,再执行)
bash scripts/ssh-key-deploy.sh --host &lt;主机A&gt; --user root
bash scripts/ssh-key-deploy.sh --host &lt;主机A&gt; --user root --apply

# ② 关闭口令登录(带 N 秒自动回滚守卫;守卫跑在目标机本地)
bash scripts/nopasswd-login.sh
bash scripts/nopasswd-login.sh --apply --window 300

# ③ 窗口内:从另一条链路免密登录并建标记(守卫认这个凭据)
ssh &lt;主机A&gt; &#x27;touch /root/i_am_here; ls -l /root/i_am_here&#x27;

# ④ 窗口后:看判定
cat /root/sshd-nopasswd/$(cat /root/sshd-nopasswd/latest)/verdict   # keep / rollback

# ⑤ 手工回滚(任何时候)
rm -f /etc/ssh/sshd_config.d/99-no-password.conf &amp;&amp; systemctl reload sshd

nopasswd-guard.sh 是守卫本体,由 nopasswd-login.sh 自动装到目标机 /root/bin/ 并由 systemd-run 拉起,
一般不需要手工调用(手工调用可用于测试回滚分支)。

11.4 等保自查(只读)

# ① 默认:按等保一级跑完全部主机可验证项,输出结论 + 证据 + 汇总
bash scripts/mlps-l1-check.sh

# ② 二级:一级全量 + 二级增量项(L2-01…L2-17),两套分数分开算
bash scripts/mlps-l1-check.sh --level 2

# ③ 除终端输出外,把报告另存(建议放运维档案,和 §10 的落地记录放一起)
bash scripts/mlps-l1-check.sh --level 2 --report /root/audit/mlps-l2-$(date +%F).txt

# ④ CI / 巡检用:出现「不符合」时退出码 2
bash scripts/mlps-l1-check.sh --strict

脚本行为约定:

项 说明
只读 不改配置、不改防火墙、不重启任何服务;只跑查询命令
分级 默认一级;--level 2 真正追加二级增量项(口令策略/失败锁定/会话超时/审计范围与留存/分权/备份演练/补丁与漏洞/变更管理)并单独计分;--level 3/4 只列出尚未覆盖的更高等级增量,不假装查过
非 root 需要读 /etc/shadow 等文件的条目会标「待人工确认(需 root)」,不会报成不符合
不替人签字 文档/访谈/现场类条目一律 待人工确认,脚本不判符合
退出码 0 正常(含存在不符合);--strict 且有不符项(含二级增量项)时 2;参数/环境错误 1

> 报告首行会打印基线等级、主机标识与时间——这三样是等保证据的必备项,别删。
> 二级材料还要补 references/mlps-l2-delta.md §4 的「二级增量结论 / 备案与测评状态 / 差距与整改计划」三节。
> 该脚本只覆盖等保;网络暴露面与登录方式仍走 §11.1~§11.3。

常见问题

5 问
答案由本站依据条目字段整理
服务器安全加固怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_b9442967/server-security-hardening。安装方式为未标注。装完刷新技能列表或重开客户端。

服务器安全加固支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

服务器安全加固权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

服务器安全加固收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

服务器安全加固用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。