☰
lego 使用 Rackspace DNS 提供商完成 DNS-01 挑战:配置指南与源码原理剖析
2026/9/25 17:21:46 网站建设 项目流程
  • 网络安全
  • 密码学

【免费下载链接】lego

Let's Encrypt/ACME client and library written in Go

项目地址:https://gitcode.com/gh_mirrors/le/lego
点击查看免费下载

本文以 lego 仓库自动生成的 Rackspace DNS 提供商文档(docs/content/dns/zz_gen_rackspace.md)为主体,结合 providers/dns/rackspace 目录下的源码、测试与测试夹具,系统讲解如何在 lego 中配置 Rackspace 账号并利用其 Cloud DNS API 自动完成 ACME DNS-01 挑战。读完本文,你将掌握 Rackspace 提供商所需的全部环境变量与参数含义、通配符证书签发命令、_FILE与 dotenv 等安全配置方式,并能从源码层面理解令牌认证、zone 定位与 TXT 记录增删的完整调用链。

Rackspace 提供商概览

Rackspace 是 lego 内置的 DNS 提供商之一,专门用于通过 Rackspace Cloud DNS 服务解决 DNS-01 挑战:在挑战期间自动为_acme-challenge域名添加 TXT 记录,验证通过后再自动删除。

属性值
提供商代码(Code)rackspace
引入版本(Since)v0.4.0
官方服务Rackspace Cloud DNS(官方 API 文档链接记录在 rackspace.toml 的[Links]配置中)

在 lego 的命令行中,通过--dns rackspace即可选中该提供商。其所有配置均通过环境变量注入,无需修改代码即可接入。

快速开始:一条命令签发通配符证书

使用 Rackspace 提供商的最小命令如下(即 rackspace.toml 中Example字段的内容):

RACKSPACE_USER=xxxx \ RACKSPACE_API_KEY=yyyy \ lego run --dns rackspace -d '*.example.com' -d example.com

要点说明:

  • -d '*.example.com'与-d example.com同时出现,表示同时申请通配符域名与根域名证书(Let's Encrypt 要求通配符证书必须走 DNS-01 挑战,因此这里必须使用--dns而非 HTTP 类挑战)。
  • RACKSPACE_USER与RACKSPACE_API_KEY是必需的凭证,缺失时 lego 会直接报错退出(源码见下文"凭证校验"一节)。
  • 证书申请、续期等通用流程与 lego 其他 DNS 提供商完全一致,Rackspace 只负责"往 DNS 里写/删 TXT 记录"这一步。

凭证配置:必需的环境变量

Rackspace 提供商需要两个凭证变量,定义于 rackspace.go 的常量区:

环境变量名说明
RACKSPACE_API_KEYRackspace API Key
RACKSPACE_USERRackspace API 用户名

源码中的读取逻辑如下(rackspace.go):

func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get(EnvUser, EnvAPIKey) if err != nil { return nil, fmt.Errorf("rackspace: %w", err) } config := NewDefaultConfig() config.APIUser = values[EnvUser] config.APIKey = values[EnvAPIKey] return NewDNSProviderConfig(config) }

env.Get会一次性检查所有必需变量,任一缺失都会返回错误;而在NewDNSProviderConfig(rackspace.go)中还会做二次校验:

if config.APIUser == "" || config.APIKey == "" { return nil, errors.New("rackspace: credentials missing") }

对应测试用例TestNewDNSProviderConfig_MissingCredErr(rackspace_test.go)验证了"凭证缺失时返回rackspace: credentials missing"这一行为。

附加配置参数

除凭证外,Rackspace 提供商还支持 4 个可选参数,用于调整 API 请求与 DNS 传播等待行为。以下为文档(docs/content/dns/zz_gen_rackspace.md)与 rackspace.toml 中记录的全部参数:

环境变量名说明默认值
RACKSPACE_HTTP_TIMEOUTAPI 请求超时(秒)30
RACKSPACE_POLLING_INTERVAL两次 DNS 传播检查之间的间隔(秒)3
RACKSPACE_PROPAGATION_TIMEOUTDNS 传播最大等待时间(秒)60
RACKSPACE_TTL挑战 TXT 记录的 TTL(秒)300

这些参数在NewDefaultConfig(rackspace.go)中通过env.GetOrDefaultSecond/env.GetOrDefaultInt读取,未设置时使用默认值:

func NewDefaultConfig() *Config { return &Config{ BaseURL: internal.DefaultIdentityURL, TTL: env.GetOrDefaultInt(EnvTTL, 300), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), }, } }

