跳转至

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 同步,请勿在站点侧修改)