Skip to content

Commit 98d2930

Browse files
committed
Improve DNS related logging
1 parent 6095ab8 commit 98d2930

File tree

3 files changed

+76
-25
lines changed

3 files changed

+76
-25
lines changed

dnsutil.go

+18-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"time"
1010

1111
"github.com/miekg/dns"
12+
"go.uber.org/zap"
1213
)
1314

1415
// Code in this file adapted from go-acme/lego, July 2020:
@@ -19,19 +20,21 @@ import (
1920

2021
// findZoneByFQDN determines the zone apex for the given fqdn by recursing
2122
// up the domain labels until the nameserver returns a SOA record in the
22-
// answer section.
23-
func findZoneByFQDN(fqdn string, nameservers []string) (string, error) {
23+
// answer section. The logger must be non-nil.
24+
func findZoneByFQDN(logger *zap.Logger, fqdn string, nameservers []string) (string, error) {
2425
if !strings.HasSuffix(fqdn, ".") {
2526
fqdn += "."
2627
}
27-
soa, err := lookupSoaByFqdn(fqdn, nameservers)
28+
soa, err := lookupSoaByFqdn(logger, fqdn, nameservers)
2829
if err != nil {
2930
return "", err
3031
}
3132
return soa.zone, nil
3233
}
3334

34-
func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
35+
func lookupSoaByFqdn(logger *zap.Logger, fqdn string, nameservers []string) (*soaCacheEntry, error) {
36+
logger = logger.Named("soa_lookup")
37+
3538
if !strings.HasSuffix(fqdn, ".") {
3639
fqdn += "."
3740
}
@@ -41,10 +44,11 @@ func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error)
4144

4245
// prefer cached version if fresh
4346
if ent := fqdnSOACache[fqdn]; ent != nil && !ent.isExpired() {
47+
logger.Debug("using cached SOA result", zap.String("entry", ent.zone))
4448
return ent, nil
4549
}
4650

47-
ent, err := fetchSoaByFqdn(fqdn, nameservers)
51+
ent, err := fetchSoaByFqdn(logger, fqdn, nameservers)
4852
if err != nil {
4953
return nil, err
5054
}
@@ -62,7 +66,7 @@ func lookupSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error)
6266
return ent, nil
6367
}
6468

