From 6e14334a9f7446142579598a12bb55a5dd644da2 Mon Sep 17 00:00:00 2001 From: Adam Talbot Date: Tue, 26 Dec 2017 22:59:13 +0000 Subject: [PATCH 1/4] tls: add cipher flag --- .gitignore | 2 + embed/config.go | 6 +++ etcdctl/ctlv2/command/util.go | 19 ++++++-- etcdctl/ctlv2/ctl.go | 8 ++++ etcdctl/ctlv3/command/global.go | 16 +++++++ etcdctl/ctlv3/ctl.go | 3 ++ etcdmain/config.go | 18 ++++++-- pkg/flags/strings.go | 79 ++++++++++++++++++++++++++++++--- pkg/flags/strings_test.go | 34 ++++++++++++++ pkg/flags/urls.go | 5 +++ pkg/tlsutil/tlscipher.go | 72 ++++++++++++++++++++++++++++++ pkg/transport/listener.go | 7 ++- 12 files changed, 253 insertions(+), 16 deletions(-) create mode 100644 pkg/tlsutil/tlscipher.go diff --git a/.gitignore b/.gitignore index 1a68387a7db..300bbb7a6c4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ hack/scripts-dev/docker-dns/.Dockerfile hack/scripts-dev/docker-dns-srv/.Dockerfile hack/tls-setup/certs .idea +vendor +.vscode \ No newline at end of file diff --git a/embed/config.go b/embed/config.go index 520291cc5c6..605cc2ef6ff 100644 --- a/embed/config.go +++ b/embed/config.go @@ -516,12 +516,15 @@ func (cfg Config) defaultClientHost() bool { } func (cfg *Config) ClientSelfCert() (err error) { + cipherSuites := cfg.ClientTLSInfo.CipherSuites + if cfg.ClientAutoTLS && cfg.ClientTLSInfo.Empty() { chosts := make([]string, len(cfg.LCUrls)) for i, u := range cfg.LCUrls { chosts[i] = u.Host } cfg.ClientTLSInfo, err = transport.SelfCert(filepath.Join(cfg.Dir, "fixtures", "client"), chosts) + cfg.ClientTLSInfo.CipherSuites = cipherSuites return err } else if cfg.ClientAutoTLS { plog.Warningf("ignoring client auto TLS since certs given") @@ -530,12 +533,15 @@ func (cfg *Config) ClientSelfCert() (err error) { } func (cfg *Config) PeerSelfCert() (err error) { + cipherSuites := cfg.PeerTLSInfo.CipherSuites + if cfg.PeerAutoTLS && cfg.PeerTLSInfo.Empty() { phosts := make([]string, len(cfg.LPUrls)) for i, u := range cfg.LPUrls { phosts[i] = u.Host } cfg.PeerTLSInfo, err = transport.SelfCert(filepath.Join(cfg.Dir, "fixtures", "peer"), phosts) + cfg.PeerTLSInfo.CipherSuites = cipherSuites return err } else if cfg.PeerAutoTLS { plog.Warningf("ignoring peer auto TLS since certs given") diff --git a/etcdctl/ctlv2/command/util.go b/etcdctl/ctlv2/command/util.go index e4719d77a1b..ad812af2983 100644 --- a/etcdctl/ctlv2/command/util.go +++ b/etcdctl/ctlv2/command/util.go @@ -29,6 +29,7 @@ import ( "time" "github.com/coreos/etcd/client" + "github.com/coreos/etcd/pkg/flags" "github.com/coreos/etcd/pkg/transport" "github.com/bgentry/speakeasy" @@ -155,6 +156,8 @@ func getTransport(c *cli.Context) (*http.Transport, error) { cafile := c.GlobalString("ca-file") certfile := c.GlobalString("cert-file") keyfile := c.GlobalString("key-file") + insecureSkipVerify := c.GlobalBool("insecure-skip-tls-verify") + ciphers := c.GlobalGeneric("cipher-suites").(*flags.StringSliceFlag) // Use an environment variable if nothing was supplied on the // command line @@ -167,16 +170,24 @@ func getTransport(c *cli.Context) (*http.Transport, error) { if keyfile == "" { keyfile = os.Getenv("ETCDCTL_KEY_FILE") } + if insecureSkipVerify == false { + insecureSkipVerify = os.Getenv("ETCDCTL_INSECURE_SKIP_TLS_VERIFY") != "" + } + if c := ciphers.Slice(); len(c) == 0 { + ciphers.Set(os.Getenv("ETCDCTL_CIPHER_SUITES")) + } discoveryDomain, insecure := getDiscoveryDomain(c) if insecure { discoveryDomain = "" } tls := transport.TLSInfo{ - CAFile: cafile, - CertFile: certfile, - KeyFile: keyfile, - ServerName: discoveryDomain, + CAFile: cafile, + CertFile: certfile, + KeyFile: keyfile, + CipherSuites: ciphers.Slice(), + ServerName: discoveryDomain, + InsecureSkipVerify: insecureSkipVerify, } dialTimeout := defaultDialTimeout diff --git a/etcdctl/ctlv2/ctl.go b/etcdctl/ctlv2/ctl.go index e949b06f09c..4bde4599d73 100644 --- a/etcdctl/ctlv2/ctl.go +++ b/etcdctl/ctlv2/ctl.go @@ -21,6 +21,8 @@ import ( "time" "github.com/coreos/etcd/etcdctl/ctlv2/command" + "github.com/coreos/etcd/pkg/flags" + "github.com/coreos/etcd/pkg/tlsutil" "github.com/coreos/etcd/version" "github.com/urfave/cli" ) @@ -54,6 +56,12 @@ func Start(apiv string) { cli.StringFlag{Name: "cert-file", Value: "", Usage: "identify HTTPS client using this SSL certificate file"}, cli.StringFlag{Name: "key-file", Value: "", Usage: "identify HTTPS client using this SSL key file"}, cli.StringFlag{Name: "ca-file", Value: "", Usage: "verify certificates of HTTPS-enabled servers using this CA bundle"}, + cli.BoolFlag{Name: "insecure-skip-tls-verify", Usage: "skip server certificate verification"}, + cli.GenericFlag{Name: "cipher-suites", + Value: flags.NewStringSliceFlag(tlsutil.AvailableCipherSuites()...), + Usage: "Comma-separated list of cipher suites for the server. " + + "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). " + + "If omitted, the default Go cipher suites will be used"}, cli.StringFlag{Name: "username, u", Value: "", Usage: "provide username[:password] and prompt if password is not supplied."}, cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connection timeout per request"}, cli.DurationFlag{Name: "total-timeout", Value: 5 * time.Second, Usage: "timeout for the command execution (except watch)"}, diff --git a/etcdctl/ctlv3/command/global.go b/etcdctl/ctlv3/command/global.go index ce607e82a0a..dbd0867574e 100644 --- a/etcdctl/ctlv3/command/global.go +++ b/etcdctl/ctlv3/command/global.go @@ -60,6 +60,7 @@ type secureCfg struct { cert string key string cacert string + ciphers []string serverName string insecureTransport bool @@ -173,6 +174,11 @@ func newClientCfg(endpoints []string, dialTimeout, keepAliveTime, keepAliveTimeo cfgtls = &tlsinfo } + if len(scfg.ciphers) != 0 { + tlsinfo.CipherSuites = scfg.ciphers + cfgtls = &tlsinfo + } + if scfg.serverName != "" { tlsinfo.ServerName = scfg.serverName cfgtls = &tlsinfo @@ -253,6 +259,7 @@ func secureCfgFromCmd(cmd *cobra.Command) *secureCfg { cert, key, cacert := keyAndCertFromCmd(cmd) insecureTr := insecureTransportFromCmd(cmd) skipVerify := insecureSkipVerifyFromCmd(cmd) + ciphers := ciphersFromCmd(cmd) discoveryCfg := discoveryCfgFromCmd(cmd) if discoveryCfg.insecure { @@ -263,6 +270,7 @@ func secureCfgFromCmd(cmd *cobra.Command) *secureCfg { cert: cert, key: key, cacert: cacert, + ciphers: ciphers, serverName: discoveryCfg.domain, insecureTransport: insecureTr, @@ -309,6 +317,14 @@ func keyAndCertFromCmd(cmd *cobra.Command) (cert, key, cacert string) { return cert, key, cacert } +func ciphersFromCmd(cmd *cobra.Command) []string { + ciphers, err := cmd.Flags().GetStringSlice("cipher-suites") + if err != nil { + ExitWithError(ExitError, err) + } + return ciphers +} + func authCfgFromCmd(cmd *cobra.Command) *authCfg { userFlag, err := cmd.Flags().GetString("user") if err != nil { diff --git a/etcdctl/ctlv3/ctl.go b/etcdctl/ctlv3/ctl.go index 8692084cfcd..fad9d1b1a7c 100644 --- a/etcdctl/ctlv3/ctl.go +++ b/etcdctl/ctlv3/ctl.go @@ -63,6 +63,9 @@ func init() { rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CertFile, "cert", "", "identify secure client using this TLS certificate file") rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.KeyFile, "key", "", "identify secure client using this TLS key file") rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle") + rootCmd.PersistentFlags().StringSliceVar(&globalFlags.TLS.CipherSuites, "cipher-suites", nil, "Comma-separated list of cipher suites for the server. "+ + "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ + "If omitted, the default Go cipher suites will be used") rootCmd.PersistentFlags().StringVar(&globalFlags.User, "user", "", "username[:password] for authentication (prompt if password is not supplied)") rootCmd.PersistentFlags().StringVarP(&globalFlags.TLS.ServerName, "discovery-srv", "d", "", "domain name to query for SRV records describing cluster endpoints") diff --git a/etcdmain/config.go b/etcdmain/config.go index 31241de9c5a..6b9e17e427f 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -27,6 +27,7 @@ import ( "github.com/coreos/etcd/embed" "github.com/coreos/etcd/pkg/flags" + "github.com/coreos/etcd/pkg/tlsutil" "github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/version" @@ -85,10 +86,11 @@ type config struct { // configFlags has the set of flags used for command line parsing a Config type configFlags struct { - flagSet *flag.FlagSet - clusterState *flags.StringsFlag - fallback *flags.StringsFlag - proxy *flags.StringsFlag + flagSet *flag.FlagSet + clusterState *flags.StringsFlag + fallback *flags.StringsFlag + proxy *flags.StringsFlag + tlsCipherSuites *flags.StringSliceFlag } func newConfig() *config { @@ -118,6 +120,9 @@ func newConfig() *config { proxyFlagReadonly, proxyFlagOn, ), + tlsCipherSuites: flags.NewStringSliceFlag( + tlsutil.AvailableCipherSuites()..., + ), } fs := cfg.cf.flagSet @@ -188,6 +193,9 @@ func newConfig() *config { fs.BoolVar(&cfg.ec.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") fs.StringVar(&cfg.ec.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.") fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedCN, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.") + fs.Var(cfg.cf.tlsCipherSuites, "cipher-suites", "Comma-separated list of cipher suites for the server. "+ + "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ + "If omitted, the default Go cipher suites will be used") // logging fs.BoolVar(&cfg.ec.Debug, "debug", false, "Enable debug-level logging for etcd.") @@ -277,6 +285,8 @@ func (cfg *config) configFromCmdLine() error { cfg.ec.ClusterState = cfg.cf.clusterState.String() cfg.cp.Fallback = cfg.cf.fallback.String() cfg.cp.Proxy = cfg.cf.proxy.String() + cfg.ec.ClientTLSInfo.CipherSuites = cfg.cf.tlsCipherSuites.Slice() + cfg.ec.PeerTLSInfo.CipherSuites = cfg.cf.tlsCipherSuites.Slice() // disable default advertise-client-urls if lcurls is set missingAC := flags.IsSet(cfg.cf.flagSet, "listen-client-urls") && !flags.IsSet(cfg.cf.flagSet, "advertise-client-urls") diff --git a/pkg/flags/strings.go b/pkg/flags/strings.go index 40ee432533a..6a2d1fad7ff 100644 --- a/pkg/flags/strings.go +++ b/pkg/flags/strings.go @@ -14,7 +14,10 @@ package flags -import "errors" +import ( + "errors" + "strings" +) // NewStringsFlag creates a new string flag for which any one of the given // strings is a valid value, and any other value is an error. @@ -25,7 +28,7 @@ func NewStringsFlag(valids ...string) *StringsFlag { return &StringsFlag{Values: valids, val: valids[0]} } -// StringsFlag implements the flag.Value interface. +// StringsFlag implements the flag.Value and pflag.Value interfaces. type StringsFlag struct { Values []string val string @@ -33,10 +36,10 @@ type StringsFlag struct { // Set verifies the argument to be a valid member of the allowed values // before setting the underlying flag value. -func (ss *StringsFlag) Set(s string) error { - for _, v := range ss.Values { +func (sf *StringsFlag) Set(s string) error { + for _, v := range sf.Values { if s == v { - ss.val = s + sf.val = s return nil } } @@ -44,6 +47,68 @@ func (ss *StringsFlag) Set(s string) error { } // String returns the set value (if any) of the StringsFlag -func (ss *StringsFlag) String() string { - return ss.val +func (sf *StringsFlag) String() string { + return sf.val +} + +// Type returns the given type as string +func (sf *StringsFlag) Type() string { + return "string" +} + +// StringSliceFlag implements the flag.Value and pflag.Value interfaces. +type StringSliceFlag struct { + Values []string + val []string +} + +// NewStringSliceFlag creates a new string slice flag for which any one of the given +// strings is a valid value, and any other value is an error. +func NewStringSliceFlag(valids ...string) *StringSliceFlag { + return &StringSliceFlag{Values: valids, val: []string{}} +} + +// Set verifies the argument to be a valid member of the allowed values +// before setting the underlying flag value. +func (ssf *StringSliceFlag) Set(s string) error { + sl := strings.Split(s, ",") + ssf.val = []string{} + + for _, s := range sl { + if !ssf.has(s) { + return errors.New("invalid value") + } + + ssf.val = append(ssf.val, s) + } + + return nil +} + +// String returns the set value (if any) of the StringSliceFlag +func (ssf *StringSliceFlag) String() string { + return strings.Join(ssf.val, ",") +} + +// Slice returns the set value (if any) of the StringSliceFlag as a slice +func (ssf *StringSliceFlag) Slice() []string { + clone := make([]string, len(ssf.val)) + copy(clone, ssf.val) + return clone +} + +// Type returns the given type as stringSlice +func (ssf *StringSliceFlag) Type() string { + return "stringSlice" +} + +// has checks to see if value in in allowed slice +func (ssf *StringSliceFlag) has(s string) bool { + for _, v := range ssf.Values { + if v == s { + return true + } + } + + return false } diff --git a/pkg/flags/strings_test.go b/pkg/flags/strings_test.go index b699961e3be..892b4dc3ec5 100644 --- a/pkg/flags/strings_test.go +++ b/pkg/flags/strings_test.go @@ -15,6 +15,7 @@ package flags import ( + "reflect" "testing" ) @@ -46,3 +47,36 @@ func TestStringsSet(t *testing.T) { } } } + +func TestStringSliceFlag(t *testing.T) { + tests := []struct { + vals []string + + val string + res []string + pass bool + }{ + // known values + {[]string{"abc", "def"}, "abc,def", []string{"abc", "def"}, true}, + {[]string{"on", "off", "false"}, "on", []string{"on"}, true}, + + // unrecognized values + {[]string{"abc", "def"}, "ghi", nil, false}, + {[]string{"abc", "def"}, "abc,ghi", nil, false}, + {[]string{"on", "off"}, "", nil, false}, + } + + for i, tt := range tests { + sf := NewStringSliceFlag(tt.vals...) + + err := sf.Set(tt.val) + if tt.pass != (err == nil) { + t.Errorf("#%d: want pass=%t, but got err=%v", i, tt.pass, err) + } + + res := sf.Slice() + if tt.pass && !reflect.DeepEqual(res, tt.res) { + t.Errorf("#%d: want slice=%v, but got slice=%v", i, tt.res, res) + } + } +} diff --git a/pkg/flags/urls.go b/pkg/flags/urls.go index 6383d7e9257..0871a78ef79 100644 --- a/pkg/flags/urls.go +++ b/pkg/flags/urls.go @@ -43,6 +43,11 @@ func (us *URLsValue) String() string { return strings.Join(all, ",") } +// Type returns the given type as urlSlice +func (us *URLsValue) Type() string { + return "urlSlice" +} + func NewURLsValue(init string) *URLsValue { v := &URLsValue{} if err := v.Set(init); err != nil { diff --git a/pkg/tlsutil/tlscipher.go b/pkg/tlsutil/tlscipher.go new file mode 100644 index 00000000000..c620e42bb17 --- /dev/null +++ b/pkg/tlsutil/tlscipher.go @@ -0,0 +1,72 @@ +// Copyright 2016 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tlsutil + +import ( + "crypto/tls" + "fmt" +) + +var ciphers = map[string]uint16{ + "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, + "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, + "TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256, + "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, +} + +func AvailableCipherSuites() []string { + c := make([]string, 0, len(ciphers)) + for v := range ciphers { + c = append(c, v) + } + + return c +} + +func TLSCipherSuites(cipherNames []string) ([]uint16, error) { + if len(cipherNames) == 0 { + return nil, nil + } + + ciphersIntSlice := make([]uint16, 0) + for _, cipher := range cipherNames { + intValue, ok := ciphers[cipher] + if !ok { + return nil, fmt.Errorf("Cipher suite %s not supported or doesn't exist", cipher) + } + + ciphersIntSlice = append(ciphersIntSlice, intValue) + } + + return ciphersIntSlice, nil +} diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 555618e6f0b..1d0c78a5026 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -59,6 +59,7 @@ func wrapTLS(addr, scheme string, tlsinfo *TLSInfo, l net.Listener) (net.Listene type TLSInfo struct { CertFile string KeyFile string + CipherSuites []string CAFile string // TODO: deprecate this in v4 TrustedCAFile string ClientCertAuth bool @@ -83,7 +84,7 @@ type TLSInfo struct { } func (info TLSInfo) String() string { - return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile) + return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v, crl-file = %s, cipher-suites = [%s]", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile, strings.Join(info.CipherSuites, ", ")) } func (info TLSInfo) Empty() bool { @@ -179,6 +180,10 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) { ServerName: info.ServerName, } + if cfg.CipherSuites, err = tlsutil.TLSCipherSuites(info.CipherSuites); err != nil { + return nil, err + } + if info.AllowedCN != "" { cfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { for _, chains := range verifiedChains { From 85bdac1213c252d900dcef310722df177df8e45a Mon Sep 17 00:00:00 2001 From: Adam Talbot Date: Tue, 6 Feb 2018 21:22:26 +0000 Subject: [PATCH 2/4] tls: handle ETCDCTL_INSECURE_SKIP_TLS_VERIFY flag in v2 client --- etcdctl/ctlv2/command/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etcdctl/ctlv2/command/util.go b/etcdctl/ctlv2/command/util.go index ad812af2983..8437eaf1606 100644 --- a/etcdctl/ctlv2/command/util.go +++ b/etcdctl/ctlv2/command/util.go @@ -171,7 +171,7 @@ func getTransport(c *cli.Context) (*http.Transport, error) { keyfile = os.Getenv("ETCDCTL_KEY_FILE") } if insecureSkipVerify == false { - insecureSkipVerify = os.Getenv("ETCDCTL_INSECURE_SKIP_TLS_VERIFY") != "" + insecureSkipVerify = strings.EqualFold(os.Getenv("ETCDCTL_INSECURE_SKIP_TLS_VERIFY"), "TRUE") } if c := ciphers.Slice(); len(c) == 0 { ciphers.Set(os.Getenv("ETCDCTL_CIPHER_SUITES")) From f50271c9ac2c4f1421c483eb7ac3876780580ae1 Mon Sep 17 00:00:00 2001 From: Adam Talbot Date: Tue, 6 Feb 2018 21:31:22 +0000 Subject: [PATCH 3/4] tls: cipher flag help message --- etcdctl/ctlv2/ctl.go | 7 ++++--- etcdctl/ctlv3/ctl.go | 6 ++++-- etcdmain/config.go | 4 ++-- etcdmain/help.go | 4 ++++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/etcdctl/ctlv2/ctl.go b/etcdctl/ctlv2/ctl.go index 9b07d9410c5..fd1cb46a6c3 100644 --- a/etcdctl/ctlv2/ctl.go +++ b/etcdctl/ctlv2/ctl.go @@ -18,12 +18,13 @@ package ctlv2 import ( "fmt" "os" + "strings" "time" "github.com/coreos/etcd/etcdctl/ctlv2/command" + "github.com/coreos/etcd/internal/version" "github.com/coreos/etcd/pkg/flags" "github.com/coreos/etcd/pkg/tlsutil" - "github.com/coreos/etcd/internal/version" "github.com/urfave/cli" ) @@ -60,8 +61,8 @@ func Start(apiv string) { cli.BoolFlag{Name: "insecure-skip-tls-verify", Usage: "skip server certificate verification"}, cli.GenericFlag{Name: "cipher-suites", Value: flags.NewStringSliceFlag(tlsutil.AvailableCipherSuites()...), - Usage: "Comma-separated list of cipher suites for the server. " + - "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). " + + Usage: "comma-separated list of cipher suites for the server. " + + "Available cipher suites include " + strings.Join(tlsutil.AvailableCipherSuites(), ",") + ". " + "If omitted, the default Go cipher suites will be used"}, cli.StringFlag{Name: "username, u", Value: "", Usage: "provide username[:password] and prompt if password is not supplied."}, cli.DurationFlag{Name: "timeout", Value: 2 * time.Second, Usage: "connection timeout per request"}, diff --git a/etcdctl/ctlv3/ctl.go b/etcdctl/ctlv3/ctl.go index fad9d1b1a7c..128151fd9f0 100644 --- a/etcdctl/ctlv3/ctl.go +++ b/etcdctl/ctlv3/ctl.go @@ -16,9 +16,11 @@ package ctlv3 import ( + "strings" "time" "github.com/coreos/etcd/etcdctl/ctlv3/command" + "github.com/coreos/etcd/pkg/tlsutil" "github.com/spf13/cobra" ) @@ -63,8 +65,8 @@ func init() { rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CertFile, "cert", "", "identify secure client using this TLS certificate file") rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.KeyFile, "key", "", "identify secure client using this TLS key file") rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle") - rootCmd.PersistentFlags().StringSliceVar(&globalFlags.TLS.CipherSuites, "cipher-suites", nil, "Comma-separated list of cipher suites for the server. "+ - "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ + rootCmd.PersistentFlags().StringSliceVar(&globalFlags.TLS.CipherSuites, "cipher-suites", nil, "comma-separated list of cipher suites for the server. "+ + "Available cipher suites include "+strings.Join(tlsutil.AvailableCipherSuites(), ",")+". "+ "If omitted, the default Go cipher suites will be used") rootCmd.PersistentFlags().StringVar(&globalFlags.User, "user", "", "username[:password] for authentication (prompt if password is not supplied)") rootCmd.PersistentFlags().StringVarP(&globalFlags.TLS.ServerName, "discovery-srv", "d", "", "domain name to query for SRV records describing cluster endpoints") diff --git a/etcdmain/config.go b/etcdmain/config.go index ac1a2b7817e..4aa1e033b2d 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -194,8 +194,8 @@ func newConfig() *config { fs.BoolVar(&cfg.ec.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") fs.StringVar(&cfg.ec.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.") fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedCN, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.") - fs.Var(cfg.cf.tlsCipherSuites, "cipher-suites", "Comma-separated list of cipher suites for the server. "+ - "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ + fs.Var(cfg.cf.tlsCipherSuites, "cipher-suites", "comma-separated list of cipher suites for the server. "+ + "Available cipher suites include "+strings.Join(tlsutil.AvailableCipherSuites(), ",")+". "+ "If omitted, the default Go cipher suites will be used") // logging diff --git a/etcdmain/help.go b/etcdmain/help.go index 6349a7e863b..5260fa46ba3 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -16,8 +16,10 @@ package etcdmain import ( "strconv" + "strings" "github.com/coreos/etcd/embed" + "github.com/coreos/etcd/pkg/tlsutil" ) var ( @@ -158,6 +160,8 @@ security flags: peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided. --peer-crl-file '' path to the peer certificate revocation list file. + --cipher-suites '' + comma-separated list of cipher suites for the server. Available cipher suites include ` + strings.Join(tlsutil.AvailableCipherSuites(), ",") + `. If omitted, the default Go cipher suites will be used. logging flags From a935f754634811cd02a14120a509b41d132e97e9 Mon Sep 17 00:00:00 2001 From: Adam Talbot Date: Tue, 6 Feb 2018 21:44:23 +0000 Subject: [PATCH 4/4] lint: gosimple util.go --- etcdctl/ctlv2/command/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etcdctl/ctlv2/command/util.go b/etcdctl/ctlv2/command/util.go index 8437eaf1606..36a96bd0cc4 100644 --- a/etcdctl/ctlv2/command/util.go +++ b/etcdctl/ctlv2/command/util.go @@ -170,7 +170,7 @@ func getTransport(c *cli.Context) (*http.Transport, error) { if keyfile == "" { keyfile = os.Getenv("ETCDCTL_KEY_FILE") } - if insecureSkipVerify == false { + if !insecureSkipVerify { insecureSkipVerify = strings.EqualFold(os.Getenv("ETCDCTL_INSECURE_SKIP_TLS_VERIFY"), "TRUE") } if c := ciphers.Slice(); len(c) == 0 {