From fc99e879128af471d831c155c06750a581e2987e Mon Sep 17 00:00:00 2001 From: Andrew Lytvynov Date: Wed, 19 May 2021 17:07:08 +0000 Subject: [PATCH] etcd: use a separate connection to check peer versions (#6905) There is a data race in etcd that breaks the internal state in etcd client implementation for some server setups (user/pass authentication with JWTs). --- lib/backend/etcdbk/etcd.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/backend/etcdbk/etcd.go b/lib/backend/etcdbk/etcd.go index 0b671acccccbe..fe772f1169115 100644 --- a/lib/backend/etcdbk/etcd.go +++ b/lib/backend/etcdbk/etcd.go @@ -35,7 +35,6 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/coreos/go-semver/semver" - "github.com/gravitational/teleport" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/prometheus/client_golang/prometheus" @@ -46,6 +45,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/gravitational/teleport" ) var ( @@ -224,11 +225,10 @@ func New(ctx context.Context, params backend.Params) (*EtcdBackend, error) { buf: buf, } + // Check that the etcd nodes are at least the minimum version supported if err = b.reconnect(ctx); err != nil { return nil, trace.Wrap(err) } - - // Check that the etcd nodes are at least the minimum version supported timeout, cancel := context.WithTimeout(ctx, time.Second*3*time.Duration(len(cfg.Nodes))) defer cancel() for _, n := range cfg.Nodes { @@ -245,6 +245,13 @@ func New(ctx context.Context, params backend.Params) (*EtcdBackend, error) { } } + // Reconnect the etcd client to work around a data race in their code. + // Upstream fix: https://github.com/etcd-io/etcd/pull/12992 + if err = b.reconnect(ctx); err != nil { + return nil, trace.Wrap(err) + } + go b.asyncWatch() + // Wrap backend in a input sanitizer and return it. return b, nil } @@ -300,6 +307,12 @@ func (b *EtcdBackend) CloseWatchers() { } func (b *EtcdBackend) reconnect(ctx context.Context) error { + if b.client != nil { + if err := b.client.Close(); err != nil { + b.Entry.WithError(err).Warning("Failed closing existing etcd client on reconnect.") + } + } + tlsConfig := utils.TLSConfig(nil) if b.cfg.TLSCertFile != "" { @@ -350,7 +363,6 @@ func (b *EtcdBackend) reconnect(ctx context.Context) error { return trace.Wrap(err) } b.client = clt - go b.asyncWatch() return nil }