- 网络安全
- 密码学
【免费下载链接】lego
Let's Encrypt/ACME client and library written in Go
本文是一份面向开发者的 DirectAdmin DNS 提供商(Provider)使用指南。基于 lego(Let's Encrypt/ACME 客户端与库)官方文档 docs/content/dns/zz_gen_directadmin.md 及providers/dns/directadmin目录下的真实源码,说明如何通过环境变量配置 DirectAdmin 凭据、通过--dns directadmin完成 ACME DNS-01 挑战,以及该提供商在 lego 内部如何调用 DirectAdmin 的CMD_API_DNS_CONTROL接口增删 TXT 记录。读完本文,你将能够为运行在 DirectAdmin 控制面板上的域名签发单域名与通配符证书,并理解其底层工作原理。
DirectAdmin 提供商概述
DirectAdmin 是一款常见的服务器控制面板(官方站点为 https://www.directadmin.com),自带完整的 DNS 管理 API。lego 的 DirectAdmin 提供商(Provider Code:directadmin)从v4.18.0版本开始提供,用于通过 DNS-01 挑战方式验证域名所有权。
与 HTTP-01 挑战需要在 Web 服务器上放置验证文件不同,DNS-01 挑战要求在你的权威 DNS 区域中临时新增一条TXT记录。lego 会在挑战前调用 DirectAdmin API添加该 TXT 记录(Present),等待 DNS 传播完成后向 ACME 服务端验证,最后在挑战结束后删除该记录(CleanUp)。
该提供商的配置元数据定义在 providers/dns/directadmin/directadmin.toml 中,并已注册进 lego 的 DNS 提供商调度表(见 providers/dns/zz_gen_dns_providers.go,case "directadmin": return directadmin.NewDNSProvider())。
快速开始:签发通配符证书
以 DirectAdmin 官方文档给出的示例命令为基准,使用环境变量传入 API 地址、用户名和密码,即可对*.example.com与example.com同时发起证书签发:
DIRECTADMIN_API_URL="http://example.com:2222" \ DIRECTADMIN_USERNAME=xxxx \ DIRECTADMIN_PASSWORD=yyy \ lego run --dns directadmin -d '*.example.com' -d example.com说明:
lego run会完成注册、签发与自动续期的完整流程(如需查看所有可用参数,可运行lego run --help)。- 示例中同时传入
'*.example.com'和example.com,正是通配符证书的典型用法——ACME 要求通配符域名必须走 DNS-01 挑战。 DIRECTADMIN_API_URL中:2222是 DirectAdmin 默认的控制面板端口;实际地址以你的服务器配置为准,务必确认该地址从 lego 运行所在主机可访问。
凭据配置(Credentials)
lego 的全部 DNS 提供商均通过环境变量注入凭据。DirectAdmin 提供商需要以下三个必填变量:
| 环境变量名 | 说明 |
|---|---|
DIRECTADMIN_API_URL | API 的 URL(如http://example.com:2222) |
DIRECTADMIN_USERNAME | API 用户名 |
DIRECTADMIN_PASSWORD | API 密码 |
这些变量名在源码中以常量形式定义在 providers/dns/directadmin/directadmin.go(命名空间前缀DIRECTADMIN_),并由NewDNSProvider()通过env.Get(EnvAPIURL, EnvUsername, EnvPassword)一次性读取;缺少任何一个变量都会返回形如directadmin: some credentials information are missing: DIRECTADMIN_API_URL的错误(该行为由 directadmin_test.go 的用例逐项验证)。
使用_FILE后缀引用文件
所有环境变量(包括下文的附加配置项)都可以通过_FILE后缀改为从文件读取,从而避免在 shell 历史或进程列表中泄露密钥。例如:
DIRECTADMIN_API_URL_FILE=/path/to/api_url \ DIRECTADMIN_USERNAME_FILE=/path/to/username \ DIRECTADMIN_PASSWORD_FILE=/path/to/password \ lego run --dns directadmin -d '*.example.com'其中每个文件内容仅允许包含该变量的值本身(不要包含换行或多余字符)。这一机制的通用说明见 docs/content/dns/_index.md(Configuration and Credentials一节),实现上由platform/env的GetOrFile系列函数支撑,例如env.GetOrFile(EnvZoneName)(见 directadmin.go)。
使用 dotenv 文件
当使用配置文件或希望集中管理变量时,可以用--env-file指定 dotenv 文件:
lego run --dns directadmin --domains 'example.org' --domains '*.example.org' --env-file .env.directadmin.env.directadmin内容示例:
DIRECTADMIN_API_URL=http://example.com:2222 DIRECTADMIN_USERNAME=xxxx DIRECTADMIN_PASSWORD=yyy该方式同样适用于通过 lego 配置文件(.lego.yml)为 DNS 挑战块指定envFile的场景,具体约定参见 docs/content/dns/_index.md。
附加配置项(Additional Configuration)
除凭据外,DirectAdmin 提供商还支持以下可调参数,均通过环境变量注入:
| 环境变量名 | 说明 | 默认值 |
|---|---|---|
DIRECTADMIN_HTTP_TIMEOUT | API 请求超时时间(秒) | 30 |
DIRECTADMIN_POLLING_INTERVAL | DNS 传播检查间隔(秒) | 5 |
DIRECTADMIN_PROPAGATION_TIMEOUT | DNS 传播最大等待时间(秒) | 60 |
DIRECTADMIN_TTL | 挑战 TXT 记录的 TTL(秒) | 30 |
DIRECTADMIN_ZONE_NAME | 用于添加 TXT 记录的 Zone 名称 | 自动探测 |
这些默认值与源码中NewDefaultConfig()的实现一一对应(directadmin.go):
func NewDefaultConfig() *Config { return &Config{ ZoneName: env.GetOrFile(EnvZoneName), TTL: env.GetOrDefaultInt(EnvTTL, 30), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 60*time.Second), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 5*time.Second), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), }, } }参数配置示例
DIRECTADMIN_API_URL="http://example.com:2222" \ DIRECTADMIN_USERNAME=xxxx \ DIRECTADMIN_PASSWORD=yyy \ DIRECTADMIN_TTL=60 \ DIRECTADMIN_POLLING_INTERVAL=10 \ DIRECTADMIN_PROPAGATION_TIMEOUT=120 \ DIRECTADMIN_HTTP_TIMEOUT=60 \ lego run --dns directadmin -d '*.example.com'参数语义与调优建议
DIRECTADMIN_TTL:控制写入的 TXT 记录 TTL。该值通过internal.Record的TTL字段随 API 请求一起提交(见下文)。TTL 越大,解析缓存生效时间越长,可能拖慢挑战完成。DIRECTADMIN_PROPAGATION_TIMEOUT与DIRECTADMIN_POLLING_INTERVAL:lego 在写入记录后会轮询公共 DNS 直到记录可见。DNSProvider实现了challenge.ProviderTimeout接口,其Timeout()方法直接返回这两个配置(directadmin.go)。如果你的权威 NS 或公共解析器响应较慢,可适当调大超时。DIRECTADMIN_ZONE_NAME:默认情况下 lego 通过dns01.DefaultClient().FindZoneByFqdn()根据待验证域名自动反查权威 Zone(见getZoneName实现,directadmin.go)。当自动探测失败,或你的 DirectAdmin 中所管理的 Zone 名称与公开 SOA 记录不一致时,可以显式指定,例如DIRECTADMIN_ZONE_NAME=example.com。注意代码中dns01.UnFqdn(authZone)会去掉域名末尾的点。
工作原理:从 Present 到 CMD_API_DNS_CONTROL
为了帮助你排查问题和评估安全性,这里结合源码梳理一次 DNS-01 挑战中该提供商的完整调用链。
1. Present:写入挑战 TXT 记录
Present()(directadmin.go)执行以下步骤:
- 通过
dns01.GetChallengeInfo(ctx, domain, keyAuth)计算挑战记录:EffectiveFQDN(_acme-challenge.<domain>)与Value(keyAuth的摘要); - 调用
getZoneName确定权威 Zone(优先使用ZoneName配置,否则自动反查); - 用
dns01.ExtractSubDomain从完整 FQDN 中剥离出子域前缀(即_acme-challenge部分); - 组装
internal.Record{Name, Type: "TXT", Value, TTL}并调用client.SetRecord写入。
SetRecord(internal/client.go)将记录序列化为表单字段后追加action=add,然后发起请求。
2. 底层 HTTP 请求细节
所有写操作最终汇入Client.do()(internal/client.go),其请求特征:
- 端点:
<BASE_URL>/CMD_API_DNS_CONTROL(DirectAdmin 标准 DNS 控制 API); - 方法:
POST,Content-Type: application/x-www-form-urlencoded; - 认证:HTTP Basic Auth(
req.SetBasicAuth(c.username, c.password)); - 查询参数:
domain=<zone>&json=yes(请求 JSON 响应); - 表单字段:
name、type、value、ttl、action; - 错误处理:非 200 响应会被解析为 JSON 格式的
APIError(error/result字段),返回如[status code 500] Cannot View Dns Record: OOPS的错误。
internal.Record的定义见 internal/types.go,字段与表单名一一对应。上述请求形态均由 internal/client_test.go 中的 mock 测试精确验证(包括domain、json=yes、action=add、name/type/value/ttl等严格匹配)。
3. CleanUp:挑战完成后清理记录
CleanUp()(directadmin.go)与Present对称:重新计算EffectiveFQDN与子域,组装Record(注意此时不携带TTL),调用client.DeleteRecord(即action=delete)。即使挑战中途失败,lego 也会尽力调用CleanUp清理残留 TXT 记录。
以库的方式使用(Go 编程接口)
除了 CLI,你还可以把 DirectAdmin 提供商嵌入自己的 Go 程序。lego 通过lego.NewClient+client.Challenge.AddDNS01Provider组合使用,提供商侧提供两个构造入口:
import ( "github.com/go-acme/lego/v4/providers/dns/directadmin" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/registration" ) // 方式一:从环境变量读取 DIRECTADMIN_API_URL / DIRECTADMIN_USERNAME / DIRECTADMIN_PASSWORD provider, err := directadmin.NewDNSProvider() if err != nil { // 缺少凭据时返回 directadmin: some credentials information are missing: ... } // 方式二:编程方式注入配置 config := directadmin.NewDefaultConfig() config.BaseURL = "http://example.com:2222" config.Username = "xxxx" config.Password = "yyy" config.TTL = 60 provider, err = directadmin.NewDNSProviderConfig(config) if err != nil { // 校验失败时返回 directadmin: missing API URL 等错误 }注意NewDNSProviderConfig会进行必要的参数校验:BaseURL为空返回directadmin: missing API URL,用户名或密码缺失返回directadmin: some credentials information are missing(见 directadmin.go)。上述两种入口的成功与失败分支均有对应的单元测试覆盖(directadmin_test.go)。
常见问题排查要点
- 提示
some credentials information are missing:逐一确认DIRECTADMIN_API_URL、DIRECTADMIN_USERNAME、DIRECTADMIN_PASSWORD三个变量都已正确导出(测试用例证明缺任意一个都会失败)。 - 提示
missing API URL:检查 URL 是否拼写完整(包含协议与端口),例如http://example.com:2222。 - API 返回 500 /
Cannot View Dns Record:通常是 DirectAdmin 账号权限不足或登录信息有误,错误信息中会带上error与result字段便于排查(参见 internal/types.go 与 client_test.go 的错误路径测试)。 - Zone 自动探测失败:为待验证域名设置正确的 NS 记录并确保公网可解析;仍失败则显式设置
DIRECTADMIN_ZONE_NAME。 - 超时类问题:DNS 传播较慢时增大
DIRECTADMIN_PROPAGATION_TIMEOUT,同时可适当降低DIRECTADMIN_POLLING_INTERVAL提高轮询频率。
更多信息
- DirectAdmin 官方 API 文档:https://www.directadmin.com/api.php(重点可关注
CMD_API_DNS_CONTROL的add/delete动作) - 提供商配置元数据与示例:providers/dns/directadmin/directadmin.toml
- 提供商主实现:providers/dns/directadmin/directadmin.go
- API 客户端实现:providers/dns/directadmin/internal/client.go
- 单元测试(含 mock 断言):providers/dns/directadmin/internal/client_test.go
- 所有 DNS 提供商的通用配置(
_FILE后缀、dotenv、配置文件约定):docs/content/dns/_index.md
- 网络安全
- 密码学
【免费下载链接】lego
Let's Encrypt/ACME client and library written in Go
相关推荐
lego 使用 dnsHome.de 作为 DNS-01 挑战提供商:配置指南与实现原理
lego 使用 dnsHome.de 作为 DNS 01 挑战提供商:配置指南与实现原理 dnsHome.de 是 lego 内置的 DNS 提供商之一,通过其
网络安全密码学使用 lego 的 Gehirn DNS 提供商解决 DNS-01 挑战:配置、凭据与源码原理
使用 lego 的 Gehirn DNS 提供商解决 DNS 01 挑战:配置、凭据与源码原理 本篇技术指南聚焦 lego(Go 编写的 Let's Encry
网络安全密码学lego 集成 Derak Cloud DNS 提供者:DNS-01 挑战配置指南与源码实现解析
lego 集成 Derak Cloud DNS 提供者:DNS 01 挑战配置指南与源码实现解析 本篇文章以 lego 项目内置的 Derak Cloud DN
网络安全密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考