☰
使用 lego 通过 Virtualname DNS 提供者签发通配符证书:从环境变量配置到源码级原理
2026/9/25 6:01:39 网站建设 项目流程
  • 网络安全
  • 密码学

【免费下载链接】lego

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

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

本文是 lego(Let's Encrypt/ACME 客户端)中 Virtualname DNS 提供者的完整使用指南。Virtualname 是一家西班牙 DNS 托管服务,从 lego v4.30.0 起支持通过其 API 完成 DNS-01 挑战,从而为通配符域名自动签发证书。读完本文,你将掌握 Virtualname 提供者的环境变量配置、命令行签发流程、调优参数含义,以及其基于 Tecnocrática 共享客户端的底层实现原理。

Virtualname 提供者是什么

Virtualname 是 lego 内置的 DNS 提供者(Provider)之一,代码名为virtualname,自v4.30.0版本起可用。它通过 Virtualname 的 DNS API 自动创建、验证并删除用于 ACME DNS-01 挑战的 TXT 记录,使 lego 能够在无需人工干预的情况下完成域名所有权验证并签发证书(包括*.example.com这类通配符证书)。

该提供者的官方说明文档位于 docs/content/dns/zz_gen_virtualname.md,其元数据定义在 providers/dns/virtualname/virtualname.toml 中。在 lego 的提供者注册表中,virtualname已被映射到对应的构造函数(见 providers/dns/zz_gen_dns_providers.go 中的case "virtualname"),因此 CLI 可直接按名称启用。

快速开始:签发通配符证书

使用 Virtualname 提供者签发证书的最小命令如下(摘自官方文档):

VIRTUALNAME_TOKEN=xxxxxx \ lego run --dns virtualname -d '*.example.com' -d example.com

命令要点:

  • VIRTUALNAME_TOKEN为你的 Virtualname API 令牌,通过环境变量传入;
  • --dns virtualname指定使用 Virtualname 提供者完成 DNS-01 挑战;
  • 同时传入-d '*.example.com'与-d example.com,可在一张证书中同时包含通配符域名与裸域名(ACME 要求裸域与通配符域同时验证,因此不能省略裸域)。

命令执行后,lego 会经历以下流程:向 ACME 服务器注册订单 → 计算 DNS-01 挑战所需的 TXT 记录值 → 调用 Virtualname API 创建 TXT 记录 → 等待 DNS 传播 → 通知 ACME 服务器验证 → 验证通过后下载证书。

凭证配置:VIRTUALNAME_TOKEN

提供者仅需要一个凭证环境变量:

环境变量名说明
VIRTUALNAME_TOKENAPI 令牌(API token)

从源码 providers/dns/virtualname/virtualname.go 可以看到,环境变量命名空间统一为VIRTUALNAME_:

const ( envNamespace = "VIRTUALNAME_" EnvToken = envNamespace + "TOKEN" // ... )

在NewDNSProvider()中,如果VIRTUALNAME_TOKEN缺失或为空,会直接返回错误:

values, err := env.Get(EnvToken) if err != nil { return nil, fmt.Errorf("virtualname: %w", err) }

对应的单元测试 providers/dns/virtualname/virtualname_test.go 也验证了这一行为:缺失令牌时返回virtualname: some credentials information are missing: VIRTUALNAME_TOKEN。

使用_FILE后缀从文件读取凭证

所有环境变量都可以追加_FILE后缀,改为从文件读取值。例如将令牌写入文件后:

echo -n 'xxxxxx' > /path/to/token.txt VIRTUALNAME_TOKEN_FILE=/path/to/token.txt \ lego run --dns virtualname -d '*.example.com' -d example.com

这一机制对 CI/CD 场景非常实用——避免把令牌直接暴露在命令行或环境变量中。有关 lego 环境变量与文件配置的完整约定,参见 docs/content/dns/_index.md 中的 "Configuration and Credentials" 一节,以及 docs/content/advanced/file-configuration.md。

附加配置参数与调优

除了令牌之外,Virtualname 提供者还支持以下可选参数:

环境变量名说明默认值
VIRTUALNAME_HTTP_TIMEOUTAPI 请求超时时间(秒)30
VIRTUALNAME_POLLING_INTERVALDNS 传播检查间隔(秒)10
VIRTUALNAME_PROPAGATION_TIMEOUTDNS 传播最大等待时间(秒)300
VIRTUALNAME_TTLDNS 挑战所用 TXT 记录的 TTL(秒)120

这些默认值并非文档凭空而来,而是直接编码在 providers/dns/virtualname/virtualname.go 的NewDefaultConfig()中:

func NewDefaultConfig() *Config { return &Config{ TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 5*time.Minute), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 10*time.Second), HTTPClient: &http.Client{ Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), }, } }

几点源码级补充说明:

  • dns01.DefaultTTL在 challenge/dns01/dns_challenge.go 中定义为120,即 TTL 默认值 120 秒的来源;
  • PROPAGATION_TIMEOUT默认 300 秒(5 分钟)、POLLING_INTERVAL默认 10 秒,二者共同决定 lego 轮询 DNS 传播的节奏;
  • HTTP_TIMEOUT控制与 Virtualname API 交互的 HTTP 客户端超时。

调优建议

  • TTL:如果 Virtualname 后台刷新速度较慢,可适当调大VIRTUALNAME_TTL;反之希望挑战尽快完成、TXT 记录尽快消失时,可调小 TTL;
  • 传播等待:DNS 记录在权威服务器间同步耗时较长时,可调大VIRTUALNAME_PROPAGATION_TIMEOUT,避免 lego 因等待不足而误报失败;
  • 所有参数同样支持_FILE后缀从文件读取。

