mirror of
https://github.com/coredns/coredns.git
synced 2026-06-15 21:50: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:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -158,6 +159,14 @@ func parseStanza(c *caddy.Controller) (*Forward, error) {
|
||||
return f, fmt.Errorf("max_age (%s) must not be less than expire (%s)", f.maxAge, f.expire)
|
||||
}
|
||||
|
||||
// Reject HTTPS upstreams that include a path, the doh implementation default to /dns-query path.
|
||||
for _, addr := range to {
|
||||
trans, h := parse.Transport(addr)
|
||||
if trans == transport.HTTPS && strings.Contains(h, "/") {
|
||||
return f, fmt.Errorf("paths are not allowed in HTTPS upstream addresses (the /dns-query path is used by default): %s", addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Classify TO addresses in order, preserving config ordering.
|
||||
entries, err := classifyToAddrs(to)
|
||||
if err != nil {
|
||||
@@ -177,7 +186,7 @@ func parseStanza(c *caddy.Controller) (*Forward, error) {
|
||||
tlsServerNames := make([]string, len(toHosts))
|
||||
perServerNameProxyCount := make(map[string]int)
|
||||
transports := make([]string, len(toHosts))
|
||||
allowedTrans := map[string]bool{"dns": true, "tls": true}
|
||||
allowedTrans := map[string]bool{"dns": true, "tls": true, "https": true}
|
||||
for i, hostWithZone := range toHosts {
|
||||
host, serverName := splitZone(hostWithZone)
|
||||
trans, h := parse.Transport(host)
|
||||
@@ -223,6 +232,21 @@ func parseStanza(c *caddy.Controller) (*Forward, error) {
|
||||
f.proxies[i].SetTLSConfig(f.tlsConfig)
|
||||
}
|
||||
}
|
||||
|
||||
if transports[i] == transport.HTTPS {
|
||||
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
httpTransport.TLSClientConfig = f.tlsConfig
|
||||
httpTransport.MaxIdleConns = f.maxIdleConns
|
||||
httpTransport.MaxIdleConnsPerHost = f.maxIdleConns
|
||||
|
||||
c := http.Client{
|
||||
Transport: httpTransport,
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
f.proxies[i].SetHTTPClient(&c)
|
||||
f.proxies[i].SetDOHRequestOptions(f.dohMethod)
|
||||
}
|
||||
|
||||
f.proxies[i].SetExpire(f.expire)
|
||||
f.proxies[i].SetMaxAge(f.maxAge)
|
||||
f.proxies[i].SetMaxIdleConns(f.maxIdleConns)
|
||||
@@ -365,6 +389,16 @@ func parseBlock(c *caddy.Controller, f *Forward) error {
|
||||
return fmt.Errorf("max_idle_conns can't be negative: %d", n)
|
||||
}
|
||||
f.maxIdleConns = n
|
||||
case "doh_method":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
switch c.Val() {
|
||||
case http.MethodPost, http.MethodGet:
|
||||
f.dohMethod = c.Val()
|
||||
default:
|
||||
return fmt.Errorf("doh_method must be either %s or %s", http.MethodPost, http.MethodGet)
|
||||
}
|
||||
case "policy":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
|
||||
Reference in New Issue
Block a user