求助:Go语言HTTPS独立服务器的ACME DNS挑战Token获取方法
实现Go中ACME DNS挑战的Token获取与TXT记录配置
要完成ACME DNS-01挑战并获取SSL证书,你可以使用go-acme/lego库处理ACME协议交互,同时结合自有DNS服务器实现TXT记录的动态添加。以下是具体步骤和代码示例:
1. 依赖安装
首先安装ACME客户端库:
go get github.com/go-acme/lego/v4
2. 核心代码实现
package main import ( "crypto/rand" "crypto/rsa" "crypto/tls" "fmt" "net/http" "os" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/registration" ) // 自定义DNS提供者,实现dns01.ChallengeProvider接口 type CustomDNSProvider struct{} // Present 挑战触发时添加TXT记录 func (d *CustomDNSProvider) Present(domain, token, keyAuth string) error { // 替换为你的DNS记录添加逻辑 // 需添加的TXT记录:_acme-challenge.realwebsite.com -> keyAuth fmt.Printf("添加TXT记录:_acme-challenge.realwebsite.com = %s\n", keyAuth) // yourDNSServer.AddTXTRecord("_acme-challenge.realwebsite.com", keyAuth) return nil } // CleanUp 挑战完成后删除TXT记录(可选) func (d *CustomDNSProvider) CleanUp(domain, token, keyAuth string) error { // 替换为你的DNS记录删除逻辑 fmt.Printf("删除TXT记录:_acme-challenge.realwebsite.com = %s\n", keyAuth) // yourDNSServer.DeleteTXTRecord("_acme-challenge.realwebsite.com", keyAuth) return nil } // ACME账户信息结构 type MyUser struct { Email string Registration *registration.Resource key interface{} } func (u *MyUser) GetEmail() string { return u.Email } func (u *MyUser) GetRegistration() *registration.Resource { return u.Registration } func (u *MyUser) GetPrivateKey() interface{} { return u.key } func main() { // 1. 生成账户私钥 privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { fmt.Println("生成私钥失败:", err) return } // 2. 初始化ACME账户 user := MyUser{ Email: "your-email@example.com", // 替换为你的邮箱 key: privateKey, } // 3. 配置ACME客户端(测试环境用 staging 地址避免速率限制) config := lego.NewConfig(&user) config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory" client, err := lego.NewClient(config) if err != nil { fmt.Println("初始化ACME客户端失败:", err) return } // 4. 注册账户(首次运行需同意服务条款) reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) if err != nil { fmt.Println("注册账户失败:", err) return } user.Registration = reg // 5. 设置DNS挑战提供者 err = client.Challenge.SetDNS01Provider(&CustomDNSProvider{}) if err != nil { fmt.Println("设置DNS提供者失败:", err) return } // 6. 请求SSL证书 request := certificate.ObtainRequest{ Domains: []string{"realwebsite.com"}, // 替换为你的域名 Bundle: true, } certificates, err := client.Certificate.Obtain(request) if err != nil { fmt.Println("获取证书失败:", err) return } // 7. 保存证书和私钥到文件 err = os.WriteFile("certs/realwebsite.com.crt", certificates.Certificate, 0644) if err != nil { fmt.Println("保存证书失败:", err) return } err = os.WriteFile("certs/realwebsite.com.key", certificates.PrivateKey, 0600) if err != nil { fmt.Println("保存私钥失败:", err) return } fmt.Println("证书获取成功!") // 8. 启动HTTPS服务器(原代码逻辑) mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello HTTP/2") }) server := http.Server{ Addr: ":443", Handler: mux, TLSConfig: &tls.Config{ NextProtos: []string{"h2", "http/1.1"}, }, } fmt.Printf("Server listening on %s\n", server.Addr) if err := server.ListenAndServeTLS("certs/realwebsite.com.crt", "certs/realwebsite.com.key"); err != nil { fmt.Println(err) } }
关键说明
- Present方法:ACME服务器发起挑战时会调用此方法,你需要在这里将
keyAuth值写入_acme-challenge.realwebsite.com的TXT记录(注意:验证需要的是keyAuth而非单纯的token)。 - 环境选择:测试阶段建议使用Let's Encrypt的staging环境(
https://acme-staging-v02.api.letsencrypt.org/directory),避免生产环境的速率限制。 - 账户复用:首次运行生成的私钥和注册信息可复用,无需每次重新注册。
内容的提问来源于stack exchange,提问作者Suhaib alnaji
相关产品推荐
相关产品推荐