工作原理:源码级解析

Virtualname 提供者本身是一个薄封装,其核心逻辑委托给共享的 Tecnocrática 客户端实现(providers/dns/internal/tecnocratica/provider.go),API 端点为https://api.virtualname.net/v1(见 providers/dns/virtualname/virtualname.go 中的defaultBaseURL)。

创建 TXT 记录(Present)

Present是 ACME 挑战生命周期中的"置备"阶段。其流程为:

  1. 由dns01.GetChallengeInfo计算挑战记录值与有效 FQDN;
  2. 通过FindZoneByFqdn查找域名所属的权威 DNS zone;
  3. 调用GetZones在 Virtualname 侧匹配 zone(按name或human_name比对);
  4. 用ExtractSubDomain提取子域前缀;
  5. 构造TXT类型记录并调用CreateRecord创建;
  6. 将zoneID与recordID以挑战 token 为键存入内存 map,供后续清理使用。
record := internal.Record{ Name: subDomain, Type: "TXT", Content: info.Value, TTL: d.config.TTL, } newRecord, err := d.client.CreateRecord(ctx, zone.ID, record)

删除 TXT 记录(CleanUp)

证书签发完成后,lego 会调用CleanUp删除挑战 TXT 记录,避免留下无用的 DNS 数据。它通过之前保存的zoneID/recordID调用DeleteRecord,成功后从内存 map 中清除对应条目。

传播等待(Timeout)

DNSProvider实现了challenge.ProviderTimeout接口:

func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval }

这正是VIRTUALNAME_PROPAGATION_TIMEOUT与VIRTUALNAME_POLLING_INTERVAL两个环境变量在运行时被消费的位置。

底层 HTTP 客户端

Tecnocrática 客户端(providers/dns/internal/tecnocratica/internal/client.go)使用X-TCpanel-Token请求头携带 API 令牌,请求路径为:

  • GET /dns/zones—— 列出全部 zone;
  • POST /dns/zones/{zoneID}/records—— 创建记录(请求体为{"record": {...}});
  • DELETE /dns/zones/{zoneID}/records/{recordID}—— 删除记录。

所有请求还会附加Accept: application/json头、lego 的 User-Agent 头,并在响应状态码非 2xx 时返回带状态码与响应体的错误信息。

在 Go 代码中集成使用

除了 CLI,你也可以在 Go 程序中将 Virtualname 提供者作为库使用。以下示例展示两种构造方式:

package main import ( "log" "github.com/go-acme/lego/v5/providers/dns/virtualname" ) func main() { // 方式一:从环境变量 VIRTUALNAME_TOKEN 读取凭证 provider, err := virtualname.NewDNSProvider() if err != nil { log.Fatal(err) } // 方式二:显式传入配置(可覆盖默认 TTL、超时等) config := virtualname.NewDefaultConfig() config.Token = "xxxxxx" config.TTL = 60 provider, err = virtualname.NewDNSProviderConfig(config) if err != nil { log.Fatal(err) } _ = provider }

Config类型直接复用了 Tecnocrática 的配置结构(providers/dns/internal/tecnocratica/provider.go),包含Token、PropagationTimeout、PollingInterval、TTL、HTTPClient五个字段,与上文的四个环境变量一一对应。

之后将provider传入 lego 的Certificate.Obtain流程即可。若config为 nil,构造会返回virtualname: the configuration of the DNS provider is nil错误;若Token为空,则返回virtualname: missing credentials——这两条错误信息同样由单元测试覆盖。

验证与测试

providers/dns/virtualname/virtualname_test.go 提供了三类测试:

  • TestNewDNSProvider:验证环境变量正常/缺失两种情况;
  • TestNewDNSProviderConfig:验证显式配置的正常/缺失令牌两种情况;
  • TestLivePresent/TestLiveCleanUp:真实调用 Virtualname API 的现场测试(需要设置VIRTUALNAME_DOMAIN环境变量,并仅在开启 live 测试标记时运行),分别验证 TXT 记录的创建与清理。

这些测试是排查"为什么我的配置不生效"时的最佳参照——它们精确列出了每种失败场景下的错误文案。

注意事项

  • Virtualname 提供者自v4.30.0起才可用,请确认你的 lego 版本不低于该版本;
  • 通配符证书(-d '*.example.com')必须同时包含裸域名,否则 ACME 校验会失败;
  • API 令牌应妥善保管,优先通过VIRTUALNAME_TOKEN_FILE或安全的密钥管理服务注入,避免提交到版本库;
  • 本文所有默认值与行为均以当前仓库源码为准(v5 版本线),若升级 lego 版本请以对应版本的文档与源码为准。

参考资料

  • 提供者官方文档:docs/content/dns/zz_gen_virtualname.md
  • 提供者实现:providers/dns/virtualname/virtualname.go
  • 共享客户端实现:providers/dns/internal/tecnocratica/provider.go 与 providers/dns/internal/tecnocratica/internal/client.go
  • 单元测试:providers/dns/virtualname/virtualname_test.go
  • 环境变量与文件配置约定:docs/content/dns/_index.md、docs/content/advanced/file-configuration.md
  • 网络安全
  • 密码学

【免费下载链接】lego

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

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

相关推荐

上一篇:StringManipulation对齐和格式化功能:如何快速创建整齐的代码注释和文档
下一篇:FontStash内存优化技巧:减少纹理占用和提升缓存效率

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

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

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

立即咨询