mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Backport tls 1.3 support.
Signed-off-by: James Blair <mail@jamesblair.net>
This commit is contained in:
parent
6ddb23ce7a
commit
358bcf3fb6
47
client/pkg/tlsutil/versions.go
Normal file
47
client/pkg/tlsutil/versions.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2023 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TLSVersion string
|
||||||
|
|
||||||
|
// Constants for TLS versions.
|
||||||
|
const (
|
||||||
|
TLSVersionDefault TLSVersion = ""
|
||||||
|
TLSVersion12 TLSVersion = "TLS1.2"
|
||||||
|
TLSVersion13 TLSVersion = "TLS1.3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTLSVersion returns the corresponding tls.Version or error.
|
||||||
|
func GetTLSVersion(version string) (uint16, error) {
|
||||||
|
var v uint16
|
||||||
|
|
||||||
|
switch version {
|
||||||
|
case string(TLSVersionDefault):
|
||||||
|
v = 0 // 0 means let Go decide.
|
||||||
|
case string(TLSVersion12):
|
||||||
|
v = tls.VersionTLS12
|
||||||
|
case string(TLSVersion13):
|
||||||
|
v = tls.VersionTLS13
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("unexpected TLS version %q (must be one of: TLS1.2, TLS1.3)", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
63
client/pkg/tlsutil/versions_test.go
Normal file
63
client/pkg/tlsutil/versions_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2023 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"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
want uint16
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TLS1.2",
|
||||||
|
version: "TLS1.2",
|
||||||
|
want: tls.VersionTLS12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TLS1.3",
|
||||||
|
version: "TLS1.3",
|
||||||
|
want: tls.VersionTLS13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty version",
|
||||||
|
version: "",
|
||||||
|
want: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Converting invalid version string to TLS version",
|
||||||
|
version: "not_existing",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := GetTLSVersion(tt.version)
|
||||||
|
if err != nil {
|
||||||
|
assert.True(t, tt.expectError, "GetTLSVersion() returned error while expecting success: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -165,6 +165,14 @@ type TLSInfo struct {
|
|||||||
// Note that cipher suites are prioritized in the given order.
|
// Note that cipher suites are prioritized in the given order.
|
||||||
CipherSuites []uint16
|
CipherSuites []uint16
|
||||||
|
|
||||||
|
// MinVersion is the minimum TLS version that is acceptable.
|
||||||
|
// If not set, the minimum version is TLS 1.2.
|
||||||
|
MinVersion uint16
|
||||||
|
|
||||||
|
// MaxVersion is the maximum TLS version that is acceptable.
|
||||||
|
// If not set, the default used by Go is selected (see tls.Config.MaxVersion).
|
||||||
|
MaxVersion uint16
|
||||||
|
|
||||||
selfCert bool
|
selfCert bool
|
||||||
|
|
||||||
// parseFunc exists to simplify testing. Typically, parseFunc
|
// parseFunc exists to simplify testing. Typically, parseFunc
|
||||||
@ -378,8 +386,17 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var minVersion uint16
|
||||||
|
if info.MinVersion != 0 {
|
||||||
|
minVersion = info.MinVersion
|
||||||
|
} else {
|
||||||
|
// Default minimum version is TLS 1.2, previous versions are insecure and deprecated.
|
||||||
|
minVersion = tls.VersionTLS12
|
||||||
|
}
|
||||||
|
|
||||||
cfg := &tls.Config{
|
cfg := &tls.Config{
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: minVersion,
|
||||||
|
MaxVersion: info.MaxVersion,
|
||||||
ServerName: info.ServerName,
|
ServerName: info.ServerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,11 +527,6 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
|||||||
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
|
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
|
||||||
cfg.NextProtos = []string{"h2"}
|
cfg.NextProtos = []string{"h2"}
|
||||||
|
|
||||||
// go1.13 enables TLS 1.3 by default
|
|
||||||
// and in TLS 1.3, cipher suites are not configurable
|
|
||||||
// setting Max TLS version to TLS 1.2 for go 1.13
|
|
||||||
cfg.MaxVersion = tls.VersionTLS12
|
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,11 +581,6 @@ func (info TLSInfo) ClientConfig() (*tls.Config, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// go1.13 enables TLS 1.3 by default
|
|
||||||
// and in TLS 1.3, cipher suites are not configurable
|
|
||||||
// setting Max TLS version to TLS 1.2 for go 1.13
|
|
||||||
cfg.MaxVersion = tls.VersionTLS12
|
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package embed
|
package embed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
@ -220,6 +221,11 @@ type Config struct {
|
|||||||
// Note that cipher suites are prioritized in the given order.
|
// Note that cipher suites are prioritized in the given order.
|
||||||
CipherSuites []string `json:"cipher-suites"`
|
CipherSuites []string `json:"cipher-suites"`
|
||||||
|
|
||||||
|
// TlsMinVersion is the minimum accepted TLS version between client/server and peers.
|
||||||
|
TlsMinVersion string `json:"tls-min-version"`
|
||||||
|
// TlsMaxVersion is the maximum accepted TLS version between client/server and peers.
|
||||||
|
TlsMaxVersion string `json:"tls-max-version"`
|
||||||
|
|
||||||
ClusterState string `json:"initial-cluster-state"`
|
ClusterState string `json:"initial-cluster-state"`
|
||||||
DNSCluster string `json:"discovery-srv"`
|
DNSCluster string `json:"discovery-srv"`
|
||||||
DNSClusterServiceName string `json:"discovery-srv-name"`
|
DNSClusterServiceName string `json:"discovery-srv-name"`
|
||||||
@ -628,6 +634,17 @@ func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateMinMaxVersions(info *transport.TLSInfo, min, max string) {
|
||||||
|
// Validate() has been called to check the user input, so it should never fail.
|
||||||
|
var err error
|
||||||
|
if info.MinVersion, err = tlsutil.GetTLSVersion(min); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if info.MaxVersion, err = tlsutil.GetTLSVersion(max); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate ensures that '*embed.Config' fields are properly configured.
|
// Validate ensures that '*embed.Config' fields are properly configured.
|
||||||
func (cfg *Config) Validate() error {
|
func (cfg *Config) Validate() error {
|
||||||
if err := cfg.setupLogging(); err != nil {
|
if err := cfg.setupLogging(); err != nil {
|
||||||
@ -703,6 +720,25 @@ func (cfg *Config) Validate() error {
|
|||||||
return fmt.Errorf("--experimental-compact-hash-check-time must be >0 (set to %v)", cfg.ExperimentalCompactHashCheckTime)
|
return fmt.Errorf("--experimental-compact-hash-check-time must be >0 (set to %v)", cfg.ExperimentalCompactHashCheckTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minVersion, err := tlsutil.GetTLSVersion(cfg.TlsMinVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
maxVersion, err := tlsutil.GetTLSVersion(cfg.TlsMaxVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxVersion == 0 means that Go selects the highest available version.
|
||||||
|
if maxVersion != 0 && minVersion > maxVersion {
|
||||||
|
return fmt.Errorf("min version (%s) is greater than max version (%s)", cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user attempted to configure ciphers for TLS1.3 only: Go does not support that currently.
|
||||||
|
if minVersion == tls.VersionTLS13 && len(cfg.CipherSuites) > 0 {
|
||||||
|
return fmt.Errorf("cipher suites cannot be configured when only TLS1.3 is enabled")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package embed
|
package embed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -24,6 +25,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"go.etcd.io/etcd/client/pkg/v3/srv"
|
"go.etcd.io/etcd/client/pkg/v3/srv"
|
||||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||||
"go.etcd.io/etcd/client/pkg/v3/types"
|
"go.etcd.io/etcd/client/pkg/v3/types"
|
||||||
@ -422,3 +424,80 @@ func TestLogRotation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSVersionMinMax(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
givenTLSMinVersion string
|
||||||
|
givenTLSMaxVersion string
|
||||||
|
givenCipherSuites []string
|
||||||
|
expectError bool
|
||||||
|
expectedMinTLSVersion uint16
|
||||||
|
expectedMaxTLSVersion uint16
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Minimum TLS version is set",
|
||||||
|
givenTLSMinVersion: "TLS1.3",
|
||||||
|
expectedMinTLSVersion: tls.VersionTLS13,
|
||||||
|
expectedMaxTLSVersion: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Maximum TLS version is set",
|
||||||
|
givenTLSMaxVersion: "TLS1.2",
|
||||||
|
expectedMinTLSVersion: 0,
|
||||||
|
expectedMaxTLSVersion: tls.VersionTLS12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Minimum and Maximum TLS versions are set",
|
||||||
|
givenTLSMinVersion: "TLS1.3",
|
||||||
|
givenTLSMaxVersion: "TLS1.3",
|
||||||
|
expectedMinTLSVersion: tls.VersionTLS13,
|
||||||
|
expectedMaxTLSVersion: tls.VersionTLS13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Minimum and Maximum TLS versions are set in reverse order",
|
||||||
|
givenTLSMinVersion: "TLS1.3",
|
||||||
|
givenTLSMaxVersion: "TLS1.2",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid minimum TLS version",
|
||||||
|
givenTLSMinVersion: "invalid version",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid maximum TLS version",
|
||||||
|
givenTLSMaxVersion: "invalid version",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cipher suites configured for TLS 1.3",
|
||||||
|
givenTLSMinVersion: "TLS1.3",
|
||||||
|
givenCipherSuites: []string{"TLS_AES_128_GCM_SHA256"},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cfg := NewConfig()
|
||||||
|
cfg.TlsMinVersion = tt.givenTLSMinVersion
|
||||||
|
cfg.TlsMaxVersion = tt.givenTLSMaxVersion
|
||||||
|
cfg.CipherSuites = tt.givenCipherSuites
|
||||||
|
|
||||||
|
err := cfg.Validate()
|
||||||
|
if err != nil {
|
||||||
|
assert.True(t, tt.expectError, "Validate() returned error while expecting success: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||||
|
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedMinTLSVersion, cfg.PeerTLSInfo.MinVersion)
|
||||||
|
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.PeerTLSInfo.MaxVersion)
|
||||||
|
assert.Equal(t, tt.expectedMinTLSVersion, cfg.ClientTLSInfo.MinVersion)
|
||||||
|
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.ClientTLSInfo.MaxVersion)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -480,6 +480,9 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
|||||||
if err = cfg.PeerSelfCert(); err != nil {
|
if err = cfg.PeerSelfCert(); err != nil {
|
||||||
cfg.logger.Fatal("failed to get peer self-signed certs", zap.Error(err))
|
cfg.logger.Fatal("failed to get peer self-signed certs", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||||
|
|
||||||
if !cfg.PeerTLSInfo.Empty() {
|
if !cfg.PeerTLSInfo.Empty() {
|
||||||
cfg.logger.Info(
|
cfg.logger.Info(
|
||||||
"starting with peer TLS",
|
"starting with peer TLS",
|
||||||
@ -600,6 +603,9 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
|||||||
if err = cfg.ClientSelfCert(); err != nil {
|
if err = cfg.ClientSelfCert(); err != nil {
|
||||||
cfg.logger.Fatal("failed to get client self-signed certs", zap.Error(err))
|
cfg.logger.Fatal("failed to get client self-signed certs", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||||
|
|
||||||
if cfg.EnablePprof {
|
if cfg.EnablePprof {
|
||||||
cfg.logger.Info("pprof is enabled", zap.String("path", debugutil.HTTPPrefixPProf))
|
cfg.logger.Info("pprof is enabled", zap.String("path", debugutil.HTTPPrefixPProf))
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"go.etcd.io/etcd/api/v3/version"
|
"go.etcd.io/etcd/api/v3/version"
|
||||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||||
|
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
|
||||||
"go.etcd.io/etcd/pkg/v3/flags"
|
"go.etcd.io/etcd/pkg/v3/flags"
|
||||||
cconfig "go.etcd.io/etcd/server/v3/config"
|
cconfig "go.etcd.io/etcd/server/v3/config"
|
||||||
"go.etcd.io/etcd/server/v3/embed"
|
"go.etcd.io/etcd/server/v3/embed"
|
||||||
@ -237,6 +238,8 @@ func newConfig() *config {
|
|||||||
fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedHostname, "peer-cert-allowed-hostname", "", "Allowed TLS hostname for inter peer authentication.")
|
fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedHostname, "peer-cert-allowed-hostname", "", "Allowed TLS hostname for inter peer authentication.")
|
||||||
fs.Var(flags.NewStringsValue(""), "cipher-suites", "Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).")
|
fs.Var(flags.NewStringsValue(""), "cipher-suites", "Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).")
|
||||||
fs.BoolVar(&cfg.ec.PeerTLSInfo.SkipClientSANVerify, "experimental-peer-skip-client-san-verification", false, "Skip verification of SAN field in client certificate for peer connections.")
|
fs.BoolVar(&cfg.ec.PeerTLSInfo.SkipClientSANVerify, "experimental-peer-skip-client-san-verification", false, "Skip verification of SAN field in client certificate for peer connections.")
|
||||||
|
fs.StringVar(&cfg.ec.TlsMinVersion, "tls-min-version", string(tlsutil.TLSVersion12), "Minimum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3.")
|
||||||
|
fs.StringVar(&cfg.ec.TlsMaxVersion, "tls-max-version", string(tlsutil.TLSVersionDefault), "Maximum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3 (empty defers to Go).")
|
||||||
|
|
||||||
fs.Var(
|
fs.Var(
|
||||||
flags.NewUniqueURLsWithExceptions("*", "*"),
|
flags.NewUniqueURLsWithExceptions("*", "*"),
|
||||||
|
@ -174,6 +174,10 @@ Security:
|
|||||||
Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
|
Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
|
||||||
--host-whitelist '*'
|
--host-whitelist '*'
|
||||||
Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
|
Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
|
||||||
|
--tls-min-version 'TLS1.2'
|
||||||
|
Minimum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3.
|
||||||
|
--tls-max-version ''
|
||||||
|
Maximum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3 (empty will be auto-populated by Go).
|
||||||
|
|
||||||
Auth:
|
Auth:
|
||||||
--auth-token 'simple'
|
--auth-token 'simple'
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"go.etcd.io/etcd/pkg/v3/expect"
|
"go.etcd.io/etcd/pkg/v3/expect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -374,3 +375,32 @@ func TestBootstrapDefragFlag(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEtcdTLSVersion(t *testing.T) {
|
||||||
|
skipInShortMode(t)
|
||||||
|
|
||||||
|
d := t.TempDir()
|
||||||
|
proc, err := spawnCmd(
|
||||||
|
[]string{
|
||||||
|
binDir + "/etcd",
|
||||||
|
"--data-dir", d,
|
||||||
|
"--name", "e1",
|
||||||
|
"--listen-client-urls", "https://0.0.0.0:0",
|
||||||
|
"--advertise-client-urls", "https://0.0.0.0:0",
|
||||||
|
"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort),
|
||||||
|
"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort),
|
||||||
|
"--initial-cluster", fmt.Sprintf("e1=https://127.0.0.1:%d", etcdProcessBasePort),
|
||||||
|
"--peer-cert-file", certPath,
|
||||||
|
"--peer-key-file", privateKeyPath,
|
||||||
|
"--cert-file", certPath2,
|
||||||
|
"--key-file", privateKeyPath2,
|
||||||
|
|
||||||
|
"--tls-min-version", "TLS1.2",
|
||||||
|
"--tls-max-version", "TLS1.3",
|
||||||
|
}, nil,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, waitReadyExpectProc(proc, etcdServerReadyLines), "did not receive expected output from etcd process")
|
||||||
|
assert.NoError(t, proc.Stop())
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"go.etcd.io/etcd/client/v3"
|
"go.etcd.io/etcd/client/v3"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
@ -47,6 +48,12 @@ func testTLSCipherSuites(t *testing.T, valid bool) {
|
|||||||
srvTLS.CipherSuites, cliTLS.CipherSuites = cipherSuites[:2], cipherSuites[2:]
|
srvTLS.CipherSuites, cliTLS.CipherSuites = cipherSuites[:2], cipherSuites[2:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go1.13 enables TLS 1.3 by default
|
||||||
|
// and in TLS 1.3, cipher suites are not configurable,
|
||||||
|
// so setting Max TLS version to TLS 1.2 to test cipher config.
|
||||||
|
srvTLS.MaxVersion = tls.VersionTLS12
|
||||||
|
cliTLS.MaxVersion = tls.VersionTLS12
|
||||||
|
|
||||||
clus := NewClusterV3(t, &ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
clus := NewClusterV3(t, &ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
||||||
defer clus.Terminate(t)
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
@ -70,3 +77,67 @@ func testTLSCipherSuites(t *testing.T, valid bool) {
|
|||||||
t.Fatalf("expected TLS handshake success, got %v", cerr)
|
t.Fatalf("expected TLS handshake success, got %v", cerr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTLSMinMaxVersion(t *testing.T) {
|
||||||
|
|
||||||
|
BeforeTest(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
minVersion uint16
|
||||||
|
maxVersion uint16
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Connect with default TLS version should succeed",
|
||||||
|
minVersion: 0,
|
||||||
|
maxVersion: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Connect with TLS 1.2 only should fail",
|
||||||
|
minVersion: tls.VersionTLS12,
|
||||||
|
maxVersion: tls.VersionTLS12,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Connect with TLS 1.2 and 1.3 should succeed",
|
||||||
|
minVersion: tls.VersionTLS12,
|
||||||
|
maxVersion: tls.VersionTLS13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Connect with TLS 1.3 only should succeed",
|
||||||
|
minVersion: tls.VersionTLS13,
|
||||||
|
maxVersion: tls.VersionTLS13,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure server to support TLS 1.3 only.
|
||||||
|
srvTLS := testTLSInfo
|
||||||
|
srvTLS.MinVersion = tls.VersionTLS13
|
||||||
|
srvTLS.MaxVersion = tls.VersionTLS13
|
||||||
|
clus := NewClusterV3(t, &ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cc, err := testTLSInfo.ClientConfig()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
cc.MinVersion = tt.minVersion
|
||||||
|
cc.MaxVersion = tt.maxVersion
|
||||||
|
cli, cerr := NewClient(t, clientv3.Config{
|
||||||
|
Endpoints: []string{clus.Members[0].GRPCURL()},
|
||||||
|
DialTimeout: time.Second,
|
||||||
|
DialOptions: []grpc.DialOption{grpc.WithBlock()},
|
||||||
|
TLS: cc,
|
||||||
|
})
|
||||||
|
if cerr != nil {
|
||||||
|
assert.True(t, tt.expectError, "got TLS handshake error while expecting success: %v", cerr)
|
||||||
|
assert.Equal(t, context.DeadlineExceeded, cerr, "expected %v with TLS handshake failure, got %v", context.DeadlineExceeded, cerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user