SOP — 凭据隔离落地(env-inventory)¶
变更日期:2026-09-29(2026-10-13 增补 GLM、DSK 凭据项) 执行主机:192.168.31.249(本机) 类型:C 类变更(改配置)
1. 背景¶
环境信息(节点 IP、角色、服务账号口令)此前散落各处:拓扑靠临时探测,口令散在 k8s Secret 与各工程 SOP 的取值命令里。需要一个统一入口,让日常运维与新会话都能快速取到,同时不把口令写进任何清单、文档或代码。
前期已确认两条事实:
- 关键业务文档中没有明文口令——写法都是
kubectl get secret ... | base64 -d,真值在 k8s Secret 中; - 本机
~/.bashrc第 5~9 行有 非交互守卫(case $- in *i*) ;; *) return;; esac),任何加在守卫之后的加载逻辑对非交互 shell 都无效。
2. 目标¶
变更完成后应达到:
~/.secrets/prod.env存在,权限600,父目录~/.secrets权限700;- 其中含 15 个
PROD_*变量,覆盖 GitLab / MinIO / MariaDB / LDAP / Grafana / Traefik / 智谱(GLM)与 DeepSeek 的账号与口令,以及少量静态环境值; - 交互式终端登录后,这些变量已自动加载并 export;
- 非交互场景(Trae 执行命令、脚本)可通过一行显式 source 取到;
- 口令明文不出现于任何清单、文档、对话与命令输出中。
3. 前置条件¶
| 项 | 要求 | 检查命令 |
|---|---|---|
| kubectl | 本机可用,且对 server、monitoring 命名空间有 secret 读权限 |
kubectl -n server get secret >/dev/null && echo OK |
| 工具 | python3 | python3 -V |
| 权限 | 家目录可写,可改 ~/.bashrc |
test -w "$HOME/.bashrc" && echo OK |
| 备份 | 改 ~/.bashrc 前必须先备份 |
见步骤 2 |
4. 影响范围¶
- 主机:仅本机(192.168.31.249),不涉及任何远端主机;
- 对象:新增
~/.secrets/;修改~/.bashrc(插入 8 行加载逻辑); - 停服:否。对 k8s 集群只读(
get secret),不写任何资源; - 窗口期:无要求,可在任意时间执行;
- 在跑作业:不涉及,本机无 Slurm/LSF 作业。
5. 分步操作¶
步骤 1:生成 ~/.secrets/prod.env¶
从 k8s Secret 抽取真值写入 env 文件。变量与来源映射如下:
| 变量 | 命名空间 | Secret | key |
|---|---|---|---|
PROD_GITLAB_ROOT_PASSWORD |
server | gitlab-gitlab-initial-root-password | password |
PROD_GITLAB_SHELL_SECRET |
server | gitlab-gitlab-shell-secret | secret |
PROD_MINIO_ROOT_USER / _PASSWORD |
server | minio-root-credentials | root-user / root-password |
PROD_MARIADB_ROOT_PASSWORD |
server | mariadb-root | password |
PROD_LDAP_ADMIN_PASSWORD |
server | openldap-backup-pw | password |
PROD_GRAFANA_ADMIN_USER / _PASSWORD |
monitoring | grafana-admin | admin-user / admin-password |
PROD_TRAEFIK_DASHBOARD_USERS |
server | traefik-dashboard-auth | users |
PROD_GLM_API_KEY |
server | glm-api-key | api-key |
PROD_DSK_API_KEY |
server | dsk-api-key | api-key |
另有 4 个静态值:PROD_K8S_MASTER_IP、PROD_DOMAIN、PROD_GITLAB_ROOT_USER、PROD_LDAP_ADMIN_DN。
增量(2026-10-13):新增 PROD_GLM_API_KEY(命名空间 server / Secret glm-api-key / key api-key),供知识库的智谱嵌入调用取用;同一份 Secret 也可注入知识库服务 Pod 作运行时凭据(单一源)。
增量(2026-10-13,同日第二次):新增 PROD_DSK_API_KEY(命名空间 server / Secret dsk-api-key / key api-key),供知识库网站问答框作生成模型凭据;同样可注入服务 Pod(单一源)。
坑:openldap-bootstrap-secret 里只有口令哈希(*_PASSWORD_HASHED),拿不到明文;明文在 openldap-backup-pw。取值时勿选错 Secret。
mkdir -p "$HOME/.secrets" && chmod 700 "$HOME/.secrets"
# 单条取值模板(输出即明文,勿在有日志的环境执行)
kubectl -n server get secret gitlab-gitlab-initial-root-password \
-o jsonpath='{.data.password}' | base64 -d
# 完整 15 项按上表逐一取值后,写成 KEY=VALUE 各行,落盘为 0600
install -m 600 /dev/null "$HOME/.secrets/prod.env"
预期:15 个变量全部取到非空值;文件权限 600。
失败判断:某项为空 → 核对 Secret 名与 key 是否与上表一致,勿用哈希字段替代。
实现体:env-inventory/build_secrets_env.py(按 ITEMS 映射逐项取值、shlex.quote 转义、原子替换);实际执行以实现体为准,本命令为等价复现。
步骤 2:配置 ~/.bashrc 自动加载¶
高危动作:覆盖 ~/.bashrc 前必须备份。
坑:加载块必须插在非交互守卫之前(原文件第 5 行前)。放在守卫之后,非交互 shell 会先 return,变量永远加载不到——这是本变更最容易做错的一点。
cp -p "$HOME/.bashrc" \
env-inventory/backup/home/rain/.bashrc.bak.$(date +%Y%m%d-%H%M%S) # 高危:备份
# 待插入内容(插在原第 5 行「# If not running interactively」之前)
cat <<'EOF'
# 凭据自动加载(env-inventory):必须放在下方非交互守卫之前,
# 否则非交互 shell 在守卫处 return,取不到这些变量。
if [ -r "$HOME/.secrets/prod.env" ]; then
set -a
. "$HOME/.secrets/prod.env"
set +a
fi
EOF
预期:插入后 ~/.bashrc 前 12 行为「注释 + if 块 + 原守卫」。
失败判断:bash -n ~/.bashrc 报错 → 立即用步骤 7 回滚。
步骤 3:非交互场景的取用约定¶
实测本机 Trae 命令 shell 为 bash -c 型(interactive=no),不读 ~/.bashrc,故不会自动加载。约定如下:
set -a; . "$HOME/.secrets/prod.env"; set +a
预期:之后同一命令内可直接用 "$PROD_*"。
失败判断:变量为空 → 确认上一步的 source 未因 set -e 之类提前退出。
6. 验证¶
# 1) 语法
bash -n "$HOME/.bashrc" && echo "语法 OK"
# 2) 权限
stat -c '%a %n' "$HOME/.secrets" "$HOME/.secrets/prod.env" # 预期 700 / 600
# 3) 变量数(只计数,不打印值)
awk -F= '/^PROD_/{n++}END{print n}' "$HOME/.secrets/prod.env" # 预期 15
# 4) 交互式 shell 自动加载(只打印是否已加载)
bash -ic 'printf "%s\n" "${PROD_GRAFANA_ADMIN_USER:+已加载}"' # 预期 已加载
# 5) 非交互显式 source
set -a; . "$HOME/.secrets/prod.env"; set +a
printf '%s\n' "${PROD_GRAFANA_ADMIN_USER:+已加载}" # 预期 已加载
# 6) 幂等:重跑实现体,两次变量行 hash 应一致
awk -F= '/^PROD_/' "$HOME/.secrets/prod.env" | md5sum
python3 env-inventory/build_secrets_env.py 2>/dev/null
awk -F= '/^PROD_/' "$HOME/.secrets/prod.env" | md5sum
实测结果(2026-09-29):第 1~6 项全部符合预期;幂等两次 hash 均为 65d4018f290dea255ddb4df46f500682。
实测结果(2026-10-13,新增 GLM 项后):共 14 项,第 1~6 项仍全部符合预期;幂等两次 hash 均为 98079784a536e0dbedf542048bef7586。
实测结果(2026-10-13,新增 DSK 项后):共 15 项,第 1~6 项仍全部符合预期;幂等两次 hash 均为 4fb3eb4822a9e916a7f90caee5246093。
7. 回滚¶
备份位置:env-inventory/backup/home/rain/.bashrc.bak.20260929-135834
台账:env-inventory/backup/README.md(登记原路径、产生时间、回滚对象、可删时点)
# 1) 恢复 .bashrc
cp -p env-inventory/backup/home/rain/.bashrc.bak.20260929-135834 "$HOME/.bashrc"
bash -n "$HOME/.bashrc" && echo "语法 OK"
# 2) 使凭据失效(可选:仅撤销加载时无需删除文件)
bash -ic 'printf "%s\n" "${PROD_GRAFANA_ADMIN_USER:+仍加载}"' # 预期 为空
# 3) 彻底撤销:删除凭据文件
rm -i "$HOME/.secrets/prod.env" # 高危:删除前确认
回滚后验证:bash -ic 'echo ${PROD_GRAFANA_ADMIN_USER:-<未设置>}' 应输出 <未设置>。
触发条件:bash -n 报错、交互终端启动异常、或变量值明显错误,任一达标即回滚,不靠临场判断。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 口令集中一处,该文件被读取即全量泄露 | ~/.secrets 700 + prod.env 600;确认不在任何 git 仓/dotfile 管理范围内 |
环境变量对所有子进程可见(含 printenv、报错输出) |
命令内只用 "$VAR";禁止 echo/env 打印;不写入日志 |
修改 ~/.bashrc 语法错误会导致 shell 启动失败 |
改前备份;改后 bash -n 校验;回滚命令已备 |
| Secret 轮换后快照过期 | 重跑 env-inventory/build_secrets_env.py 刷新 |
| 非交互场景误以为已自动加载 | 已在第 5 章步骤 3 与本文档明确约定;执行前 source 校验 |
执行本 SOP 时用手工 kubectl 取值,明文可能进入终端回滚缓冲/日志 |
优先用实现体(值不落 stdout);手工执行时关闭终端记录 |
附录 A:交付物清单¶
| 文件 | 说明 |
|---|---|
env-inventory/SOP.md |
本文档 |
env-inventory/build_secrets_env.py |
凭据抽取实现体(15 项映射,值不进 stdout) |
env-inventory/scan_credentials.py |
文档凭据扫描工具(脱敏侦察,--scan/--strict/--write) |
env-inventory/backup/README.md |
备份台账 |
env-inventory/backup/home/rain/.bashrc.bak.20260929-135834 |
.bashrc 原始备份 |
~/.secrets/prod.env |
运行态凭据文件(600,由实现体生成,不纳入项目) |
来源:
/home/rain/Documents/k8s_ins/env-inventory/SOP.md| 最后更新:2026-10-13 16:23(由build/sync.py同步,请勿在站点侧修改)