65-
func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
69+
func fetchSoaByFqdn(logger *zap.Logger, fqdn string, nameservers []string) (*soaCacheEntry, error) {
6670
var err error
6771
var in *dns.Msg
6872

@@ -77,6 +81,7 @@ func fetchSoaByFqdn(fqdn string, nameservers []string) (*soaCacheEntry, error) {
7781
if in == nil {
7882
continue
7983
}
84+
logger.Debug("fetched SOA", zap.String("msg", in.String()))
8085

8186
switch in.Rcode {
8287
case dns.RcodeSuccess:
@@ -211,7 +216,9 @@ func populateNameserverPorts(servers []string) {
211216
}
212217

213218
// checkDNSPropagation checks if the expected record has been propagated to all authoritative nameservers.
214-
func checkDNSPropagation(fqdn string, recType uint16, expectedValue string, checkAuthoritativeServers bool, resolvers []string) (bool, error) {
219+
func checkDNSPropagation(logger *zap.Logger, fqdn string, recType uint16, expectedValue string, checkAuthoritativeServers bool, resolvers []string) (bool, error) {
220+
logger = logger.Named("propagation")
221+
215222
if !strings.HasSuffix(fqdn, ".") {
216223
fqdn += "."
217224
}
@@ -230,13 +237,14 @@ func checkDNSPropagation(fqdn string, recType uint16, expectedValue string, chec
230237
}
231238

232239
if checkAuthoritativeServers {
233-
authoritativeServers, err := lookupNameservers(fqdn, resolvers)
240+
authoritativeServers, err := lookupNameservers(logger, fqdn, resolvers)
234241
if err != nil {
235242
return false, fmt.Errorf("looking up authoritative nameservers: %v", err)
236243
}
237244
populateNameserverPorts(authoritativeServers)
238245
resolvers = authoritativeServers
239246
}
247+
logger.Debug("checking authoritative nameservers", zap.Strings("resolvers", resolvers))
240248

241249
return checkAuthoritativeNss(fqdn, recType, expectedValue, resolvers)
242250
}
@@ -285,10 +293,10 @@ func checkAuthoritativeNss(fqdn string, recType uint16, expectedValue string, na
285293
}
286294

287295
// lookupNameservers returns the authoritative nameservers for the given fqdn.
288-
func lookupNameservers(fqdn string, resolvers []string) ([]string, error) {
296+
func lookupNameservers(logger *zap.Logger, fqdn string, resolvers []string) ([]string, error) {
289297
var authoritativeNss []string
290298

291-
zone, err := findZoneByFQDN(fqdn, resolvers)
299+
zone, err := findZoneByFQDN(logger, fqdn, resolvers)
292300
if err != nil {
293301
return nil, fmt.Errorf("could not determine the zone for '%s': %w", fqdn, err)
294302
}

dnsutil_test.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"sort"
1414
"strings"
1515
"testing"
16+
17+
"go.uber.org/zap"
1618
)
1719

1820
func TestLookupNameserversOK(t *testing.T) {
@@ -32,7 +34,7 @@ func TestLookupNameserversOK(t *testing.T) {
3234
t.Run(test.fqdn, func(t *testing.T) {
3335
t.Parallel()
3436

35-
nss, err := lookupNameservers(test.fqdn, recursiveNameservers(nil))
37+
nss, err := lookupNameservers(zap.NewNop(), test.fqdn, recursiveNameservers(nil))
3638
if err != nil {
3739
t.Errorf("Expected no error, got: %v", err)
3840
}
@@ -66,7 +68,7 @@ func TestLookupNameserversErr(t *testing.T) {
6668
t.Run(test.desc, func(t *testing.T) {
6769
t.Parallel()
6870

69-
_, err := lookupNameservers(test.fqdn, nil)
71+
_, err := lookupNameservers(zap.NewNop(), test.fqdn, nil)
7072
if err == nil {
7173
t.Errorf("expected error, got none")
7274
}
@@ -158,7 +160,7 @@ func TestFindZoneByFqdn(t *testing.T) {
158160
}
159161
clearFqdnCache()
160162

161-
zone, err := findZoneByFQDN(test.fqdn, test.nameservers)
163+
zone, err := findZoneByFQDN(zap.NewNop(), test.fqdn, test.nameservers)
162164
if test.expectedError != "" {
163165
if err == nil {
164166
t.Errorf("test %d: expected error, got none", i)

solvers.go

+53-12
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/mholt/acmez"
3434
"github.com/mholt/acmez/acme"
3535
"github.com/miekg/dns"
36+
"go.uber.org/zap"
3637
)
3738

3839
// httpSolver solves the HTTP challenge. It must be
@@ -357,6 +358,9 @@ type DNSManager struct {
357358
// that the solver doesn't follow CNAME/NS record.
358359
OverrideDomain string
359360

361+
// An optional logger.
362+
Logger *zap.Logger
363+
360364
// Remember DNS records while challenges are active; i.e.
361365
// records we have presented and not yet cleaned up.
362366
// This lets us clean them up quickly and efficiently.
@@ -373,7 +377,9 @@ type DNSManager struct {
373377
}
374378

375379
func (m *DNSManager) createRecord(ctx context.Context, dnsName, recordType, recordValue string) (zoneRecord, error) {
376-
zone, err := findZoneByFQDN(dnsName, recursiveNameservers(m.Resolvers))
380+
logger := m.logger()
381+
382+
zone, err := findZoneByFQDN(logger, dnsName, recursiveNameservers(m.Resolvers))
377383
if err != nil {
378384
return zoneRecord{}, fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err)
379385
}
@@ -384,6 +390,14 @@ func (m *DNSManager) createRecord(ctx context.Context, dnsName, recordType, reco
384390
TTL: m.TTL,
385391
}
386392

393+
logger.Debug("creating DNS record",
394+
zap.String("dns_name", dnsName),
395+
zap.String("zone", zone),
396+
zap.String("record_name", rec.Name),
397+
zap.String("record_type", rec.Type),
398+
zap.String("record_value", rec.Value),
399+
zap.Duration("record_ttl", rec.TTL))
400+
387401
results, err := m.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec})
388402
if err != nil {
389403
return zoneRecord{}, fmt.Errorf("adding temporary record for zone %q: %w", zone, err)
@@ -398,32 +412,34 @@ func (m *DNSManager) createRecord(ctx context.Context, dnsName, recordType, reco
398412
// wait blocks until the TXT record created in Present() appears in
399413
// authoritative lookups, i.e. until it has propagated, or until
400414
// timeout, whichever is first.
401-
func (s *DNSManager) wait(ctx context.Context, zrec zoneRecord) error {
415+
func (m *DNSManager) wait(ctx context.Context, zrec zoneRecord) error {
416+
logger := m.logger()
417+
402418
// if configured to, pause before doing propagation checks
403419
// (even if they are disabled, the wait might be desirable on its own)
404-
if s.PropagationDelay > 0 {
420+
if m.PropagationDelay > 0 {
405421
select {
406-
case <-time.After(s.PropagationDelay):
422+
case <-time.After(m.PropagationDelay):
407423
case <-ctx.Done():
408424
return ctx.Err()
409425
}
410426
}
411427

412428
// skip propagation checks if configured to do so
413-
if s.PropagationTimeout == -1 {
429+
if m.PropagationTimeout == -1 {
414430
return nil
415431
}
416432

417433
// timings
418-
timeout := s.PropagationTimeout
434+
timeout := m.PropagationTimeout
419435
if timeout == 0 {
420436
timeout = defaultDNSPropagationTimeout
421437
}
422438
const interval = 2 * time.Second
423439

424440
// how we'll do the checks
425-
checkAuthoritativeServers := len(s.Resolvers) == 0
426-
resolvers := recursiveNameservers(s.Resolvers)
441+
checkAuthoritativeServers := len(m.Resolvers) == 0
442+
resolvers := recursiveNameservers(m.Resolvers)
427443

428444
recType := dns.TypeTXT
429445
if zrec.record.Type == "CNAME" {
@@ -440,8 +456,15 @@ func (s *DNSManager) wait(ctx context.Context, zrec zoneRecord) error {
440456
case <-ctx.Done():
441457
return ctx.Err()
442458
}
459+
460+
logger.Debug("checking DNS propagation",
461+
zap.String("fqdn", absName),
462+
zap.String("record_type", zrec.record.Type),
463+
zap.String("expected_value", zrec.record.Value),
464+
zap.Strings("resolvers", resolvers))
465+
443466
var ready bool
444-
ready, err = checkDNSPropagation(absName, recType, zrec.record.Value, checkAuthoritativeServers, resolvers)
467+
ready, err = checkDNSPropagation(logger, absName, recType, zrec.record.Value, checkAuthoritativeServers, resolvers)
445468
if err != nil {
446469
return fmt.Errorf("checking DNS propagation of %q (relative=%s zone=%s resolvers=%v): %w", absName, zrec.record.Name, zrec.zone, resolvers, err)
447470
}
@@ -464,25 +487,43 @@ type zoneRecord struct {
464487
// a context cancellation, and properly-implemented DNS providers should
465488
// honor cancellation, which would result in cleanup being aborted.
466489
// Cleanup must always occur.
467-
func (s *DNSManager) cleanUpRecord(_ context.Context, zrec zoneRecord) error {
490+
func (m *DNSManager) cleanUpRecord(_ context.Context, zrec zoneRecord) error {
491+
logger := m.logger()
492+
468493
// clean up the record - use a different context though, since
469494
// one common reason cleanup is performed is because a context
470495
// was canceled, and if so, any HTTP requests by this provider
471496
// should fail if the provider is properly implemented
472497
// (see issue #200)
473-
timeout := s.PropagationTimeout
498+
timeout := m.PropagationTimeout
474499
if timeout <= 0 {
475500
timeout = defaultDNSPropagationTimeout
476501
}
477502
ctx, cancel := context.WithTimeout(context.Background(), timeout)
478503
defer cancel()
479-
_, err := s.DNSProvider.DeleteRecords(ctx, zrec.zone, []libdns.Record{zrec.record})
504+
505+
logger.Debug("deleting DNS record",
506+
zap.String("zone", zrec.zone),
507+
zap.String("record_id", zrec.record.ID),
508+
zap.String("record_name", zrec.record.Name),
509+
zap.String("record_type", zrec.record.Type),
510+
zap.String("record_value", zrec.record.Value))
511+
512+
_, err := m.DNSProvider.DeleteRecords(ctx, zrec.zone, []libdns.Record{zrec.record})
480513
if err != nil {
481514
return fmt.Errorf("deleting temporary record for name %q in zone %q: %w", zrec.zone, zrec.record, err)
482515
}
483516
return nil
484517
}
485518

519+
func (m *DNSManager) logger() *zap.Logger {
520+
logger := m.Logger
521+
if logger == nil {
522+
logger = zap.NewNop()
523+
}
524+
return logger.Named("dns_manager")
525+
}
526+
486527
const defaultDNSPropagationTimeout = 2 * time.Minute
487528

488529
// dnsPresentMemory associates a created DNS record with its zone

0 commit comments

Comments
 (0)