fix: handle ACME mock directory and NO_DNS_01 configuration

- Add MockDirectory constant to avoid hardcoded strings
- Skip certificate validation when using mock directory
- Skip ACME client creation for mock directory to avoid TLS errors
- Properly handle NO_DNS_01 configuration to disable DNS-01 challenges
- Fix certificate verification errors when ACME_API=https://acme.mock.directory
This commit is contained in:
hongwei.chen 2025-07-13 01:32:19 +08:00
parent d19542233a
commit 63014e493f
3 changed files with 92 additions and 65 deletions

View File

@ -13,8 +13,8 @@ var ErrAcmeMissConfig = errors.New("ACME client has wrong config")
func CreateAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache cache.ICache) (*certificates.AcmeClient, error) { func CreateAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache cache.ICache) (*certificates.AcmeClient, error) {
// check config // check config
if (!cfg.AcceptTerms || (cfg.DNSProvider == "" && !cfg.NoDNS01)) && cfg.APIEndpoint != "https://acme.mock.directory" { if (!cfg.AcceptTerms || (cfg.DNSProvider == "" && !cfg.NoDNS01)) && cfg.APIEndpoint != certificates.MockDirectory {
return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER or $NO_DNS_01, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER or $NO_DNS_01, unless $ACME_API is set to %s", ErrAcmeMissConfig, certificates.MockDirectory)
} }
if cfg.EAB_HMAC != "" && cfg.EAB_KID == "" { if cfg.EAB_HMAC != "" && cfg.EAB_KID == "" {
return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig)

View File

@ -35,7 +35,18 @@ func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache
return nil, err return nil, err
} }
acmeClient, err := lego.NewClient(acmeConfig) // Check if this is a mock directory
isMockDirectory := cfg.APIEndpoint == MockDirectory
var acmeClient *lego.Client
var mainDomainAcmeClient *lego.Client
if isMockDirectory {
log.Info().Msg("Using mock ACME directory, skipping ACME client creation")
acmeClient = nil
mainDomainAcmeClient = nil
} else {
acmeClient, err = lego.NewClient(acmeConfig)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only")
} else { } else {
@ -51,12 +62,12 @@ func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache
} }
} }
mainDomainAcmeClient, err := lego.NewClient(acmeConfig) mainDomainAcmeClient, err = lego.NewClient(acmeConfig)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only")
} else { } else {
if cfg.DNSProvider == "" { if cfg.DNSProvider == "" || cfg.NoDNS01 {
// using mock wildcard certs // using mock wildcard certs when no DNS provider is configured or NO_DNS_01 is set
mainDomainAcmeClient = nil mainDomainAcmeClient = nil
} else { } else {
// use DNS-Challenge https://go-acme.github.io/lego/dns/ // use DNS-Challenge https://go-acme.github.io/lego/dns/
@ -69,6 +80,7 @@ func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache
} }
} }
} }
}
return &AcmeClient{ return &AcmeClient{
legoClient: acmeClient, legoClient: acmeClient,

View File

@ -15,7 +15,10 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
const challengePath = "/.well-known/acme-challenge/" const (
challengePath = "/.well-known/acme-challenge/"
MockDirectory = "https://acme.mock.directory"
)
func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) { func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
var myAcmeAccount AcmeAccount var myAcmeAccount AcmeAccount
@ -25,6 +28,9 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
return nil, fmt.Errorf("invalid acme config file: '%s'", cfg.AccountConfigFile) return nil, fmt.Errorf("invalid acme config file: '%s'", cfg.AccountConfigFile)
} }
// Check if this is a mock directory - if so, skip certificate validation
isMockDirectory := cfg.APIEndpoint == MockDirectory
if account, err := os.ReadFile(cfg.AccountConfigFile); err == nil { if account, err := os.ReadFile(cfg.AccountConfigFile); err == nil {
log.Info().Msgf("found existing acme account config file '%s'", cfg.AccountConfigFile) log.Info().Msgf("found existing acme account config file '%s'", cfg.AccountConfigFile)
if err := json.Unmarshal(account, &myAcmeAccount); err != nil { if err := json.Unmarshal(account, &myAcmeAccount); err != nil {
@ -40,12 +46,15 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.CADirURL = cfg.APIEndpoint
myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048
// Skip validation for mock directory
if !isMockDirectory {
// Validate Config // Validate Config
_, err := lego.NewClient(myAcmeConfig) _, err := lego.NewClient(myAcmeConfig)
if err != nil { if err != nil {
log.Info().Err(err).Msg("config validation failed, you might just delete the config file and let it recreate") log.Info().Err(err).Msg("config validation failed, you might just delete the config file and let it recreate")
return nil, fmt.Errorf("acme config validation failed: %w", err) return nil, fmt.Errorf("acme config validation failed: %w", err)
} }
}
return myAcmeConfig, nil return myAcmeConfig, nil
} else if !os.IsNotExist(err) { } else if !os.IsNotExist(err) {
@ -66,6 +75,11 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig = lego.NewConfig(&myAcmeAccount)
myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.CADirURL = cfg.APIEndpoint
myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048
// Skip client creation for mock directory to avoid certificate verification errors
if isMockDirectory {
log.Info().Msg("Using mock ACME directory, skipping client creation and registration")
} else {
tempClient, err := lego.NewClient(myAcmeConfig) tempClient, err := lego.NewClient(myAcmeConfig)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only")
@ -105,6 +119,7 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) {
} }
} }
} }
}
return myAcmeConfig, nil return myAcmeConfig, nil
} }