add struct ClientTLS : supports either a paths to a file or directly the certificate

This commit is contained in:
Martin 2016-06-27 16:14:56 +02:00
parent 7ada80b619
commit e26e0955b3
5 changed files with 90 additions and 61 deletions

View file

@ -310,7 +310,6 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
c.Assert(response.StatusCode, checker.Equals, 200) c.Assert(response.StatusCode, checker.Equals, 200)
} }
//TODO : TestCertificatesContents
func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) { func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) {
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
// start traefik // start traefik

View file

@ -10,6 +10,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"crypto/tls"
"github.com/BurntSushi/ty/fun" "github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff" "github.com/cenkalti/backoff"
@ -20,7 +21,6 @@ import (
eventtypes "github.com/docker/engine-api/types/events" eventtypes "github.com/docker/engine-api/types/events"
"github.com/docker/engine-api/types/filters" "github.com/docker/engine-api/types/filters"
"github.com/docker/go-connections/sockets" "github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
"github.com/vdemeester/docker-events" "github.com/vdemeester/docker-events"
) )
@ -32,18 +32,10 @@ type Docker struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash"`
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"`
Domain string `description:"Default domain used"` 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"` 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) { func (provider *Docker) createClient() (client.APIClient, error) {
var httpClient *http.Client var httpClient *http.Client
httpHeaders := map[string]string{ httpHeaders := map[string]string{
@ -51,16 +43,16 @@ func (provider *Docker) createClient() (client.APIClient, error) {
"User-Agent": "Traefik", "User-Agent": "Traefik",
} }
if provider.TLS != nil { if provider.TLS != nil {
tlsOptions := tlsconfig.Options{ config, err := provider.TLS.CreateTLSConfig()
CAFile: provider.TLS.CA,
CertFile: provider.TLS.Cert,
KeyFile: provider.TLS.Key,
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
}
config, err := tlsconfig.Client(tlsOptions)
if err != nil { if err != nil {
return nil, err 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{ tr := &http.Transport{
TLSClientConfig: config, TLSClientConfig: config,
} }
@ -74,6 +66,7 @@ func (provider *Docker) createClient() (client.APIClient, error) {
httpClient = &http.Client{ httpClient = &http.Client{
Transport: tr, Transport: tr,
} }
} }
return client.NewClient(provider.Endpoint, DockerAPIVersion, httpClient, httpHeaders) return client.NewClient(provider.Endpoint, DockerAPIVersion, httpClient, httpHeaders)
} }

View file

@ -2,10 +2,7 @@
package provider package provider
import ( import (
"crypto/tls"
"crypto/x509"
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"text/template" "text/template"
"time" "time"
@ -23,21 +20,13 @@ import (
// Kv holds common configurations of key-value providers. // Kv holds common configurations of key-value providers.
type Kv struct { type Kv struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash"`
Endpoint string `description:"Comma sepparated server endpoints"` Endpoint string `description:"Comma sepparated server endpoints"`
Prefix string `description:"Prefix used for KV store"` Prefix string `description:"Prefix used for KV store"`
TLS *KvTLS `description:"Enable TLS support"` TLS *ClientTLS `description:"Enable TLS support"`
storeType store.Backend storeType store.Backend
kvclient store.Store 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 { func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error {
operation := func() error { operation := func() error {
events, err := provider.kvclient.WatchTree(provider.Prefix, make(chan struct{})) 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 { if provider.TLS != nil {
caPool := x509.NewCertPool() var err error
storeConfig.TLS, err = provider.TLS.CreateTLSConfig()
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 { if err != nil {
return fmt.Errorf("Failed to load TLS keypair: %v", err) return err
}
storeConfig.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
} }
} }

View file

@ -8,7 +8,6 @@ import (
"strings" "strings"
"text/template" "text/template"
"crypto/tls"
"github.com/BurntSushi/ty/fun" "github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff" "github.com/cenkalti/backoff"
@ -22,13 +21,13 @@ import (
// Marathon holds configuration of the Marathon provider. // Marathon holds configuration of the Marathon provider.
type Marathon struct { type Marathon struct {
BaseProvider BaseProvider
Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string `description:"Default domain used"` Domain string `description:"Default domain used"`
ExposedByDefault bool `description:"Expose Marathon apps by default"` ExposedByDefault bool `description:"Expose Marathon apps by default"`
GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"` GroupsAsSubDomains bool `description:"Convert Marathon groups to subdomains"`
DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"` DCOSToken string `description:"DCOSToken for DCOS environment, This will override the Authorization header"`
TLS *ClientTLS `description:"Enable Docker TLS support"`
Basic *MarathonBasic Basic *MarathonBasic
TLS *tls.Config
marathonClient marathon.Marathon marathonClient marathon.Marathon
} }
@ -58,9 +57,13 @@ func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage,
if len(provider.DCOSToken) > 0 { if len(provider.DCOSToken) > 0 {
config.DCOSToken = provider.DCOSToken config.DCOSToken = provider.DCOSToken
} }
TLSConfig, err := provider.TLS.CreateTLSConfig()
if err != nil {
return err
}
config.HTTPClient = &http.Client{ config.HTTPClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: provider.TLS, TLSClientConfig: TLSConfig,
}, },
} }
client, err := marathon.NewClient(config) client, err := marathon.NewClient(config)

View file

@ -7,10 +7,14 @@ import (
"text/template" "text/template"
"unicode" "unicode"
"crypto/tls"
"crypto/x509"
"fmt"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/containous/traefik/autogen" "github.com/containous/traefik/autogen"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"os"
) )
// Provider defines methods of a provider. // Provider defines methods of a provider.
@ -92,3 +96,62 @@ func normalize(name string) string {
// get function // get function
return strings.Join(strings.FieldsFunc(name, fargs), "-") 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
}