SOP — 全内网集群 SELinux 转 permissive 与 firewalld 关闭¶
变更主题:把 19 台受管主机的 SELinux 统一为
permissive(免重启)、firewalld 全部停止并禁用, 消除排查期与后续业务部署时的强制访问控制 / 包过滤干扰。 工程与实现体:本目录(main.py/deployer.py/utils//config/selinux-firewall.yaml)。
目录¶
1. 背景¶
- 环境为全内网(
192.168.31.0/24),无公网入站面(公网链路另经香港 frps → OpenWrt frpc,不在本次范围)。 - SELinux 曾造成实打实的故障:
k8s-master-01的 haproxy 因name_bind被拒,VIP:16443入口不可用 (见k8s-restart-16443-selinux-20260929/SOP.md)。该策略依赖haproxy_port_t端口标签与haproxy_connect_any布尔,属"随时可能再踩"的干扰源。 - firewalld 现状是 public zone
target: default(默认拒绝):除已放行的业务端口与 ssh/cockpit 外, 其余入站一律拒绝。任何新业务端口没手动--add-port就会被挡——这正是干扰来源。 - 变更前实测基线:13 台 SELinux
Enforcing(k8s 4 / slurm 5 / lustre 2 / FlexLM 1 / harbor 1), 6 台已Disabled(lsf 5 + hpc);12 台 firewalld 运行中、6 台未启用、1 台failed。
2. 目标¶
变更后应达到的可验证状态(全部 19 台一致):
| 项 | 期望 |
|---|---|
| SELinux 运行模式 | Permissive(原 Disabled 的保持 Disabled,不回改) |
/etc/selinux/config |
SELINUX=permissive(原 disabled 的保持不动) |
| firewalld 运行 | is-active = inactive/not-found |
| firewalld 自启 | is-enabled = disabled/not-found,且无残留 failed 标 |
不需要重启任何主机(setenforce 0 立即生效;配置文件为下次启动的持久化)。
3. 前置条件¶
| 条件 | 检查方式 | 本次实测 |
|---|---|---|
| 本机可 root 免密 ssh 全部 19 台 | timeout 6 ssh -n -o BatchMode=yes root@<ip> hostname |
通过 |
| Slurm 无运行中作业 | ssh root@192.168.31.16 'squeue -h \| wc -l' |
0 作业、节点全 idle |
| LSF 无未完成作业 | ssh root@192.168.31.23 'export LSF_ENVDIR=/data/nfs/lsf/lsf/conf; $LSF_SERVERDIR/bjobs -u all' |
No unfinished job found |
| SELinux / firewalld 状态可探测 | 预检查子命令 | 19/19 可探测,0 unknown |
| 配置校验无 BLOCK | 预检查子命令 | BLOCK 0 / WARN 0 |
凭据:全部走本机 root 免密密钥(~/.ssh/id_rsa 等),工程不落任何口令;
如需口令,走 ~/.secrets/(600,禁止 cat,仅在命令内 source)。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | 19 台:k8s .10/.13/.14/.15、slurm .16–.20、lustre .21/.22、lsf .23–.27、hpc .28、FlexLM .29、harbor .31 |
| 组件 | 各主机 SELinux 策略、firewalld 服务与 unit 状态、/etc/selinux/config |
| 是否停服 | 否。不重启主机、不重启业务服务 |
| 窗口期 | 无窗口要求(秒级完成);实测全量 17 台变更耗时 8.5s |
| 在跑作业 | 变更前实测 Slurm / LSF 均无作业;工具另设作业门禁,有作业的节点自动排除 |
| 明确排除 | 香港 129.226.138.204(nginx+frps,公网入口,关防火墙将暴露公网);OpenWrt .2(frpc + 旁路由,关防火墙会改变整网 NAT);Dell R620 .249/.250(Ubuntu,无 SELinux、未装 firewalld) |
5. 分步操作¶
命令中的
<ip>取值见config/selinux-firewall.yaml的nodes段。 实现体:deployer.py(生命周期)与utils/state_probe.py(探测判定);下列 shell 为其等价展开。 全部动作幂等:状态达标即跳过。
步骤 1:预检查(只读,门禁第一步)¶
校验配置合法性、逐台探测四项状态(SELinux 运行/持久化、firewalld 运行/自启)、查调度器作业占用。
坑:LSF 的 bjobs 必须带 LSF_ENVDIR=/data/nfs/lsf/lsf/conf(注意不是版本目录下的 conf)与
LSF_SERVERDIR,否则 lsb_init 报 /etc/lsf.conf 不存在、返回 rc≠0,会被误读成"无作业"。
# 逐台四态探测(一条命令取回,避免多次握手)
timeout 30 ssh -n -o BatchMode=yes root@<ip> '
printf "SELINUX_RUNTIME=%s\n" "$(getenforce 2>/dev/null || echo UNKNOWN)"
printf "SELINUX_CONFIG=%s\n" "$(awk -F= "/^SELINUX=/{print \$2}" /etc/selinux/config)"
printf "FW_ACTIVE=%s\n" "$(systemctl is-active firewalld 2>/dev/null || true)"
printf "FW_ENABLED=%s\n" "$(systemctl is-enabled firewalld 2>/dev/null || true)"'
# 调度器作业
timeout 15 ssh -n -o BatchMode=yes root@192.168.31.16 'squeue -h -o "%N" | sort -u'
timeout 25 ssh -n -o BatchMode=yes root@192.168.31.23 \
'export LSF_ENVDIR=/data/nfs/lsf/lsf/conf; export LSF_SERVERDIR=/data/nfs/lsf/lsf/10.1/linux2.6-glibc2.3-x86_64/bin; \
$LSF_SERVERDIR/bjobs -u all -o "EXEC_HOST" | tail -n +2'
预期:19 台均返回四项值(无 UNKNOWN);作业查询无输出。
失败判断:出现 UNKNOWN → 中止(探测不可用不得当作需要变更);作业查询返回节点名 →
该节点从本次变更中排除。
步骤 2:变更前备份¶
高危(覆盖配置类动作的前置)。为待变更主机留 selinux-config、firewall-state.txt
与 pre-state.json(探测原值),远端 + 本地各一份。
STAMP=$(date +%Y%m%d-%H%M%S) # 语义:备份时间戳,与远端目录同名
timeout 60 ssh -n -o BatchMode=yes root@<ip> "
mkdir -p /root/selinux-fw-backup/${STAMP} &&
cp -a /etc/selinux/config /root/selinux-fw-backup/${STAMP}/selinux-config;
[ -d /etc/firewalld ] && cp -a /etc/firewalld /root/selinux-fw-backup/${STAMP}/firewalld-etc;
firewall-cmd --list-all > /root/selinux-fw-backup/${STAMP}/firewall-state.txt 2>&1 || true"
预期:rc=0,远端目录含 selinux-config、firewall-state.txt。
失败判断:rc≠0 → 该主机跳过变更(不得"无备份硬改")。
步骤 3:SELinux 转 permissive(运行态 + 持久化)¶
高危。setenforce 0 立即生效、不重启;配置文件仅影响下次启动。
坑:原为 Disabled 的主机不得回改为 permissive——disabled → enforcing/permissive 需要全盘
relabel,代价远大于收益;实现体对此显式跳过。
# 运行态(仅当当前为 Enforcing)
timeout 30 ssh -n -o BatchMode=yes root@<ip> 'setenforce 0 && getenforce'
# 持久化(仅当当前为 enforcing)
timeout 30 ssh -n -o BatchMode=yes root@<ip> \
"sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config && grep '^SELINUX=' /etc/selinux/config"
预期:Permissive;配置回显 SELINUX=permissive。
失败判断:getenforce 仍为 Enforcing → 检查是否有其他安全模块接管,转第 7 章回滚。
步骤 4:停止并禁用 firewalld¶
高危(停服)。--now 立即停止,disable 取消开机自启。
坑:处于 failed 的单元(本次 .25 为 3 天前的 Result: timeout 残留)不会被 disable 清掉失败标,
需补 systemctl reset-failed,否则状态判定一直停在 failed(表现为"改了但没达标")。
timeout 120 ssh -n -o BatchMode=yes root@<ip> '
systemctl disable --now firewalld 2>/dev/null || true
systemctl reset-failed firewalld 2>/dev/null || true
printf "FW_NOW=%s/%s\n" \
"$(systemctl is-active firewalld 2>/dev/null || true)" \
"$(systemctl is-enabled firewalld 2>/dev/null || true)"'
预期:FW_NOW=inactive/disabled(或 not-found/not-found)。
失败判断:输出非上述组合 → 该主机计失败,达到失败阈值即停止后续批次。
步骤 5:分批与失败阈值¶
实现体按 execution.batch_size(默认 5)分批、批内并发 max_workers(默认 4)、
批内失败达 failure_threshold(默认 1)即停止后续批次。每批变更前重新复核该批可达性。
本次实测:4 批(5/5/5/2),首轮 17 台全部成功。
6. 验证¶
# 6.1 目标态复核(独立于工程,逐台一行)
for ip in 10 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 31; do
timeout 12 ssh -n -o BatchMode=yes root@192.168.31.$ip \
'printf "%s | SEL=%s | FW=%s/%s\n" "$(hostname)" "$(getenforce)" \
"$(systemctl is-active firewalld)" "$(systemctl is-enabled firewalld)"' &
done; wait
# 6.2 业务侧健康
kubectl get nodes # 预期 4 节点 Ready
ssh -n root@192.168.31.16 'sinfo -N -o "%N %t" | sort -u' # 预期全 idle
ssh -n root@192.168.31.28 'df -h /mnt/lustre | tail -1' # 预期 Lustre 已挂载
curl -sk https://192.168.31.8:16443/healthz # 预期 ok(VIP 入口)
curl -s -o /dev/null -w "%{http_code}\n" http://192.168.31.16:9100/metrics # 预期 200
curl -sk -o /dev/null -w "%{http_code}\n" https://192.168.31.31/v2/ # 预期 401(registry 在跑)
本次实测结果:19/19 台 SEL=Permissive|Disabled 且 FW=inactive/disabled;K8s 4 节点 Ready、
Slurm 全 idle、LSF 全 ok、Lustre 挂载正常、VIP 16443 = ok、9100 = 200、Harbor /v2/ = 401、
监控命名空间 monitoring 全部 Pod Running、Grafana = 200。
幂等复跑:连续第二次执行返回 状态一致,无需变更,跳过备份与执行;只读漂移检测返回 无漂移。
7. 回滚¶
备份位置:backup/(本地)与 /root/selinux-fw-backup/<时间戳>/(各主机);
逐台原值在 backup/<hostname>/pre-state.json,台账见 backup/README.md。
# 单台回滚(按 pre-state.json 的原值恢复)
timeout 120 ssh -n -o BatchMode=yes root@<ip> '
sed -i "s/^SELINUX=.*/SELINUX=enforcing/" /etc/selinux/config # 原为 enforcing 时
setenforce 1 || true # 原为 Enforcing 时
systemctl enable --now firewalld' # 原为 active/enabled 时
实现体入口:main.py rollback(按 backup/<hostname>/pre-state.json 逐台自动判定该恢复哪几项)。
回滚后按第 6.1 节复核,预期回到变更前状态。
回滚触发条件(量化,任一满足即回滚):① 任一主机 Permissive 未生效或 firewalld 仍 active;
② 第 6.2 节任一业务检查不通过;③ Slurm / LSF 出现节点 down。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 失去 SELinux 强制访问控制(MAC) | 全内网、单 KVM 宿主、无多租户;setenforce 1 可随时就地恢复(未 disabled,无需 relabel) |
| 9100 失去来源限制(原仅 k8s 四节点可访问) | 内网可达面本就限于 192.168.31.0/24;监控采集不受影响,实测 200 |
| 误关公网入口主机防火墙 | 香港 frps 与 OpenWrt 已明确排除,不在节点清单内 |
| 未来需要恢复 firewalld | 配置未删(/etc/firewalld/ 与 zones 均在),systemctl enable --now firewalld 即恢复;备份留存原放行清单 |
把 Disabled 误改为 permissive |
判定逻辑显式跳过(避免触发不必要的 relabel) |
firewalld 停在 failed 残标 |
变更动作补 reset-failed,并在变更后显式核对 FW_NOW |
附录:交付物清单¶
| 文件 | 说明 |
|---|---|
main.py / deployer.py / utils/ |
变更实现体(幂等 + 分批 + 备份 + 回滚) |
config/selinux-firewall.yaml |
配置唯一真源(期望态、执行参数、节点清单) |
backup/README.md、backup/<hostname>/ |
备份台账与逐台原值 |
reports/precheck-*.json |
预检查报告(含逐台探测值与判定) |
docs/SOP.md、docs/RUNBOOK.md |
本文件与运行手册 |
附录 B:本次同步修订的项目级文档(回滚对照)¶
项目根 docs/ 下三份档案随本次变更做了定点修订,改动点如下(便于手工回滚):
| 文件 | 改动点 | 备份 |
|---|---|---|
docs/HANDOFF.md |
§2「一句话」改述为 SELinux/firewalld 统一;§2 指针表新增 1 行;§4 红线新增 1 条(防火墙不得一刀切) | backup/docs/HANDOFF.md.bak.20261003-004753.pre_change(改前版本) |
docs/BASELINE.md |
§2 新增「SELinux / firewalld 期望态」一条;§4 命令速查新增 1 行(main.py drift) |
backup/docs/BASELINE.md.bak.20261003-004802.post_change(改后留档) |
docs/PROJECT-MAP.md |
§1 路由表新增 1 行;§2 依赖图「兼容 / OS 层」改述;§3 活工程新增工程卡片 1 行 | backup/docs/PROJECT-MAP.md.bak.20261003-004802.post_change(改后留档) |
注:项目未纳入 git,BASELINE / PROJECT-MAP 的备份为改后版本;回滚请按上表「改动点」手工撤销。
来源:
/home/rain/Documents/k8s_ins/selinux-firewall-20261003/docs/SOP.md| 最后更新:2026-10-03 00:48(由build/sync.py同步,请勿在站点侧修改)