From 3661b6e63bd1591ef1554b139e30c7f54651d212 Mon Sep 17 00:00:00 2001 From: Daniel Hiltgen Date: Fri, 2 Oct 2015 12:08:45 -0700 Subject: [PATCH] Add TLS support for libkv This adds TLS support into the KV store for swarm. The manage, join, and list commands all have a new CLI argument, matching the docker engine discovery backend. This required adding the tlsconfig utility package from docker engine. Here's an example showing re-use of the cluster certs for the KV store: swarm manage --tlsverify \ --tlscacert /etc/docker/ssl/ca.pem --tlscert /etc/docker/ssl/cert.pem --tlskey /etc/docker/ssl/key.pem --discovery-opt kv.cacertfile=/etc/docker/ssl/ca.pem --discovery-opt kv.certfile=/etc/docker/ssl/cert.pem --discovery-opt kv.keyfile=/etc/docker/ssl/key.pem --advertise 192.168.122.47:3376 etcd://192.168.122.47:2379 Signed-off-by: Daniel Hiltgen --- Godeps/Godeps.json | 4 + .../docker/docker/pkg/tlsconfig/config.go | 132 ++++++++++++++++++ cli/commands.go | 6 +- cli/create.go | 2 +- cli/flags.go | 5 + cli/join.go | 3 +- cli/list.go | 2 +- cli/manage.go | 20 ++- discovery/discovery.go | 8 +- discovery/file/file.go | 2 +- discovery/file/file_test.go | 8 +- discovery/kv/kv.go | 30 +++- discovery/kv/kv_test.go | 85 ++++++++++- discovery/nodes/nodes.go | 2 +- discovery/nodes/nodes_test.go | 6 +- discovery/token/token.go | 2 +- discovery/token/token_test.go | 6 +- 17 files changed, 291 insertions(+), 32 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/tlsconfig/config.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index e29b7d1bcb..2b1e8c70a5 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -35,6 +35,10 @@ "Comment": "v1.4.1-3245-g443437f", "Rev": "443437f5ea04da9d62bf3e05d7951f7d30e77d96" }, + { + "ImportPath": "github.com/docker/docker/pkg/tlsconfig", + "Rev": "c7a04fda2ad804601385f054c19b69cf43fcfe46" + }, { "ImportPath": "github.com/docker/docker/pkg/parsers/filters", "Comment": "v1.4.1-3245-g443437f", diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/tlsconfig/config.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/tlsconfig/config.go new file mode 100644 index 0000000000..9f7f336947 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docker/docker/pkg/tlsconfig/config.go @@ -0,0 +1,132 @@ +// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. +// +// As a reminder from https://golang.org/pkg/crypto/tls/#Config: +// A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified. +// A Config may be reused; the tls package will also not modify it. +package tlsconfig + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + + "github.com/Sirupsen/logrus" +) + +// Options represents the information needed to create client and server TLS configurations. +type Options struct { + CAFile string + + // If either CertFile or KeyFile is empty, Client() will not load them + // preventing the client from authenticating to the server. + // However, Server() requires them and will error out if they are empty. + CertFile string + KeyFile string + + // client-only option + InsecureSkipVerify bool + // server-only option + ClientAuth tls.ClientAuthType +} + +// Extra (server-side) accepted CBC cipher suites - will phase out in the future +var acceptedCBCCiphers = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_128_CBC_SHA, +} + +// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) +var clientCipherSuites = []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, +} + +// For use by code which already has a crypto/tls options struct but wants to +// use a commonly accepted set of TLS cipher suites, with known weak algorithms removed +var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) + +// ServerDefault is a secure-enough TLS configuration for the server TLS configuration. +var ServerDefault = tls.Config{ + // Avoid fallback to SSL protocols < TLS1.0 + MinVersion: tls.VersionTLS10, + PreferServerCipherSuites: true, + CipherSuites: DefaultServerAcceptedCiphers, +} + +// ClientDefault is a secure-enough TLS configuration for the client TLS configuration. +var ClientDefault = tls.Config{ + // Prefer TLS1.2 as the client minimum + MinVersion: tls.VersionTLS12, + CipherSuites: clientCipherSuites, +} + +// certPool returns an X.509 certificate pool from `caFile`, the certificate file. +func certPool(caFile string) (*x509.CertPool, error) { + // If we should verify the server, we need to load a trusted ca + certPool := x509.NewCertPool() + pem, err := ioutil.ReadFile(caFile) + if err != nil { + return nil, fmt.Errorf("Could not read CA certificate %q: %v", caFile, err) + } + if !certPool.AppendCertsFromPEM(pem) { + return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) + } + s := certPool.Subjects() + subjects := make([]string, len(s)) + for i, subject := range s { + subjects[i] = string(subject) + } + logrus.Debugf("Trusting certs with subjects: %v", subjects) + return certPool, nil +} + +// Client returns a TLS configuration meant to be used by a client. +func Client(options Options) (*tls.Config, error) { + tlsConfig := ClientDefault + tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify + if !options.InsecureSkipVerify { + CAs, err := certPool(options.CAFile) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = CAs + } + + if options.CertFile != "" && options.KeyFile != "" { + tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) + if err != nil { + return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err) + } + tlsConfig.Certificates = []tls.Certificate{tlsCert} + } + + return &tlsConfig, nil +} + +// Server returns a TLS configuration meant to be used by a server. +func Server(options Options) (*tls.Config, error) { + tlsConfig := ServerDefault + tlsConfig.ClientAuth = options.ClientAuth + tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err) + } + return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err) + } + tlsConfig.Certificates = []tls.Certificate{tlsCert} + if options.ClientAuth >= tls.VerifyClientCertIfGiven { + CAs, err := certPool(options.CAFile) + if err != nil { + return nil, err + } + tlsConfig.ClientCAs = CAs + } + return &tlsConfig, nil +} diff --git a/cli/commands.go b/cli/commands.go index 873c2228cb..117dddd18c 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -14,7 +14,7 @@ var ( Name: "list", ShortName: "l", Usage: "List nodes in a cluster", - Flags: []cli.Flag{flTimeout}, + Flags: []cli.Flag{flTimeout, flDiscoveryOpt}, Action: list, }, { @@ -28,14 +28,14 @@ var ( flTLS, flTLSCaCert, flTLSCert, flTLSKey, flTLSVerify, flHeartBeat, flEnableCors, - flCluster, flClusterOpt}, + flCluster, flDiscoveryOpt, flClusterOpt}, Action: manage, }, { Name: "join", ShortName: "j", Usage: "join a docker cluster", - Flags: []cli.Flag{flJoinAdvertise, flHeartBeat, flTTL}, + Flags: []cli.Flag{flJoinAdvertise, flHeartBeat, flTTL, flDiscoveryOpt}, Action: join, }, } diff --git a/cli/create.go b/cli/create.go index f325a1f6cf..afe95879c8 100644 --- a/cli/create.go +++ b/cli/create.go @@ -13,7 +13,7 @@ func create(c *cli.Context) { log.Fatalf("the `create` command takes no arguments. See '%s create --help'.", c.App.Name) } discovery := &token.Discovery{} - discovery.Initialize("", 0, 0) + discovery.Initialize("", 0, 0, nil) token, err := discovery.CreateCluster() if err != nil { log.Fatal(err) diff --git a/cli/flags.go b/cli/flags.go index feef1ce41f..d974030cbe 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -112,6 +112,11 @@ var ( Usage: "cluster driver options", Value: &cli.StringSlice{}, } + flDiscoveryOpt = cli.StringSliceFlag{ + Name: "discovery-opt", + Usage: "discovery options", + Value: &cli.StringSlice{}, + } flLeaderElection = cli.BoolFlag{ Name: "replication", diff --git a/cli/join.go b/cli/join.go index f56f85a076..d8aa732b77 100644 --- a/cli/join.go +++ b/cli/join.go @@ -42,7 +42,8 @@ func join(c *cli.Context) { if ttl <= hb { log.Fatal("--ttl must be strictly superior to the heartbeat value") } - d, err := discovery.New(dflag, hb, ttl) + + d, err := discovery.New(dflag, hb, ttl, getDiscoveryOpt(c)) if err != nil { log.Fatal(err) } diff --git a/cli/list.go b/cli/list.go index 2f3fd783d6..24e9db5467 100644 --- a/cli/list.go +++ b/cli/list.go @@ -19,7 +19,7 @@ func list(c *cli.Context) { log.Fatalf("invalid --timeout: %v", err) } - d, err := discovery.New(dflag, timeout, 0) + d, err := discovery.New(dflag, timeout, 0, getDiscoveryOpt(c)) if err != nil { log.Fatal(err) } diff --git a/cli/manage.go b/cli/manage.go index 19493ae154..801c801655 100644 --- a/cli/manage.go +++ b/cli/manage.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "path" + "strings" "time" log "github.com/Sirupsen/logrus" @@ -97,7 +98,7 @@ func loadTLSConfig(ca, cert, key string, verify bool) (*tls.Config, error) { } // Initialize the discovery service. -func createDiscovery(uri string, c *cli.Context) discovery.Discovery { +func createDiscovery(uri string, c *cli.Context, discoveryOpt []string) discovery.Discovery { hb, err := time.ParseDuration(c.String("heartbeat")) if err != nil { log.Fatalf("invalid --heartbeat: %v", err) @@ -107,7 +108,7 @@ func createDiscovery(uri string, c *cli.Context) discovery.Discovery { } // Set up discovery. - discovery, err := discovery.New(uri, hb, 0) + discovery, err := discovery.New(uri, hb, 0, getDiscoveryOpt(c)) if err != nil { log.Fatal(err) } @@ -115,6 +116,19 @@ func createDiscovery(uri string, c *cli.Context) discovery.Discovery { return discovery } +func getDiscoveryOpt(c *cli.Context) map[string]string { + // Process the store options + options := map[string]string{} + for _, option := range c.StringSlice("discovery-opt") { + if !strings.Contains(option, "=") { + log.Fatal("--discovery-opt must contain key=value strings") + } + kvpair := strings.SplitN(option, "=", 2) + options[kvpair[0]] = kvpair[1] + } + return options +} + func setupReplication(c *cli.Context, cluster cluster.Cluster, server *api.Server, discovery discovery.Discovery, addr string, leaderTTL time.Duration, tlsConfig *tls.Config) { kvDiscovery, ok := discovery.(*kvdiscovery.Discovery) if !ok { @@ -222,7 +236,7 @@ func manage(c *cli.Context) { if uri == "" { log.Fatalf("discovery required to manage a cluster. See '%s manage --help'.", c.App.Name) } - discovery := createDiscovery(uri, c) + discovery := createDiscovery(uri, c, c.StringSlice("discovery-opt")) s, err := strategy.New(c.String("strategy")) if err != nil { log.Fatal(err) diff --git a/discovery/discovery.go b/discovery/discovery.go index 9942de7cef..1888d1a0f4 100644 --- a/discovery/discovery.go +++ b/discovery/discovery.go @@ -84,8 +84,8 @@ func (e Entries) Diff(cmp Entries) (Entries, Entries) { // The Discovery interface is implemented by Discovery backends which // manage swarm host entries. type Discovery interface { - // Initialize the discovery with URIs, a heartbeat and a ttl. - Initialize(string, time.Duration, time.Duration) error + // Initialize the discovery with URIs, a heartbeat, a ttl and optional settings. + Initialize(string, time.Duration, time.Duration, map[string]string) error // Watch the discovery for entry changes. // Returns a channel that will receive changes or an error. @@ -133,12 +133,12 @@ func parse(rawurl string) (string, string) { // New returns a new Discovery given a URL, heartbeat and ttl settings. // Returns an error if the URL scheme is not supported. -func New(rawurl string, heartbeat time.Duration, ttl time.Duration) (Discovery, error) { +func New(rawurl string, heartbeat time.Duration, ttl time.Duration, discoveryOpt map[string]string) (Discovery, error) { scheme, uri := parse(rawurl) if discovery, exists := discoveries[scheme]; exists { log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service") - err := discovery.Initialize(uri, heartbeat, ttl) + err := discovery.Initialize(uri, heartbeat, ttl, discoveryOpt) return discovery, err } diff --git a/discovery/file/file.go b/discovery/file/file.go index 3e1566ea59..1ec72ad398 100644 --- a/discovery/file/file.go +++ b/discovery/file/file.go @@ -25,7 +25,7 @@ func Init() { } // Initialize is exported -func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration) error { +func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration, _ map[string]string) error { s.path = path s.heartbeat = heartbeat return nil diff --git a/discovery/file/file_test.go b/discovery/file/file_test.go index 6e861f9a82..05f248fb87 100644 --- a/discovery/file/file_test.go +++ b/discovery/file/file_test.go @@ -11,12 +11,12 @@ import ( func TestInitialize(t *testing.T) { d := &Discovery{} - d.Initialize("/path/to/file", 1000, 0) + d.Initialize("/path/to/file", 1000, 0, nil) assert.Equal(t, d.path, "/path/to/file") } func TestNew(t *testing.T) { - d, err := discovery.New("file:///path/to/file", 0, 0) + d, err := discovery.New("file:///path/to/file", 0, 0, nil) assert.NoError(t, err) assert.Equal(t, d.(*Discovery).path, "/path/to/file") } @@ -73,7 +73,7 @@ func TestWatch(t *testing.T) { // Set up file discovery. d := &Discovery{} - d.Initialize(tmp.Name(), 1000, 0) + d.Initialize(tmp.Name(), 1000, 0, nil) stopCh := make(chan struct{}) ch, errCh := d.Watch(stopCh) @@ -81,7 +81,7 @@ func TestWatch(t *testing.T) { assert.Error(t, <-errCh) // We have to drain the error channel otherwise Watch will get stuck. go func() { - for _ = range errCh { + for range errCh { } }() diff --git a/discovery/kv/kv.go b/discovery/kv/kv.go index 6e29d224a5..0abb94abae 100644 --- a/discovery/kv/kv.go +++ b/discovery/kv/kv.go @@ -7,6 +7,7 @@ import ( "time" log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/tlsconfig" "github.com/docker/libkv" "github.com/docker/libkv/store" "github.com/docker/libkv/store/consul" @@ -47,7 +48,7 @@ func Init() { } // Initialize is exported -func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration) error { +func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, discoveryOpt map[string]string) error { var ( parts = strings.SplitN(uris, "/", 2) addrs = strings.Split(parts[0], ",") @@ -63,9 +64,34 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du s.ttl = ttl s.path = path.Join(s.prefix, discoveryPath) + var config *store.Config + if discoveryOpt["kv.cacertfile"] != "" && discoveryOpt["kv.certfile"] != "" && discoveryOpt["kv.keyfile"] != "" { + log.Debug("Initializing discovery with TLS") + tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ + CAFile: discoveryOpt["kv.cacertfile"], + CertFile: discoveryOpt["kv.certfile"], + KeyFile: discoveryOpt["kv.keyfile"], + }) + if err != nil { + return err + } + config = &store.Config{ + // Set ClientTLS to trigger https (bug in libkv/etcd) + ClientTLS: &store.ClientTLSConfig{ + CACertFile: discoveryOpt["kv.cacertfile"], + CertFile: discoveryOpt["kv.certfile"], + KeyFile: discoveryOpt["kv.keyfile"], + }, + // The actual TLS config that will be used + TLS: tlsConfig, + } + } else { + log.Debug("Initializing discovery without TLS") + } + // Creates a new store, will ignore options given // if not supported by the chosen store - s.store, err = libkv.NewStore(s.backend, addrs, &store.Config{}) + s.store, err = libkv.NewStore(s.backend, addrs, config) return err } diff --git a/discovery/kv/kv_test.go b/discovery/kv/kv_test.go index d1e827db7a..a56f73aeb8 100644 --- a/discovery/kv/kv_test.go +++ b/discovery/kv/kv_test.go @@ -2,10 +2,13 @@ package kv import ( "errors" + "io/ioutil" + "os" "path" "testing" "time" + "github.com/docker/libkv" "github.com/docker/libkv/store" libkvmock "github.com/docker/libkv/store/mock" "github.com/docker/swarm/discovery" @@ -19,7 +22,7 @@ func TestInitialize(t *testing.T) { assert.NoError(t, err) d := &Discovery{backend: store.CONSUL} - d.Initialize("127.0.0.1", 0, 0) + d.Initialize("127.0.0.1", 0, 0, nil) d.store = storeMock s := d.store.(*libkvmock.Mock) @@ -32,7 +35,7 @@ func TestInitialize(t *testing.T) { assert.NoError(t, err) d = &Discovery{backend: store.CONSUL} - d.Initialize("127.0.0.1:1234/path", 0, 0) + d.Initialize("127.0.0.1:1234/path", 0, 0, nil) d.store = storeMock s = d.store.(*libkvmock.Mock) @@ -45,7 +48,7 @@ func TestInitialize(t *testing.T) { assert.NoError(t, err) d = &Discovery{backend: store.CONSUL} - d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0) + d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0, nil) d.store = storeMock s = d.store.(*libkvmock.Mock) @@ -57,13 +60,87 @@ func TestInitialize(t *testing.T) { assert.Equal(t, d.path, "path/"+discoveryPath) } +func TestInitializeWithCerts(t *testing.T) { + cert := `-----BEGIN CERTIFICATE----- +MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT +B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD +VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC +O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds ++J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q +V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb +UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55 +Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT +V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/ +BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j +BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz +7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI +xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M +ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY +8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn +t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX +FpTxDmJHEV4bzUzh +-----END CERTIFICATE----- +` + key := `-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4 ++zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR +SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr +pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe +rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj +xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj +i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx +qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO +1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5 +5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony +MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0 +ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP +L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N +XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT +Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B +LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU +t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+ +QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV +xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj +xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc +qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa +V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV +PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk +dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL +BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= +-----END RSA PRIVATE KEY----- +` + certFile, err := ioutil.TempFile("", "cert") + assert.Nil(t, err) + defer os.Remove(certFile.Name()) + certFile.Write([]byte(cert)) + certFile.Close() + keyFile, err := ioutil.TempFile("", "key") + assert.Nil(t, err) + defer os.Remove(keyFile.Name()) + keyFile.Write([]byte(key)) + keyFile.Close() + + libkv.AddStore("mock", libkvmock.New) + d := &Discovery{backend: "mock"} + err = d.Initialize("127.0.0.3:1234", 0, 0, map[string]string{ + "kv.cacertfile": certFile.Name(), + "kv.certfile": certFile.Name(), + "kv.keyfile": keyFile.Name(), + }) + assert.Nil(t, err) + s := d.store.(*libkvmock.Mock) + assert.Equal(t, s.Options.ClientTLS.CACertFile, certFile.Name()) + assert.Equal(t, s.Options.ClientTLS.CertFile, certFile.Name()) + assert.Equal(t, s.Options.ClientTLS.KeyFile, keyFile.Name()) +} + func TestWatch(t *testing.T) { storeMock, err := libkvmock.New([]string{"127.0.0.1:1234"}, nil) assert.NotNil(t, storeMock) assert.NoError(t, err) d := &Discovery{backend: store.CONSUL} - d.Initialize("127.0.0.1:1234/path", 0, 0) + d.Initialize("127.0.0.1:1234/path", 0, 0, nil) d.store = storeMock s := d.store.(*libkvmock.Mock) diff --git a/discovery/nodes/nodes.go b/discovery/nodes/nodes.go index f53061cfa0..181a15879f 100644 --- a/discovery/nodes/nodes.go +++ b/discovery/nodes/nodes.go @@ -23,7 +23,7 @@ func Init() { } // Initialize is exported -func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration) error { +func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration, _ map[string]string) error { for _, input := range strings.Split(uris, ",") { for _, ip := range discovery.Generate(input) { entry, err := discovery.NewEntry(ip) diff --git a/discovery/nodes/nodes_test.go b/discovery/nodes/nodes_test.go index d59e38621d..dd1ed4fe26 100644 --- a/discovery/nodes/nodes_test.go +++ b/discovery/nodes/nodes_test.go @@ -9,7 +9,7 @@ import ( func TestInitialize(t *testing.T) { d := &Discovery{} - d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0) + d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) assert.Equal(t, len(d.entries), 2) assert.Equal(t, d.entries[0].String(), "1.1.1.1:1111") assert.Equal(t, d.entries[1].String(), "2.2.2.2:2222") @@ -17,7 +17,7 @@ func TestInitialize(t *testing.T) { func TestInitializeWithPattern(t *testing.T) { d := &Discovery{} - d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0) + d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0, nil) assert.Equal(t, len(d.entries), 5) assert.Equal(t, d.entries[0].String(), "1.1.1.1:1111") assert.Equal(t, d.entries[1].String(), "1.1.1.2:1111") @@ -28,7 +28,7 @@ func TestInitializeWithPattern(t *testing.T) { func TestWatch(t *testing.T) { d := &Discovery{} - d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0) + d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) expected := discovery.Entries{ &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, diff --git a/discovery/token/token.go b/discovery/token/token.go index b2f389c436..e187a25308 100644 --- a/discovery/token/token.go +++ b/discovery/token/token.go @@ -33,7 +33,7 @@ func Init() { } // Initialize is exported -func (s *Discovery) Initialize(urltoken string, heartbeat time.Duration, ttl time.Duration) error { +func (s *Discovery) Initialize(urltoken string, heartbeat time.Duration, ttl time.Duration, _ map[string]string) error { if i := strings.LastIndex(urltoken, "/"); i != -1 { s.url = "https://" + urltoken[:i] s.token = urltoken[i+1:] diff --git a/discovery/token/token_test.go b/discovery/token/token_test.go index 16e3fa6ab9..0a409e186a 100644 --- a/discovery/token/token_test.go +++ b/discovery/token/token_test.go @@ -10,17 +10,17 @@ import ( func TestInitialize(t *testing.T) { discovery := &Discovery{} - err := discovery.Initialize("token", 0, 0) + err := discovery.Initialize("token", 0, 0, nil) assert.NoError(t, err) assert.Equal(t, discovery.token, "token") assert.Equal(t, discovery.url, DiscoveryURL) - err = discovery.Initialize("custom/path/token", 0, 0) + err = discovery.Initialize("custom/path/token", 0, 0, nil) assert.NoError(t, err) assert.Equal(t, discovery.token, "token") assert.Equal(t, discovery.url, "https://custom/path") - err = discovery.Initialize("", 0, 0) + err = discovery.Initialize("", 0, 0, nil) assert.Error(t, err) }