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:
Miek Gieben
2018-05-24 14:30:01 +01:00
committed by GitHub
parent b0fd575c65
commit 2758a756dd
20 changed files with 9 additions and 1306 deletions

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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."
)

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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)