You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 15:02:19