diff --git a/main.go b/main.go index cc71319667..d6e1eefbb8 100644 --- a/main.go +++ b/main.go @@ -99,7 +99,7 @@ func main() { case "inmemory": p, err = provider.NewInMemoryProvider(provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil case "coredns", "skydns": - p, err = provider.NewCoreDNSProvider(domainFilter, cfg.ETCD, cfg.DryRun) + p, err = provider.NewCoreDNSProvider(domainFilter, cfg.DryRun) default: log.Fatalf("unknown dns provider: %s", cfg.Provider) } diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index e77b1d8d1c..4b4a3b1842 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -49,7 +49,6 @@ type Config struct { LogFormat string MetricsAddress string Debug bool - ETCD string } var defaultConfig = &Config{ @@ -74,7 +73,6 @@ var defaultConfig = &Config{ LogFormat: "text", MetricsAddress: ":7979", Debug: false, - ETCD: "http://localhost:2379", } // NewConfig returns new Config object @@ -104,7 +102,6 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) - app.Flag("etcd", "ETCD cluster URI(s) for coredns/skydns provider").Default(defaultConfig.ETCD).StringVar(&cfg.ETCD) // Flags related to policies app.Flag("policy", "Modify how DNS records are sychronized between sources and providers (default: sync, options: sync, upsert-only)").Default(defaultConfig.Policy).EnumVar(&cfg.Policy, "sync", "upsert-only") diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index e01ad18a97..061027e169 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -48,7 +48,6 @@ var ( LogFormat: "text", MetricsAddress: ":7979", Debug: false, - ETCD: "http://localhost:2379", } overriddenConfig = &Config{ @@ -73,7 +72,6 @@ var ( LogFormat: "json", MetricsAddress: "127.0.0.1:9099", Debug: true, - ETCD: "http://host:3378,http://host:3379", } ) @@ -119,7 +117,6 @@ func TestParseFlags(t *testing.T) { "--log-format=json", "--metrics-address=127.0.0.1:9099", "--debug", - "--etcd=http://host:3378,http://host:3379", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -149,7 +146,6 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_LOG_FORMAT": "json", "EXTERNAL_DNS_METRICS_ADDRESS": "127.0.0.1:9099", "EXTERNAL_DNS_DEBUG": "1", - "EXTERNAL_DNS_ETCD": "http://host:3378,http://host:3379", }, expected: overriddenConfig, }, diff --git a/provider/coredns.go b/provider/coredns.go index 665a869aa8..ea03c6e623 100644 --- a/provider/coredns.go +++ b/provider/coredns.go @@ -18,9 +18,16 @@ package provider import ( "container/list" + "crypto/tls" + "crypto/x509" "encoding/json" + "errors" "fmt" + "io/ioutil" "math/rand" + "net" + "net/http" + "os" "strings" "time" @@ -135,10 +142,100 @@ func (c etcdClient) DeleteService(key string) error { } +// loads TLS artifacts and builds tls.Clonfig object +func newTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool) (*tls.Config, error) { + if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" { + return nil, errors.New("either both cert and key or none must be provided") + } + var certificates []tls.Certificate + if certPath != "" { + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, fmt.Errorf("could not load TLS cert: %s", err) + } + certificates = append(certificates, cert) + } + roots, err := loadRoots(caPath) + if err != nil { + return nil, err + } + + return &tls.Config{ + Certificates: certificates, + RootCAs: roots, + InsecureSkipVerify: insecure, + ServerName: serverName, + }, nil +} + +// loads CA cert +func loadRoots(caPath string) (*x509.CertPool, error) { + if caPath == "" { + return nil, nil + } + + roots := x509.NewCertPool() + pem, err := ioutil.ReadFile(caPath) + if err != nil { + return nil, fmt.Errorf("error reading %s: %s", caPath, err) + } + ok := roots.AppendCertsFromPEM(pem) + if !ok { + return nil, fmt.Errorf("could not read root certs: %s", err) + } + return roots, nil +} + +// constructs http.Transport object for https protocol +func newHTTPSTransport(cc *tls.Config) *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + TLSClientConfig: cc, + } +} + +// builds etcd client config depending on connection scheme and TLS parameters +func getETCDConfig() (*etcd.Config, error) { + etcdURLsStr := os.Getenv("ETCD_URLS") + if etcdURLsStr == "" { + etcdURLsStr = "http://localhost:2379" + } + etcdURLs := strings.Split(etcdURLsStr, ",") + firstURL := strings.ToLower(etcdURLs[0]) + if strings.HasPrefix(firstURL, "http://") { + return &etcd.Config{Endpoints: etcdURLs}, nil + } else if strings.HasPrefix(firstURL, "https://") { + caFile := os.Getenv("ETCD_CA_FILE") + certFile := os.Getenv("ETCD_CERT_FILE") + keyFile := os.Getenv("ETCD_KEY_FILE") + serverName := os.Getenv("ETCD_TLS_SERVER_NAME") + isInsecureStr := strings.ToLower(os.Getenv("ETCD_TLS_INSECURE")) + isInsecure := isInsecureStr == "true" || isInsecureStr == "yes" || isInsecureStr == "1" + tlsConfig, err := newTLSConfig(certFile, keyFile, caFile, serverName, isInsecure) + if err != nil { + return nil, err + } + return &etcd.Config{ + Endpoints: etcdURLs, + Transport: newHTTPSTransport(tlsConfig), + }, nil + } else { + return nil, fmt.Errorf("etcd URLs must start with either http:// or https://: %s (%s)", etcdURLsStr, firstURL) + } +} + //newETCDClient is an etcd client constructor -func newETCDClient(etcdURIs string) (skyDNSClient, error) { - cfg := etcd.Config{Endpoints: strings.Split(etcdURIs, ",")} - c, err := etcd.New(cfg) +func newETCDClient() (skyDNSClient, error) { + cfg, err := getETCDConfig() + if err != nil { + return nil, err + } + c, err := etcd.New(*cfg) if err != nil { return nil, err } @@ -146,8 +243,8 @@ func newETCDClient(etcdURIs string) (skyDNSClient, error) { } // NewCoreDNSProvider is a CoreDNS provider constructor -func NewCoreDNSProvider(domainFilter DomainFilter, etcdURIs string, dryRun bool) (Provider, error) { - client, err := newETCDClient(etcdURIs) +func NewCoreDNSProvider(domainFilter DomainFilter, dryRun bool) (Provider, error) { + client, err := newETCDClient() if err != nil { return nil, err }