mirror of
https://github.com/coredns/coredns.git
synced 2025-11-01 18:53:43 -04:00
Implement deprecation notice for 1.1.4 (#1833)
* Implement deprecation notice for 1.1.4 This still allows all the config to be parsed, but noops it: * -log; always set the log to stdout; no matter what. * https_google; removed from the proxy implementation. * reverse plugin: set to deprecated. * Whole of reverse can go * Remove test for deprecated plugin
This commit is contained in:
@@ -31,7 +31,7 @@ proxy FROM TO... {
|
||||
health_check PATH:PORT [DURATION]
|
||||
except IGNORED_NAMES...
|
||||
spray
|
||||
protocol [dns [force_tcp]|https_google [bootstrap ADDRESS...]|grpc [insecure|CACERT|KEY CERT|KEY CERT CACERT]]
|
||||
protocol [dns [force_tcp]|grpc [insecure|CACERT|KEY CERT|KEY CERT CACERT]]
|
||||
}
|
||||
~~~
|
||||
|
||||
@@ -54,8 +54,7 @@ proxy FROM TO... {
|
||||
* `spray` when all backends are unhealthy, randomly pick one to send the traffic to. (This is
|
||||
a failsafe.)
|
||||
* `protocol` specifies what protocol to use to speak to an upstream, `dns` (the default) is plain
|
||||
old DNS, and `https_google` uses `https://dns.google.com` and speaks a JSON DNS dialect. Note when
|
||||
using this **TO** will be ignored. The `grpc` option will talk to a server that has implemented
|
||||
old DNS. The `grpc` option will talk to a server that has implemented
|
||||
the [DnsService](https://github.com/coredns/coredns/blob/master/pb/dns.proto).
|
||||
|
||||
## Policies
|
||||
@@ -73,10 +72,6 @@ available. This is to preeempt the case where the healthchecking (as a mechanism
|
||||
|
||||
## Upstream Protocols
|
||||
|
||||
Currently `protocol` supports `dns` (i.e., standard DNS over UDP/TCP) and `https_google` (JSON
|
||||
payload over HTTPS). Note that with `https_google` the entire transport is encrypted. Only *you* and
|
||||
*Google* can see your DNS activity.
|
||||
|
||||
`dns`
|
||||
: uses the standard DNS exchange. You can pass `force_tcp` to make sure that the proxied connection is performed
|
||||
over TCP, regardless of the inbound request's protocol.
|
||||
@@ -92,13 +87,6 @@ payload over HTTPS). Note that with `https_google` the entire transport is encry
|
||||
* **KEY** **CERT** **CACERT** - Client authentication is used with the specified key/cert pair. The
|
||||
server certificate is verified using the **CACERT** file.
|
||||
|
||||
`https_google`
|
||||
: bootstrap **ADDRESS...** is used to (re-)resolve `dns.google.com`.
|
||||
|
||||
This happens every 300s. If not specified the default is used: 8.8.8.8:53/8.8.4.4:53.
|
||||
Note that **TO** is *ignored* when `https_google` is used, as its upstream is defined as `dns.google.com`.
|
||||
|
||||
|
||||
## Metrics
|
||||
|
||||
If monitoring is enabled (via the *prometheus* directive) then the following metric is exported:
|
||||
@@ -108,7 +96,7 @@ If monitoring is enabled (via the *prometheus* directive) then the following met
|
||||
* `coredns_proxy_request_count_total{server, proto, proto_proxy, family, to}` - query count per
|
||||
upstream.
|
||||
|
||||
Where `proxy_proto` is the protocol used (`dns`, `grpc`, or `https_google`) and `to` is **TO**
|
||||
Where `proxy_proto` is the protocol used (`dns` or `grpc`) and `to` is **TO**
|
||||
specified in the config, `proto` is the protocol used by the incoming query ("tcp" or "udp"), family
|
||||
the transport family ("1" for IPv4, and "2" for IPv6). `Server` is the server responsible for the
|
||||
request (and metric). See the documention in the metrics plugin.
|
||||
@@ -169,34 +157,3 @@ Proxy everything except `example.org` using the host's `resolv.conf`'s nameserve
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Proxy all requests within `example.org` to Google's `dns.google.com`.
|
||||
|
||||
~~~ corefile
|
||||
. {
|
||||
proxy example.org 1.2.3.4:53 {
|
||||
protocol https_google
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Proxy everything with HTTPS to `dns.google.com`, except `example.org`. Then have another proxy in
|
||||
another stanza that uses plain DNS to resolve names under `example.org`.
|
||||
|
||||
~~~ corefile
|
||||
. {
|
||||
proxy . 1.2.3.4:53 {
|
||||
except example.org
|
||||
protocol https_google
|
||||
}
|
||||
}
|
||||
|
||||
example.org {
|
||||
proxy . 8.8.8.8:53
|
||||
}
|
||||
~~~
|
||||
|
||||
## Bugs
|
||||
|
||||
When using the `google_https` protocol the health checking will health check the wrong endpoint.
|
||||
See <https://github.com/coredns/coredns/issues/1202> for some background.
|
||||
|
||||
@@ -47,7 +47,6 @@ func TestDnstap(t *testing.T) {
|
||||
tapq.SocketProto = tap.SocketProtocol_TCP
|
||||
tapr.SocketProto = tap.SocketProtocol_TCP
|
||||
testCase(t, newDNSExWithOption(Options{ForceTCP: true}), q, r, tapq, tapr)
|
||||
testCase(t, newGoogle("", []string{"8.8.8.8:53", "8.8.4.4:53"}), q, r, tapq, tapr)
|
||||
}
|
||||
|
||||
func TestNoDnstap(t *testing.T) {
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/healthcheck"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type google struct {
|
||||
client *http.Client
|
||||
|
||||
endpoint string // Name to resolve via 'bootstrapProxy'
|
||||
|
||||
bootstrapProxy Proxy
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
func newGoogle(endpoint string, bootstrap []string) *google {
|
||||
// TODO(miek): Deprecate after 1.1.3 (that would be 1.2.0)
|
||||
log.Warning("https_google will be deprecated in the next release")
|
||||
|
||||
if endpoint == "" {
|
||||
endpoint = ghost
|
||||
}
|
||||
tls := &tls.Config{ServerName: endpoint}
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * defaultTimeout,
|
||||
Transport: &http.Transport{TLSClientConfig: tls},
|
||||
}
|
||||
|
||||
boot := NewLookup(bootstrap)
|
||||
|
||||
return &google{client: client, endpoint: dns.Fqdn(endpoint), bootstrapProxy: boot, quit: make(chan bool)}
|
||||
}
|
||||
|
||||
func (g *google) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) {
|
||||
v := url.Values{}
|
||||
|
||||
v.Set("name", state.Name())
|
||||
v.Set("type", fmt.Sprintf("%d", state.QType()))
|
||||
|
||||
buf, backendErr := g.exchangeJSON(addr, v.Encode())
|
||||
|
||||
if backendErr == nil {
|
||||
gm := new(googleMsg)
|
||||
if err := json.Unmarshal(buf, gm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m, err := toMsg(gm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.Id = state.Req.Id
|
||||
return m, nil
|
||||
}
|
||||
|
||||
log.Warningf("Failed to connect to HTTPS backend %q: %s", g.endpoint, backendErr)
|
||||
return nil, backendErr
|
||||
}
|
||||
|
||||
func (g *google) exchangeJSON(addr, json string) ([]byte, error) {
|
||||
url := "https://" + addr + "/resolve?" + json
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Host = g.endpoint // TODO(miek): works with the extra dot at the end?
|
||||
|
||||
resp, err := g.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("failed to get 200 status code, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (g *google) Transport() string { return "tcp" }
|
||||
func (g *google) Protocol() string { return "https_google" }
|
||||
|
||||
func (g *google) OnShutdown(p *Proxy) error {
|
||||
g.quit <- true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *google) OnStartup(p *Proxy) error {
|
||||
// We fake a state because normally the proxy is called after we already got a incoming query.
|
||||
// This is a non-edns0, udp request to g.endpoint.
|
||||
req := new(dns.Msg)
|
||||
req.SetQuestion(g.endpoint, dns.TypeA)
|
||||
state := request.Request{W: new(fakeBootWriter), Req: req}
|
||||
|
||||
if len(*p.Upstreams) == 0 {
|
||||
return fmt.Errorf("no upstreams defined")
|
||||
}
|
||||
|
||||
oldUpstream := (*p.Upstreams)[0]
|
||||
|
||||
new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to bootstrap A records %q: %s", g.endpoint, err)
|
||||
} else {
|
||||
addrs, err1 := extractAnswer(new)
|
||||
if err1 != nil {
|
||||
log.Warningf("Failed to bootstrap A records %q: %s", g.endpoint, err1)
|
||||
} else {
|
||||
|
||||
up := newUpstream(addrs, oldUpstream.(*staticUpstream))
|
||||
p.Upstreams = &[]Upstream{up}
|
||||
|
||||
log.Infof("Bootstrapping A records %q found: %v", g.endpoint, addrs)
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
tick := time.NewTicker(120 * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
|
||||
log.Infof("Resolving A records %q", g.endpoint)
|
||||
|
||||
new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to resolve A records %q: %s", g.endpoint, err)
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err1 := extractAnswer(new)
|
||||
if err1 != nil {
|
||||
log.Warningf("Failed to resolve A records %q: %s", g.endpoint, err1)
|
||||
continue
|
||||
}
|
||||
|
||||
up := newUpstream(addrs, oldUpstream.(*staticUpstream))
|
||||
p.Upstreams = &[]Upstream{up}
|
||||
|
||||
log.Infof("Resolving A records %q found: %v", g.endpoint, addrs)
|
||||
|
||||
case <-g.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractAnswer(m *dns.Msg) ([]string, error) {
|
||||
if len(m.Answer) == 0 {
|
||||
return nil, fmt.Errorf("no answer section in response")
|
||||
}
|
||||
ret := []string{}
|
||||
for _, an := range m.Answer {
|
||||
if a, ok := an.(*dns.A); ok {
|
||||
ret = append(ret, net.JoinHostPort(a.A.String(), "443"))
|
||||
}
|
||||
}
|
||||
if len(ret) > 0 {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no address records in answer section")
|
||||
}
|
||||
|
||||
// newUpstream returns an upstream initialized with hosts.
|
||||
func newUpstream(hosts []string, old *staticUpstream) Upstream {
|
||||
upstream := &staticUpstream{
|
||||
from: old.from,
|
||||
HealthCheck: healthcheck.HealthCheck{
|
||||
FailTimeout: 5 * time.Second,
|
||||
MaxFails: 3,
|
||||
},
|
||||
ex: old.ex,
|
||||
IgnoredSubDomains: old.IgnoredSubDomains,
|
||||
}
|
||||
|
||||
upstream.Hosts = make([]*healthcheck.UpstreamHost, len(hosts))
|
||||
for i, host := range hosts {
|
||||
uh := &healthcheck.UpstreamHost{
|
||||
Name: host,
|
||||
Conns: 0,
|
||||
Fails: 0,
|
||||
FailTimeout: upstream.FailTimeout,
|
||||
CheckDown: checkDownFunc(upstream),
|
||||
}
|
||||
upstream.Hosts[i] = uh
|
||||
}
|
||||
return upstream
|
||||
}
|
||||
|
||||
const (
|
||||
// Default endpoint for this service.
|
||||
ghost = "dns.google.com."
|
||||
)
|
||||
@@ -1,89 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// toMsg converts a googleMsg into the dns message.
|
||||
func toMsg(g *googleMsg) (*dns.Msg, error) {
|
||||
m := new(dns.Msg)
|
||||
m.Response = true
|
||||
m.Rcode = g.Status
|
||||
m.Truncated = g.TC
|
||||
m.RecursionDesired = g.RD
|
||||
m.RecursionAvailable = g.RA
|
||||
m.AuthenticatedData = g.AD
|
||||
m.CheckingDisabled = g.CD
|
||||
|
||||
m.Question = make([]dns.Question, 1)
|
||||
m.Answer = make([]dns.RR, len(g.Answer))
|
||||
m.Ns = make([]dns.RR, len(g.Authority))
|
||||
m.Extra = make([]dns.RR, len(g.Additional))
|
||||
|
||||
m.Question[0] = dns.Question{Name: g.Question[0].Name, Qtype: g.Question[0].Type, Qclass: dns.ClassINET}
|
||||
|
||||
var err error
|
||||
for i := 0; i < len(m.Answer); i++ {
|
||||
m.Answer[i], err = toRR(g.Answer[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(m.Ns); i++ {
|
||||
m.Ns[i], err = toRR(g.Authority[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(m.Extra); i++ {
|
||||
m.Extra[i], err = toRR(g.Additional[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// toRR transforms a "google" RR to a dns.RR.
|
||||
func toRR(g googleRR) (dns.RR, error) {
|
||||
typ, ok := dns.TypeToString[g.Type]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert type %q", g.Type)
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%s %d %s %s", g.Name, g.TTL, typ, g.Data)
|
||||
rr, err := dns.NewRR(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %q: %s", str, err)
|
||||
}
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
// googleRR represents a dns.RR in another form.
|
||||
type googleRR struct {
|
||||
Name string
|
||||
Type uint16
|
||||
TTL uint32
|
||||
Data string
|
||||
}
|
||||
|
||||
// googleMsg is a JSON representation of the dns.Msg.
|
||||
type googleMsg struct {
|
||||
Status int
|
||||
TC bool
|
||||
RD bool
|
||||
RA bool
|
||||
AD bool
|
||||
CD bool
|
||||
Question []struct {
|
||||
Name string
|
||||
Type uint16
|
||||
}
|
||||
Answer []googleRR
|
||||
Authority []googleRR
|
||||
Additional []googleRR
|
||||
Comment string
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package proxy
|
||||
|
||||
// TODO(miek):
|
||||
// Test cert failures - put those in SERVFAIL messages, but attach error code in TXT
|
||||
// Test connecting to a a bad host.
|
||||
@@ -129,14 +129,6 @@ func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
||||
}
|
||||
}
|
||||
|
||||
// If protocol is https_google we do the health checks wrong, i.e. we're healthchecking the wrong
|
||||
// endpoint, hence the health check code below should not be executed. See issue #1202.
|
||||
// This is an ugly hack and the thing requires a rethink. Possibly in conjunction with moving
|
||||
// to the *forward* plugin.
|
||||
if upstream.Exchanger().Protocol() == "https_google" {
|
||||
continue
|
||||
}
|
||||
|
||||
timeout := host.FailTimeout
|
||||
if timeout == 0 {
|
||||
timeout = defaultFailTimeout
|
||||
|
||||
@@ -165,12 +165,7 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
|
||||
u.ex = newDNSEx()
|
||||
}
|
||||
case "https_google":
|
||||
boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
|
||||
if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
|
||||
boot = encArgs[2:]
|
||||
}
|
||||
|
||||
u.ex = newGoogle("", boot) // "" for default in google.go
|
||||
// allow the config, but make noop
|
||||
case "grpc":
|
||||
if len(encArgs) == 2 && encArgs[1] == "insecure" {
|
||||
u.ex = newGrpcClient(nil, u)
|
||||
|
||||
Reference in New Issue
Block a user