From b83fb525a8bca5fa1c950c3d438c4bf4d7646f7f Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 7 Jul 2016 17:25:42 +0200 Subject: [PATCH] Add TLS support for etcd and consul --- glide.lock | 14 ++-- integration/consul_test.go | 88 +++++++++++++++++++- integration/resources/compose/consul_tls.yml | 14 ++++ integration/resources/tls/ca.cert | 22 +++++ integration/resources/tls/consul.cert | 19 +++++ integration/resources/tls/consul.key | 16 ++++ integration/resources/tls/consul_config.json | 9 ++ traefik.go | 42 +++++----- 8 files changed, 197 insertions(+), 27 deletions(-) create mode 100644 integration/resources/compose/consul_tls.yml create mode 100644 integration/resources/tls/ca.cert create mode 100644 integration/resources/tls/consul.cert create mode 100644 integration/resources/tls/consul.key create mode 100644 integration/resources/tls/consul_config.json diff --git a/glide.lock b/glide.lock index ee4e37f06..ba9b6d010 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: cf8f42aae193bd26b7670157094e6b11590443bc500f0a78243ee21bd3ed314e -updated: 2016-06-23T17:46:42.381770076+02:00 +hash: 70ad4e576bc1fa845512cce6b4ade5c422ba4fb5bb0472b37e1d3a93f13809cd +updated: 2016-07-07T17:33:16.358775373+02:00 imports: - name: github.com/boltdb/bolt version: 3f7947a25d970e1e5f512276c14d5dcf731ccd5e @@ -36,7 +36,7 @@ imports: subpackages: - spew - name: github.com/docker/distribution - version: edd7cb5249d0a45262b20bb58b838ecf4fb368bd + version: 4e17ab5d319ac5b70b2769442947567a83386fbc subpackages: - reference - digest @@ -100,7 +100,7 @@ imports: - name: github.com/gorilla/context version: aed02d124ae4a0e94fea4541c8effd05bf0c8296 - name: github.com/hashicorp/consul - version: 6e061b2d580d80347b7c5c4dfc8730de7403a145 + version: 64e3033d3c7f80af3f925f4665a9bc1af75d6153 subpackages: - api - name: github.com/hashicorp/go-cleanhttp @@ -134,7 +134,7 @@ imports: - name: github.com/Microsoft/go-winio version: ce2922f643c8fd76b46cadc7f404a06282678b34 - name: github.com/miekg/dns - version: 48ab6605c66ac797e07f615101c3e9e10e932b66 + version: 5d001d020961ae1c184f9f8152fdc73810481677 - name: github.com/mitchellh/mapstructure version: d2dd0262208475919e1a362f675cfc0e7c10e905 - name: github.com/moul/http2curl @@ -142,7 +142,7 @@ imports: - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: 5dc3f3576efb5262bf582217e93f86c93944374d + version: 9d7831e41d3ef428b67685eeb27f2b4a22a92391 subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest @@ -219,7 +219,7 @@ imports: subpackages: - acme - name: golang.org/x/crypto - version: f3241ce8505855877cc8a9717bd61a0f7c4ea83c + version: d81fdb778bf2c40a91b24519d60cdc5767318829 subpackages: - ocsp - name: golang.org/x/net diff --git a/integration/consul_test.go b/integration/consul_test.go index 9002a778c..79a5e6846 100644 --- a/integration/consul_test.go +++ b/integration/consul_test.go @@ -12,6 +12,7 @@ import ( "errors" "github.com/containous/traefik/integration/utils" + "github.com/containous/traefik/provider" checker "github.com/vdemeester/shakers" "io/ioutil" "os" @@ -24,7 +25,7 @@ type ConsulSuite struct { kv store.Store } -func (s *ConsulSuite) SetUpTest(c *check.C) { +func (s *ConsulSuite) setupConsul(c *check.C) { s.createComposeProject(c, "consul") s.composeProject.Start(c) @@ -52,6 +53,45 @@ func (s *ConsulSuite) SetUpTest(c *check.C) { c.Assert(err, checker.IsNil) } +func (s *ConsulSuite) setupConsulTLS(c *check.C) { + s.createComposeProject(c, "consul_tls") + s.composeProject.Start(c) + + consul.Register() + clientTLS := &provider.ClientTLS{ + CA: "resources/tls/ca.cert", + Cert: "resources/tls/consul.cert", + Key: "resources/tls/consul.key", + InsecureSkipVerify: true, + } + TLSConfig, err := clientTLS.CreateTLSConfig() + c.Assert(err, checker.IsNil) + + kv, err := libkv.NewStore( + store.CONSUL, + []string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8585"}, + &store.Config{ + ConnectionTimeout: 10 * time.Second, + TLS: TLSConfig, + }, + ) + + if err != nil { + c.Fatal("Cannot create store consul") + } + s.kv = kv + + // wait for consul + err = utils.Try(60*time.Second, func() error { + _, err := kv.Exists("test") + if err != nil { + return err + } + return nil + }) + c.Assert(err, checker.IsNil) +} + func (s *ConsulSuite) TearDownTest(c *check.C) { // shutdown and delete compose project if s.composeProject != nil { @@ -62,6 +102,7 @@ func (s *ConsulSuite) TearDownTest(c *check.C) { func (s *ConsulSuite) TearDownSuite(c *check.C) {} func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) { + s.setupConsul(c) consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulHost string }{consulHost}) defer os.Remove(file) @@ -79,6 +120,7 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) { } func (s *ConsulSuite) TestNominalConfiguration(c *check.C) { + s.setupConsul(c) consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress file := s.adaptFile(c, "fixtures/consul/simple.toml", struct{ ConsulHost string }{consulHost}) defer os.Remove(file) @@ -201,6 +243,7 @@ func (s *ConsulSuite) TestNominalConfiguration(c *check.C) { } func (s *ConsulSuite) TestGlobalConfiguration(c *check.C) { + s.setupConsul(c) consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress err := s.kv.Put("traefik/entrypoints/http/address", []byte(":8001"), nil) c.Assert(err, checker.IsNil) @@ -305,3 +348,46 @@ func (s *ConsulSuite) TestGlobalConfiguration(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(response.StatusCode, checker.Equals, 200) } + +func (s *ConsulSuite) TestGlobalConfigurationWithClientTLS(c *check.C) { + s.setupConsulTLS(c) + consulHost := s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + + err := s.kv.Put("traefik/web/address", []byte(":8081"), nil) + c.Assert(err, checker.IsNil) + + // wait for consul + err = utils.Try(60*time.Second, func() error { + _, err := s.kv.Exists("traefik/web/address") + if err != nil { + return err + } + return nil + }) + c.Assert(err, checker.IsNil) + + // start traefik + cmd := exec.Command(traefikBinary, "--configFile=fixtures/simple_web.toml", + "--consul", "--consul.endpoint="+consulHost+":8585", + "--consul.tls.ca=resources/tls/ca.cert", + "--consul.tls.cert=resources/tls/consul.cert", + "--consul.tls.key=resources/tls/consul.key", + "--consul.tls.insecureskipverify") + // cmd.Stdout = os.Stdout + // cmd.Stderr = os.Stderr + + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + // wait for traefik + err = utils.TryRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, func(res *http.Response) error { + _, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + return nil + }) + c.Assert(err, checker.IsNil) + +} diff --git a/integration/resources/compose/consul_tls.yml b/integration/resources/compose/consul_tls.yml new file mode 100644 index 000000000..41a666799 --- /dev/null +++ b/integration/resources/compose/consul_tls.yml @@ -0,0 +1,14 @@ +consul: + image: progrium/consul + command: -server -bootstrap -log-level debug -ui-dir /ui -config-dir /configs + ports: + - "8500:8500" + - "8585:8585" + expose: + - "8300" + - "8301" + - "8301/udp" + - "8302" + - "8302/udp" + volumes: + - ../tls:/configs \ No newline at end of file diff --git a/integration/resources/tls/ca.cert b/integration/resources/tls/ca.cert new file mode 100644 index 000000000..7d549690f --- /dev/null +++ b/integration/resources/tls/ca.cert @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrTCCApWgAwIBAgIJAO8QudN/gvGqMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxDTALBgNVBAcMBEx5b24xDzANBgNVBAoM +BlplbmlrYTENMAsGA1UEAwwEdGVzdDEeMBwGCSqGSIb3DQEJARYPdGVzdEB6ZW5p +a2EuY29tMB4XDTE2MDcwNjA5MTA1MloXDTI2MDcwNDA5MTA1MlowbTELMAkGA1UE +BhMCRlIxDzANBgNVBAgMBkZyYW5jZTENMAsGA1UEBwwETHlvbjEPMA0GA1UECgwG +WmVuaWthMQ0wCwYDVQQDDAR0ZXN0MR4wHAYJKoZIhvcNAQkBFg90ZXN0QHplbmlr +YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKwrXlde3J8hFY +hEvH1GH5SfA64/yb7pjXOwI3kvdS2dkbLglOL/jsolARAfFWxhhnnyJUy/BBzZtS +rZN/IukuLCypzjnF6I9koVwILU2EkhPcBUzPZWD6gDU42XZH/lgglZyTyLA/pi24 +eAag5xVuTBMmBGbRsJJEq8MYgzSOAQLu2K8vFPARZdnvOMXVpfrC5+RxDj1AzyxU +5s7olWWG13cWkkh2PUNdb1gCXsz34ALG3EmD2S92tovkKHUZS5zHnOvFl8bF7bKC +MoXBi4bL2cUQXq815uFl0gfRrBgN4U+uT2UjzhIV9ax/xnkGueXi9wGPYP3Yanu8 +dguEtevRAgMBAAGjUDBOMB0GA1UdDgQWBBSxdmZrC6APPhMg73JGRa1sKPB2CDAf +BgNVHSMEGDAWgBSxdmZrC6APPhMg73JGRa1sKPB2CDAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQBEZNfJxlKr/hv/cyfbJX6yUKDRG/sIFVD4G9uNEKak +N9Dm5/FZ3pzosq/mBuMjXyY/5kYfiBPpyJfUK7CpWfa/U1RP76dDPm+3aaTNK0XS +rWWxP/n5plfb6bt53cfKrnk9ud9ZqY6jX0vQzbVp6F4+jN3ZZfl4SEwlbK0jnrYV +pbjPKbDS5o0RNgLuk/KN9x/KLb9FdgTYxVrB4orDUzpxx54sjfHRGodUAO9VIlbZ +WteavUhCqbVWvYBB64vxKY695PeX79nmwCMVmsy8luquJYgIn27Czexuei3+2mxX +f1rPZL+iCzi8cuShXqhrxH2dNyxsmYFjiPwFHSVgYtL2 +-----END CERTIFICATE----- diff --git a/integration/resources/tls/consul.cert b/integration/resources/tls/consul.cert new file mode 100644 index 000000000..786e1b991 --- /dev/null +++ b/integration/resources/tls/consul.cert @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBEDANBgkqhkiG9w0BAQUFADBtMQswCQYDVQQGEwJGUjEP +MA0GA1UECAwGRnJhbmNlMQ0wCwYDVQQHDARMeW9uMQ8wDQYDVQQKDAZaZW5pa2Ex +DTALBgNVBAMMBHRlc3QxHjAcBgkqhkiG9w0BCQEWD3Rlc3RAemVuaWthLmNvbTAe +Fw0xNjA3MDYxMzMzMTJaFw0yNjA3MDQxMzMzMTJaMBQxEjAQBgNVBAMMCWxvY2Fs +aG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyOvmLF7FLDur27bX7OYo +lh3RF/9Bo0Eq1+uFbqs7/KNVp66njn4QYT+OLcRTovoCIbTqaFT7jeqIKxpJ+DWL +n61BENZvsfSPkxTyF/zekarMHhvrMSpPqEP+NFnfmEVQ4kUELAyREmq6qkZloavV +8X8obRjGbNGuWpNLAlO0g68CAwEAAaOBizCBiDAJBgNVHRMEAjAAMB0GA1UdDgQW +BBQPlr+xQCpVYfksoxb+tsnNkL63EjAfBgNVHSMEGDAWgBSxdmZrC6APPhMg73JG +Ra1sKPB2CDALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF +BwMCMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQEFBQADggEBAEUgwhGh48yJ +JDlR7lT+uWQkPGXuXuATYKOt2fnqYFQ3z3jzP2Og8mR/iZJm85GvsN7CyfVi8hZL +sNJOJ1KPRrGVrOFGD4fd8e1sYYw1wowyEiBQii9f/BGy8khw7rl5RrZotuffTulx +PWXF9EyO+vLhpkPzCXG7CkJdakWfJX/83C7xfC+wOpyeGG89IW2l4W6yofLV88hL +LqBLfuL3J9ZknplSmHDB4W4TFr9aHd2zXdgAUgGd+b0+JfxZtxClivn8RoIkR6Dr +JKhxFO9i714+0MKQMEWAvAFcTw8I9ddkQ4cWs9YoYKYdF/cxowAxGYuykI+H92PO +ABAA2aH8ilE= +-----END CERTIFICATE----- diff --git a/integration/resources/tls/consul.key b/integration/resources/tls/consul.key new file mode 100644 index 000000000..182a186b6 --- /dev/null +++ b/integration/resources/tls/consul.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMjr5ixexSw7q9u2 +1+zmKJYd0Rf/QaNBKtfrhW6rO/yjVaeup45+EGE/ji3EU6L6AiG06mhU+43qiCsa +Sfg1i5+tQRDWb7H0j5MU8hf83pGqzB4b6zEqT6hD/jRZ35hFUOJFBCwMkRJquqpG +ZaGr1fF/KG0YxmzRrlqTSwJTtIOvAgMBAAECgYB2kymK4/8vRKP/DeBOkeI//abJ +p73/79SuCvP7RRko1ugVBrEiGenmypBJGEVXuH4LkG6KViUDMvdboK8oycj0zL6y +4naKuWct6EOcxSLhdFyCFLPY+0ggl3F9oG92D02H/3oU7ORBNFBaigSYRSP8EieT +5LxCkM2L1cElMJ/6cQJBAP901eNQZSvikAPma+9oPySD01e9yr0AE06wBxGNMkHH +OS07WknvIdJAMDKng5Umbp4EG/3UbV5ED/y3NoO22YkCQQDJWVrP5nEx53EXCENb +LWDA7SBxjBX60pqvuguDZSjsONQJUlMlqebZSzf/ezLGRUhkzRek8uOwz8MGKnTV +sf13AkAGvE3ncHc6cP7bG3g9F8KSc+deqOJvmVDpAjste0uX8GjRiH8Y8/UwVgDv +VPtjM2A3SmRyjOdVVPYW8728O1YBAkEAl4aPOPYLKasrCFJHnk5ACfBqAgmSYPgt +QSGZmICAk4UQzRMPT8DU4aIhujpUs7FgEbvml1PS1jUEZ5d75XXVcQJAZI50y14r +LJ4H+Q2NvvmeyuI8csX+63IGGd/Zt9/EYj4TQnKISnTV3cr/vkmsdoCevC4dT8rS +0d1rqCvfNzBUPA== +-----END PRIVATE KEY----- diff --git a/integration/resources/tls/consul_config.json b/integration/resources/tls/consul_config.json new file mode 100644 index 000000000..005d381a7 --- /dev/null +++ b/integration/resources/tls/consul_config.json @@ -0,0 +1,9 @@ +{ + "ports": { + "https": 8585 + }, + "ca_file": "/configs/ca.cert", + "cert_file": "/configs/consul.cert", + "key_file": "/configs/consul.key", + "verify_outgoing": true +} \ No newline at end of file diff --git a/traefik.go b/traefik.go index 49af4e1a4..5574ee977 100644 --- a/traefik.go +++ b/traefik.go @@ -22,6 +22,7 @@ import ( "runtime" "strings" "text/template" + "time" ) var versionTemplate = `Version: {{.Version}} @@ -118,22 +119,40 @@ Complete documentation is available at https://traefik.io`, var kv *staert.KvSource var err error + storeConfig := &store.Config{ + ConnectionTimeout: 30 * time.Second, + Bucket: "traefik", + } if traefikConfiguration.Consul != nil { //init KvSource + if traefikConfiguration.Consul.TLS != nil { + storeConfig.TLS, err = traefikConfiguration.Consul.TLS.CreateTLSConfig() + if err != nil { + fmtlog.Println(err) + os.Exit(-1) + } + } consul.Register() kv, err = staert.NewKvSource( store.CONSUL, strings.Split(traefikConfiguration.Consul.Endpoint, ","), - nil, - strings.TrimPrefix(traefikConfiguration.Consul.Prefix, "/"), // TrimPrefix should be done in https://github.com/docker/libkv/blob/master/store/consul/consul.go#L113 : IDK why it doen't work + storeConfig, + strings.TrimPrefix(traefikConfiguration.Consul.Prefix, "/"), ) } else if traefikConfiguration.Etcd != nil { //init KvSource + if traefikConfiguration.Etcd.TLS != nil { + storeConfig.TLS, err = traefikConfiguration.Etcd.TLS.CreateTLSConfig() + if err != nil { + fmtlog.Println(err) + os.Exit(-1) + } + } etcd.Register() kv, err = staert.NewKvSource( store.ETCD, strings.Split(traefikConfiguration.Etcd.Endpoint, ","), - nil, + storeConfig, traefikConfiguration.Etcd.Prefix, ) } else if traefikConfiguration.Zookeeper != nil { @@ -160,25 +179,10 @@ Complete documentation is available at https://traefik.io`, os.Exit(-1) } - // TO DELETE : Used once to fill the kv store - // if kv != nil { - // fmtlog.Println("Try to store global configuration in consul store") - // if err := kv.StoreConfig(traefikConfiguration); err != nil { - // fmtlog.Println(fmt.Errorf("Error : %s", err)) - // os.Exit(-1) - // } else { - // fmtlog.Println("It seems okay :)") - // jsonConf, _ := json.Marshal(traefikConfiguration) - // fmtlog.Printf("Global configuration loaded %s", string(jsonConf)) - // os.Exit(0) - // } - // } - - //TODO : log warning if many KvStore or set priority if kv != nil { s.AddSource(kv) if _, err := s.LoadConfig(); err != nil { - fmtlog.Println(fmt.Errorf("Error : %s", err)) + fmtlog.Println(err) } }