From e26e0955b355bd6ab7c62afdc22b3a8975916a81 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 27 Jun 2016 16:14:56 +0200 Subject: [PATCH] add struct ClientTLS : supports either a paths to a file or directly the certificate --- integration/etcd_test.go | 1 - provider/docker.go | 27 +++++++---------- provider/kv.go | 41 ++++---------------------- provider/marathon.go | 19 +++++++----- provider/provider.go | 63 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 61 deletions(-) diff --git a/integration/etcd_test.go b/integration/etcd_test.go index 51d27a2ef..0b6b2d077 100644 --- a/integration/etcd_test.go +++ b/integration/etcd_test.go @@ -310,7 +310,6 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) { c.Assert(response.StatusCode, checker.Equals, 200) } -//TODO : TestCertificatesContents func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) { etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress // start traefik diff --git a/provider/docker.go b/provider/docker.go index a8655c8ed..52f70ae28 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -10,6 +10,7 @@ import ( "golang.org/x/net/context" + "crypto/tls" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" @@ -20,7 +21,6 @@ import ( eventtypes "github.com/docker/engine-api/types/events" "github.com/docker/engine-api/types/filters" "github.com/docker/go-connections/sockets" - "github.com/docker/go-connections/tlsconfig" "github.com/vdemeester/docker-events" ) @@ -32,18 +32,10 @@ type Docker struct { BaseProvider `mapstructure:",squash"` Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` Domain string `description:"Default domain used"` - TLS *DockerTLS `description:"Enable Docker TLS support"` + TLS *ClientTLS `description:"Enable Docker TLS support"` ExposedByDefault bool `description:"Expose containers by default"` } -// DockerTLS holds TLS specific configurations -type DockerTLS struct { - CA string `description:"TLS CA"` - Cert string `description:"TLS cert"` - Key string `description:"TLS key"` - InsecureSkipVerify bool `description:"TLS insecure skip verify"` -} - func (provider *Docker) createClient() (client.APIClient, error) { var httpClient *http.Client httpHeaders := map[string]string{ @@ -51,16 +43,16 @@ func (provider *Docker) createClient() (client.APIClient, error) { "User-Agent": "Traefik", } if provider.TLS != nil { - tlsOptions := tlsconfig.Options{ - CAFile: provider.TLS.CA, - CertFile: provider.TLS.Cert, - KeyFile: provider.TLS.Key, - InsecureSkipVerify: provider.TLS.InsecureSkipVerify, - } - config, err := tlsconfig.Client(tlsOptions) + config, err := provider.TLS.CreateTLSConfig() if err != nil { return nil, err } + // TO DELETE IF USELESS : default docker TLS Client config + config.MaxVersion = tls.VersionTLS12 + config.CipherSuites = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + } tr := &http.Transport{ TLSClientConfig: config, } @@ -74,6 +66,7 @@ func (provider *Docker) createClient() (client.APIClient, error) { httpClient = &http.Client{ Transport: tr, } + } return client.NewClient(provider.Endpoint, DockerAPIVersion, httpClient, httpHeaders) } diff --git a/provider/kv.go b/provider/kv.go index 3b7f6418c..f27a085fe 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -2,10 +2,7 @@ package provider import ( - "crypto/tls" - "crypto/x509" "fmt" - "io/ioutil" "strings" "text/template" "time" @@ -23,21 +20,13 @@ import ( // Kv holds common configurations of key-value providers. type Kv struct { BaseProvider `mapstructure:",squash"` - Endpoint string `description:"Comma sepparated server endpoints"` - Prefix string `description:"Prefix used for KV store"` - TLS *KvTLS `description:"Enable TLS support"` + Endpoint string `description:"Comma sepparated server endpoints"` + Prefix string `description:"Prefix used for KV store"` + TLS *ClientTLS `description:"Enable TLS support"` storeType store.Backend kvclient store.Store } -// KvTLS holds TLS specific configurations -type KvTLS struct { - CA string `description:"TLS CA"` - Cert string `description:"TLS cert"` - Key string `description:"TLS key"` - InsecureSkipVerify bool `description:"TLS insecure skip verify"` -} - func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error { operation := func() error { events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{})) @@ -80,28 +69,10 @@ func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool * } 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) - + var err error + storeConfig.TLS, err = provider.TLS.CreateTLSConfig() if err != nil { - return fmt.Errorf("Failed to load TLS keypair: %v", err) - } - - storeConfig.TLS = &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caPool, - InsecureSkipVerify: provider.TLS.InsecureSkipVerify, + return err } } diff --git a/provider/marathon.go b/provider/marathon.go index 628dd69ad..52a7f7522 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -8,7 +8,6 @@ import ( "strings" "text/template" - "crypto/tls" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" @@ -22,13 +21,13 @@ import ( // Marathon holds configuration of the Marathon provider. type Marathon struct { BaseProvider - Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` - Domain string `description:"Default domain used"` - ExposedByDefault bool `description:"Expose Marathon apps by default"` - GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"` - DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"` + Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` + Domain string `description:"Default domain used"` + ExposedByDefault bool `description:"Expose Marathon apps by default"` + GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"` + DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"` + TLS *ClientTLS `description:"Enable Docker TLS support"` Basic *MarathonBasic - TLS *tls.Config marathonClient marathon.Marathon } @@ -58,9 +57,13 @@ func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, if len(provider.DCOSToken) > 0 { config.DCOSToken = provider.DCOSToken } + TLSConfig, err := provider.TLS.CreateTLSConfig() + if err != nil { + return err + } config.HTTPClient = &http.Client{ Transport: &http.Transport{ - TLSClientConfig: provider.TLS, + TLSClientConfig: TLSConfig, }, } client, err := marathon.NewClient(config) diff --git a/provider/provider.go b/provider/provider.go index 5f52988e0..28f75f564 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -7,10 +7,14 @@ import ( "text/template" "unicode" + "crypto/tls" + "crypto/x509" + "fmt" "github.com/BurntSushi/toml" "github.com/containous/traefik/autogen" "github.com/containous/traefik/safe" "github.com/containous/traefik/types" + "os" ) // Provider defines methods of a provider. @@ -92,3 +96,62 @@ func normalize(name string) string { // get function return strings.Join(strings.FieldsFunc(name, fargs), "-") } + +// ClientTLS holds TLS specific configurations as client +// CA, Cert and Key can be either path or file contents +type ClientTLS struct { + CA string `description:"TLS CA"` + Cert string `description:"TLS cert"` + Key string `description:"TLS key"` + InsecureSkipVerify bool `description:"TLS insecure skip verify"` +} + +// CreateTLSConfig creates a TLS config from ClientTLS structures +func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) { + var err error + caPool := x509.NewCertPool() + // TODO : error if CA=="" || Cert=="" || Key=="" + if clientTLS.CA != "" { + var ca []byte + if _, errCA := os.Stat(clientTLS.CA); errCA == nil { + ca, err = ioutil.ReadFile(clientTLS.CA) + if err != nil { + return nil, fmt.Errorf("Failed to read CA. %s", err) + } + } else { + ca = []byte(clientTLS.CA) + } + caPool.AppendCertsFromPEM(ca) + } + + cert := tls.Certificate{} + _, errKeyIsFile := os.Stat(clientTLS.Key) + + if _, errCertIsFile := os.Stat(clientTLS.Cert); errCertIsFile == nil { + if errKeyIsFile == nil { + cert, err = tls.LoadX509KeyPair(clientTLS.Cert, clientTLS.Key) + if err != nil { + return nil, fmt.Errorf("Failed to load TLS keypair: %v", err) + } + } else { + return nil, fmt.Errorf("tls cert is a file, but tls key is not") + } + } else { + if errKeyIsFile != nil { + cert, err = tls.X509KeyPair([]byte(clientTLS.Cert), []byte(clientTLS.Key)) + if err != nil { + return nil, fmt.Errorf("Failed to load TLS keypair: %v", err) + + } + } else { + return nil, fmt.Errorf("tls key is a file, but tls cert is not") + } + } + + TLSConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caPool, + InsecureSkipVerify: clientTLS.InsecureSkipVerify, + } + return TLSConfig, nil +}