- 网络安全
- 密码学
【免费下载链接】lego
Let's Encrypt/ACME client and library written in Go
本文以 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_KEY | Rackspace API Key |
RACKSPACE_USER | Rackspace 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_TIMEOUT | API 请求超时(秒) | 30 |
RACKSPACE_POLLING_INTERVAL | 两次 DNS 传播检查之间的间隔(秒) | 3 |
RACKSPACE_PROPAGATION_TIMEOUT | DNS 传播最大等待时间(秒) | 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 记录,流程为:
- 通过
dns01.GetChallengeInfo计算挑战域名(_acme-challenge.example.com)与挑战值; - 调用
GetHostedZoneID定位所属 DNS zone; - 构造
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)在验证完成后清理记录:
- 重新定位 zone;
- 调用
FindTxtRecord按名称与类型精确搜索 TXT 记录(client.go),要求恰好命中 1 条,0 条或 2 条以上均视为错误; - 携带记录 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
相关推荐
使用 Curanet DNS 提供商完成 lego 的 DNS-01 挑战:配置指南与源码解析
使用 Curanet DNS 提供商完成 lego 的 DNS 01 挑战:配置指南与源码解析 本指南介绍如何在 lego https://link.gitco
网络安全密码学使用 lego 与 Gandi DNS 完成 ACME DNS-01 挑战:配置指南与源码原理剖析
使用 lego 与 Gandi DNS 完成 ACME DNS 01 挑战:配置指南与源码原理剖析 导读 :本文围绕 lego(Let's Encrypt/AC
网络安全密码学lego 使用 Digital Ocean DNS 提供商完成 DNS-01 挑战:配置、凭证与源码解析
lego 使用 Digital Ocean DNS 提供商完成 DNS 01 挑战:配置、凭证与源码解析 本篇技术指南聚焦 lego(Let's Encrypt
网络安全密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考