mirror of
https://github.com/coredns/coredns.git
synced 2026-07-10 17:50:12 -04:00
* fix(dnssec): avoid caching empty signing results (#7996) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * save only Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * do #8008 Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Fix Address used if tcp Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Fix bad using of dialer type Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * core: Add full TSIG verification in gRPC transport (#8006) * core: Add full TSIG verification in gRPC transport This PR add full TSIG verification in gRPC using dns.TsigVerify() so invalid signatures and timestamps are correctly detected instead of only checking key presence. Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Fix Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Fix Signed-off-by: Yong Tang <yong.tang.github@outlook.com> --------- Signed-off-by: Yong Tang <yong.tang.github@outlook.com> Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * core: Add full TSIG verification in QUIC transport (#8007) * core: Add full TSIG verification in QUIC transport This PR add full TSIG verification in QUIC using dns.TsigVerify() Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Fix Signed-off-by: Yong Tang <yong.tang.github@outlook.com> --------- Signed-off-by: Yong Tang <yong.tang.github@outlook.com> Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(test): deduplicate TSIG test helpers (#8009) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(dnssec): return nil sigs on sign error (#7999) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(dnssec): return nil from ParseKeyFile on error (#8000) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(dnsserver): allow view server blocks in any declaration order (#8001) When using the view plugin, filtered and unfiltered server blocks can share the same zone and port. The zone overlap validation rejected this configuration when the unfiltered block was not declared last, because filtered configs treated an already-registered zone as an error. Skip the 'already defined' check for configs that have filter functions, since they are expected to coexist with an unfiltered catch-all block on the same zone/port. Fixes #7733 Signed-off-by: umut-polat <52835619+umut-polat@users.noreply.github.com> Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(doh): use per-connection local address for PROXY protocol (#8005) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(transfer): batch AXFR records by message size instead of count (#8002) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix(tls): use temp dir for keylog test path (#8010) Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Rename local_address option to source_address Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Rename local_address also in readme Add test Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Resolve change request in pr Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix ci lint Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Improve doc on source_address routing needs Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Remove added timeout Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * add use of source address also for health check query Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * remove untrailing newline from health_test.go Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix file format Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Update plugin/forward/setup_test.go Co-authored-by: Ville Vesilehto <ville@vesilehto.fi> Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Remove dead code in TestHealthLocalAddress Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Fix misspelling Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * Try to set default timeout Signed-off-by: Filippo <filippo.ferrazini@gmail.com> * fix format in health.go Signed-off-by: Filippo <filippo.ferrazini@gmail.com> --------- Signed-off-by: Filippo <filippo.ferrazini@gmail.com> Signed-off-by: Yong Tang <yong.tang.github@outlook.com> Signed-off-by: umut-polat <52835619+umut-polat@users.noreply.github.com> Co-authored-by: Ville Vesilehto <ville@vesilehto.fi> Co-authored-by: Yong Tang <yong.tang.github@outlook.com> Co-authored-by: Umut Polat <52835619+umut-polat@users.noreply.github.com> Co-authored-by: Cedric Wang <wangzongqi@msn.com>
307 lines
6.9 KiB
Go
307 lines
6.9 KiB
Go
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net"
|
|
"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"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// HealthChecker checks the upstream health.
|
|
type HealthChecker interface {
|
|
Check(*Proxy) error
|
|
SetTLSConfig(*tls.Config)
|
|
GetTLSConfig() *tls.Config
|
|
SetRecursionDesired(bool)
|
|
GetRecursionDesired() bool
|
|
SetDomain(domain string)
|
|
GetDomain() string
|
|
SetTCPTransport()
|
|
GetReadTimeout() time.Duration
|
|
SetReadTimeout(time.Duration)
|
|
GetWriteTimeout() time.Duration
|
|
SetWriteTimeout(time.Duration)
|
|
SetLocalAddress(net.IP)
|
|
GetLocalAddress() net.IP
|
|
}
|
|
|
|
// dnsHc is a health checker for a DNS endpoint (DNS, and DoT).
|
|
type dnsHc struct {
|
|
c *dns.Client
|
|
recursionDesired bool
|
|
domain string
|
|
|
|
proxyName string
|
|
|
|
localAddress net.IP
|
|
}
|
|
|
|
const defaultTimeout = 1 * time.Second
|
|
|
|
// NewHealthChecker returns a new HealthChecker based on transport.
|
|
func NewHealthChecker(proxyName, protocol string, recursionDesired bool, domain string) HealthChecker {
|
|
switch protocol {
|
|
case transport.DNS, transport.TLS:
|
|
c := new(dns.Client)
|
|
c.Net = "udp"
|
|
setDefaultTimeout(c)
|
|
|
|
return &dnsHc{
|
|
c: c,
|
|
recursionDesired: recursionDesired,
|
|
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", protocol)
|
|
return nil
|
|
}
|
|
|
|
func (h *dnsHc) SetTLSConfig(cfg *tls.Config) {
|
|
h.c.Net = "tcp-tls"
|
|
h.c.TLSConfig = cfg
|
|
// update the dialer accordingly with the protocol changed
|
|
h.setDialer()
|
|
}
|
|
|
|
func (h *dnsHc) GetTLSConfig() *tls.Config {
|
|
return h.c.TLSConfig
|
|
}
|
|
|
|
func (h *dnsHc) SetRecursionDesired(recursionDesired bool) {
|
|
h.recursionDesired = recursionDesired
|
|
}
|
|
func (h *dnsHc) GetRecursionDesired() bool {
|
|
return h.recursionDesired
|
|
}
|
|
|
|
func (h *dnsHc) SetDomain(domain string) {
|
|
h.domain = domain
|
|
}
|
|
func (h *dnsHc) GetDomain() string {
|
|
return h.domain
|
|
}
|
|
|
|
func (h *dnsHc) SetTCPTransport() {
|
|
h.c.Net = "tcp"
|
|
// update the dialer accordingly with the protocol changed
|
|
h.setDialer()
|
|
}
|
|
|
|
func (h *dnsHc) GetReadTimeout() time.Duration {
|
|
return h.c.ReadTimeout
|
|
}
|
|
|
|
func (h *dnsHc) SetReadTimeout(t time.Duration) {
|
|
h.c.ReadTimeout = t
|
|
}
|
|
|
|
func (h *dnsHc) GetWriteTimeout() time.Duration {
|
|
return h.c.WriteTimeout
|
|
}
|
|
|
|
func (h *dnsHc) SetWriteTimeout(t time.Duration) {
|
|
h.c.WriteTimeout = t
|
|
}
|
|
|
|
// For HC, we send to . IN NS +[no]rec message to the upstream. Dial timeouts and empty
|
|
// replies are considered fails, basically anything else constitutes a healthy upstream.
|
|
|
|
// Check is used as the up.Func in the up.Probe.
|
|
func (h *dnsHc) 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 *dnsHc) send(addr string) error {
|
|
ping := new(dns.Msg)
|
|
ping.SetQuestion(h.domain, dns.TypeNS)
|
|
ping.RecursionDesired = h.recursionDesired
|
|
|
|
m, _, err := h.c.Exchange(ping, addr)
|
|
// If we got a header, we're alright, basically only care about I/O errors 'n stuff.
|
|
if err != nil && m != nil {
|
|
// Silly check, something sane came back.
|
|
if m.Response || m.Opcode == dns.OpcodeQuery {
|
|
err = nil
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// SetLocalAddress sets the local address in transport.
|
|
func (h *dnsHc) SetLocalAddress(localAddr net.IP) {
|
|
h.localAddress = localAddr
|
|
h.setDialer()
|
|
}
|
|
|
|
// GetLocalAddress returns the local address in transport.
|
|
func (h *dnsHc) GetLocalAddress() net.IP {
|
|
return h.localAddress
|
|
}
|
|
|
|
// setDialer sets the local address in the underlying dialer
|
|
func (h *dnsHc) setDialer() {
|
|
if h.localAddress == nil {
|
|
if h.c.Dialer != nil {
|
|
h.c.Dialer.LocalAddr = nil
|
|
}
|
|
return
|
|
}
|
|
if h.c.Dialer == nil {
|
|
h.c.Dialer = new(net.Dialer)
|
|
setDefaultTimeout(h.c)
|
|
}
|
|
if h.c.Net == "udp" {
|
|
h.c.Dialer.LocalAddr = &net.UDPAddr{IP: h.localAddress}
|
|
} else {
|
|
h.c.Dialer.LocalAddr = &net.TCPAddr{IP: h.localAddress}
|
|
}
|
|
}
|
|
|
|
// setDefaultTimeout sets the default read and write timeout values for the DNS client to 1 second.
|
|
func setDefaultTimeout(c *dns.Client) {
|
|
c.ReadTimeout = 1 * time.Second
|
|
c.WriteTimeout = 1 * time.Second
|
|
}
|
|
|
|
// dohHc is a health checker for a DNS-over-HTTPS (DoH) endpoint.
|
|
type dohHc struct {
|
|
client *http.Client
|
|
recursionDesired bool
|
|
domain string
|
|
proxyName string
|
|
localAddress net.IP
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (h *dohHc) SetLocalAddress(localAddr net.IP) {
|
|
h.localAddress = localAddr
|
|
httpTransport := h.client.Transport.(*http.Transport)
|
|
if localAddr == nil {
|
|
httpTransport.DialContext = nil
|
|
return
|
|
}
|
|
dialer := &net.Dialer{LocalAddr: &net.TCPAddr{IP: localAddr}}
|
|
httpTransport.DialContext = dialer.DialContext
|
|
}
|
|
|
|
func (h *dohHc) GetLocalAddress() net.IP {
|
|
return h.localAddress
|
|
}
|