mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Add TLSv1.3 support.
Added optional TLS min/max protocol version and command line switches to set versions for the etcd server. If max version is not explicitly set by the user, let Go select the max version which is currently TLSv1.3. Previously max version was set to TLSv1.2. Signed-off-by: Tero Saarni <tero.saarni@est.tech>
This commit is contained in:
parent
3b612ce345
commit
588b98d085
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -166,6 +166,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
|
||||||
@ -380,8 +388,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,11 +529,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,11 +583,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"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@ -226,6 +227,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"`
|
||||||
@ -660,6 +666,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 {
|
||||||
@ -776,6 +793,25 @@ func (cfg *Config) Validate() error {
|
|||||||
zap.String("name", cfg.Name))
|
zap.String("name", cfg.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
"net"
|
"net"
|
||||||
@ -429,3 +430,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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -499,6 +499,7 @@ 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",
|
||||||
@ -611,6 +612,7 @@ 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"
|
||||||
@ -215,6 +216,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("*", "*"),
|
||||||
|
@ -199,6 +199,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'
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@ -424,3 +425,32 @@ func TestEtcdHealthyWithTinySnapshotCatchupEntries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, g.Wait())
|
require.NoError(t, g.Wait())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEtcdTLSVersion(t *testing.T) {
|
||||||
|
e2e.SkipInShortMode(t)
|
||||||
|
|
||||||
|
d := t.TempDir()
|
||||||
|
proc, err := e2e.SpawnCmd(
|
||||||
|
[]string{
|
||||||
|
e2e.BinPath.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", e2e.EtcdProcessBasePort),
|
||||||
|
"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", e2e.EtcdProcessBasePort),
|
||||||
|
"--initial-cluster", fmt.Sprintf("e1=https://127.0.0.1:%d", e2e.EtcdProcessBasePort),
|
||||||
|
"--peer-cert-file", e2e.CertPath,
|
||||||
|
"--peer-key-file", e2e.PrivateKeyPath,
|
||||||
|
"--cert-file", e2e.CertPath2,
|
||||||
|
"--key-file", e2e.PrivateKeyPath2,
|
||||||
|
|
||||||
|
"--tls-min-version", "TLS1.2",
|
||||||
|
"--tls-max-version", "TLS1.3",
|
||||||
|
}, nil,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, e2e.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"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
clientv3 "go.etcd.io/etcd/client/v3"
|
clientv3 "go.etcd.io/etcd/client/v3"
|
||||||
@ -49,6 +50,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 := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
||||||
defer clus.Terminate(t)
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
@ -72,3 +79,66 @@ 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) {
|
||||||
|
integration.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 := integration.TestTLSInfo
|
||||||
|
srvTLS.MinVersion = tls.VersionTLS13
|
||||||
|
srvTLS.MaxVersion = tls.VersionTLS13
|
||||||
|
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, ClientTLS: &srvTLS})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cc, err := integration.TestTLSInfo.ClientConfig()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
cc.MinVersion = tt.minVersion
|
||||||
|
cc.MaxVersion = tt.maxVersion
|
||||||
|
cli, cerr := integration.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