From a960d6b1c7d5265580364ea79d167a5b096cb0b6 Mon Sep 17 00:00:00 2001 From: tangcong Date: Wed, 28 Oct 2020 01:04:45 +0800 Subject: [PATCH] *: add self-signed-cert-validity flag --- Documentation/op-guide/security.md | 1 - pkg/transport/listener.go | 21 ++++++++++++++++++--- pkg/transport/listener_test.go | 4 ++-- server/embed/config.go | 8 ++++++-- server/etcdmain/config.go | 1 + server/etcdmain/etcd.go | 2 +- server/etcdmain/grpc_proxy.go | 8 +++++++- server/etcdmain/help.go | 2 ++ 8 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Documentation/op-guide/security.md b/Documentation/op-guide/security.md index 62a44bc6b..fa300fac1 100644 --- a/Documentation/op-guide/security.md +++ b/Documentation/op-guide/security.md @@ -197,7 +197,6 @@ The etcd members will form a cluster and all communication between members in th ## Example 4: Automatic self-signed transport security For cases where communication encryption, but not authentication, is needed, etcd supports encrypting its messages with automatically generated self-signed certificates. This simplifies deployment because there is no need for managing certificates and keys outside of etcd. - Configure etcd to use self-signed certificates for client and peer connections with the flags `--auto-tls` and `--peer-auto-tls`: ```sh diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index d95b28c4e..7ce11a3fb 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -114,8 +114,16 @@ func (info TLSInfo) Empty() bool { return info.CertFile == "" && info.KeyFile == "" } -func SelfCert(lg *zap.Logger, dirpath string, hosts []string, additionalUsages ...x509.ExtKeyUsage) (info TLSInfo, err error) { +func SelfCert(lg *zap.Logger, dirpath string, hosts []string, selfSignedCertValidity uint, additionalUsages ...x509.ExtKeyUsage) (info TLSInfo, err error) { info.Logger = lg + if selfSignedCertValidity == 0 { + err = fmt.Errorf("selfSignedCertValidity is invalid,it should be greater than 0") + info.Logger.Warn( + "cannot generate cert", + zap.Error(err), + ) + return + } err = fileutil.TouchDirAll(dirpath) if err != nil { if info.Logger != nil { @@ -154,13 +162,20 @@ func SelfCert(lg *zap.Logger, dirpath string, hosts []string, additionalUsages . SerialNumber: serialNumber, Subject: pkix.Name{Organization: []string{"etcd"}}, NotBefore: time.Now(), - NotAfter: time.Now().Add(365 * (24 * time.Hour)), + NotAfter: time.Now().Add(time.Duration(selfSignedCertValidity) * 365 * (24 * time.Hour)), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: append([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, additionalUsages...), BasicConstraintsValid: true, } + if info.Logger != nil { + info.Logger.Warn( + "automatically generate certificates", + zap.Time("certificate-validity-bound-not-after", tmpl.NotAfter), + ) + } + for _, host := range hosts { h, _, _ := net.SplitHostPort(host) if ip := net.ParseIP(h); ip != nil { @@ -227,7 +242,7 @@ func SelfCert(lg *zap.Logger, dirpath string, hosts []string, additionalUsages . if info.Logger != nil { info.Logger.Info("created key file", zap.String("path", keyPath)) } - return SelfCert(lg, dirpath, hosts) + return SelfCert(lg, dirpath, hosts, selfSignedCertValidity) } // baseConfig is called on initial TLS handshake start. diff --git a/pkg/transport/listener_test.go b/pkg/transport/listener_test.go index c92b89298..dbded1946 100644 --- a/pkg/transport/listener_test.go +++ b/pkg/transport/listener_test.go @@ -37,7 +37,7 @@ func createSelfCertEx(host string, additionalUsages ...x509.ExtKeyUsage) (*TLSIn if terr != nil { return nil, nil, terr } - info, err := SelfCert(zap.NewExample(), d, []string{host + ":0"}, additionalUsages...) + info, err := SelfCert(zap.NewExample(), d, []string{host + ":0"}, 1, additionalUsages...) if err != nil { return nil, nil, err } @@ -366,7 +366,7 @@ func TestNewListenerTLSInfoSelfCert(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(tmpdir) - tlsinfo, err := SelfCert(zap.NewExample(), tmpdir, []string{"127.0.0.1"}) + tlsinfo, err := SelfCert(zap.NewExample(), tmpdir, []string{"127.0.0.1"}, 1) if err != nil { t.Fatal(err) } diff --git a/server/embed/config.go b/server/embed/config.go index b6626a6fa..a77931d5a 100644 --- a/server/embed/config.go +++ b/server/embed/config.go @@ -184,6 +184,10 @@ type Config struct { ClientAutoTLS bool PeerTLSInfo transport.TLSInfo PeerAutoTLS bool + // SelfSignedCertValidity specifies the validity period of the client and peer certificates + // that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS, + // the unit is year, and the default is 1 + SelfSignedCertValidity uint // CipherSuites is a list of supported TLS cipher suites between // client/server and peers. If empty, Go auto-populates the list. @@ -731,7 +735,7 @@ func (cfg *Config) ClientSelfCert() (err error) { for i, u := range cfg.LCUrls { chosts[i] = u.Host } - cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts) + cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts, cfg.SelfSignedCertValidity) if err != nil { return err } @@ -750,7 +754,7 @@ func (cfg *Config) PeerSelfCert() (err error) { for i, u := range cfg.LPUrls { phosts[i] = u.Host } - cfg.PeerTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "peer"), phosts) + cfg.PeerTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "peer"), phosts, cfg.SelfSignedCertValidity) if err != nil { return err } diff --git a/server/etcdmain/config.go b/server/etcdmain/config.go index 9bb6b4d45..67b37deeb 100644 --- a/server/etcdmain/config.go +++ b/server/etcdmain/config.go @@ -207,6 +207,7 @@ func newConfig() *config { fs.BoolVar(&cfg.ec.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.") fs.StringVar(&cfg.ec.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") fs.BoolVar(&cfg.ec.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") + fs.UintVar(&cfg.ec.SelfSignedCertValidity, "self-signed-cert-validity", 1, "The validity period of the client and peer certificates, unit is year") fs.StringVar(&cfg.ec.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.") fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedCN, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.") fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedHostname, "peer-cert-allowed-hostname", "", "Allowed TLS hostname for inter peer authentication.") diff --git a/server/etcdmain/etcd.go b/server/etcdmain/etcd.go index 3961200e5..8f7e4f661 100644 --- a/server/etcdmain/etcd.go +++ b/server/etcdmain/etcd.go @@ -393,7 +393,7 @@ func startProxy(cfg *config) error { } listenerTLS := cfg.ec.ClientTLSInfo if cfg.ec.ClientAutoTLS && cTLS { - listenerTLS, err = transport.SelfCert(cfg.ec.GetLogger(), filepath.Join(cfg.ec.Dir, "clientCerts"), cHosts) + listenerTLS, err = transport.SelfCert(cfg.ec.GetLogger(), filepath.Join(cfg.ec.Dir, "clientCerts"), cHosts, cfg.ec.SelfSignedCertValidity) if err != nil { lg.Fatal("failed to initialize self-signed client cert", zap.Error(err)) } diff --git a/server/etcdmain/grpc_proxy.go b/server/etcdmain/grpc_proxy.go index 0327034d4..83bee5015 100644 --- a/server/etcdmain/grpc_proxy.go +++ b/server/etcdmain/grpc_proxy.go @@ -76,6 +76,7 @@ var ( grpcProxyListenKey string grpcProxyListenAutoTLS bool grpcProxyListenCRL string + selfSignedCertValidity uint grpcProxyAdvertiseClientURL string grpcProxyResolverPrefix string @@ -149,6 +150,7 @@ func newGRPCProxyStartCommand() *cobra.Command { cmd.Flags().StringVar(&grpcProxyListenCA, "trusted-ca-file", "", "verify certificates of TLS-enabled secure proxy using this CA bundle") 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") // experimental flags cmd.Flags().BoolVar(&grpcProxyEnableOrdering, "experimental-serializable-ordering", false, "Ensure serializable reads have monotonically increasing store revisions across endpoints.") @@ -189,7 +191,7 @@ func startGRPCProxy(cmd *cobra.Command, args []string) { if tlsinfo == nil && grpcProxyListenAutoTLS { host := []string{"https://" + grpcProxyListenAddr} dir := filepath.Join(grpcProxyDataDir, "fixtures", "proxy") - autoTLS, err := transport.SelfCert(lg, dir, host) + autoTLS, err := transport.SelfCert(lg, dir, host, selfSignedCertValidity) if err != nil { log.Fatal(err) } @@ -254,6 +256,10 @@ func checkArgs() { fmt.Fprintln(os.Stderr, fmt.Errorf("invalid advertise-client-url %q", grpcProxyAdvertiseClientURL)) os.Exit(1) } + if grpcProxyListenAutoTLS && selfSignedCertValidity == 0 { + fmt.Fprintln(os.Stderr, fmt.Errorf("selfSignedCertValidity is invalid,it should be greater than 0")) + os.Exit(1) + } } func mustNewClient(lg *zap.Logger) *clientv3.Client { diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go index ce074f050..81682573e 100644 --- a/server/etcdmain/help.go +++ b/server/etcdmain/help.go @@ -150,6 +150,8 @@ Security: Allowed TLS hostname for inter peer authentication. --peer-auto-tls 'false' Peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided. + --self-signed-cert-validity '1' + The validity period of the client and peer certificates that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS, the unit is year, and the default is 1. --peer-crl-file '' Path to the peer certificate revocation list file. --cipher-suites ''