2016-03-20 17:54:21 +00:00
|
|
|
package setup
|
|
|
|
|
|
|
|
|
|
import (
|
2016-03-20 18:17:07 +00:00
|
|
|
"crypto/tls"
|
|
|
|
|
"crypto/x509"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net"
|
|
|
|
|
"net/http"
|
|
|
|
|
"time"
|
2016-03-20 17:54:21 +00:00
|
|
|
|
|
|
|
|
"github.com/miekg/coredns/middleware"
|
2016-03-20 21:36:55 +00:00
|
|
|
"github.com/miekg/coredns/middleware/etcd"
|
2016-03-22 22:44:50 +00:00
|
|
|
"github.com/miekg/coredns/middleware/proxy"
|
2016-04-26 17:57:11 +01:00
|
|
|
"github.com/miekg/coredns/middleware/singleflight"
|
2016-03-20 21:36:55 +00:00
|
|
|
|
|
|
|
|
etcdc "github.com/coreos/etcd/client"
|
2016-03-22 22:44:50 +00:00
|
|
|
"golang.org/x/net/context"
|
2016-03-20 17:54:21 +00:00
|
|
|
)
|
|
|
|
|
|
2016-04-19 12:52:05 +00:00
|
|
|
const defaultEndpoint = "http://localhost:2379"
|
2016-03-20 18:17:07 +00:00
|
|
|
|
|
|
|
|
// Etcd sets up the etcd middleware.
|
|
|
|
|
func Etcd(c *Controller) (middleware.Middleware, error) {
|
2016-03-25 20:26:42 +00:00
|
|
|
etcd, stubzones, err := etcdParse(c)
|
2016-03-20 17:54:21 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2016-03-25 20:26:42 +00:00
|
|
|
if stubzones {
|
|
|
|
|
c.Startup = append(c.Startup, func() error {
|
|
|
|
|
etcd.UpdateStubZones()
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
}
|
2016-03-20 18:17:07 +00:00
|
|
|
|
2016-03-20 17:54:21 +00:00
|
|
|
return func(next middleware.Handler) middleware.Handler {
|
2016-03-22 22:44:50 +00:00
|
|
|
etcd.Next = next
|
|
|
|
|
return etcd
|
2016-03-20 17:54:21 +00:00
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-25 20:26:42 +00:00
|
|
|
func etcdParse(c *Controller) (etcd.Etcd, bool, error) {
|
|
|
|
|
stub := make(map[string]proxy.Proxy)
|
2016-03-22 22:44:50 +00:00
|
|
|
etc := etcd.Etcd{
|
2016-04-11 15:56:22 +01:00
|
|
|
Proxy: proxy.New([]string{"8.8.8.8:53", "8.8.4.4:53"}),
|
2016-03-22 22:44:50 +00:00
|
|
|
PathPrefix: "skydns",
|
|
|
|
|
Ctx: context.Background(),
|
|
|
|
|
Inflight: &singleflight.Group{},
|
2016-03-25 20:26:42 +00:00
|
|
|
Stubmap: &stub,
|
2016-03-22 22:44:50 +00:00
|
|
|
}
|
2016-03-25 20:26:42 +00:00
|
|
|
var (
|
|
|
|
|
client etcdc.KeysAPI
|
|
|
|
|
tlsCertFile = ""
|
|
|
|
|
tlsKeyFile = ""
|
|
|
|
|
tlsCAcertFile = ""
|
|
|
|
|
endpoints = []string{defaultEndpoint}
|
|
|
|
|
stubzones = false
|
|
|
|
|
)
|
2016-03-20 17:54:21 +00:00
|
|
|
for c.Next() {
|
2016-03-20 18:17:07 +00:00
|
|
|
if c.Val() == "etcd" {
|
2016-03-22 22:44:50 +00:00
|
|
|
etc.Client = client
|
|
|
|
|
etc.Zones = c.RemainingArgs()
|
|
|
|
|
if len(etc.Zones) == 0 {
|
|
|
|
|
etc.Zones = c.ServerBlockHosts
|
2016-03-20 17:54:21 +00:00
|
|
|
}
|
2016-03-22 22:44:50 +00:00
|
|
|
middleware.Zones(etc.Zones).FullyQualify()
|
2016-03-25 20:26:42 +00:00
|
|
|
if c.NextBlock() {
|
|
|
|
|
// TODO(miek): 2 switches?
|
|
|
|
|
switch c.Val() {
|
|
|
|
|
case "stubzones":
|
|
|
|
|
stubzones = true
|
Allow debug queries to etcd middleware (#150)
With this you can retreive the raw data that the etcd middleware
used to create the reply. The debug data is put in TXT records
that are stuffed in the CH classs. This is only enabled if you
specify `debug` in the etcd stanza.
You can retrieve it by prefixing your query with 'o-o.debug.'
For instance:
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost -p 1053 SRV o-o.debug.production.*.skydns.local
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47798
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;o-o.debug.production.*.skydns.local. IN SRV
;; ANSWER SECTION:
production.*.skydns.local. 154 IN SRV 10 50 8080 service1.example.com.
production.*.skydns.local. 154 IN SRV 10 50 8080 service2.example.com.
;; ADDITIONAL SECTION:
skydns.local.skydns.east.production.rails.1. 154 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
skydns.local.skydns.west.production.rails.2. 154 CH TXT "service2.example.com:8080(10,0,,false)[0,]"
2016-05-22 21:16:26 +01:00
|
|
|
case "debug":
|
|
|
|
|
etc.Debug = true
|
2016-03-25 20:26:42 +00:00
|
|
|
case "path":
|
|
|
|
|
if !c.NextArg() {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
etc.PathPrefix = c.Val()
|
|
|
|
|
case "endpoint":
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) == 0 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
endpoints = args
|
|
|
|
|
case "upstream":
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) == 0 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < len(args); i++ {
|
|
|
|
|
h, p, e := net.SplitHostPort(args[i])
|
|
|
|
|
if e != nil && p == "" {
|
|
|
|
|
args[i] = h + ":53"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
endpoints = args
|
|
|
|
|
etc.Proxy = proxy.New(args)
|
|
|
|
|
case "tls": // cert key cacertfile
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) != 3 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
tlsCertFile, tlsKeyFile, tlsCAcertFile = args[0], args[1], args[2]
|
|
|
|
|
}
|
|
|
|
|
for c.Next() {
|
|
|
|
|
switch c.Val() {
|
|
|
|
|
case "stubzones":
|
|
|
|
|
stubzones = true
|
Allow debug queries to etcd middleware (#150)
With this you can retreive the raw data that the etcd middleware
used to create the reply. The debug data is put in TXT records
that are stuffed in the CH classs. This is only enabled if you
specify `debug` in the etcd stanza.
You can retrieve it by prefixing your query with 'o-o.debug.'
For instance:
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost -p 1053 SRV o-o.debug.production.*.skydns.local
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47798
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;o-o.debug.production.*.skydns.local. IN SRV
;; ANSWER SECTION:
production.*.skydns.local. 154 IN SRV 10 50 8080 service1.example.com.
production.*.skydns.local. 154 IN SRV 10 50 8080 service2.example.com.
;; ADDITIONAL SECTION:
skydns.local.skydns.east.production.rails.1. 154 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
skydns.local.skydns.west.production.rails.2. 154 CH TXT "service2.example.com:8080(10,0,,false)[0,]"
2016-05-22 21:16:26 +01:00
|
|
|
case "debug":
|
|
|
|
|
etc.Debug = true
|
2016-03-25 20:26:42 +00:00
|
|
|
case "path":
|
|
|
|
|
if !c.NextArg() {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
etc.PathPrefix = c.Val()
|
|
|
|
|
case "endpoint":
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) == 0 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
endpoints = args
|
|
|
|
|
case "upstream":
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) == 0 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < len(args); i++ {
|
|
|
|
|
h, p, e := net.SplitHostPort(args[i])
|
|
|
|
|
if e != nil && p == "" {
|
|
|
|
|
args[i] = h + ":53"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
etc.Proxy = proxy.New(args)
|
|
|
|
|
case "tls": // cert key cacertfile
|
|
|
|
|
args := c.RemainingArgs()
|
|
|
|
|
if len(args) != 3 {
|
|
|
|
|
return etcd.Etcd{}, false, c.ArgErr()
|
|
|
|
|
}
|
|
|
|
|
tlsCertFile, tlsKeyFile, tlsCAcertFile = args[0], args[1], args[2]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
client, err := newEtcdClient(endpoints, tlsCertFile, tlsKeyFile, tlsCAcertFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return etcd.Etcd{}, false, err
|
|
|
|
|
}
|
|
|
|
|
etc.Client = client
|
|
|
|
|
return etc, stubzones, nil
|
2016-03-20 17:54:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-03-25 20:26:42 +00:00
|
|
|
return etcd.Etcd{}, false, nil
|
2016-03-20 17:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-20 21:36:55 +00:00
|
|
|
func newEtcdClient(endpoints []string, tlsCert, tlsKey, tlsCACert string) (etcdc.KeysAPI, error) {
|
|
|
|
|
etcdCfg := etcdc.Config{
|
|
|
|
|
Endpoints: endpoints,
|
2016-03-20 18:17:07 +00:00
|
|
|
Transport: newHTTPSTransport(tlsCert, tlsKey, tlsCACert),
|
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
cli, err := etcdc.New(etcdCfg)
|
2016-03-20 17:54:21 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
return etcdc.NewKeysAPI(cli), nil
|
2016-03-20 18:17:07 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-20 21:36:55 +00:00
|
|
|
func newHTTPSTransport(tlsCertFile, tlsKeyFile, tlsCACertFile string) etcdc.CancelableTransport {
|
2016-03-20 18:17:07 +00:00
|
|
|
var cc *tls.Config = nil
|
|
|
|
|
|
|
|
|
|
if tlsCertFile != "" && tlsKeyFile != "" {
|
|
|
|
|
var rpool *x509.CertPool
|
|
|
|
|
if tlsCACertFile != "" {
|
|
|
|
|
if pemBytes, err := ioutil.ReadFile(tlsCACertFile); err == nil {
|
|
|
|
|
rpool = x509.NewCertPool()
|
|
|
|
|
rpool.AppendCertsFromPEM(pemBytes)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tlsCert, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile); err == nil {
|
|
|
|
|
cc = &tls.Config{
|
|
|
|
|
RootCAs: rpool,
|
|
|
|
|
Certificates: []tls.Certificate{tlsCert},
|
|
|
|
|
InsecureSkipVerify: true,
|
|
|
|
|
}
|
2016-03-20 17:54:21 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 18:17:07 +00:00
|
|
|
tr := &http.Transport{
|
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
|
Dial: (&net.Dialer{
|
|
|
|
|
Timeout: 30 * time.Second,
|
|
|
|
|
KeepAlive: 30 * time.Second,
|
|
|
|
|
}).Dial,
|
|
|
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
|
|
|
TLSClientConfig: cc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tr
|
|
|
|
|
}
|