diff --git a/acme/client.go b/acme/client.go index c11a0b2e0..9e7e9c5c0 100644 --- a/acme/client.go +++ b/acme/client.go @@ -9,6 +9,7 @@ import ( "os" "time" + "github.com/miekg/dns" "github.com/sirupsen/logrus" ) @@ -49,9 +50,8 @@ func MustClientFromContext(ctx context.Context) Client { } type client struct { - http *http.Client - dialer *net.Dialer - resolver *net.Resolver + http *http.Client + dialer *net.Dialer } // NewClient returns an implementation of Client for verifying ACME challenges. @@ -70,31 +70,69 @@ func NewClient() Client { dialer: &net.Dialer{ Timeout: 30 * time.Second, }, - resolver: getResolver(), } } -func getResolver() *net.Resolver { - if os.Getenv("DNS_RESOLVER") != "" { - logrus.Infof("Using custom DNS resolver: %s", os.Getenv("DNS_RESOLVER")) - return &net.Resolver{ - PreferGo: true, - Dial: func(ctx context.Context, network, address string) (net.Conn, error) { - d := net.Dialer{ - Timeout: time.Millisecond * time.Duration(10000), - } - return d.DialContext(ctx, network, fmt.Sprintf("%s:53", os.Getenv("DNS_RESOLVER"))) - }} - } - return net.DefaultResolver -} - func (c *client) Get(url string) (*http.Response, error) { return c.http.Get(url) } +var timeouts [5]time.Duration = [5]time.Duration{(time.Second * 1), (time.Second * 1), (time.Second * 2), (time.Second * 4), (time.Second * 2)} + +func ResolveWithTimeout(name, resolver string) (*dns.Msg, error) { + client := new(dns.Client) + msg := &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Id: dns.Id(), + RecursionDesired: true, + }, + Question: []dns.Question{{Name: dns.Fqdn(name), Qtype: dns.TypeTXT, Qclass: dns.ClassINET}}, + } + msg.AuthenticatedData = true + msg.SetEdns0(4096, true) + + for i := 0; i < len(timeouts); i++ { + + client.Timeout = timeouts[i] + resp, _, err := client.Exchange(msg, fmt.Sprintf("%s:53", resolver)) + if err == nil && resp.Truncated { + tcpConn, _ := dns.Dial("tcp", fmt.Sprintf("%s:53", resolver)) + resp, _, err = client.ExchangeWithConn(msg, tcpConn) + } + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + logrus.Warnf("Timeout querying %s records '%s' after %v", dns.TypeToString[dns.TypeTXT], name, timeouts[i]) + continue + } + return nil, err + } + + return resp, nil + + } + return nil, &net.DNSError{ + Name: name, + Err: "Final timeout.", + IsTimeout: true, + } +} + func (c *client) LookupTxt(name string) ([]string, error) { - return c.resolver.LookupTXT(context.Background(), name) + resolver := os.Getenv("DNS_RESOLVER") + if resolver != "" { + resp, err := ResolveWithTimeout(name, resolver) + if err != nil { + return nil, err + } + data := []string{} + for _, answer := range resp.Answer { + if txt, ok := answer.(*dns.TXT); ok { + data = append(data, txt.Txt...) + } + } + return data, nil + } + return c.LookupTxt(name) } func (c *client) TLSDial(network, addr string, config *tls.Config) (*tls.Conn, error) { diff --git a/go.mod b/go.mod index a3dba7aef..46d01537d 100644 --- a/go.mod +++ b/go.mod @@ -171,11 +171,13 @@ require ( go.opentelemetry.io/otel/metric v1.27.0 // indirect go.opentelemetry.io/otel/trace v1.27.0 // indirect go.opentelemetry.io/proto/otlp v1.2.0 // indirect + golang.org/x/mod v0.18.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect @@ -187,4 +189,5 @@ replace github.com/smallstep/nosql => github.com/hm-edu/nosql v0.4.1-0.202405061 require ( github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/gorilla/websocket v1.5.0 // indirect + github.com/miekg/dns v1.1.61 ) diff --git a/go.sum b/go.sum index be61176a9..e4a7c0757 100644 --- a/go.sum +++ b/go.sum @@ -372,6 +372,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -564,6 +566,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -663,6 +667,8 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=