diff --git a/server/acme/client.go b/server/acme/client.go index d5c83d0..bfb5a8d 100644 --- a/server/acme/client.go +++ b/server/acme/client.go @@ -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) { // check config - if (!cfg.AcceptTerms || (cfg.DNSProvider == "" && !cfg.NoDNS01)) && cfg.APIEndpoint != "https://acme.mock.directory" { - 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) + 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 %s", ErrAcmeMissConfig, certificates.MockDirectory) } if cfg.EAB_HMAC != "" && cfg.EAB_KID == "" { return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) diff --git a/server/certificates/acme_client.go b/server/certificates/acme_client.go index f42fd8f..e83540a 100644 --- a/server/certificates/acme_client.go +++ b/server/certificates/acme_client.go @@ -35,37 +35,49 @@ func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache return nil, err } - acmeClient, err := lego.NewClient(acmeConfig) - if err != nil { - log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") - } else { - err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") - } - if enableHTTPServer { - err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create HTTP-01 provider") - } - } - } + // Check if this is a mock directory + isMockDirectory := cfg.APIEndpoint == MockDirectory - mainDomainAcmeClient, err := lego.NewClient(acmeConfig) - if err != nil { - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + 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 { - if cfg.DNSProvider == "" { - // using mock wildcard certs - mainDomainAcmeClient = nil + acmeClient, err = lego.NewClient(acmeConfig) + if err != nil { + log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { - // use DNS-Challenge https://go-acme.github.io/lego/dns/ - provider, err := dns.NewDNSChallengeProviderByName(cfg.DNSProvider) + err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { - return nil, fmt.Errorf("can not create DNS Challenge provider: %w", err) + log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") } - if err := mainDomainAcmeClient.Challenge.SetDNS01Provider(provider); err != nil { - return nil, fmt.Errorf("can not create DNS-01 provider: %w", err) + if enableHTTPServer { + err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) + if err != nil { + log.Error().Err(err).Msg("Can't create HTTP-01 provider") + } + } + } + + mainDomainAcmeClient, err = lego.NewClient(acmeConfig) + if err != nil { + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + } else { + if cfg.DNSProvider == "" || cfg.NoDNS01 { + // using mock wildcard certs when no DNS provider is configured or NO_DNS_01 is set + mainDomainAcmeClient = nil + } else { + // use DNS-Challenge https://go-acme.github.io/lego/dns/ + provider, err := dns.NewDNSChallengeProviderByName(cfg.DNSProvider) + if err != nil { + return nil, fmt.Errorf("can not create DNS Challenge provider: %w", err) + } + if err := mainDomainAcmeClient.Challenge.SetDNS01Provider(provider); err != nil { + return nil, fmt.Errorf("can not create DNS-01 provider: %w", err) + } } } } diff --git a/server/certificates/acme_config.go b/server/certificates/acme_config.go index 2b5151d..abb111e 100644 --- a/server/certificates/acme_config.go +++ b/server/certificates/acme_config.go @@ -15,7 +15,10 @@ import ( "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) { 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) } + // 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 { log.Info().Msgf("found existing acme account config file '%s'", cfg.AccountConfigFile) if err := json.Unmarshal(account, &myAcmeAccount); err != nil { @@ -40,11 +46,14 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) { myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - // Validate Config - _, err := lego.NewClient(myAcmeConfig) - if err != nil { - 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) + // Skip validation for mock directory + if !isMockDirectory { + // Validate Config + _, err := lego.NewClient(myAcmeConfig) + if err != nil { + 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 myAcmeConfig, nil @@ -66,42 +75,48 @@ func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) { myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - tempClient, err := lego.NewClient(myAcmeConfig) - if err != nil { - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + + // 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 { - // accept terms & log in to EAB - if cfg.EAB_KID == "" || cfg.EAB_HMAC == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.AcceptTerms}) - if err != nil { - log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") - } else { - myAcmeAccount.Registration = reg - } + tempClient, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { - reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: cfg.AcceptTerms, - Kid: cfg.EAB_KID, - HmacEncoded: cfg.EAB_HMAC, - }) - if err != nil { - log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") + // accept terms & log in to EAB + if cfg.EAB_KID == "" || cfg.EAB_HMAC == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.AcceptTerms}) + if err != nil { + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") + } else { + myAcmeAccount.Registration = reg + } } else { - myAcmeAccount.Registration = reg + reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: cfg.AcceptTerms, + Kid: cfg.EAB_KID, + HmacEncoded: cfg.EAB_HMAC, + }) + if err != nil { + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") + } else { + myAcmeAccount.Registration = reg + } } - } - if myAcmeAccount.Registration != nil { - acmeAccountJSON, err := json.Marshal(myAcmeAccount) - if err != nil { - log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") - select {} - } - log.Info().Msgf("new acme account created. write to config file '%s'", cfg.AccountConfigFile) - err = os.WriteFile(cfg.AccountConfigFile, acmeAccountJSON, 0o600) - if err != nil { - log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") - select {} + if myAcmeAccount.Registration != nil { + acmeAccountJSON, err := json.Marshal(myAcmeAccount) + if err != nil { + log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") + select {} + } + log.Info().Msgf("new acme account created. write to config file '%s'", cfg.AccountConfigFile) + err = os.WriteFile(cfg.AccountConfigFile, acmeAccountJSON, 0o600) + if err != nil { + log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") + select {} + } } } }