Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.5] add tls min/max version to grpc proxy #18829

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 58 additions & 15 deletions server/etcdmain/grpc_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,16 @@ var (

// tls for clients connecting to proxy

grpcProxyListenCA string
grpcProxyListenCert string
grpcProxyListenKey string
grpcProxyListenCipherSuites []string
grpcProxyListenAutoTLS bool
grpcProxyListenCRL string
selfSignedCertValidity uint
grpcProxyListenCA string
grpcProxyListenCert string
grpcProxyListenKey string
grpcProxyListenCipherSuites []string
grpcProxyListenAutoTLS bool
grpcProxyListenCRL string
grpcProxyListenTLSMinVersion string
grpcProxyListenTLSMaxVersion string

selfSignedCertValidity uint

grpcProxyAdvertiseClientURL string
grpcProxyResolverPrefix string
Expand Down Expand Up @@ -166,6 +169,8 @@ func newGRPCProxyStartCommand() *cobra.Command {
cmd.Flags().BoolVar(&grpcProxyListenAutoTLS, "auto-tls", false, "proxy TLS using generated certificates")
cmd.Flags().StringVar(&grpcProxyListenCRL, "client-crl-file", "", "proxy client certificate revocation list file.")
cmd.Flags().UintVar(&selfSignedCertValidity, "self-signed-cert-validity", 1, "The validity period of the proxy certificates, unit is year")
cmd.Flags().StringVar(&grpcProxyListenTLSMinVersion, "tls-min-version", string(tlsutil.TLSVersion12), "Minimum TLS version supported by grpc proxy. Possible values: TLS1.2, TLS1.3.")
cmd.Flags().StringVar(&grpcProxyListenTLSMaxVersion, "tls-max-version", string(tlsutil.TLSVersionDefault), "Maximum TLS version supported by grpc proxy. Possible values: TLS1.2, TLS1.3 (empty defers to Go).")

// experimental flags
cmd.Flags().BoolVar(&grpcProxyEnableOrdering, "experimental-serializable-ordering", false, "Ensure serializable reads have monotonically increasing store revisions across endpoints.")
Expand Down Expand Up @@ -197,13 +202,6 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
// The empty CN is required for grpcProxyCert.
// Please see https://github.com/etcd-io/etcd/issues/11970#issuecomment-687875315 for more context.
tlsInfo := newTLS(grpcProxyListenCA, grpcProxyListenCert, grpcProxyListenKey, false)
if len(grpcProxyListenCipherSuites) > 0 {
cs, err := tlsutil.GetCipherSuites(grpcProxyListenCipherSuites)
if err != nil {
log.Fatal(err)
}
tlsInfo.CipherSuites = cs
}
if tlsInfo == nil && grpcProxyListenAutoTLS {
host := []string{"https://" + grpcProxyListenAddr}
dir := filepath.Join(grpcProxyDataDir, "fixtures", "proxy")
Expand All @@ -213,10 +211,32 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
}
tlsInfo = &autoTLS
}

if tlsInfo != nil {
if len(grpcProxyListenCipherSuites) > 0 {
cs, err := tlsutil.GetCipherSuites(grpcProxyListenCipherSuites)
if err != nil {
log.Fatal(err)
}
tlsInfo.CipherSuites = cs
}
if grpcProxyListenTLSMinVersion != "" {
version, err := tlsutil.GetTLSVersion(grpcProxyListenTLSMinVersion)
if err != nil {
log.Fatal(err)
}
tlsInfo.MinVersion = version
}
if grpcProxyListenTLSMaxVersion != "" {
version, err := tlsutil.GetTLSVersion(grpcProxyListenTLSMaxVersion)
if err != nil {
log.Fatal(err)
}
tlsInfo.MaxVersion = version
}

lg.Info("gRPC proxy server TLS", zap.String("tls-info", fmt.Sprintf("%+v", tlsInfo)))
}

m := mustListenCMux(lg, tlsInfo)
grpcl := m.Match(cmux.HTTP2())
defer func() {
Expand Down Expand Up @@ -290,6 +310,29 @@ func checkArgs() {
fmt.Fprintln(os.Stderr, fmt.Errorf("selfSignedCertValidity is invalid,it should be greater than 0"))
os.Exit(1)
}

minVersion, err := tlsutil.GetTLSVersion(grpcProxyListenTLSMinVersion)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Errorf("tls-min-version is invalid: %w", err))
os.Exit(1)
}
maxVersion, err := tlsutil.GetTLSVersion(grpcProxyListenTLSMaxVersion)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Errorf("tls-max-version is invalid: %w", err))
os.Exit(1)
}

// maxVersion == 0 means that Go selects the highest available version.
if maxVersion != 0 && minVersion > maxVersion {
fmt.Fprintln(os.Stderr, fmt.Errorf("min version (%s) is greater than max version (%s)", grpcProxyListenTLSMinVersion, grpcProxyListenTLSMaxVersion))
os.Exit(1)
}

// Check if user attempted to configure ciphers for TLS1.3 only: Go does not support that currently.
if minVersion == tls.VersionTLS13 && len(grpcProxyListenCipherSuites) > 0 {
fmt.Fprintln(os.Stderr, fmt.Errorf("cipher suites cannot be configured when only TLS1.3 is enabled"))
os.Exit(1)
}
}

func mustNewClient(lg *zap.Logger) *clientv3.Client {
Expand Down
33 changes: 33 additions & 0 deletions tests/e2e/etcd_grpcproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
Expand Down Expand Up @@ -126,6 +127,38 @@ func TestGrpcProxyAutoSync(t *testing.T) {
require.NoError(t, proxyProc.Stop())
}

func TestGrpcProxyTLSVersions(t *testing.T) {
e2e.SkipInShortMode(t)

epc, err := e2e.NewEtcdProcessCluster(t, e2e.NewConfigClientBoth())
require.NoError(t, err)
defer func() {
assert.NoError(t, epc.Close())
}()

var (
node1ClientURL = epc.Procs[0].Config().ClientHttpUrl
proxyClientURL = "127.0.0.1:42379"
)

// Run independent grpc-proxy instance
proxyProc, err := e2e.SpawnCmd([]string{e2e.BinDir + "/etcd", "grpc-proxy", "start",
"--advertise-client-url", proxyClientURL,
"--listen-addr", proxyClientURL,
"--endpoints", node1ClientURL,
"--endpoints-auto-sync-interval", "1s",
"--cert-file", e2e.CertPath2,
"--key-file", e2e.PrivateKeyPath2,
"--tls-min-version", "TLS1.2",
"--tls-max-version", "TLS1.3",
}, nil)
require.NoError(t, err)
defer proxyProc.Stop()

_, err = proxyProc.Expect("listening for gRPC proxy client requests")
require.NoError(t, err)
}

func runEtcdNode(name, dataDir, clientURL, peerURL, clusterState, initialCluster string) (*expect.ExpectProcess, error) {
proc, err := e2e.SpawnCmd([]string{e2e.BinPath,
"--name", name,
Expand Down
Loading