Certificate can contain path or file contents
Signed-off-by: Martin <martin.piegay@zenika.com>
This commit is contained in:
parent
056e0fe2d9
commit
7ada80b619
5 changed files with 172 additions and 14 deletions
|
@ -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
10
glide.lock
generated
|
@ -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
|
||||||
|
|
|
@ -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"))
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue