Skip to content

Commit

Permalink
Merge pull request etcd-io#14308 from dusk125/main
Browse files Browse the repository at this point in the history
server/etcdmain: add configurable cipher list to gRPC proxy listener
  • Loading branch information
spzala authored and dusk125 committed Nov 1, 2022
1 parent 29c3b0f commit 31cdf49
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 24 deletions.
Empty file added CHANGELOG/CHANGELOG-3.6.md
Empty file.
19 changes: 18 additions & 1 deletion client/pkg/tlsutil/cipher_suites.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

package tlsutil

import "crypto/tls"
import (
"crypto/tls"
"fmt"
)

// GetCipherSuite returns the corresponding cipher suite,
// and boolean value if it is supported.
Expand All @@ -37,3 +40,17 @@ func GetCipherSuite(s string) (uint16, bool) {
}
return 0, false
}

// GetCipherSuites returns list of corresponding cipher suite IDs.
func GetCipherSuites(ss []string) ([]uint16, error) {
cs := make([]uint16, len(ss))
for i, s := range ss {
var ok bool
cs[i], ok = GetCipherSuite(s)
if !ok {
return nil, fmt.Errorf("unexpected TLS cipher suite %q", s)
}
}

return cs, nil
}
10 changes: 3 additions & 7 deletions server/embed/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,13 +621,9 @@ func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
return fmt.Errorf("TLSInfo.CipherSuites is already specified (given %v)", ss)
}
if len(ss) > 0 {
cs := make([]uint16, len(ss))
for i, s := range ss {
var ok bool
cs[i], ok = tlsutil.GetCipherSuite(s)
if !ok {
return fmt.Errorf("unexpected TLS cipher suite %q", s)
}
cs, err := tlsutil.GetCipherSuites(ss)
if err != nil {
return err
}
tls.CipherSuites = cs
}
Expand Down
42 changes: 26 additions & 16 deletions server/etcdmain/grpc_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/leasing"
Expand Down Expand Up @@ -72,12 +73,13 @@ var (

// tls for clients connecting to proxy

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

grpcProxyAdvertiseClientURL string
grpcProxyResolverPrefix string
Expand Down Expand Up @@ -149,6 +151,7 @@ func newGRPCProxyStartCommand() *cobra.Command {
cmd.Flags().StringVar(&grpcProxyListenCert, "cert-file", "", "identify secure connections to the proxy using this TLS certificate file")
cmd.Flags().StringVar(&grpcProxyListenKey, "key-file", "", "identify secure connections to the proxy using this TLS key file")
cmd.Flags().StringVar(&grpcProxyListenCA, "trusted-ca-file", "", "verify certificates of TLS-enabled secure proxy using this CA bundle")
cmd.Flags().StringSliceVar(&grpcProxyListenCipherSuites, "listen-cipher-suites", grpcProxyListenCipherSuites, "Comma-separated list of supported TLS cipher suites between client/proxy (empty will be auto-populated by Go).")
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")
Expand Down Expand Up @@ -182,21 +185,28 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
// The proxy itself (ListenCert) can have not-empty CN.
// 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 tlsinfo == nil && grpcProxyListenAutoTLS {
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")
autoTLS, err := transport.SelfCert(lg, dir, host, selfSignedCertValidity)
if err != nil {
log.Fatal(err)
}
tlsinfo = &autoTLS
tlsInfo = &autoTLS
}
if tlsinfo != nil {
lg.Info("gRPC proxy server TLS", zap.String("tls-info", fmt.Sprintf("%+v", tlsinfo)))

if tlsInfo != nil {
lg.Info("gRPC proxy server TLS", zap.String("tls-info", fmt.Sprintf("%+v", tlsInfo)))
}
m := mustListenCMux(lg, tlsinfo)
m := mustListenCMux(lg, tlsInfo)
grpcl := m.Match(cmux.HTTP2())
defer func() {
grpcl.Close()
Expand All @@ -209,17 +219,17 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
// TODO: The mechanism should be refactored to use internal connection.
var proxyClient *clientv3.Client
if grpcProxyAdvertiseClientURL != "" {
proxyClient = mustNewProxyClient(lg, tlsinfo)
proxyClient = mustNewProxyClient(lg, tlsInfo)
}
httpClient := mustNewHTTPClient(lg)

srvhttp, httpl := mustHTTPListener(lg, m, tlsinfo, client, proxyClient)
srvhttp, httpl := mustHTTPListener(lg, m, tlsInfo, client, proxyClient)
errc := make(chan error, 3)
go func() { errc <- newGRPCProxyServer(lg, client).Serve(grpcl) }()
go func() { errc <- srvhttp.Serve(httpl) }()
go func() { errc <- m.Serve() }()
if len(grpcProxyMetricsListenAddr) > 0 {
mhttpl := mustMetricsListener(lg, tlsinfo)
mhttpl := mustMetricsListener(lg, tlsInfo)
go func() {
mux := http.NewServeMux()
grpcproxy.HandleMetrics(mux, httpClient, client.Endpoints())
Expand Down
40 changes: 40 additions & 0 deletions tests/e2e/etcd_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,46 @@ func TestGrpcproxyAndCommonName(t *testing.T) {
}
}

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

cases := []struct {
name string
args []string
}{
{
name: "ArgsWithCipherSuites",
args: []string{
e2e.BinDir + "/etcd",
"grpc-proxy",
"start",
"--listen-cipher-suites", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
},
},
{
name: "ArgsWithoutCipherSuites",
args: []string{
e2e.BinDir + "/etcd",
"grpc-proxy",
"start",
"--listen-cipher-suites", "",
},
},
}

for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
pw, err := e2e.SpawnCmd(test.args, nil)
if err != nil {
t.Fatal(err)
}
if err = pw.Stop(); err != nil {
t.Fatal(err)
}
})
}
}

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

Expand Down

0 comments on commit 31cdf49

Please sign in to comment.