easynet.run · 全部问题场景

密钥轮换留在提供方,别让每个助手各存一份

数据服务要换密钥,工程师开始逐个找 Agent 配置。有人放在环境变量,有人复制进脚本,还有一份在旧笔记本里。轮换工作迟迟不能结束,因为没人知道遗漏的副本会在下一次什么任务中被用到。

有工作负载身份或集中密钥管理,就先用它们的轮换机制。EasyNet 不应该替代 secret store。更值得改变的是消费者关系:助手是否真的需要持有上游密钥,还是只需要调用一次获准的数据查询?

可以让查询函数运行在提供方环境,由那里读取上游凭据,接收明确的查询条件,返回必要业务字段。通过 EasyNet 接入后,各助手使用自己的获准调用身份,不再分别配置同一份服务秘密。

函数不能因此变成任意网址请求代理。提供方校验查询对象和范围,不把密钥放进参数、返回值或错误文本。调用身份的授权与上游密钥的权限分别管理,轮换其中一项不表示另一项也已经撤销。

实际轮换仍遵循上游流程:更新提供方配置,验证新凭据,再确认旧值被上游拒绝。是否允许短期双凭据、是否需要维护窗口,取决于该服务;这些逻辑和故障告警要由运行程序实现。

让两个调用者在测试环境完成同一查询,换钥后再次核对,同时检查旧凭据确实失效。以后上游再轮换,维护者改的是自己负责的一处接入,而不是要求每个助手重新领取一个秘密。

  1. 01

    两个获准调用者

    不接收同一份上游密钥

  2. 02

    提供方查询函数

    固定服务和必要查询范围

  3. 03

    轮换后双向检查

    新键能用,旧键被拒绝

助手持有自己的调用身份,提供方管理上游凭据。

测试的是旧值被拒绝,不是删了一行配置

使用无秘密的 mock token 演示验证条件,不连接 secret store,不输出或轮换真实密钥。

本地示例 · python
class MockUpstream:
    active_token = "mock-v1"
    def query(self, token):
        if token != self.active_token:
            raise PermissionError("credential rejected")
        return {"rows": 3}

upstream = MockUpstream()
provider_token = "mock-v2"
upstream.active_token = provider_token
assert upstream.query(provider_token) == {"rows": 3}
try:
    upstream.query("mock-v1")
except PermissionError:
    print("new credential works; old credential rejected")
else:
    raise AssertionError("rotation incomplete")

预期结果:输出新凭据可用且旧凭据被拒绝;真实轮换还需使用供应商流程验证。

接入时的检查项
  • 调用者配置与返回结果中是否没有上游秘密。
  • 轮换后两个调用者能否继续完成获准操作。
  • 旧凭据是否确实被上游拒绝,而非仅从某份配置删除。
源码与接入资料