From dc10c56b35eaa44b9d47cd5c7541271fda6ce90e Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 19 Feb 2016 17:10:48 +0100 Subject: [PATCH] Integrated TLS auth for etcd and consul --- cmd.go | 34 +++++++++++++++++++++++++------ configuration.go | 6 ++++++ docs/index.md | 31 +++++++++++++++++++++++++++- provider/kv.go | 49 +++++++++++++++++++++++++++++++++++++++++---- traefik.sample.toml | 20 ++++++++++++++++++ 5 files changed, 129 insertions(+), 11 deletions(-) diff --git a/cmd.go b/cmd.go index 9a33874ae..e846bc464 100644 --- a/cmd.go +++ b/cmd.go @@ -45,9 +45,11 @@ var arguments = struct { dockerTLS bool marathon bool consul bool + consulTLS bool consulCatalog bool zookeeper bool etcd bool + etcdTLS bool boltdb bool }{ GlobalConfiguration{ @@ -55,14 +57,22 @@ var arguments = struct { Docker: &provider.Docker{ TLS: &provider.DockerTLS{}, }, - File: &provider.File{}, - Web: &WebProvider{}, - Marathon: &provider.Marathon{}, - Consul: &provider.Consul{}, + File: &provider.File{}, + Web: &WebProvider{}, + Marathon: &provider.Marathon{}, + Consul: &provider.Consul{ + Kv: provider.Kv{ + TLS: &provider.KvTLS{}, + }, + }, ConsulCatalog: &provider.ConsulCatalog{}, Zookeeper: &provider.Zookepper{}, - Etcd: &provider.Etcd{}, - Boltdb: &provider.BoltDb{}, + Etcd: &provider.Etcd{ + Kv: provider.Kv{ + TLS: &provider.KvTLS{}, + }, + }, + Boltdb: &provider.BoltDb{}, }, false, false, @@ -74,6 +84,8 @@ var arguments = struct { false, false, false, + false, + false, } func init() { @@ -121,6 +133,11 @@ func init() { traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Filename, "consul.filename", "", "Override default configuration template. For advanced users :)") traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Consul server endpoint") traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Prefix, "consul.prefix", "/traefik", "Prefix used for KV store") + traefikCmd.PersistentFlags().BoolVar(&arguments.consulTLS, "consul.tls", false, "Enable Consul TLS support") + traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.CA, "consul.tls.ca", "", "TLS CA") + traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Cert, "consul.tls.cert", "", "TLS cert") + traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Key, "consul.tls.key", "", "TLS key") + traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.TLS.InsecureSkipVerify, "consul.tls.insecureSkipVerify", false, "TLS insecure skip verify") traefikCmd.PersistentFlags().BoolVar(&arguments.consulCatalog, "consulCatalog", false, "Enable Consul catalog backend") traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Domain, "consulCatalog.domain", "", "Default domain used") @@ -137,6 +154,11 @@ func init() { traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Filename, "etcd.filename", "", "Override default configuration template. For advanced users :)") traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Etcd server endpoint") traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Prefix, "etcd.prefix", "/traefik", "Prefix used for KV store") + traefikCmd.PersistentFlags().BoolVar(&arguments.etcdTLS, "etcd.tls", false, "Enable Etcd TLS support") + traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.CA, "etcd.tls.ca", "", "TLS CA") + traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Cert, "etcd.tls.cert", "", "TLS cert") + traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Key, "etcd.tls.key", "", "TLS key") + traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.TLS.InsecureSkipVerify, "etcd.tls.insecureSkipVerify", false, "TLS insecure skip verify") traefikCmd.PersistentFlags().BoolVar(&arguments.boltdb, "boltdb", false, "Enable Boltdb backend") traefikCmd.PersistentFlags().BoolVar(&arguments.Boltdb.Watch, "boltdb.watch", true, "Watch provider") diff --git a/configuration.go b/configuration.go index 559ae55cb..c7e97a934 100644 --- a/configuration.go +++ b/configuration.go @@ -222,6 +222,9 @@ func LoadConfiguration() *GlobalConfiguration { if arguments.marathon { viper.Set("marathon", arguments.Marathon) } + if !arguments.consulTLS { + arguments.Consul.TLS = nil + } if arguments.consul { viper.Set("consul", arguments.Consul) } @@ -231,6 +234,9 @@ func LoadConfiguration() *GlobalConfiguration { if arguments.zookeeper { viper.Set("zookeeper", arguments.Zookeeper) } + if !arguments.etcdTLS { + arguments.Etcd.TLS = nil + } if arguments.etcd { viper.Set("etcd", arguments.Etcd) } diff --git a/docs/index.md b/docs/index.md index d1cfb64e4..0ca267c72 100644 --- a/docs/index.md +++ b/docs/index.md @@ -109,6 +109,11 @@ Flags: --consul.endpoint string Consul server endpoint (default "127.0.0.1:8500") --consul.filename string Override default configuration template. For advanced users :) --consul.prefix string Prefix used for KV store (default "/traefik") + --consul.tls Enable Consul TLS support + --consul.tls.ca string TLS CA + --consul.tls.cert string TLS cert + --consul.tls.insecureSkipVerify TLS insecure skip verify + --consul.tls.key string TLS key --consul.watch Watch provider (default true) --consulCatalog Enable Consul catalog backend --consulCatalog.domain string Default domain used @@ -129,6 +134,11 @@ Flags: --etcd.endpoint string Etcd server endpoint (default "127.0.0.1:4001") --etcd.filename string Override default configuration template. For advanced users :) --etcd.prefix string Prefix used for KV store (default "/traefik") + --etcd.tls Enable Etcd TLS support + --etcd.tls.ca string TLS CA + --etcd.tls.cert string TLS cert + --etcd.tls.insecureSkipVerify TLS insecure skip verify + --etcd.tls.key string TLS key --etcd.watch Watch provider (default true) --file Enable File backend --file.filename string Override default configuration template. For advanced users :) @@ -142,7 +152,6 @@ Flags: --marathon.networkInterface string Network interface used to call Marathon web services. Needed in case of multiple network interfaces (default "eth0") --marathon.watch Watch provider (default true) --maxIdleConnsPerHost int If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used - -p, --port string Reverse proxy port (default ":80") --providersThrottleDuration duration Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time. (default 2s) --traefikLogsFile string Traefik logs file (default "log/traefik.log") --web Enable Web backend @@ -722,6 +731,16 @@ prefix = "traefik" # Optional # # filename = "consul.tmpl" + +# Enable consul TLS connection +# +# Optional +# +# [consul.tls] +# ca = "/etc/ssl/ca.crt" +# cert = "/etc/ssl/consul.crt" +# key = "/etc/ssl/consul.key" +# insecureskipverify = true ``` The Keys-Values structure should look (using `prefix = "/traefik"`): @@ -803,6 +822,16 @@ Træfɪk can be configured to use Etcd as a backend configuration: # Optional # # filename = "etcd.tmpl" + +# Enable etcd TLS connection +# +# Optional +# +# [etcd.tls] +# ca = "/etc/ssl/ca.crt" +# cert = "/etc/ssl/etcd.crt" +# key = "/etc/ssl/etcd.key" +# insecureskipverify = true ``` The Keys-Values structure should look (using `prefix = "/traefik"`): diff --git a/provider/kv.go b/provider/kv.go index c4d2403b8..8699a5f20 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -2,6 +2,10 @@ package provider import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" "strings" "text/template" "time" @@ -18,18 +22,55 @@ type Kv struct { BaseProvider `mapstructure:",squash"` Endpoint string Prefix string + TLS *KvTLS storeType store.Backend kvclient store.Store } +// KvTLS holds TLS specific configurations +type KvTLS struct { + CA string + Cert string + Key string + InsecureSkipVerify bool +} + func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage) error { + storeConfig := &store.Config{ + ConnectionTimeout: 30 * time.Second, + Bucket: "traefik", + } + + if provider.TLS != nil { + caPool := x509.NewCertPool() + + if provider.TLS.CA != "" { + ca, err := ioutil.ReadFile(provider.TLS.CA) + + if err != nil { + return fmt.Errorf("Failed to read CA. %s", err) + } + + caPool.AppendCertsFromPEM(ca) + } + + cert, err := tls.LoadX509KeyPair(provider.TLS.Cert, provider.TLS.Key) + + if err != nil { + return fmt.Errorf("Failed to load keypair. %s", err) + } + + storeConfig.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caPool, + InsecureSkipVerify: provider.TLS.InsecureSkipVerify, + } + } + kv, err := libkv.NewStore( provider.storeType, []string{provider.Endpoint}, - &store.Config{ - ConnectionTimeout: 30 * time.Second, - Bucket: "traefik", - }, + storeConfig, ) if err != nil { return err diff --git a/traefik.sample.toml b/traefik.sample.toml index a773f2a17..addbe187c 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -281,6 +281,16 @@ # # filename = "consul.tmpl" +# Enable consul TLS connection +# +# Optional +# +# [consul.tls] +# ca = "/etc/ssl/ca.crt" +# cert = "/etc/ssl/consul.crt" +# key = "/etc/ssl/consul.key" +# insecureskipverify = true + ################################################################ # Etcd configuration backend @@ -316,6 +326,16 @@ # # filename = "etcd.tmpl" +# Enable etcd TLS connection +# +# Optional +# +# [etcd.tls] +# ca = "/etc/ssl/ca.crt" +# cert = "/etc/ssl/etcd.crt" +# key = "/etc/ssl/etcd.key" +# insecureskipverify = true + ################################################################ # Zookeeper configuration backend