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)
}
//TODO : TestCertificatesContents
func (s *EtcdSuite) TestCertificatesContentstWithSNIConfigHandshake(c *check.C) {
etcdHost := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress
// start traefik

View file

@ -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)
}

View file

@ -2,10 +2,7 @@
package provider
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"strings"
"text/template"
"time"
@ -25,19 +22,11 @@ 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"`
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)
var err error
storeConfig.TLS, err = provider.TLS.CreateTLSConfig()
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 TLS keypair: %v", err)
}
storeConfig.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
InsecureSkipVerify: provider.TLS.InsecureSkipVerify,
return err
}
}

View file

@ -8,7 +8,6 @@ import (
"strings"
"text/template"
"crypto/tls"
"github.com/BurntSushi/ty/fun"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
@ -27,8 +26,8 @@ type Marathon struct {
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)

View file

@ -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
}