Certificate can contain path or file contents

Signed-off-by: Martin <martin.piegay@zenika.com>
This commit is contained in:
Martin 2016-06-27 12:19:14 +02:00
parent 056e0fe2d9
commit 7ada80b619
5 changed files with 172 additions and 14 deletions

View file

@ -1,11 +1,13 @@
package main package main
import ( import (
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"github.com/containous/traefik/acme" "github.com/containous/traefik/acme"
"github.com/containous/traefik/provider" "github.com/containous/traefik/provider"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"os"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@ -179,6 +181,43 @@ type TLS struct {
// Certificates defines traefik certificates type // Certificates defines traefik certificates type
type Certificates []Certificate type Certificates []Certificate
//CreateTLSConfig creates a TLS config from Certificate structures
func (certs *Certificates) CreateTLSConfig() (*tls.Config, error) {
config := &tls.Config{}
config.Certificates = []tls.Certificate{}
certsSlice := []Certificate(*certs)
for _, v := range certsSlice {
isAPath := false
_, errCert := os.Stat(v.CertFile)
_, errKey := os.Stat(v.KeyFile)
if errCert == nil {
if errKey == nil {
isAPath = true
} else {
return nil, fmt.Errorf("Bad TLS Certificate KeyFile format. Expected a path.")
}
} else if errKey == nil {
return nil, fmt.Errorf("Bad TLS Certificate KeyFile format. Expected a path.")
}
cert := tls.Certificate{}
var err error
if isAPath {
cert, err = tls.LoadX509KeyPair(v.CertFile, v.KeyFile)
if err != nil {
return nil, err
}
} else {
cert, err = tls.X509KeyPair([]byte(v.CertFile), []byte(v.KeyFile))
if err != nil {
return nil, err
}
}
config.Certificates = append(config.Certificates, cert)
}
return config, nil
}
// String is the method to format the flag's value, part of the flag.Value interface. // String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics. // The String method's output will be used in diagnostics.
func (certs *Certificates) String() string { func (certs *Certificates) String() string {
@ -209,6 +248,7 @@ func (certs *Certificates) Type() string {
} }
// Certificate holds a SSL cert/key pair // Certificate holds a SSL cert/key pair
// May can contain either path or file contents
type Certificate struct { type Certificate struct {
CertFile string CertFile string
KeyFile string KeyFile string

10
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 22c20a7d7419e9624267d7f0041cd8ad87afc876d2738fa559527c74f9917c3a hash: cf8f42aae193bd26b7670157094e6b11590443bc500f0a78243ee21bd3ed314e
updated: 2016-07-05T14:48:30.023831407+02:00 updated: 2016-06-23T17:46:42.381770076+02:00
imports: imports:
- name: github.com/boltdb/bolt - name: github.com/boltdb/bolt
version: 3f7947a25d970e1e5f512276c14d5dcf731ccd5e version: 3f7947a25d970e1e5f512276c14d5dcf731ccd5e
@ -36,7 +36,7 @@ imports:
subpackages: subpackages:
- spew - spew
- name: github.com/docker/distribution - name: github.com/docker/distribution
version: 4e17ab5d319ac5b70b2769442947567a83386fbc version: edd7cb5249d0a45262b20bb58b838ecf4fb368bd
subpackages: subpackages:
- reference - reference
- digest - digest
@ -142,7 +142,7 @@ imports:
- name: github.com/ogier/pflag - name: github.com/ogier/pflag
version: 45c278ab3607870051a2ea9040bb85fcb8557481 version: 45c278ab3607870051a2ea9040bb85fcb8557481
- name: github.com/opencontainers/runc - name: github.com/opencontainers/runc
version: 7221e387826c9918fa9fd6e7975baf4d30c8fa54 version: 5dc3f3576efb5262bf582217e93f86c93944374d
subpackages: subpackages:
- libcontainer/user - libcontainer/user
- name: github.com/parnurzeal/gorequest - name: github.com/parnurzeal/gorequest
@ -219,7 +219,7 @@ imports:
subpackages: subpackages:
- acme - acme
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: 0c565bf13221fb55497d7ae2bb95694db1fd1bff version: f3241ce8505855877cc8a9717bd61a0f7c4ea83c
subpackages: subpackages:
- ocsp - ocsp
- name: golang.org/x/net - name: golang.org/x/net

View file

@ -8,6 +8,7 @@ import (
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"github.com/containous/traefik/integration/utils" "github.com/containous/traefik/integration/utils"
@ -308,3 +309,126 @@ func (s *EtcdSuite) TestGlobalConfiguration(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
c.Assert(response.StatusCode, checker.Equals, 200) 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
cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml", "--etcd", "--etcd.endpoint="+etcdHost+":4001")
// cmd.Stdout = os.Stdout
// cmd.Stderr = os.Stderr
whoami1 := s.composeProject.Container(c, "whoami1")
whoami2 := s.composeProject.Container(c, "whoami2")
whoami3 := s.composeProject.Container(c, "whoami3")
whoami4 := s.composeProject.Container(c, "whoami4")
//Copy the contents of the certificate files into ETCD
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
c.Assert(err, checker.IsNil)
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
c.Assert(err, checker.IsNil)
snitestOrgCert, err := ioutil.ReadFile("fixtures/https/snitest.org.cert")
c.Assert(err, checker.IsNil)
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
c.Assert(err, checker.IsNil)
globalConfig := map[string]string{
"/traefik/entrypoints/https/address": ":4443",
"/traefik/entrypoints/https/tls/certificates/0/certfile": string(snitestComCert),
"/traefik/entrypoints/https/tls/certificates/0/keyfile": string(snitestComKey),
"/traefik/entrypoints/https/tls/certificates/1/certfile": string(snitestOrgCert),
"/traefik/entrypoints/https/tls/certificates/1/keyfile": string(snitestOrgKey),
"/traefik/defaultentrypoints/0": "https",
}
backend1 := map[string]string{
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
"/traefik/backends/backend1/servers/server1/url": "http://" + whoami1.NetworkSettings.IPAddress + ":80",
"/traefik/backends/backend1/servers/server1/weight": "10",
"/traefik/backends/backend1/servers/server2/url": "http://" + whoami2.NetworkSettings.IPAddress + ":80",
"/traefik/backends/backend1/servers/server2/weight": "1",
}
backend2 := map[string]string{
"/traefik/backends/backend2/loadbalancer/method": "drr",
"/traefik/backends/backend2/servers/server1/url": "http://" + whoami3.NetworkSettings.IPAddress + ":80",
"/traefik/backends/backend2/servers/server1/weight": "1",
"/traefik/backends/backend2/servers/server2/url": "http://" + whoami4.NetworkSettings.IPAddress + ":80",
"/traefik/backends/backend2/servers/server2/weight": "2",
}
frontend1 := map[string]string{
"/traefik/frontends/frontend1/backend": "backend2",
"/traefik/frontends/frontend1/entrypoints": "http",
"/traefik/frontends/frontend1/priority": "1",
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
}
frontend2 := map[string]string{
"/traefik/frontends/frontend2/backend": "backend1",
"/traefik/frontends/frontend2/entrypoints": "http",
"/traefik/frontends/frontend2/priority": "10",
"/traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
}
for key, value := range globalConfig {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range backend1 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range backend2 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range frontend1 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range frontend2 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
// wait for etcd
err = utils.Try(60*time.Second, func() error {
_, err := s.kv.Exists("/traefik/frontends/frontend2/routes/test_2/rule")
if err != nil {
return err
}
return nil
})
c.Assert(err, checker.IsNil)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// wait for traefik
err = utils.TryRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, func(res *http.Response) error {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
if !strings.Contains(string(body), "Host:snitest.org") {
return errors.New("Incorrect traefik config")
}
return nil
})
c.Assert(err, checker.IsNil)
//check
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: "snitest.com",
}
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
c.Assert(err, checker.IsNil, check.Commentf("failed to connect to server"))
defer conn.Close()
err = conn.Handshake()
c.Assert(err, checker.IsNil, check.Commentf("TLS handshake error"))
cs := conn.ConnectionState()
err = cs.PeerCertificates[0].VerifyHostname("snitest.com")
c.Assert(err, checker.IsNil, check.Commentf("certificate did not match SNI servername"))
}

View file

@ -290,15 +290,10 @@ func (server *Server) createTLSConfig(entryPointName string, tlsOption *TLS, rou
return nil, nil return nil, nil
} }
config := &tls.Config{} config, err := tlsOption.Certificates.CreateTLSConfig()
config.Certificates = []tls.Certificate{}
for _, v := range tlsOption.Certificates {
cert, err := tls.LoadX509KeyPair(v.CertFile, v.KeyFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
config.Certificates = append(config.Certificates, cert)
}
if len(tlsOption.ClientCAFiles) > 0 { if len(tlsOption.ClientCAFiles) > 0 {
pool := x509.NewCertPool() pool := x509.NewCertPool()

View file

@ -176,7 +176,6 @@ Complete documentation is available at https://traefik.io`,
//TODO : log warning if many KvStore or set priority //TODO : log warning if many KvStore or set priority
if kv != nil { if kv != nil {
fmtlog.Println("KV Store found")
s.AddSource(kv) s.AddSource(kv)
if _, err := s.LoadConfig(); err != nil { if _, err := s.LoadConfig(); err != nil {
fmtlog.Println(fmt.Errorf("Error : %s", err)) fmtlog.Println(fmt.Errorf("Error : %s", err))