Backport tls 1.3 support.

Signed-off-by: James Blair <mail@jamesblair.net>
This commit is contained in:
James Blair
2023-03-15 14:10:14 +13:00
parent 6ddb23ce7a
commit 358bcf3fb6
10 changed files with 357 additions and 11 deletions

View File

@@ -15,6 +15,7 @@
package embed
import (
"crypto/tls"
"fmt"
"io/ioutil"
"math"
@@ -220,6 +221,11 @@ type Config struct {
// Note that cipher suites are prioritized in the given order.
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"`
DNSCluster string `json:"discovery-srv"`
DNSClusterServiceName string `json:"discovery-srv-name"`
@@ -628,6 +634,17 @@ func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
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.
func (cfg *Config) Validate() error {
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)
}
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
}

View File

@@ -15,6 +15,7 @@
package embed
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
@@ -24,6 +25,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.etcd.io/etcd/client/pkg/v3/srv"
"go.etcd.io/etcd/client/pkg/v3/transport"
"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)
})
}
}

View File

@@ -480,6 +480,9 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
if err = cfg.PeerSelfCert(); err != nil {
cfg.logger.Fatal("failed to get peer self-signed certs", zap.Error(err))
}
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
if !cfg.PeerTLSInfo.Empty() {
cfg.logger.Info(
"starting with peer TLS",
@@ -600,6 +603,9 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
if err = cfg.ClientSelfCert(); err != nil {
cfg.logger.Fatal("failed to get client self-signed certs", zap.Error(err))
}
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
if cfg.EnablePprof {
cfg.logger.Info("pprof is enabled", zap.String("path", debugutil.HTTPPrefixPProf))
}

View File

@@ -26,6 +26,7 @@ import (
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
"go.etcd.io/etcd/pkg/v3/flags"
cconfig "go.etcd.io/etcd/server/v3/config"
"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.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.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(
flags.NewUniqueURLsWithExceptions("*", "*"),

View File

@@ -174,6 +174,10 @@ Security:
Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
--host-whitelist '*'
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-token 'simple'