值得注意的源码细节:

  • PropagationTimeout的兜底值来自 challenge/dns01/dns_challenge.go 中定义的dns01.DefaultPropagationTimeout(60 秒),与文档默认值一致。
  • TTL会写入每次创建的 TXT 记录(见Present方法),默认 300 秒;传播等待期间Timeout()方法(rackspace.go)返回配置的传播超时与轮询间隔,lego 据此驱动--dns模式的等待循环。
  • HTTPClient的超时同时作用于身份认证请求与后续所有 DNS API 请求。

配置加载方式:环境变量、_FILE后缀与 dotenv

环境变量直传

如快速开始示例,直接以VAR=value前缀方式注入即可。

_FILE后缀:从文件读取凭证

所有环境变量名均可追加_FILE后缀,改为引用文件路径而非字面值(详见 docs/content/dns/_index.md 的 "_FILEsuffix" 一节),文件内容必须仅为该变量的值。例如:

RACKSPACE_USER_FILE=/etc/lego/rackspace_user \ RACKSPACE_API_KEY_FILE=/etc/lego/rackspace_key \ lego run --dns rackspace -d example.com

其底层实现位于 platform/env/env.go 的GetOrFile:优先读取VAR本身,为空时再尝试VAR_FILE指向的文件内容。这便于在 CI、容器或需要权限隔离的场景中避免把密钥直接写进命令行。

dotenv 文件

使用lego run时可通过--env-file指定 dotenv 文件:

lego run --dns rackspace --domains 'example.com' --domains '*.example.com' --env-file .env.rackspace

.env.rackspace内容示例:

RACKSPACE_USER=your_api_user RACKSPACE_API_KEY=your_api_key RACKSPACE_TTL=300

当使用配置文件(.lego.yml)运行 lego 时,也可为挑战定义envFile字段指向 dotenv 文件,完整语法参考 docs/content/dns/_index.md 中"Dotenv File"一节的challenges.<name>.dns.envFile示例。

认证与 API 调用链:源码级原理

Rackspace 提供商采用的是 Rackspace 经典的身份认证流程:先登录 Identity API 换取令牌与服务目录,再定位 Cloud DNS 端点执行记录操作。整个流程可以在 providers/dns/rackspace/internal 目录中逐段对照。

第一步:Identity API 登录换取令牌

身份认证端点默认值为internal.DefaultIdentityURL(identity.go):

const DefaultIdentityURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"

Identifier.Login(identity.go)以RAX-KSKEY:apiKeyCredentials格式提交用户名与 API Key:

