mirror of
https://github.com/coredns/coredns.git
synced 2026-06-15 13:40:11 -04:00
feat(forward): add doh support (#8004)
* chore(pkg/proxy): prepare for DoH implementation Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore(pkg/proxy): prepare for DoH implementation Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * feat(proxy): implement basic DoH resolution Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * feat(forward): implement DoH forwarding Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * feat(proxy): add basic DoH health checker Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore: align http transport with Go's DefaultTransport and resolve some of the TODOs Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * docs(forward): add basic documentation for DoH Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore: add basic tests to cover DoH Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore(health): unify default timeout to 1s Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * feat(forward): make doh method configurable Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore: remove maxIdleConnsPerHost setting & update docs Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> * chore(forward): reject https upstreams with path Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch> --------- Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch>
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/doh"
|
||||
"github.com/coredns/coredns/plugin/pkg/log"
|
||||
"github.com/coredns/coredns/plugin/pkg/transport"
|
||||
|
||||
@@ -36,14 +39,16 @@ type dnsHc struct {
|
||||
proxyName string
|
||||
}
|
||||
|
||||
const defaultTimeout = 1 * time.Second
|
||||
|
||||
// NewHealthChecker returns a new HealthChecker based on transport.
|
||||
func NewHealthChecker(proxyName, trans string, recursionDesired bool, domain string) HealthChecker {
|
||||
switch trans {
|
||||
func NewHealthChecker(proxyName, protocol string, recursionDesired bool, domain string) HealthChecker {
|
||||
switch protocol {
|
||||
case transport.DNS, transport.TLS:
|
||||
c := new(dns.Client)
|
||||
c.Net = "udp"
|
||||
c.ReadTimeout = 1 * time.Second
|
||||
c.WriteTimeout = 1 * time.Second
|
||||
c.ReadTimeout = defaultTimeout
|
||||
c.WriteTimeout = defaultTimeout
|
||||
|
||||
return &dnsHc{
|
||||
c: c,
|
||||
@@ -51,9 +56,22 @@ func NewHealthChecker(proxyName, trans string, recursionDesired bool, domain str
|
||||
domain: domain,
|
||||
proxyName: proxyName,
|
||||
}
|
||||
case transport.HTTPS:
|
||||
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
httpTransport.TLSClientConfig = new(tls.Config)
|
||||
|
||||
return &dohHc{
|
||||
client: &http.Client{
|
||||
Transport: httpTransport,
|
||||
Timeout: defaultTimeout,
|
||||
},
|
||||
recursionDesired: recursionDesired,
|
||||
domain: domain,
|
||||
proxyName: proxyName,
|
||||
}
|
||||
}
|
||||
|
||||
log.Warningf("No healthchecker for transport %q", trans)
|
||||
log.Warningf("No healthchecker for transport %q", protocol)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -132,3 +150,97 @@ func (h *dnsHc) send(addr string) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// dohHc is a health checker for a DNS-over-HTTPS (DoH) endpoint.
|
||||
type dohHc struct {
|
||||
client *http.Client
|
||||
recursionDesired bool
|
||||
domain string
|
||||
proxyName string
|
||||
}
|
||||
|
||||
func (h *dohHc) Check(p *Proxy) error {
|
||||
err := h.send(p.addr)
|
||||
if err != nil {
|
||||
healthcheckFailureCount.WithLabelValues(p.proxyName, p.addr).Add(1)
|
||||
p.incrementFails()
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&p.fails, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *dohHc) send(addr string) error {
|
||||
ping := new(dns.Msg)
|
||||
ping.SetQuestion(h.domain, dns.TypeNS)
|
||||
ping.RecursionDesired = h.recursionDesired
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), h.client.Timeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := doh.NewRequestWithContext(ctx, http.MethodPost, addr, ping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ResponseToMsg always closes the body via defer resp.Body.Close().
|
||||
m, err := doh.ResponseToMsg(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we got a header, we're alright.
|
||||
if m.Response || m.Opcode == dns.OpcodeQuery {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *dohHc) SetTLSConfig(cfg *tls.Config) {
|
||||
h.client.Transport.(*http.Transport).TLSClientConfig = cfg
|
||||
}
|
||||
|
||||
func (h *dohHc) GetTLSConfig() *tls.Config {
|
||||
return h.client.Transport.(*http.Transport).TLSClientConfig
|
||||
}
|
||||
|
||||
func (h *dohHc) SetRecursionDesired(recursionDesired bool) {
|
||||
h.recursionDesired = recursionDesired
|
||||
}
|
||||
func (h *dohHc) GetRecursionDesired() bool {
|
||||
return h.recursionDesired
|
||||
}
|
||||
|
||||
func (h *dohHc) SetDomain(domain string) {
|
||||
h.domain = domain
|
||||
}
|
||||
func (h *dohHc) GetDomain() string {
|
||||
return h.domain
|
||||
}
|
||||
|
||||
func (h *dohHc) SetTCPTransport() {
|
||||
// no-op for DoH
|
||||
}
|
||||
|
||||
func (h *dohHc) GetReadTimeout() time.Duration {
|
||||
return h.client.Transport.(*http.Transport).ResponseHeaderTimeout
|
||||
}
|
||||
|
||||
func (h *dohHc) SetReadTimeout(t time.Duration) {
|
||||
h.client.Transport.(*http.Transport).ResponseHeaderTimeout = t
|
||||
}
|
||||
|
||||
func (h *dohHc) GetWriteTimeout() time.Duration {
|
||||
return h.client.Timeout
|
||||
}
|
||||
|
||||
func (h *dohHc) SetWriteTimeout(t time.Duration) {
|
||||
h.client.Timeout = t
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user