From b352c12cb45288e876fc9c2f1c6dde75c3de1589 Mon Sep 17 00:00:00 2001 From: Ashmita Date: Sat, 5 Mar 2022 07:15:59 +0545 Subject: [PATCH 1/4] Add support for ciphersuites in tls config Signed-off-by: Ashmita --- pkg/config/tlscfg/ciphersuites.go | 64 +++++++++++++++++++++++ pkg/config/tlscfg/ciphersuites_test.go | 70 ++++++++++++++++++++++++++ pkg/config/tlscfg/flags.go | 3 ++ pkg/config/tlscfg/flags_test.go | 2 + pkg/config/tlscfg/options.go | 10 +++- 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 pkg/config/tlscfg/ciphersuites.go create mode 100644 pkg/config/tlscfg/ciphersuites_test.go diff --git a/pkg/config/tlscfg/ciphersuites.go b/pkg/config/tlscfg/ciphersuites.go new file mode 100644 index 00000000000..f10dcf80dc4 --- /dev/null +++ b/pkg/config/tlscfg/ciphersuites.go @@ -0,0 +1,64 @@ +// Copyright (c) 2022 The Jaeger 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 tlscfg + +import ( + "crypto/tls" + "fmt" + "strings" +) + +var ( + // https://golang.org/pkg/crypto/tls/#pkg-constants + ciphers = map[string]uint16{} + insecureCiphers = map[string]uint16{} +) + +func init() { + for _, suite := range tls.CipherSuites() { + ciphers[suite.Name] = suite.ID + } + for _, suite := range tls.InsecureCipherSuites() { + insecureCiphers[suite.Name] = suite.ID + } +} + +func allCiphers() map[string]uint16 { + acceptedCiphers := make(map[string]uint16, len(ciphers)) + for k, v := range ciphers { + acceptedCiphers[k] = v + } + for k, v := range insecureCiphers { + acceptedCiphers[k] = v + } + return acceptedCiphers +} + +// TLSCipherSuites returns a list of cipher suite IDs from the cipher suite names passed. +func TLSCipherSuites(cipherNames string) ([]uint16, error) { + if cipherNames == "" { + return nil, nil + } + ciphersIntSlice := make([]uint16, 0) + possibleCiphers := allCiphers() + for _, cipher := range strings.Split(cipherNames, ",") { + intValue, ok := possibleCiphers[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/config/tlscfg/ciphersuites_test.go b/pkg/config/tlscfg/ciphersuites_test.go new file mode 100644 index 00000000000..fc83f84fb09 --- /dev/null +++ b/pkg/config/tlscfg/ciphersuites_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2022 The Jaeger 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 tlscfg + +import ( + "crypto/tls" + "reflect" + "testing" +) + +func TestTLSCipherSuites(t *testing.T) { + tests := []struct { + flag string + expected []uint16 + expectedError bool + }{ + { + // Happy case + flag: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + expected: []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, + expectedError: false, + }, + { + // One flag only + flag: "TLS_AES_128_GCM_SHA256", + expected: []uint16{tls.TLS_AES_128_GCM_SHA256}, + expectedError: false, + }, + { + // Empty flag + flag: "", + expected: nil, + expectedError: false, + }, + { + // Duplicated flag + flag: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256", + expected: []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_AES_128_GCM_SHA256}, + expectedError: false, + }, + { + // Invalid flag + flag: "TLS_INVALID_CIPHER_SUITE", + expected: nil, + expectedError: true, + }, + } + + for i, test := range tests { + uIntFlags, err := TLSCipherSuites(test.flag) + if !reflect.DeepEqual(uIntFlags, test.expected) { + t.Errorf("%d: expected %+v, got %+v", i, test.expected, uIntFlags) + } + if test.expectedError && err == nil { + t.Errorf("%d: expecting error, got %+v", i, err) + } + } +} diff --git a/pkg/config/tlscfg/flags.go b/pkg/config/tlscfg/flags.go index a59cca7d4f7..12994dbac9d 100644 --- a/pkg/config/tlscfg/flags.go +++ b/pkg/config/tlscfg/flags.go @@ -29,6 +29,7 @@ const ( tlsServerName = tlsPrefix + ".server-name" tlsClientCA = tlsPrefix + ".client-ca" tlsSkipHostVerify = tlsPrefix + ".skip-host-verify" + tlsCipherSuites = tlsPrefix + ".cipher-suites" ) // ClientFlagsConfig describes which CLI flags for TLS client should be generated. @@ -57,6 +58,7 @@ func (c ServerFlagsConfig) AddFlags(flags *flag.FlagSet) { flags.String(c.Prefix+tlsCert, "", "Path to a TLS Certificate file, used to identify this server to clients") flags.String(c.Prefix+tlsKey, "", "Path to a TLS Private Key file, used to identify this server to clients") flags.String(c.Prefix+tlsClientCA, "", "Path to a TLS CA (Certification Authority) file used to verify certificates presented by clients (if unset, all clients are permitted)") + flags.String(c.Prefix+tlsCipherSuites, "", "Comma-separated list of cipher suites for the server, values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).") } // InitFromViper creates tls.Config populated with values retrieved from Viper. @@ -78,5 +80,6 @@ func (c ServerFlagsConfig) InitFromViper(v *viper.Viper) Options { p.CertPath = v.GetString(c.Prefix + tlsCert) p.KeyPath = v.GetString(c.Prefix + tlsKey) p.ClientCAPath = v.GetString(c.Prefix + tlsClientCA) + p.CipherSuites = v.GetString(c.Prefix + tlsCipherSuites) return p } diff --git a/pkg/config/tlscfg/flags_test.go b/pkg/config/tlscfg/flags_test.go index 908c848e954..f1cf9ba46b1 100644 --- a/pkg/config/tlscfg/flags_test.go +++ b/pkg/config/tlscfg/flags_test.go @@ -74,6 +74,7 @@ func TestServerFlags(t *testing.T) { "--prefix.tls.enabled=true", "--prefix.tls.cert=cert-file", "--prefix.tls.key=key-file", + "--prefix.tls.cipher-suites=TLS_RSA_WITH_RC4_128_SHA", } tests := []struct { @@ -107,6 +108,7 @@ func TestServerFlags(t *testing.T) { CertPath: "cert-file", KeyPath: "key-file", ClientCAPath: test.file, + CipherSuites: "TLS_RSA_WITH_RC4_128_SHA", }, tlsOpts) }) } diff --git a/pkg/config/tlscfg/options.go b/pkg/config/tlscfg/options.go index dc2e316cb5b..c7f35702ec3 100644 --- a/pkg/config/tlscfg/options.go +++ b/pkg/config/tlscfg/options.go @@ -33,6 +33,7 @@ type Options struct { KeyPath string `mapstructure:"key"` ServerName string `mapstructure:"server_name"` // only for client-side TLS config ClientCAPath string `mapstructure:"client_ca"` // only for server-side TLS config for client auth + CipherSuites string `mapstructure:"cipher_suites"` SkipHostVerify bool `mapstructure:"skip_host_verify"` certWatcher *certWatcher `mapstructure:"-"` } @@ -41,17 +42,24 @@ var systemCertPool = x509.SystemCertPool // to allow overriding in unit test // Config loads TLS certificates and returns a TLS Config. func (p *Options) Config(logger *zap.Logger) (*tls.Config, error) { - certPool, err := p.loadCertPool() if err != nil { return nil, fmt.Errorf("failed to load CA CertPool: %w", err) } + + cipherSuiteIds, err := TLSCipherSuites(p.CipherSuites) + if err != nil { + return nil, fmt.Errorf("failed to get cipher suite ids from cipher suite names: %w", err) + } + // #nosec G402 tlsCfg := &tls.Config{ RootCAs: certPool, ServerName: p.ServerName, InsecureSkipVerify: p.SkipHostVerify, + CipherSuites: cipherSuiteIds, } + if p.ClientCAPath != "" { certPool := x509.NewCertPool() if err := addCertToPool(p.ClientCAPath, certPool); err != nil { From 5be90d15afa0355ecbda50b1029c0cd6366c6d21 Mon Sep 17 00:00:00 2001 From: Ashmita152 Date: Sat, 5 Mar 2022 22:45:56 +0545 Subject: [PATCH 2/4] Feedbacks Signed-off-by: Ashmita152 --- pkg/config/tlscfg/ciphersuites.go | 39 +++++++------------------- pkg/config/tlscfg/ciphersuites_test.go | 14 ++++----- pkg/config/tlscfg/flags.go | 4 +-- pkg/config/tlscfg/flags_test.go | 4 +-- pkg/config/tlscfg/options.go | 4 +-- pkg/config/tlscfg/options_test.go | 7 +++++ 6 files changed, 30 insertions(+), 42 deletions(-) diff --git a/pkg/config/tlscfg/ciphersuites.go b/pkg/config/tlscfg/ciphersuites.go index f10dcf80dc4..f87228123b0 100644 --- a/pkg/config/tlscfg/ciphersuites.go +++ b/pkg/config/tlscfg/ciphersuites.go @@ -17,48 +17,29 @@ package tlscfg import ( "crypto/tls" "fmt" - "strings" ) -var ( - // https://golang.org/pkg/crypto/tls/#pkg-constants - ciphers = map[string]uint16{} - insecureCiphers = map[string]uint16{} -) - -func init() { - for _, suite := range tls.CipherSuites() { - ciphers[suite.Name] = suite.ID - } - for _, suite := range tls.InsecureCipherSuites() { - insecureCiphers[suite.Name] = suite.ID - } -} - func allCiphers() map[string]uint16 { - acceptedCiphers := make(map[string]uint16, len(ciphers)) - for k, v := range ciphers { - acceptedCiphers[k] = v - } - for k, v := range insecureCiphers { - acceptedCiphers[k] = v + acceptedCiphers := make(map[string]uint16) + for _, suite := range tls.CipherSuites() { + acceptedCiphers[suite.Name] = suite.ID } return acceptedCiphers } -// TLSCipherSuites returns a list of cipher suite IDs from the cipher suite names passed. -func TLSCipherSuites(cipherNames string) ([]uint16, error) { - if cipherNames == "" { +// CipherSuiteNamesToIDs returns a list of cipher suite IDs from the cipher suite names passed. +func CipherSuiteNamesToIDs(cipherNames []string) ([]uint16, error) { + if len(cipherNames) == 0 { return nil, nil } - ciphersIntSlice := make([]uint16, 0) + var ciphersIDs []uint16 possibleCiphers := allCiphers() - for _, cipher := range strings.Split(cipherNames, ",") { + for _, cipher := range cipherNames { intValue, ok := possibleCiphers[cipher] if !ok { return nil, fmt.Errorf("cipher suite %s not supported or doesn't exist", cipher) } - ciphersIntSlice = append(ciphersIntSlice, intValue) + ciphersIDs = append(ciphersIDs, intValue) } - return ciphersIntSlice, nil + return ciphersIDs, nil } diff --git a/pkg/config/tlscfg/ciphersuites_test.go b/pkg/config/tlscfg/ciphersuites_test.go index fc83f84fb09..cae0f845db2 100644 --- a/pkg/config/tlscfg/ciphersuites_test.go +++ b/pkg/config/tlscfg/ciphersuites_test.go @@ -22,44 +22,44 @@ import ( func TestTLSCipherSuites(t *testing.T) { tests := []struct { - flag string + flag []string expected []uint16 expectedError bool }{ { // Happy case - flag: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + flag: []string{"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, expected: []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, expectedError: false, }, { // One flag only - flag: "TLS_AES_128_GCM_SHA256", + flag: []string{"TLS_AES_128_GCM_SHA256"}, expected: []uint16{tls.TLS_AES_128_GCM_SHA256}, expectedError: false, }, { // Empty flag - flag: "", + flag: []string{}, expected: nil, expectedError: false, }, { // Duplicated flag - flag: "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256", + flag: []string{"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256"}, expected: []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_AES_128_GCM_SHA256}, expectedError: false, }, { // Invalid flag - flag: "TLS_INVALID_CIPHER_SUITE", + flag: []string{"TLS_INVALID_CIPHER_SUITE"}, expected: nil, expectedError: true, }, } for i, test := range tests { - uIntFlags, err := TLSCipherSuites(test.flag) + uIntFlags, err := CipherSuiteNamesToIDs(test.flag) if !reflect.DeepEqual(uIntFlags, test.expected) { t.Errorf("%d: expected %+v, got %+v", i, test.expected, uIntFlags) } diff --git a/pkg/config/tlscfg/flags.go b/pkg/config/tlscfg/flags.go index 12994dbac9d..dc04d85cd7d 100644 --- a/pkg/config/tlscfg/flags.go +++ b/pkg/config/tlscfg/flags.go @@ -58,7 +58,7 @@ func (c ServerFlagsConfig) AddFlags(flags *flag.FlagSet) { flags.String(c.Prefix+tlsCert, "", "Path to a TLS Certificate file, used to identify this server to clients") flags.String(c.Prefix+tlsKey, "", "Path to a TLS Private Key file, used to identify this server to clients") flags.String(c.Prefix+tlsClientCA, "", "Path to a TLS CA (Certification Authority) file used to verify certificates presented by clients (if unset, all clients are permitted)") - flags.String(c.Prefix+tlsCipherSuites, "", "Comma-separated list of cipher suites for the server, values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).") + flags.String(c.Prefix+tlsCipherSuites, "", "Space-separated list of cipher suites for the server, values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).") } // InitFromViper creates tls.Config populated with values retrieved from Viper. @@ -80,6 +80,6 @@ func (c ServerFlagsConfig) InitFromViper(v *viper.Viper) Options { p.CertPath = v.GetString(c.Prefix + tlsCert) p.KeyPath = v.GetString(c.Prefix + tlsKey) p.ClientCAPath = v.GetString(c.Prefix + tlsClientCA) - p.CipherSuites = v.GetString(c.Prefix + tlsCipherSuites) + p.CipherSuites = v.GetStringSlice(c.Prefix + tlsCipherSuites) return p } diff --git a/pkg/config/tlscfg/flags_test.go b/pkg/config/tlscfg/flags_test.go index f1cf9ba46b1..2bebd1837f4 100644 --- a/pkg/config/tlscfg/flags_test.go +++ b/pkg/config/tlscfg/flags_test.go @@ -74,7 +74,7 @@ func TestServerFlags(t *testing.T) { "--prefix.tls.enabled=true", "--prefix.tls.cert=cert-file", "--prefix.tls.key=key-file", - "--prefix.tls.cipher-suites=TLS_RSA_WITH_RC4_128_SHA", + "--prefix.tls.cipher-suites=TLS_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", } tests := []struct { @@ -108,7 +108,7 @@ func TestServerFlags(t *testing.T) { CertPath: "cert-file", KeyPath: "key-file", ClientCAPath: test.file, - CipherSuites: "TLS_RSA_WITH_RC4_128_SHA", + CipherSuites: []string{"TLS_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"}, }, tlsOpts) }) } diff --git a/pkg/config/tlscfg/options.go b/pkg/config/tlscfg/options.go index c7f35702ec3..52ab518aec6 100644 --- a/pkg/config/tlscfg/options.go +++ b/pkg/config/tlscfg/options.go @@ -33,7 +33,7 @@ type Options struct { KeyPath string `mapstructure:"key"` ServerName string `mapstructure:"server_name"` // only for client-side TLS config ClientCAPath string `mapstructure:"client_ca"` // only for server-side TLS config for client auth - CipherSuites string `mapstructure:"cipher_suites"` + CipherSuites []string `mapstructure:"cipher_suites"` SkipHostVerify bool `mapstructure:"skip_host_verify"` certWatcher *certWatcher `mapstructure:"-"` } @@ -47,7 +47,7 @@ func (p *Options) Config(logger *zap.Logger) (*tls.Config, error) { return nil, fmt.Errorf("failed to load CA CertPool: %w", err) } - cipherSuiteIds, err := TLSCipherSuites(p.CipherSuites) + cipherSuiteIds, err := CipherSuiteNamesToIDs(p.CipherSuites) if err != nil { return nil, fmt.Errorf("failed to get cipher suite ids from cipher suite names: %w", err) } diff --git a/pkg/config/tlscfg/options_test.go b/pkg/config/tlscfg/options_test.go index 4f8df04129d..6f8bbdbd766 100644 --- a/pkg/config/tlscfg/options_test.go +++ b/pkg/config/tlscfg/options_test.go @@ -121,6 +121,13 @@ func TestOptionsToConfig(t *testing.T) { ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem", }, }, + { + name: "should fail with invalid Cipher Suite", + options: Options{ + CipherSuites: []string{"TLS_INVALID_CIPHER_SUITE"}, + }, + expectError: "failed to get cipher suite ids from cipher suite names: cipher suite TLS_INVALID_CIPHER_SUITE not supported or doesn't exist", + }, } for _, test := range tests { From 8105fdff2857956ff402c3e1faf5da3cc4e7ca24 Mon Sep 17 00:00:00 2001 From: Ashmita152 Date: Sun, 6 Mar 2022 07:49:03 +0545 Subject: [PATCH 3/4] Feedbacks Signed-off-by: Ashmita152 --- pkg/config/tlscfg/ciphersuites.go | 3 --- pkg/config/tlscfg/flags.go | 8 +++++++- pkg/config/tlscfg/flags_test.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/config/tlscfg/ciphersuites.go b/pkg/config/tlscfg/ciphersuites.go index f87228123b0..8580312de73 100644 --- a/pkg/config/tlscfg/ciphersuites.go +++ b/pkg/config/tlscfg/ciphersuites.go @@ -29,9 +29,6 @@ func allCiphers() map[string]uint16 { // CipherSuiteNamesToIDs returns a list of cipher suite IDs from the cipher suite names passed. func CipherSuiteNamesToIDs(cipherNames []string) ([]uint16, error) { - if len(cipherNames) == 0 { - return nil, nil - } var ciphersIDs []uint16 possibleCiphers := allCiphers() for _, cipher := range cipherNames { diff --git a/pkg/config/tlscfg/flags.go b/pkg/config/tlscfg/flags.go index dc04d85cd7d..9af77e6b86b 100644 --- a/pkg/config/tlscfg/flags.go +++ b/pkg/config/tlscfg/flags.go @@ -16,6 +16,7 @@ package tlscfg import ( "flag" + "strings" "github.com/spf13/viper" ) @@ -80,6 +81,11 @@ func (c ServerFlagsConfig) InitFromViper(v *viper.Viper) Options { p.CertPath = v.GetString(c.Prefix + tlsCert) p.KeyPath = v.GetString(c.Prefix + tlsKey) p.ClientCAPath = v.GetString(c.Prefix + tlsClientCA) - p.CipherSuites = v.GetStringSlice(c.Prefix + tlsCipherSuites) + p.CipherSuites = strings.Split(stripWhiteSpace(v.GetString(c.Prefix+tlsCipherSuites)), ",") return p } + +// stripWhiteSpace removes all whitespace characters from a string +func stripWhiteSpace(str string) string { + return strings.Replace(str, " ", "", -1) +} diff --git a/pkg/config/tlscfg/flags_test.go b/pkg/config/tlscfg/flags_test.go index 2bebd1837f4..159f4753127 100644 --- a/pkg/config/tlscfg/flags_test.go +++ b/pkg/config/tlscfg/flags_test.go @@ -74,7 +74,7 @@ func TestServerFlags(t *testing.T) { "--prefix.tls.enabled=true", "--prefix.tls.cert=cert-file", "--prefix.tls.key=key-file", - "--prefix.tls.cipher-suites=TLS_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "--prefix.tls.cipher-suites=TLS_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", } tests := []struct { From a8fa61be0d232787f0e6fe95d217c93dab759df4 Mon Sep 17 00:00:00 2001 From: Ashmita152 Date: Sun, 6 Mar 2022 07:50:55 +0545 Subject: [PATCH 4/4] Feedbacks Signed-off-by: Ashmita152 --- pkg/config/tlscfg/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/tlscfg/flags.go b/pkg/config/tlscfg/flags.go index 9af77e6b86b..f74f73fb895 100644 --- a/pkg/config/tlscfg/flags.go +++ b/pkg/config/tlscfg/flags.go @@ -59,7 +59,7 @@ func (c ServerFlagsConfig) AddFlags(flags *flag.FlagSet) { flags.String(c.Prefix+tlsCert, "", "Path to a TLS Certificate file, used to identify this server to clients") flags.String(c.Prefix+tlsKey, "", "Path to a TLS Private Key file, used to identify this server to clients") flags.String(c.Prefix+tlsClientCA, "", "Path to a TLS CA (Certification Authority) file used to verify certificates presented by clients (if unset, all clients are permitted)") - flags.String(c.Prefix+tlsCipherSuites, "", "Space-separated list of cipher suites for the server, values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).") + flags.String(c.Prefix+tlsCipherSuites, "", "Comma-separated list of cipher suites for the server, values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).") } // InitFromViper creates tls.Config populated with values retrieved from Viper.