{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username":"testUser","apiKey":"testKey"}}}

请求体结构与响应结构分别定义在 types.go 的AuthData/Auth/APIKeyCredentials与Identity/Access/Token类型中。测试用例mockBuilder(rackspace_test.go)通过 mock 服务器严格校验了该 JSON 请求体。

第二步:从 Service Catalog 定位 Cloud DNS 端点

登录响应中带有access.serviceCatalog,NewDNSProviderConfig遍历其中名为cloudDNS的服务,取第一个端点的publicURL作为后续 API 基址(rackspace.go):

for _, service := range identity.Access.ServiceCatalog { if service.Name == "cloudDNS" { dnsEndpoint = service.Endpoints[0].PublicURL break } } if dnsEndpoint == "" { return nil, errors.New("rackspace: failed to populate DNS endpoint, check Rackspace API for changes") }

这也是TestLiveNewDNSProvider_ValidEnv(rackspace_test.go)断言端点形如https://dns.api.rackspacecloud.com/v1.0/的原因——真实环境下 endpoint 由认证响应动态给出,而非写死在代码里。

第三步:携带令牌调用 Cloud DNS API

internal.NewClient(dnsEndpoint, token)创建 API 客户端,后续所有请求都会带上X-Auth-Token请求头(client.go 的do方法)。所有请求统一使用application/json,并接受 200/202 作为成功状态码。clientdebug.Wrap还包装了 HTTPClient 以支持调试输出(rackspace.go)。

挑战生命周期:Present 与 CleanUp 的实现

Present:写入挑战 TXT 记录

Present(rackspace.go)负责在挑战开始前添加 TXT 记录,流程为:

  1. 通过dns01.GetChallengeInfo计算挑战域名(_acme-challenge.example.com)与挑战值;
  2. 调用GetHostedZoneID定位所属 DNS zone;
  3. 构造TXT记录(名称、数据、TTL)并调用AddRecord写入。

zone 定位在 client.go 的GetHostedZoneID中完成:先由dns01.DefaultClient().FindZoneByFqdn解析出权威 zone,再通过listDomainsByName精确匹配域名,并强制要求TotalEntries == 1,否则报错。写入请求通过POST /domains/{zoneId}/records发送。

TestDNSProvider_Present(rackspace_test.go)使用 mock 服务器严格校验了请求体:

{"records":[{"name":"_acme-challenge.example.com","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300}]}

其中ttl:300正是默认 TTL 注入记录的证据,也验证了Present接收keyAuth并计算info.Value的完整链路。

CleanUp:挑战结束后删除记录

CleanUp(rackspace.go)在验证完成后清理记录:

  1. 重新定位 zone;
  2. 调用FindTxtRecord按名称与类型精确搜索 TXT 记录(client.go),要求恰好命中 1 条,0 条或 2 条以上均视为错误;
  3. 携带记录 ID 调用DELETE /domains/{zoneId}/records?id={recordId}删除。

TestDNSProvider_CleanUp(rackspace_test.go)验证了搜索请求的查询参数(type=TXT、name=_acme-challenge.example.com)与删除请求的id=TXT-654321参数。测试夹具 zone_details.json 与 record_details.json 分别模拟了 zone 查询与记录搜索的响应。

传播等待

DNSProvider实现了challenge.ProviderTimeout接口(rackspace.go),Timeout()返回PropagationTimeout与PollingInterval,lego 在Present之后据此轮询检查 TXT 记录是否已在权威 DNS 生效,再向 ACME 服务器提交挑战。

测试与验证

仓库对该提供商提供了两层测试保障:

  • 单元/集成测试(rackspace_test.go):通过servermock构建的 mock 服务器模拟 Identity 登录、zone 查询、记录增删等全部 HTTP 交互,严格校验请求头、查询参数与 JSON 请求体,无需真实 Rackspace 账号即可验证 Present/CleanUp 行为;
  • 真实环境测试(TestLiveNewDNSProvider_ValidEnv、TestLivePresent、TestLiveCleanUp):通过tester.NewEnvTest控制,仅在设置了真实凭证且开启 live 测试时运行,可用于验证端点发现与实际增删记录的连通性。

自行验证时,可先设置RACKSPACE_USER与RACKSPACE_API_KEY,再运行:

go test ./providers/dns/rackspace/...

未设置 live 环境时,带TestLive前缀的用例会自动跳过。

使用注意事项

  • 凭证必须成对提供:RACKSPACE_USER与RACKSPACE_API_KEY缺一不可,缺失时 lego 会以rackspace: credentials missing报错。
  • 账号需开通 Cloud DNS 服务:认证成功只是第一步,若账号的服务目录(Service Catalog)中不存在cloudDNS服务,provider 初始化会以 "failed to populate DNS endpoint" 失败。
  • zone 必须精确存在:待签发域名对应的 DNS zone 必须在 Rackspace 账号内且名称精确匹配,GetHostedZoneID要求恰好找到 1 个 zone。
  • 参数单位均为秒:RACKSPACE_HTTP_TIMEOUT、RACKSPACE_PROPAGATION_TIMEOUT、RACKSPACE_POLLING_INTERVAL均以秒计,RACKSPACE_TTL为 TXT 记录 TTL 秒数;调整传播参数有助于应对 DNS 传播延迟波动(源码注释中明确说明 "Adjusting here to cope with spikes in propagation times")。
  • 配置优先走文件方式:生产环境建议配合_FILE后缀或 dotenv 文件注入凭证,避免密钥出现在 shell 历史与进程列表中。

延伸阅读

  • 提供商实现主体:providers/dns/rackspace/rackspace.go
  • 认证与 API 客户端:providers/dns/rackspace/internal/identity.go、providers/dns/rackspace/internal/client.go
  • 数据结构定义:providers/dns/rackspace/internal/types.go
  • 配置元数据(自动生成文档的数据源):providers/dns/rackspace/rackspace.toml
  • DNS 提供商的通用配置规范(环境变量 /_FILE/ dotenv):docs/content/dns/_index.md
  • DNS-01 传播默认参数:challenge/dns01/dns_challenge.go
  • 网络安全
  • 密码学

【免费下载链接】lego

Let's Encrypt/ACME client and library written in Go

项目地址:https://gitcode.com/gh_mirrors/le/lego
点击查看免费下载
上一篇:OpenClaw Skill Creator 实战指南:SKILL.md 的编写、校验与打包全流程
下一篇:从流式错误修复到原生 streamEvents:`@langchain/ibm` 集成包版本演进深度解读

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询