add struct ClientTLS : supports either a paths to a file or directly the certificate
This commit is contained in:
parent
7ada80b619
commit
e26e0955b3
5 changed files with 90 additions and 61 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue