fix(acme): stop double-normalizing DNS-01 challenge params
YoonHyunWoo committed
Feb 26, 2026 at 00:00 UTC
9b33c068b693cea19e60ca6139fa1c2f0f262cee
2 files changed
+13
-52
portal/utils/cert/acme.go
+9
-47
@@ -6,9 +6,7 @@ import (
6
"crypto/ecdsa"
7
"crypto/elliptic"
8
"crypto/rand"
9
- "crypto/sha256"
9
"crypto/x509"
11
- "encoding/base64"
10
"encoding/pem"
11
"fmt"
12
"os"
@@ -17,7 +15,6 @@ import (
15
"time"
16
17
"github.com/go-acme/lego/v4/certificate"
20
- "github.com/go-acme/lego/v4/challenge"
18
"github.com/go-acme/lego/v4/lego"
19
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
20
"github.com/go-acme/lego/v4/providers/dns/route53"
@@ -82,8 +79,7 @@ func NewACMEManager(ctx context.Context, cfg *ACMEConfig) (*ACMEManager, error)
79
}
80
81
// Set up DNS-01 challenge
85
- provider := &dnsProviderAdapter{dnsProvider: dnsProvider}
86
- if err := client.Challenge.SetDNS01Provider(provider); err != nil {
82
+ if err := client.Challenge.SetDNS01Provider(dnsProvider); err != nil {
83
return nil, fmt.Errorf("set DNS01 provider: %w", err)
84
}
85
@@ -274,51 +270,17 @@ func (u *acmeUser) GetPrivateKey() crypto.PrivateKey {
270
return u.key
271
}
272
277
-// dnsProviderAdapter adapts our DNSProvider to lego's challenge.Provider interface
278
-type dnsProviderAdapter struct {
279
- dnsProvider DNSProvider
280
-}
281
-
282
-func (a *dnsProviderAdapter) Present(domain, token, keyAuth string) error {
283
- fqdn, value := extractDNS01Record(domain, keyAuth)
284
- return a.dnsProvider.Present(context.Background(), fqdn, value)
285
-}
286
-
287
-func (a *dnsProviderAdapter) CleanUp(domain, token, keyAuth string) error {
288
- fqdn, value := extractDNS01Record(domain, keyAuth)
289
- return a.dnsProvider.CleanUp(context.Background(), fqdn, value)
290
-}
291
-
292
-func (a *dnsProviderAdapter) Timeout() (timeout, interval time.Duration) {
293
- return a.dnsProvider.Timeout()
294
-}
295
-
296
-// extractDNS01Record computes the FQDN and value for DNS-01 challenge
297
-func extractDNS01Record(domain, keyAuth string) (fqdn, value string) {
298
- // DNS-01 challenge uses _acme-challenge subdomain
299
- fqdn = "_acme-challenge." + domain
300
-
301
- // Value is base64url-encoded SHA256 of keyAuth
302
- h := sha256.Sum256([]byte(keyAuth))
303
- value = base64.RawURLEncoding.EncodeToString(h[:])
304
-
305
- return fqdn, value
306
-}
307
-
308
-// Ensure dnsProviderAdapter implements challenge.Provider
309
-var _ challenge.Provider = (*dnsProviderAdapter)(nil)
310
-
273
// cloudflareProviderAdapter adapts cloudflare provider to our DNSProvider interface
274
type cloudflareProviderAdapter struct {
275
provider *cloudflare.DNSProvider
276
}
277
316
-func (a *cloudflareProviderAdapter) Present(ctx context.Context, fqdn, value string) error {
317
- return a.provider.Present(fqdn, "", value)
278
+func (a *cloudflareProviderAdapter) Present(domain, token, keyAuth string) error {
279
+ return a.provider.Present(domain, token, keyAuth)
280
}
281
320
-func (a *cloudflareProviderAdapter) CleanUp(ctx context.Context, fqdn, value string) error {
321
- return a.provider.CleanUp(fqdn, "", value)
282
+func (a *cloudflareProviderAdapter) CleanUp(domain, token, keyAuth string) error {
283
+ return a.provider.CleanUp(domain, token, keyAuth)
284
}
285
286
func (a *cloudflareProviderAdapter) Timeout() (timeout, interval time.Duration) {
@@ -330,12 +292,12 @@ type route53ProviderAdapter struct {
292
provider *route53.DNSProvider
293
}
294
333
-func (a *route53ProviderAdapter) Present(ctx context.Context, fqdn, value string) error {
334
- return a.provider.Present(fqdn, "", value)
295
+func (a *route53ProviderAdapter) Present(domain, token, keyAuth string) error {
296
+ return a.provider.Present(domain, token, keyAuth)
297
}
298
337
-func (a *route53ProviderAdapter) CleanUp(ctx context.Context, fqdn, value string) error {
338
- return a.provider.CleanUp(fqdn, "", value)
299
+func (a *route53ProviderAdapter) CleanUp(domain, token, keyAuth string) error {
300
+ return a.provider.CleanUp(domain, token, keyAuth)
301
}
302
303
func (a *route53ProviderAdapter) Timeout() (timeout, interval time.Duration) {
portal/utils/cert/cert.go
+4
-5
@@ -29,15 +29,14 @@ type Manager interface {
29
GetCACertificate(ctx context.Context) ([]byte, error)
30
}
31
32
-// DNSProvider handles DNS record management for ACME DNS-01 challenges.
32
+// DNSProvider handles DNS-01 challenge lifecycle.
33
+// Signature matches lego's challenge.Provider.
34
type DNSProvider interface {
35
// Present creates a TXT record for the DNS-01 challenge.
35
- // fqdn is the full domain name (e.g., "_acme-challenge.app1.portal.com")
36
- // value is the challenge token.
37
- Present(ctx context.Context, fqdn, value string) error
36
+ Present(domain, token, keyAuth string) error
37
38
// CleanUp removes the TXT record after the challenge is complete.
40
- CleanUp(ctx context.Context, fqdn, value string) error
39
+ CleanUp(domain, token, keyAuth string) error
40
41
// Timeout returns the timeout and interval for DNS propagation checking.
42
Timeout() (timeout time.Duration, interval time.Duration)