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

add tls min/max version to grpc proxy #18816

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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 @@ -80,13 +80,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 @@ -169,6 +172,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 @@ -201,13 +206,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 @@ -217,10 +215,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 @@ -294,6 +314,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
38 changes: 38 additions & 0 deletions tests/e2e/etcd_grpcproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,44 @@ func TestGrpcProxyAutoSync(t *testing.T) {
assert.Equal(t, []testutils.KV{{Key: "k1", Val: "v1"}}, kvs)
}

func TestGrpcProxyTlsVersions(t *testing.T) {
e2e.SkipInShortMode(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

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

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

// Run independent grpc-proxy instance
proxyProc, err := e2e.SpawnCmd([]string{e2e.BinPath.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 func() {
assert.NoError(t, proxyProc.Stop())
}()

_, err = proxyProc.ExpectFunc(ctx, func(s string) bool {
return strings.Contains(s, "started gRPC proxy")
})
require.NoError(t, err)
}

func waitForEndpointInLog(ctx context.Context, proxyProc *expect.ExpectProcess, endpoint string) error {
endpoint = strings.Replace(endpoint, "http://", "", 1)

Expand Down