从 15 次输密码到 1 次:envchain 多 Scope 机制与扩容指南
这篇是一次真实踩坑后的收敛方案。
我们原来的痛点很直白:同一条 envchain mom <cmd>,可能连续弹十几次密码框,体验非常差,自动化也跑不稳。
这次改造后的目标只有一个:
- 同一 scope 在同一个解锁窗口内,最多输入一次密码。
先说结论:新机制是什么
我们把 secrets 注入机制从“单 keychain + 混合授权”,改成了“多 scope keychain + 显式授权边界”。
核心变化有 5 个:
- scope 级存储隔离
- 每个 namespace 对应一个独立 keychain 文件,比如:
mom.keychain-db、work.keychain-db。 - 物理隔离后,测试环境和生产环境不会互相污染。
- 命令保持不变,路径自动映射
- 仍然用
envchain <scope> <cmd>,不改调用习惯。 - 通过
ENVCHAIN_KEYCHAIN_DIR把<scope>自动映射到DIR/<scope>.keychain-db。
- 授权策略从“模糊信任”改为“可验证信任”
- ACL 里同时使用
Trusted Application和partition_id。 partition_id里必须包含当前envchain二进制的cdhash,否则会出现“每个 key 都重复授权”。
- 锁策略显式化
- 对每个 scope keychain 统一设置
timeout=1200(20 分钟)。 - 超时后需要重新解锁一次;窗口内不重复输入。
- 后台任务默认 fail-fast
- 非交互任务建议启用
ENVCHAIN_NO_PROMPT=1。 - 拿不到授权就直接失败,不要挂住等待用户输入。
原理图(和执行链路)
你可以把它理解成四步流水线:
- 命令入口:
envchain mom <cmd> - 命名映射:找到
~/Library/Keychains/envchain-scopes/mom.keychain-db - 授权判断:校验 ACL / partition_id / cdhash
- 注入执行:把 scope 下的 key 注入子进程
如果第 3 步授权配置不完整,就会退化成“每个 key 单独确认”,也就是我们最痛的 15 次输入。
为什么以前会出现 15 次输入
根因通常是这两个之一:
- binary hash 变了,但 ACL 还是旧 hash
- 比如你升级或重编译过
envchain,cdhash会变化。 - keychain 里还信任旧
cdhash,每个条目都要重新问一次。
- 命令走错 keychain
ENVCHAIN_KEYCHAIN_DIR没生效时,可能回退到login.keychain。- 如果
login.keychain里也有同名envchain-<scope>条目,会触发混乱授权行为。
如果你要新增一个 scope,该怎么做
下面是可复制的最短流程。示例 scope 用 work。
1) 创建 scope keychain 并设定超时
SCOPE=work
KC_DIR="$HOME/Library/Keychains/envchain-scopes"
KC_PATH="$KC_DIR/${SCOPE}.keychain-db"
mkdir -p "$KC_DIR"
security create-keychain "$KC_PATH" # 首次创建会提示你设置该 keychain 密码
security set-keychain-settings -lut 1200 "$KC_PATH"
2) 把 secrets 写入这个 scope
envchain --keychain "$KC_PATH" --set "$SCOPE" AI_API_KEY OPENAI_API_KEY DATABASE_URL
3) 一次性修正 partition list(关键)
CDHASH=$(codesign -dv --verbose=4 /opt/homebrew/bin/envchain 2>&1 | awk -F= '/^CDHash=/{print $2}')
security set-generic-password-partition-list \
-s "envchain-${SCOPE}" \
-S "apple-tool:,apple:,cdhash:${CDHASH}" \
"$KC_PATH"
这一步做完,才真正具备“一次输入,窗口内复用”的基础。
4) 开启自动映射(命令习惯不变)
export ENVCHAIN_KEYCHAIN_DIR="$HOME/Library/Keychains/envchain-scopes"
建议把这一行写进 ~/.zshrc 和 ~/.zprofile。
5) 验证行为
envchain work /bin/echo first
envchain work /bin/echo second
预期:
- 第一条:最多 1 次输入
- 第二条:0 次输入(在 20 分钟窗口内)
给自动化任务的推荐配置
交互命令和后台命令建议分开策略:
- 日常终端:正常
envchain <scope> <cmd> - 定时/CI/守护进程:
ENVCHAIN_NO_PROMPT=1 envchain <scope> <cmd>
这样做的好处是:拿不到授权就立刻失败,你可以在日志里快速定位问题,而不是挂死等待密码。
运维排查清单(实用)
检查当前 envchain 的 cdhash
codesign -dv --verbose=4 /opt/homebrew/bin/envchain 2>&1 | rg '^CDHash='
检查 scope keychain 的 partition_id
security dump-keychain -a "$HOME/Library/Keychains/envchain-scopes/mom.keychain-db" \
| awk '/"acct"<blob>="AI_API_KEY"/{f=1} f&&/authorizations \(1\): partition_id/{p=1;next} p&&/description:/{print;exit}'
检查有没有误走 login.keychain
security dump-keychain "$HOME/Library/Keychains/login.keychain-db" \
| rg '"svce"<blob>="envchain-mom"'
如果你在 login.keychain 和 scope keychain 里都看到同名 service,就要先收敛到单一来源,避免行为飘忽。
安全边界说明
这套方案是“本机工程化安全”,不是“绝对零风险”。
它解决的是:
- 明文
.env到处散落 - 多环境串用
- 交互注入体验差
它不直接解决的是:
- 机器被完全攻破后的根权限风险
- 团队级审批/审计/最小权限治理
如果你已经进入多人协作、审计合规场景,应该再叠加 Vault/云 KMS 这类服务。
最后给你一个最小模板
以后你每新增一个 scope,就按这个顺序:
create-keychainset-keychain-settings -lut 1200envchain --keychain ... --set <scope> ...set-generic-password-partition-list(带当前 cdhash)envchain <scope> /bin/echo first && second
这样你就能把“重复输密码”这个历史问题,压到可控范围内。
如果你愿意,我下一篇可以写成「一键脚本化版本」:输入 scope 名称和 key 列表,自动完成创建、授权、验收和回滚脚本。