Golang实现ACME协议的DNS示例
Golang实现ACME协议的DNS示例 我正在寻找使用ACME协议进行DNS验证的实际示例。我找到了很多使用HTTP验证的例子,但一个DNS验证的都没有。有没有人实际可用的代码或者好的实践示例?
我已经阅读了这个包的GoDoc文档,但帮助不大。
2 回复
我从 Let’s Encrypt 论坛得到了回复:
使用 DNS 验证的 Golang 示例
哪个包?/x/crypto/acme 只支持 ACME v1,不支持通配符。尝试使用 github.com/eggsampler/acme 或 github.com/xenolf/lego 来获取 ACME v2 库。但即使使用 v1 包,使用 DNS 验证也只需要很小的改动…
更多关于Golang实现ACME协议的DNS示例的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是一个使用Go语言实现ACME协议DNS验证的完整示例,基于golang.org/x/crypto/acme包。这个示例展示了如何通过DNS-01挑战完成证书颁发过程。
package main
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"log"
"time"
"golang.org/x/crypto/acme"
)
// DNSProvider 接口定义了DNS记录操作的方法
type DNSProvider interface {
// 设置TXT记录
SetTXTRecord(ctx context.Context, domain, value string) error
// 清理TXT记录
CleanupTXTRecord(ctx context.Context, domain string) error
}
// ACMEClient 封装ACME客户端和DNS操作
type ACMEClient struct {
client *acme.Client
privateKey crypto.Signer
dnsProvider DNSProvider
}
// NewACMEClient 创建新的ACME客户端
func NewACMEClient(dnsProvider DNSProvider) (*ACMEClient, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("生成私钥失败: %v", err)
}
client := &acme.Client{
Key: privateKey,
DirectoryURL: "https://acme-staging-v02.api.letsencrypt.org/directory", // 测试环境
}
return &ACMEClient{
client: client,
privateKey: privateKey,
dnsProvider: dnsProvider,
}, nil
}
// RequestCertificate 请求证书
func (a *ACMEClient) RequestCertificate(ctx context.Context, domain string) error {
// 创建ACME账户
account := &acme.Account{
Contact: []string{"mailto:admin@example.com"},
}
if _, err := a.client.Register(ctx, account, acme.AcceptTOS); err != nil {
return fmt.Errorf("注册ACME账户失败: %v", err)
}
// 授权请求
authz, err := a.client.Authorize(ctx, domain)
if err != nil {
return fmt.Errorf("获取授权失败: %v", err)
}
// 查找DNS-01挑战
var challenge *acme.Challenge
for _, c := range authz.Challenges {
if c.Type == "dns-01" {
challenge = c
break
}
}
if challenge == nil {
return fmt.Errorf("未找到DNS挑战")
}
// 生成DNS挑战记录值
keyAuth, err := a.client.DNS01ChallengeRecord(challenge.Token)
if err != nil {
return fmt.Errorf("生成挑战记录失败: %v", err)
}
// DNS记录名称
dnsRecord := "_acme-challenge." + domain
// 设置DNS TXT记录
if err := a.dnsProvider.SetTXTRecord(ctx, dnsRecord, keyAuth); err != nil {
return fmt.Errorf("设置DNS记录失败: %v", err)
}
defer a.dnsProvider.CleanupTXTRecord(ctx, dnsRecord)
// 等待DNS传播
time.Sleep(30 * time.Second)
// 接受挑战
if _, err := a.client.Accept(ctx, challenge); err != nil {
return fmt.Errorf("接受挑战失败: %v", err)
}
// 等待授权完成
if _, err := a.client.WaitAuthorization(ctx, authz.URI); err != nil {
return fmt.Errorf("等待授权完成失败: %v", err)
}
// 创建证书请求
csr, err := a.generateCSR(domain)
if err != nil {
return fmt.Errorf("生成CSR失败: %v", err)
}
// 请求证书
cert, _, err := a.client.CreateCert(ctx, csr, 0, true)
if err != nil {
return fmt.Errorf("创建证书失败: %v", err)
}
fmt.Printf("证书获取成功! 证书数量: %d\n", len(cert))
return nil
}
// generateCSR 生成证书签名请求
func (a *ACMEClient) generateCSR(domain string) ([]byte, error) {
template := &x509.CertificateRequest{
Subject: pkix.Name{CommonName: domain},
DNSNames: []string{domain},
}
return x509.CreateCertificateRequest(rand.Reader, template, a.privateKey)
}
// 示例DNS提供者实现
type ExampleDNSProvider struct{}
func (e *ExampleDNSProvider) SetTXTRecord(ctx context.Context, domain, value string) error {
// 这里实现具体的DNS记录设置逻辑
// 例如使用Cloudflare、AWS Route53等DNS服务的API
fmt.Printf("设置DNS记录: %s -> %s\n", domain, value)
return nil
}
func (e *ExampleDNSProvider) CleanupTXTRecord(ctx context.Context, domain string) error {
// 清理DNS记录
fmt.Printf("清理DNS记录: %s\n", domain)
return nil
}
func main() {
ctx := context.Background()
dnsProvider := &ExampleDNSProvider{}
client, err := NewACMEClient(dnsProvider)
if err != nil {
log.Fatalf("创建ACME客户端失败: %v", err)
}
domain := "example.com"
if err := client.RequestCertificate(ctx, domain); err != nil {
log.Fatalf("请求证书失败: %v", err)
}
}
这个示例包含以下关键组件:
- DNSProvider接口:定义DNS记录操作的标准方法
- ACMEClient结构体:封装ACME客户端逻辑
- 完整的DNS-01挑战流程:
- 注册ACME账户
- 获取域名授权
- 设置DNS TXT记录
- 接受挑战并等待验证
- 获取证书
你需要根据实际使用的DNS服务商实现SetTXTRecord和CleanupTXTRecord方法。对于生产环境,建议使用正式的ACME端点(移除-staging)。
对于具体的DNS服务商实现,可以参考相应的Go SDK,如:
- Cloudflare:
github.com/cloudflare/cloudflare-go - AWS Route53:
github.com/aws/aws-sdk-go-v2/service/route53 - Google Cloud DNS:
cloud.google.com/dns/docs/reference/libraries


