feat: refactor DNS handling in providers to improve zone ID management and enhance ECH DNS record synchronization
Kim committed
May 8, 2026 at 17:29 UTC
8669f74e239f08f06bbad838fb6d0250c32848ec
7 files changed
+209
-62
portal/acme/cloudflare/provider.go
+38
-14
@@ -7,6 +7,7 @@ import (
7
"net/http"
8
"net/url"
9
"strings"
10
+ "sync"
11
12
"github.com/go-acme/lego/v4/challenge"
13
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
@@ -20,6 +21,9 @@ const (
21
22
type Provider struct {
23
token string
24
+
25
+ zoneMu sync.RWMutex
26
+ zones map[string]string
27
}
28
29
type apiError struct {
@@ -117,7 +121,7 @@ func (p *Provider) EnsureARecords(ctx context.Context, baseDomain, publicIPv4 st
121
}
122
publicIPv4 = strings.TrimSpace(publicIPv4)
123
120
- zoneID, err := findZoneID(ctx, p.token, baseDomain)
124
+ zoneID, err := p.findZoneID(ctx, baseDomain)
125
if err != nil {
126
return fmt.Errorf("find cloudflare zone: %w", err)
127
}
@@ -146,7 +150,7 @@ func (p *Provider) EnsureARecord(ctx context.Context, name, publicIPv4 string) e
150
}
151
publicIPv4 = strings.TrimSpace(publicIPv4)
152
149
- zoneID, err := findZoneID(ctx, p.token, name)
153
+ zoneID, err := p.findZoneID(ctx, name)
154
if err != nil {
155
return fmt.Errorf("find cloudflare zone: %w", err)
156
}
@@ -168,7 +172,7 @@ func (p *Provider) DeleteARecord(ctx context.Context, name string) error {
172
return errors.New("cloudflare token is required")
173
}
174
171
- zoneID, err := findZoneID(ctx, p.token, name)
175
+ zoneID, err := p.findZoneID(ctx, name)
176
if err != nil {
177
return fmt.Errorf("find cloudflare zone: %w", err)
178
}
@@ -204,7 +208,7 @@ func (p *Provider) EnsureTXTRecord(ctx context.Context, name, value string) erro
208
return errors.New("txt record value is required")
209
}
210
207
- zoneID, err := findZoneID(ctx, p.token, name)
211
+ zoneID, err := p.findZoneID(ctx, name)
212
if err != nil {
213
return fmt.Errorf("find cloudflare zone: %w", err)
214
}
@@ -230,7 +234,7 @@ func (p *Provider) DeleteTXTRecords(ctx context.Context, name, matchPrefix strin
234
return errors.New("txt record match prefix is required")
235
}
236
233
- zoneID, err := findZoneID(ctx, p.token, name)
237
+ zoneID, err := p.findZoneID(ctx, name)
238
if err != nil {
239
return fmt.Errorf("find cloudflare zone: %w", err)
240
}
@@ -274,7 +278,7 @@ func (p *Provider) EnsureHTTPSRecord(ctx context.Context, name string, priority
278
return errors.New("https record content is required")
279
}
280
277
- zoneID, err := findZoneID(ctx, p.token, name)
281
+ zoneID, err := p.findZoneID(ctx, name)
282
if err != nil {
283
return fmt.Errorf("find cloudflare zone: %w", err)
284
}
@@ -296,7 +300,7 @@ func (p *Provider) DeleteHTTPSRecord(ctx context.Context, name string) error {
300
return errors.New("cloudflare token is required")
301
}
302
299
- zoneID, err := findZoneID(ctx, p.token, name)
303
+ zoneID, err := p.findZoneID(ctx, name)
304
if err != nil {
305
return fmt.Errorf("find cloudflare zone: %w", err)
306
}
@@ -328,7 +332,7 @@ func (p *Provider) EnsureDNSSEC(ctx context.Context, baseDomain string) (state,
332
return "", "", "", errors.New("cloudflare token is required")
333
}
334
331
- zoneID, err := findZoneID(ctx, p.token, baseDomain)
335
+ zoneID, err := p.findZoneID(ctx, baseDomain)
336
if err != nil {
337
return "", "", "", fmt.Errorf("find cloudflare zone: %w", err)
338
}
@@ -358,17 +362,37 @@ func (p *Provider) EnsureDNSSEC(ctx context.Context, baseDomain string) (state,
362
return state, dsRecord, message, nil
363
}
364
361
-func findZoneID(ctx context.Context, token, domain string) (string, error) {
362
- parts := strings.Split(domain, ".")
363
- for i := range len(parts) - 1 {
364
- candidate := strings.Join(parts[i:], ".")
365
- zones, err := listZones(ctx, token, candidate)
365
+func (p *Provider) findZoneID(ctx context.Context, domain string) (string, error) {
366
+ domain = utils.NormalizeHostname(domain)
367
+ candidates := utils.DomainCandidates(domain)
368
+
369
+ p.zoneMu.RLock()
370
+ for _, candidate := range candidates {
371
+ if zoneID := p.zones[candidate]; zoneID != "" {
372
+ p.zoneMu.RUnlock()
373
+ return zoneID, nil
374
+ }
375
+ }
376
+ p.zoneMu.RUnlock()
377
+
378
+ for _, candidate := range candidates {
379
+ zones, err := listZones(ctx, p.token, candidate)
380
if err != nil {
381
return "", err
382
}
383
for _, z := range zones {
384
if strings.EqualFold(z.Name, candidate) {
371
- return z.ID, nil
385
+ zoneID := strings.TrimSpace(z.ID)
386
+ if zoneID == "" {
387
+ continue
388
+ }
389
+ p.zoneMu.Lock()
390
+ if p.zones == nil {
391
+ p.zones = make(map[string]string)
392
+ }
393
+ p.zones[utils.NormalizeHostname(z.Name)] = zoneID
394
+ p.zoneMu.Unlock()
395
+ return zoneID, nil
396
}
397
}
398
}
portal/acme/gcloud/provider.go
+48
-14
@@ -7,6 +7,7 @@ import (
7
"net/http"
8
"strconv"
9
"strings"
10
+ "sync"
11
"time"
12
13
"cloud.google.com/go/compute/metadata"
@@ -31,7 +32,9 @@ type Config struct {
32
}
33
34
type Provider struct {
34
- cfg Config
35
+ cfg Config
36
+ zoneMu sync.RWMutex
37
+ zones map[string]string
38
}
39
40
type runtimeConfig struct {
@@ -87,7 +90,7 @@ func (p *Provider) EnsureARecords(ctx context.Context, baseDomain, publicIPv4 st
90
return err
91
}
92
90
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, baseDomain)
93
+ service, runtimeCfg, zone, err := p.newService(ctx, baseDomain)
94
if err != nil {
95
return err
96
}
@@ -117,7 +120,7 @@ func (p *Provider) EnsureARecord(ctx context.Context, name, publicIPv4 string) e
120
return err
121
}
122
120
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
123
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
124
if err != nil {
125
return err
126
}
@@ -142,7 +145,7 @@ func (p *Provider) DeleteARecord(ctx context.Context, name string) error {
145
return errors.New("record name is required")
146
}
147
145
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
148
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
149
if err != nil {
150
return err
151
}
@@ -175,7 +178,7 @@ func (p *Provider) EnsureTXTRecord(ctx context.Context, name, value string) erro
178
return errors.New("txt record value is required")
179
}
180
178
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
181
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
182
if err != nil {
183
return err
184
}
@@ -229,7 +232,7 @@ func (p *Provider) DeleteTXTRecords(ctx context.Context, name, matchPrefix strin
232
return errors.New("txt record match prefix is required")
233
}
234
232
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
235
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
236
if err != nil {
237
return err
238
}
@@ -299,7 +302,7 @@ func (p *Provider) EnsureHTTPSRecord(ctx context.Context, name string, _ uint16,
302
return errors.New("https record content is required")
303
}
304
302
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
305
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
306
if err != nil {
307
return err
308
}
@@ -324,7 +327,7 @@ func (p *Provider) DeleteHTTPSRecord(ctx context.Context, name string) error {
327
return errors.New("record name is required")
328
}
329
327
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, name)
330
+ service, runtimeCfg, zone, err := p.newService(ctx, name)
331
if err != nil {
332
return err
333
}
@@ -353,11 +356,15 @@ func (p *Provider) EnsureDNSSEC(ctx context.Context, baseDomain string) (state,
356
return "", "", "", errors.New("base domain is required")
357
}
358
356
- service, runtimeCfg, zone, err := newService(ctx, p.cfg, baseDomain)
359
+ service, runtimeCfg, zone, err := p.newService(ctx, baseDomain)
360
if err != nil {
361
return "", "", "", err
362
}
363
managedZone := zone.Name
364
+ zone, err = service.ManagedZones.Get(runtimeCfg.ProjectID, managedZone).Context(ctx).Do()
365
+ if err != nil {
366
+ return "", "", "", fmt.Errorf("get gcloud managed zone %s: %w", managedZone, err)
367
+ }
368
369
currentState := strings.ToLower(strings.TrimSpace(dnssecState(zone)))
370
if currentState != "on" && currentState != "transfer" {
@@ -404,8 +411,8 @@ func newRuntimeConfig(ctx context.Context, cfg Config) (runtimeConfig, error) {
411
}, nil
412
}
413
407
-func newService(ctx context.Context, cfg Config, domain string) (*dns.Service, runtimeConfig, *dns.ManagedZone, error) {
408
- runtimeCfg, err := newRuntimeConfig(ctx, cfg)
414
+func (p *Provider) newService(ctx context.Context, domain string) (*dns.Service, runtimeConfig, *dns.ManagedZone, error) {
415
+ runtimeCfg, err := newRuntimeConfig(ctx, p.cfg)
416
if err != nil {
417
return nil, runtimeConfig{}, nil, err
418
}
@@ -415,17 +422,28 @@ func newService(ctx context.Context, cfg Config, domain string) (*dns.Service, r
422
return nil, runtimeConfig{}, nil, fmt.Errorf("create gcloud dns service: %w", err)
423
}
424
418
- zone, err := findManagedZone(ctx, service, runtimeCfg.ProjectID, domain, runtimeCfg.ManagedZone)
425
+ zone, err := p.findManagedZone(ctx, service, runtimeCfg.ProjectID, domain, runtimeCfg.ManagedZone)
426
if err != nil {
427
return nil, runtimeConfig{}, nil, err
428
}
429
return service, runtimeCfg, zone, nil
430
}
431
425
-func findManagedZone(ctx context.Context, service *dns.Service, projectID, domain, explicit string) (*dns.ManagedZone, error) {
432
+func (p *Provider) findManagedZone(ctx context.Context, service *dns.Service, projectID, domain, explicit string) (*dns.ManagedZone, error) {
433
if service == nil {
434
return nil, errors.New("gcloud dns service is nil")
435
}
436
+ domain = utils.NormalizeHostname(domain)
437
+ candidates := utils.DomainCandidates(domain)
438
+
439
+ p.zoneMu.RLock()
440
+ for _, candidate := range candidates {
441
+ if zoneName := p.zones[candidate]; zoneName != "" {
442
+ p.zoneMu.RUnlock()
443
+ return &dns.ManagedZone{Name: zoneName, DnsName: fqdn(candidate)}, nil
444
+ }
445
+ }
446
+ p.zoneMu.RUnlock()
447
448
if explicit = strings.TrimSpace(explicit); explicit != "" {
449
zone, err := service.ManagedZones.Get(projectID, explicit).Context(ctx).Do()
@@ -435,10 +453,18 @@ func findManagedZone(ctx context.Context, service *dns.Service, projectID, domai
453
if err := validateManagedZone(zone, domain, explicit); err != nil {
454
return nil, err
455
}
456
+ if zoneDomain := utils.NormalizeHostname(zone.DnsName); zoneDomain != "" && zone.Name != "" {
457
+ p.zoneMu.Lock()
458
+ if p.zones == nil {
459
+ p.zones = make(map[string]string)
460
+ }
461
+ p.zones[zoneDomain] = zone.Name
462
+ p.zoneMu.Unlock()
463
+ }
464
return zone, nil
465
}
466
441
- for _, candidate := range utils.DomainCandidates(domain) {
467
+ for _, candidate := range candidates {
468
out, err := service.ManagedZones.List(projectID).DnsName(fqdn(candidate)).Context(ctx).Do()
469
if err != nil {
470
return nil, fmt.Errorf("list gcloud managed zones: %w", err)
@@ -447,6 +473,14 @@ func findManagedZone(ctx context.Context, service *dns.Service, projectID, domai
473
if !isPublicZone(zone) || utils.NormalizeHostname(zone.DnsName) != candidate {
474
continue
475
}
476
+ if zone.Name != "" {
477
+ p.zoneMu.Lock()
478
+ if p.zones == nil {
479
+ p.zones = make(map[string]string)
480
+ }
481
+ p.zones[candidate] = zone.Name
482
+ p.zoneMu.Unlock()
483
+ }
484
return zone, nil
485
}
486
}
portal/acme/route53/provider.go
+34
-11
@@ -6,6 +6,7 @@ import (
6
"fmt"
7
"strconv"
8
"strings"
9
+ "sync"
10
"time"
11
12
"github.com/aws/aws-sdk-go-v2/aws"
@@ -34,7 +35,9 @@ type Config struct {
35
}
36
37
type Provider struct {
37
- cfg Config
38
+ cfg Config
39
+ zoneMu sync.RWMutex
40
+ zones map[string]string
41
}
42
43
func New(cfg Config) *Provider {
@@ -93,7 +96,7 @@ func (p *Provider) EnsureARecords(ctx context.Context, baseDomain, publicIPv4 st
96
return err
97
}
98
96
- hostedZoneID, err := findHostedZoneID(ctx, client, baseDomain, p.cfg.HostedZoneID)
99
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, baseDomain)
100
if err != nil {
101
return err
102
}
@@ -123,7 +126,7 @@ func (p *Provider) EnsureARecord(ctx context.Context, name, publicIPv4 string) e
126
return err
127
}
128
126
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
129
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
130
if err != nil {
131
return err
132
}
@@ -147,7 +150,7 @@ func (p *Provider) DeleteARecord(ctx context.Context, name string) error {
150
return err
151
}
152
150
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
153
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
154
if err != nil {
155
return err
156
}
@@ -182,7 +185,7 @@ func (p *Provider) EnsureTXTRecord(ctx context.Context, name, value string) erro
185
return err
186
}
187
185
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
188
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
189
if err != nil {
190
return err
191
}
@@ -210,7 +213,7 @@ func (p *Provider) DeleteTXTRecords(ctx context.Context, name, matchPrefix strin
213
return err
214
}
215
213
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
216
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
217
if err != nil {
218
return err
219
}
@@ -238,7 +241,7 @@ func (p *Provider) EnsureHTTPSRecord(ctx context.Context, name string, _ uint16,
241
return err
242
}
243
241
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
244
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
245
if err != nil {
246
return err
247
}
@@ -262,7 +265,7 @@ func (p *Provider) DeleteHTTPSRecord(ctx context.Context, name string) error {
265
return err
266
}
267
265
- hostedZoneID, err := findHostedZoneID(ctx, client, name, p.cfg.HostedZoneID)
268
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, name)
269
if err != nil {
270
return err
271
}
@@ -293,7 +296,7 @@ func (p *Provider) EnsureDNSSEC(ctx context.Context, baseDomain string) (state,
296
return "", "", "", err
297
}
298
296
- hostedZoneID, err := findHostedZoneID(ctx, client, baseDomain, p.cfg.HostedZoneID)
299
+ hostedZoneID, err := p.findHostedZoneID(ctx, client, baseDomain)
300
if err != nil {
301
return "", "", "", err
302
}
@@ -355,8 +358,8 @@ func newClient(ctx context.Context, cfg Config) (*awsroute53.Client, error) {
358
return awsroute53.NewFromConfig(awsCfg), nil
359
}
360
358
-func findHostedZoneID(ctx context.Context, client *awsroute53.Client, domain, explicitZoneID string) (string, error) {
359
- if explicitZoneID = normalizeZoneID(explicitZoneID); explicitZoneID != "" {
361
+func (p *Provider) findHostedZoneID(ctx context.Context, client *awsroute53.Client, domain string) (string, error) {
362
+ if explicitZoneID := normalizeZoneID(p.cfg.HostedZoneID); explicitZoneID != "" {
363
return explicitZoneID, nil
364
}
365
if client == nil {
@@ -368,6 +371,15 @@ func findHostedZoneID(ctx context.Context, client *awsroute53.Client, domain, ex
371
return "", fmt.Errorf("invalid base domain for hosted zone lookup: %q", domain)
372
}
373
374
+ p.zoneMu.RLock()
375
+ for _, candidate := range candidates {
376
+ if zoneID := p.zones[candidate]; zoneID != "" {
377
+ p.zoneMu.RUnlock()
378
+ return zoneID, nil
379
+ }
380
+ }
381
+ p.zoneMu.RUnlock()
382
+
383
zonesByName := make(map[string]string)
384
paginator := awsroute53.NewListHostedZonesPaginator(client, &awsroute53.ListHostedZonesInput{})
385
for paginator.HasMorePages() {
@@ -388,6 +400,17 @@ func findHostedZoneID(ctx context.Context, client *awsroute53.Client, domain, ex
400
}
401
}
402
403
+ if len(zonesByName) > 0 {
404
+ p.zoneMu.Lock()
405
+ if p.zones == nil {
406
+ p.zones = make(map[string]string)
407
+ }
408
+ for zoneName, zoneID := range zonesByName {
409
+ p.zones[zoneName] = zoneID
410
+ }
411
+ p.zoneMu.Unlock()
412
+ }
413
+
414
for _, candidate := range candidates {
415
if zoneID, ok := zonesByName[candidate]; ok {
416
return zoneID, nil
portal/acme/route53/provider_test.go
+1
-1
@@ -8,7 +8,7 @@ import (
8
func TestFindHostedZoneIDExplicitOverride(t *testing.T) {
9
t.Parallel()
10
11
- got, err := findHostedZoneID(context.Background(), nil, "portal.example.com", "/hostedzone/Z123456789")
11
+ got, err := New(Config{HostedZoneID: "/hostedzone/Z123456789"}).findHostedZoneID(context.Background(), nil, "portal.example.com")
12
if err != nil {
13
t.Fatalf("findHostedZoneID() error = %v", err)
14
}
portal/api_server.go
+6
-4
@@ -284,7 +284,7 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
284
return
285
}
286
dnsCtx, cancel := context.WithTimeout(context.WithoutCancel(r.Context()), defaultClaimTimeout)
287
- deleteECHOnRollback, err := record.syncDNS(dnsCtx, s.acmeManager, s.cfg.SNIPort)
287
+ err = record.syncENSGaslessDNS(dnsCtx, s.acmeManager)
288
cancel()
289
if err != nil {
290
removed, _ := s.registry.Unregister(types.UnregisterRequest{AccessToken: resp.AccessToken})
@@ -293,11 +293,12 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
293
removed = record
294
}
295
cleanupCtx, cleanupCancel := context.WithTimeout(context.WithoutCancel(r.Context()), defaultClaimTimeout)
296
- removed.deleteDNS(cleanupCtx, s.acmeManager, deleteECHOnRollback)
296
+ removed.deleteDNS(cleanupCtx, s.acmeManager, false)
297
cleanupCancel()
298
writeAPIErrorResponse(w, err)
299
return
300
}
301
+ s.registry.promoteECHDNS(record, s.acmeManager, s.cfg.SNIPort)
302
303
utils.WriteAPIData(w, http.StatusCreated, resp)
304
}
@@ -468,7 +469,7 @@ func (s *Server) handleHop(w http.ResponseWriter, r *http.Request) {
469
return
470
}
471
dnsCtx, cancel := context.WithTimeout(context.WithoutCancel(r.Context()), defaultClaimTimeout)
471
- deleteECHOnRollback, err := record.syncDNS(dnsCtx, s.acmeManager, s.cfg.SNIPort)
472
+ err = record.syncENSGaslessDNS(dnsCtx, s.acmeManager)
473
cancel()
474
if err != nil {
475
removed := s.registry.DeleteHopRoute(&route)
@@ -476,11 +477,12 @@ func (s *Server) handleHop(w http.ResponseWriter, r *http.Request) {
477
removed = record
478
}
479
cleanupCtx, cleanupCancel := context.WithTimeout(context.WithoutCancel(r.Context()), defaultClaimTimeout)
479
- removed.deleteDNS(cleanupCtx, s.acmeManager, deleteECHOnRollback)
480
+ removed.deleteDNS(cleanupCtx, s.acmeManager, false)
481
cleanupCancel()
482
writeAPIErrorResponse(w, err)
483
return
484
}
485
+ s.registry.promoteECHDNS(record, s.acmeManager, s.cfg.SNIPort)
486
var accessToken string
487
if record.isPublicEntry() {
488
accessToken, err = s.registry.issueLeaseAccessToken(record, now)
portal/lease.go
+55
@@ -2,6 +2,7 @@ package portal
2
3
import (
4
"bytes"
5
+ "context"
6
"errors"
7
"fmt"
8
"net"
@@ -10,6 +11,9 @@ import (
11
"sync"
12
"time"
13
14
+ "github.com/rs/zerolog/log"
15
+
16
+ "github.com/gosuda/portal-tunnel/v2/portal/acme"
17
"github.com/gosuda/portal-tunnel/v2/portal/auth"
18
"github.com/gosuda/portal-tunnel/v2/portal/keyless"
19
"github.com/gosuda/portal-tunnel/v2/portal/policy"
@@ -647,6 +651,57 @@ func (r *leaseRegistry) DeleteHopRoute(route *types.HopRoute) *leaseRecord {
651
return deleted
652
}
653
654
+func (r *leaseRegistry) promoteECHDNS(record *leaseRecord, manager *acme.Manager, sniPort int) {
655
+ if !record.hasECHDNSRecord() {
656
+ return
657
+ }
658
+
659
+ go func() {
660
+ active := false
661
+ now := time.Now()
662
+ r.mu.RLock()
663
+ for _, existing := range r.records {
664
+ if existing == record && !existing.isExpired(now) {
665
+ active = true
666
+ break
667
+ }
668
+ }
669
+ r.mu.RUnlock()
670
+ if !active {
671
+ return
672
+ }
673
+
674
+ ctx, cancel := context.WithTimeout(context.Background(), defaultClaimTimeout)
675
+ err := record.syncECHDNS(ctx, manager, sniPort)
676
+ cancel()
677
+
678
+ if err != nil {
679
+ log.Warn().
680
+ Err(err).
681
+ Str("hostname", record.ECHDNSHostname).
682
+ Str("route_hostname", record.Hostname).
683
+ Str("address", record.Address).
684
+ Msg("promote ech dns record")
685
+ }
686
+
687
+ hostnameActive := false
688
+ now = time.Now()
689
+ r.mu.RLock()
690
+ for _, existing := range r.records {
691
+ if existing != nil && !existing.isExpired(now) && existing.hasECHDNSRecord() && existing.ECHDNSHostname == record.ECHDNSHostname {
692
+ hostnameActive = true
693
+ break
694
+ }
695
+ }
696
+ r.mu.RUnlock()
697
+ if !hostnameActive {
698
+ cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), defaultClaimTimeout)
699
+ record.deleteECHDNS(cleanupCtx, manager)
700
+ cleanupCancel()
701
+ }
702
+ }()
703
+}
704
+
705
func (r *leaseRegistry) issueRegisterChallenge(req types.RegisterChallengeRequest, domain, uri, clientIP string) (types.RegisterChallengeResponse, error) {
706
if r == nil {
707
return types.RegisterChallengeResponse{}, errFeatureUnavailable
portal/record.go
+27
-18
@@ -132,21 +132,38 @@ func (r *leaseRecord) Close() {
132
}
133
}
134
135
-func (r *leaseRecord) syncDNS(ctx context.Context, manager *acme.Manager, sniPort int) (bool, error) {
135
+func (r *leaseRecord) syncENSGaslessDNS(ctx context.Context, manager *acme.Manager) error {
136
if r == nil || manager == nil {
137
- return false, nil
137
+ return nil
138
}
139
if ensHostname := r.ensGaslessDNSHostname(); ensHostname != "" {
140
if err := manager.SyncENSGaslessHostname(ctx, ensHostname, r.Address); err != nil {
141
- return false, err
141
+ return err
142
}
143
}
144
- if r.hasECHDNSRecord() {
145
- if err := manager.SyncECHConfig(ctx, r.ECHDNSHostname, r.ECHConfigList, sniPort); err != nil {
146
- return true, err
147
- }
144
+ return nil
145
+}
146
+
147
+func (r *leaseRecord) syncECHDNS(ctx context.Context, manager *acme.Manager, sniPort int) error {
148
+ if r == nil || manager == nil || !r.hasECHDNSRecord() {
149
+ return nil
150
+ }
151
+ return manager.SyncECHConfig(ctx, r.ECHDNSHostname, r.ECHConfigList, sniPort)
152
+}
153
+
154
+func (r *leaseRecord) deleteECHDNS(ctx context.Context, manager *acme.Manager) {
155
+ if r == nil || manager == nil || !r.hasECHDNSRecord() {
156
+ return
157
+ }
158
+ err := manager.DeleteECHConfig(ctx, r.ECHDNSHostname)
159
+ if err != nil {
160
+ log.Warn().
161
+ Err(err).
162
+ Str("hostname", r.ECHDNSHostname).
163
+ Str("route_hostname", r.Hostname).
164
+ Str("address", r.Address).
165
+ Msg("delete ech dns record")
166
}
149
- return false, nil
167
}
168
169
func (r *leaseRecord) deleteDNS(ctx context.Context, manager *acme.Manager, includeECH bool) {
@@ -163,15 +180,7 @@ func (r *leaseRecord) deleteDNS(ctx context.Context, manager *acme.Manager, incl
180
Msg("delete ens gasless hostname")
181
}
182
}
166
- if includeECH && r.hasECHDNSRecord() {
167
- err := manager.DeleteECHConfig(ctx, r.ECHDNSHostname)
168
- if err != nil {
169
- log.Warn().
170
- Err(err).
171
- Str("hostname", r.ECHDNSHostname).
172
- Str("route_hostname", r.Hostname).
173
- Str("address", r.Address).
174
- Msg("delete ech dns record")
175
- }
183
+ if includeECH {
184
+ r.deleteECHDNS(ctx, manager)
185
}
186
}