OpenTofu 使用 OCI 注册表时的认证配置指南:环境凭据自动发现与显式配置 OpenTofu 使用 OCI 注册表时的认证配置指南环境凭据自动发现与显式配置【免费下载链接】opentofuOpenTofu lets you declaratively manage your cloud infrastructure.项目地址: https://gitcode.com/gh_mirrors/op/opentofu本篇指南围绕 OpenTofu 通过 OCI 注册表OCI Registry获取 Provider 与 Module 时的认证机制展开系统讲解环境凭据自动发现Ambient Credentials与OpenTofu 专属显式配置两套方案、oci_default_credentials与oci_credentials两个 CLI 配置块的全部参数语义以及凭据选择的具体优先级规则。读完本文你将能够在私有的 Harbor、DockerHub 或任意兼容 OCI Distribution 的注册表场景下正确复用docker login/podman login产生的既有凭据或在纯 OpenTofu 场景中显式声明 Basic Auth、OAuth 与 Docker-style credential helper 三种认证方式并理解底层源码是如何执行最具体匹配优先的凭据选择的。本文基于 RFC 6-authentication.md 及其实现细节附录 8-auth-implementation-details.md 展开并结合当前仓库中 oci_credentials.go 与 ociauthconfig 包 的实际实现与测试用例进行印证。背景为什么 OCI 注册表的认证是独立问题OpenTofu 原生支持的credentials块位于 CLI 配置文件为 OpenTofu 自有服务定义特定主机上的访问令牌。但 OCI 注册表并非 OpenTofu 原生服务其认证期望完全不同许多注册表支持username/password 风格的认证Basic Auth而不是或不仅仅是OAuth 之类的 bearer-token 风格生态中已经存在大量由docker login、podman login、oras login写入的凭据文件重新配置会产生凭据蔓延credentials sprawl。因此OpenTofu 的 OCI 注册表认证设计包含两条互补路径默认行为对任何提示需要认证的注册表先尝试匿名认证随后从 Docker CLI / Podman 等工具管理的配置文件即containers-auth.json规范 描述的搜索位置中自动发现既有凭据显式配置通过 CLI 配置语言中新增的两个块类型oci_default_credentials与oci_credentials进行精确控制。值得强调的是认证是一个横切关注点cross-cutting concernProvider 与 Module 两套安装流程共享同一套认证实现未来 OpenTofu 若支持将状态快照作为 OCI 制品存储也会复用这套机制。这在 8-auth-implementation-details.md 中被称为集中管理凭据设置再分发给 Provider 与 Module 安装器。环境凭据的自动发现Ambient Credentials对于已经用 Docker / Podman / ORAS 等工具登录过 OCI 注册表的用户系统里通常已存在有效凭据。OpenTofu 默认会按照containers-auth.json规范去搜索这些凭据位置以复用既有凭据、避免凭据蔓延。默认搜索位置从源码 docker_cli_credentials_config.go 的dockerCLIStyleAuthFileSearchLocations可以看出搜索位置因操作系统而异操作系统搜索的文件路径Linux$XDG_RUNTIME_DIR/containers/auth.json若设置了XDG_RUNTIME_DIRWindows / macOS$HOME/.config/containers/auth.json所有平台$XDG_CONFIG_HOME/containers/auth.json未设置XDG_CONFIG_HOME时回退到$HOME/.config/containers/auth.json所有平台$HOME/.docker/config.json所有平台$HOME/.dockercfg旧式 Docker CLI 配置文件在自动发现模式下OpenTofu 只是探测这些标准位置文件不存在会被静默跳过即使某个位置的文件出现异常也只会记录[WARN]日志而不会阻止 OpenTofu 使用因为这些文件并非 OpenTofu 独占。但如果是用户显式指定了docker_style_config_files则任何一个指定文件加载失败都会被当作错误处理。理解 Docker-style 配置文件格式这些配置文件是 JSON 格式其中包含三个与认证相关的字段见源码中dockerCLIStyleConfigFile结构体{ auths: { example.com: { auth: base64(username:password) } }, credHelpers: { example.com: osxkeychain }, credsStore: osxkeychain }auths按域名或仓库路径前缀存储的静态凭据auth值为username:password的 base64 编码credHelpers按域名指定的 credential helpercredsStore全局默认 credential helper对任何仓库都生效优先级最低。源码中CredentialsSourcesForRepository会依次处理这三类条目先遍历auths中所有匹配的属性名返回静态凭据再匹配credHelpers中的域名级 helper最后返回credsStore全局 helper。oci_default_credentials块定制自动发现行为oci_default_credentials是一个可选的新块类型用于定制 OpenTofu 的自动发现行为。整个 CLI 配置中最多只能出现一个该块多个会被校验拒绝见测试oci-default-credentials-duplicate。完全禁用自动发现oci_default_credentials { # 设为 false 将完全禁用所有自动凭据发现 # 强制 OpenTofu 只使用自己 CLI 配置中显式提供的凭据。 discover_ambient_credentials false }源码中的默认值对象newDefaultOCIDefaultCredentials表明discover_ambient_credentials默认为true即默认行为就是自动发现。注意当它被设为false时不能同时设置docker_style_config_files因为后者只是环境凭据发现行为的一个修饰项校验逻辑见 oci_credentials.go 的decodeOCIDefaultCredentialsBlockBody。覆盖 Docker-style 配置文件的搜索位置oci_default_credentials { # 覆盖 Docker-style 配置文件的默认搜索位置 # 使 OpenTofu 能够与生态中其他使用 Docker 文件格式、 # 但配置文件路径不同的工具互操作。 docker_style_config_files [ /etc/awesome-oci-tool/auth.json, ] # 一旦设置该参数默认搜索位置即被禁用 # 只搜索列表中明确列出的文件。 }源码语义中有一个容易被忽略的关键细节区分nil与空列表docker_style_config_files为nil未设置使用默认搜索位置为非 nil 但长度为 0 的空列表完全禁用对 Docker-style 配置文件的搜索此时若discover_ambient_credentials仍为true未来版本的 OpenTofu 可能会尝试其他来源的环境凭据为非空列表只搜索列表中的文件。此外源码会对该列表中相对路径做绝对化处理相对路径基于 CLI 配置文件所在目录解析并调用filepath.Abs固化避免进程切换工作目录如-chdir全局选项后路径被重新解释。设置默认 Docker-style credential helperoci_default_credentials { docker_credentials_helper osxkeychain }该参数为任何没有在其他地方配置更具体凭据的仓库指定默认的 Docker-style credential helper。它是唯一的全局级GlobalCredentialsSpecificity凭据设置。若未显式设置仍可能从环境凭据来源中发现默认 helper。未来扩展约定未来版本可能支持其他类型的环境凭据且每种新增的发现方式都必须在该块中有独立的开关以便单独禁用。例如若操作者希望用另一种发现方式替代Docker-style 配置文件可将docker_style_config_files设为空列表来禁用 Docker 方式再按需配置备选方式。oci_credentials块OpenTofu 专属显式配置对于只用 OpenTofu 访问 OCI 注册表、或希望将 OpenTofu 使用的凭据与其他工具分离的用户CLI 配置语言新增了oci_credentials块类型。它以块标签label给出一个 OCI 仓库地址前缀为所有匹配该前缀的仓库指定凭据oci_credentials example.com/foo/bar { # 这些凭据用于注册表 example.com 中路径以 foo/bar 开头的所有仓库。 username foobar password example }块标签的语法与containers-auth.json的auths属性名一致可以是域名或域名/仓库路径前缀形式域名部分可带端口号如localhost:5000。源码 repository_addr.go 中的ParseRepositoryAddressPrefix使用 ORAS 库解析该地址并明确拒绝包含 tag 或 digest 的地址本设计不针对具体制品的地址。三组互斥的凭据参数每个oci_credentials块必须且只能设置以下三组之一中的全部参数认证风格参数适用场景Basic Authusernamepassword使用 basic-auth 风格凭据的注册表OAuthaccess_tokenrefresh_token使用 OAuth 风格凭据的注册表Credential Helperdocker_credentials_helper单独使用通过 Docker-style credential helper间接提供 username/password各组之间互斥违反时校验会直接报错错误信息在 oci_credentials_test.go 中被逐一验证例如三组都未设置 →must set either usernamepassword, access_tokenrefresh_token, or docker_credentials_helper设置了多组 →must set only one group out of ...只设置了username没设置password→must set both username and password together when using static credentialsOAuth 只设置了一半 →must set both access_token and refresh_token together when using OAuth-style credentials此外还有两条额外的块级约束docker_credentials_helper只支持整个域名粒度的凭据因此不能与仓库路径前缀同时使用报错cannot set docker_credentials_helper with a repository pathcredential helper 名称必须是能用作可执行文件名的非空字符串不能包含路径分隔符报错specifies the invalid Docker credential helper name ...。为什么采用每个仓库一个顶层块的设计RFC 中用一个说明块[NOTE]解释了该设计动机沿用 OpenTofu 原生服务认证配置的先例支持将 CLI 配置拆分到多个文件——例如每个注册表主机由系统级配置管理系统各管理一个配置文件。这也是tofu login命令的做法它故意把生成的凭据写入与操作者手工编辑的 CLI 配置文件不同的文件。OCI 注册表场景同样适用该情况初始版本暂不打算提供tofu login风格的 OCI 凭据获取命令但未来版本可能将其写入tofu login当前使用的配置文件或另一个保留给自动获取的 OCI 注册表凭据的独立文件。凭据选择的优先级Credentials Selection Precedence作为 Docker CLI 配置格式凭据匹配规则的扩展OpenTofu 会同时搜索环境凭据来源与显式配置来源中所有匹配请求仓库的条目然后选择最具体的匹配example.com/foo/bar比example.com/foo更具体example.com/foo比example.com更具体任何涉及域名匹配的条目都比全局设置更具体且只有默认 Docker-style credential helper 属于全局设置。如果同一仓库地址同时存在显式配置与环境凭据配置显式oci_credentials块优先。CLI 配置解析器会拒绝包含多个同仓库前缀oci_credentials块错误信息为Duplicate oci_credentials block for example.com的配置但对环境凭据没有该约束此时 OpenTofu 倾向于使用自动发现序列中更靠前的文件中的凭据或docker_style_config_files列表中更靠前的文件。源码中的具体化实现在 8-auth-implementation-details.md 中该策略被封装为internal/command/cliconfig/ociauthconfig包即ociauthconfig。其核心抽象如下CredentialsConfig接口代表某种可提供零个或多个凭据来源的配置载体例如单个 Docker CLI 风格配置文件、或 OpenTofu 自己的oci_credentials块CredentialsSource接口代表获取一组凭据的方法暴露CredentialsSpecificity()与Credentials(ctx)两个方法——先比较具体度选出唯一来源之后才调用Credentials获取真实凭据。这一间接层非常重要在选中最优来源之前不会执行任何 credential helper 程序CredentialsSpecificity类型表示具体度等级依次为NoCredentialsSpecificity零值未选择、GlobalCredentialsSpecificity全局、DomainCredentialsSpecificity域名级、RepositoryCredentialsSpecificity(pathSegments)按仓库路径段数递增CredentialsConfigs.CredentialsSourceForRepository(ctx, registryDomain, repositoryPath)主入口遍历所有CredentialsConfig为每个来源计算具体度保留最高者若多个来源具体度相同则保留更早声明的那个。关键的顺序决策体现在 oci_credentials.go 的ociCredentialsPolicy中显式配置的oci_credentials块总是先加入序列环境凭据配置随后加入从而保证两者具体度相同时显式配置胜出——这与 RFC 的优先级描述完全一致。实际匹配函数是ContainersAuthPropertyNameMatch见 docker_cli_credentials_config.goOpenTofu 的oci_credentials块与 Docker-style 配置文件的auths属性共用同一套匹配规则保证行为一致。值得注意的是该containers auth地址格式实际上是 Docker CLI 原格式的扩展Docker CLI 原本只支持整域名粒度的auths而containers-auth.json规范扩展出了按仓库路径前缀匹配并最具体优先的能力。凭据在系统内的流转路径从架构上看认证实现遵循依赖倒置原则Dependency Inversion Principle由package main充当各子系统装配的最终仲裁者package cliconfiginternal/command/cliconfig负责解码并校验oci_default_credentials与oci_credentials块将其纳入cliconfig.Config对象同时它把 Docker CLI 等配置文件解析映射到同一套内部数据类型使下游无需关心凭据来源Config.OCICredentialsPolicy(ctx)构建一个ociauthconfig.CredentialsConfigs对象封装完整的凭据选择策略package main将该对象注入 OCI Distribution 客户端基于 ORAS 库由该客户端封装所有 OCI 注册表交互包括在请求时选择并附加合适的凭据Provider 安装侧oci_mirror安装方法会被表示为getproviders.Source的新实现见 9-provider-implementation-details.mdModule 安装侧则通过改造getmodules.PackageFetcher与initwd.NewModuleInstaller新增一个go-getter的Getter实现来接入 OCI见 10-module-implementation-details.md。Credentials是一个不透明结构体初始实现只提供ForORAS() orasauth.Credential一个方法将内部表示翻译为 ORAS 客户端库所需的格式。未来若更换客户端库只需调整这一处 API。测试验证策略行为的完整覆盖当前仓库对这套凭据策略有非常详尽的测试可以作为理解行为边界的活文档oci_credentials_test.go 中的TestLoadConfig_ociDefaultCredentials与TestLoadConfig_ociCredentials验证了两类块的解码与全部校验错误分支其中的TestConfigOCICredentialsPolicy是覆盖面最广的策略集成测试通过testdata/oci-credentials-policy下的夹具验证了多种组合显式配置与环境配置并存时的胜出者、仓库路径具体度递增example.com/foo/bar胜过example.com/foo胜过example.com、全局 helper 仅在其他来源不匹配时生效、空配置返回未找到凭据错误等各平台搜索位置差异Linux 的XDG_RUNTIME_DIR、Windows/macOS 的~/.config/containers/auth.json、以及XDG_CONFIG_HOME回退规则通过ambient-global-credhelper-*-linux/windows/darwin系列用例逐一验证。这些测试夹具中的实际配置示例oci-credentials-basic、oci-default-credentials与本文介绍的配置语法完全对应可作为编写真实配置时的参考模板。实操小结如何为你的场景选择认证方案综合全文实际使用中的决策路径可以归纳为已经在用 Docker / Podman 等工具访问目标注册表无需任何配置OpenTofu 默认自动发现containers-auth.json规范定义的搜索位置中的凭据若希望复用但搜索位置不标准用oci_default_credentials的docker_style_config_files显式指定不希望 OpenTofu 触碰任何系统凭据设置discover_ambient_credentials false然后只用oci_credentials块显式声明凭据需要与其他工具隔离、或只属于 OpenTofu为每个仓库前缀写一个oci_credentials块按注册表支持的认证方式选择 username/password、access_token/refresh_token 或 docker_credentials_helper 三组互斥参数之一为所有未单独配置的仓库提供兜底凭据在oci_default_credentials中设置docker_credentials_helper这是唯一全局级设置优先级最低。记住最核心的优先级结论显式配置优先于环境凭据、仓库路径越长越具体、域名级高于全局级在相同具体度下显式块按声明顺序、环境文件按搜索顺序靠前者胜出。这套规则与 Docker 生态既有惯例一脉相承迁移成本低也保证了多文件拆分配置如按注册表主机分文件、由配置管理系统下发的可行性。【免费下载链接】opentofuOpenTofu lets you declaratively manage your cloud infrastructure.项目地址: https://gitcode.com/gh_mirrors/op/opentofu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考