CVE-2018-16886
Description
etcd versions 3.2.x before 3.2.26 and 3.3.x before 3.3.11 are vulnerable to an improper authentication issue when role-based access control (RBAC) is used and client-cert-auth is enabled. If an etcd client server TLS certificate contains a Common Name (CN) which matches a valid RBAC username, a remote attacker may authenticate as that user with any valid (trusted) client certificate in a REST API request to the gRPC-gateway.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
etcd 3.2.x before 3.2.26 and 3.3.x before 3.3.11 allow authentication bypass via CN matching in client certificates when RBAC and client-cert-auth are enabled.
Vulnerability
etcd versions 3.2.x before 3.2.26 and 3.3.x before 3.3.11 contain an improper authentication vulnerability in the gRPC-gateway when role-based access control (RBAC) is used and --client-cert-auth is enabled [1][2]. The AuthInfoFromTLS() function in auth/store.go incorrectly uses the Common Name (CN) from the etcd client server TLS certificate to authenticate REST API requests. If the CN matches a valid RBAC username, the request is authenticated as that user, bypassing proper certificate-based authentication [4].
Exploitation
An attacker must possess a valid TLS client certificate that is trusted by the etcd server (i.e., signed by a CA in the server's trust store). The attacker then crafts a REST API request to the gRPC-gateway endpoint, ensuring the client certificate's CN field matches the username of an existing RBAC user. No additional authentication credentials are required; the server will accept the request as if it came from that user [4]. The attack is network-based and does not require prior authentication or user interaction.
Impact
Successful exploitation allows the attacker to impersonate any RBAC user whose username matches the CN in the attacker's certificate. This can lead to unauthorized access to etcd data and operations, including reading or writing keys, modifying cluster configuration, or performing administrative actions, depending on the privileges assigned to the impersonated user. The confidentiality, integrity, and availability of the etcd cluster may be compromised [4].
Mitigation
The vulnerability is fixed in etcd versions 3.2.26 and 3.3.11, released on 2019-01-11 [1][2]. Users running affected versions should upgrade immediately. As a workaround, if upgrading is not possible, disable the gRPC-gateway or avoid using --client-cert-auth in conjunction with RBAC. The fix disables CommonName authentication for gRPC-gateway proxy requests [1][2]. No known exploitation in the wild has been reported as of the publication date.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
go.etcd.io/etcd/v3Go | >= 3.2.0, < 3.2.26 | 3.2.26 |
go.etcd.io/etcd/v3Go | >= 3.3.0, < 3.3.11 | 3.3.11 |
go.etcd.io/etcdGo | < 0.5.0-alpha.5.0.20190108173120-83c051b701d3 | 0.5.0-alpha.5.0.20190108173120-83c051b701d3 |
Affected products
10- osv-coords9 versionspkg:apk/chainguard/etcd-3.4pkg:apk/chainguard/etcd-3.4-bitnami-compatpkg:apk/chainguard/etcd-3.4-iamguarded-compatpkg:apk/chainguard/etcd-fips-3.4pkg:golang/go.etcd.io/etcdpkg:golang/go.etcd.io/etcd/v3pkg:rpm/opensuse/etcd&distro=openSUSE%20Leap%2015.5pkg:rpm/opensuse/etcd&distro=openSUSE%20Leap%2015.6pkg:rpm/opensuse/etcd&distro=openSUSE%20Tumbleweed
< 3.4.36-r1+ 8 more
- (no CPE)range: < 3.4.36-r1
- (no CPE)range: < 3.4.36-r1
- (no CPE)range: < 3.4.36-r1
- (no CPE)range: < 3.4.36-r1
- (no CPE)range: < 0.5.0-alpha.5.0.20190108173120-83c051b701d3
- (no CPE)range: >= 3.2.0, < 3.2.26
- (no CPE)range: < 3.5.12-150000.7.6.1
- (no CPE)range: < 3.5.12-150000.7.6.1
- (no CPE)range: < 3.4.16-3.1
- The etcd Project/etcd:v5Range: versions 3.2.x before 3.2.26 and 3.3.x before 3.3.11
Patches
2bf9d0d8291dcauth: disable CommonName auth for gRPC-gateway
1 file changed · +21 −0
auth/store.go+21 −0 modified@@ -1166,6 +1166,27 @@ func (as *authStore) AuthInfoFromTLS(ctx context.Context) (ai *AuthInfo) { Username: chains[0].Subject.CommonName, Revision: as.Revision(), } + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil + } + + // gRPC-gateway proxy request to etcd server includes Grpcgateway-Accept + // header. The proxy uses etcd client server certificate. If the certificate + // has a CommonName we should never use this for authentication. + if gw := md["grpcgateway-accept"]; len(gw) > 0 { + if as.lg != nil { + as.lg.Warn( + "ignoring common name in gRPC-gateway proxy request", + zap.String("common-name", ai.Username), + zap.String("user-name", ai.Username), + zap.Uint64("revision", ai.Revision), + ) + } else { + plog.Warningf("ignoring common name in gRPC-gateway proxy request %s", ai.Username) + } + return nil + } if as.lg != nil { as.lg.Debug( "found command name",
019150963754auth, etcdserver: authenticate clients based on certificate CommonName
4 files changed · +43 −4
auth/store.go+27 −0 modified@@ -30,7 +30,9 @@ import ( "github.com/coreos/pkg/capnslog" "golang.org/x/crypto/bcrypt" "golang.org/x/net/context" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" ) var ( @@ -159,6 +161,9 @@ type AuthStore interface { // AuthInfoFromCtx gets AuthInfo from gRPC's context AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) + + // AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context + AuthInfoFromTLS(ctx context.Context) *AuthInfo } type authStore struct { @@ -950,6 +955,28 @@ func (as *authStore) isValidSimpleToken(token string, ctx context.Context) bool return false } +func (as *authStore) AuthInfoFromTLS(ctx context.Context) *AuthInfo { + peer, ok := peer.FromContext(ctx) + if !ok || peer == nil || peer.AuthInfo == nil { + return nil + } + + tlsInfo := peer.AuthInfo.(credentials.TLSInfo) + for _, chains := range tlsInfo.State.VerifiedChains { + for _, chain := range chains { + cn := chain.Subject.CommonName + plog.Debugf("found common name %s", cn) + + return &AuthInfo{ + Username: cn, + Revision: as.Revision(), + } + } + } + + return nil +} + func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) { md, ok := metadata.FromContext(ctx) if !ok {
etcdserver/api/v3rpc/maintenance.go+2 −1 modified@@ -47,6 +47,7 @@ type RaftStatusGetter interface { } type AuthGetter interface { + AuthInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error) AuthStore() auth.AuthStore } @@ -152,7 +153,7 @@ type authMaintenanceServer struct { } func (ams *authMaintenanceServer) isAuthenticated(ctx context.Context) error { - authInfo, err := ams.ag.AuthStore().AuthInfoFromCtx(ctx) + authInfo, err := ams.ag.AuthInfoFromCtx(ctx) if err != nil { return err }
etcdserver/server.go+1 −1 modified@@ -1022,7 +1022,7 @@ func (s *EtcdServer) checkMembershipOperationPermission(ctx context.Context) err // in the state machine layer // However, both of membership change and role management requires the root privilege. // So careful operation by admins can prevent the problem. - authInfo, err := s.AuthStore().AuthInfoFromCtx(ctx) + authInfo, err := s.AuthInfoFromCtx(ctx) if err != nil { return err }
etcdserver/v3_server.go+13 −2 modified@@ -617,7 +617,7 @@ func (s *EtcdServer) RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest // doSerialize handles the auth logic, with permissions checked by "chk", for a serialized request "get". Returns a non-nil error on authentication failure. func (s *EtcdServer) doSerialize(ctx context.Context, chk func(*auth.AuthInfo) error, get func()) error { for { - ai, err := s.AuthStore().AuthInfoFromCtx(ctx) + ai, err := s.AuthInfoFromCtx(ctx) if err != nil { return err } @@ -652,7 +652,7 @@ func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.In ID: s.reqIDGen.Next(), } - authInfo, err := s.AuthStore().AuthInfoFromCtx(ctx) + authInfo, err := s.AuthInfoFromCtx(ctx) if err != nil { return nil, err } @@ -802,3 +802,14 @@ func (s *EtcdServer) linearizableReadNotify(ctx context.Context) error { return ErrStopped } } + +func (s *EtcdServer) AuthInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error) { + if s.Cfg.ClientCertAuthEnabled { + authInfo := s.AuthStore().AuthInfoFromTLS(ctx) + if authInfo != nil { + return authInfo, nil + } + } + + return s.AuthStore().AuthInfoFromCtx(ctx) +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
16- access.redhat.com/errata/RHSA-2019:0237ghsavendor-advisoryx_refsource_REDHATWEB
- access.redhat.com/errata/RHSA-2019:1352ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-h6xx-pmxh-3wgpghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/JX7QTIT465BQGRGNCE74RATRQLKT2QE4/mitrevendor-advisoryx_refsource_FEDORA
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/UPGYHMSKDPW5GAMI7BEP3XQRVRLLBJKS/mitrevendor-advisoryx_refsource_FEDORA
- nvd.nist.gov/vuln/detail/CVE-2018-16886ghsaADVISORY
- www.securityfocus.com/bid/106540ghsavdb-entryx_refsource_BIDWEB
- bugzilla.redhat.com/show_bug.cgighsax_refsource_CONFIRMWEB
- github.com/etcd-io/etcd/blob/1eee465a43720d713bb69f7b7f5e120135fdb1ac/CHANGELOG-3.2.mdghsax_refsource_MISCWEB
- github.com/etcd-io/etcd/blob/1eee465a43720d713bb69f7b7f5e120135fdb1ac/CHANGELOG-3.3.mdghsax_refsource_MISCWEB
- github.com/etcd-io/etcd/commit/0191509637546621d6f2e18e074e955ab8ef374dghsaWEB
- github.com/etcd-io/etcd/commit/bf9d0d8291dc71ecbfb2690612954e1a298154b2ghsaWEB
- github.com/etcd-io/etcd/pull/10366ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JX7QTIT465BQGRGNCE74RATRQLKT2QE4ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UPGYHMSKDPW5GAMI7BEP3XQRVRLLBJKSghsaWEB
- pkg.go.dev/vuln/GO-2021-0077ghsaWEB
News mentions
0No linked articles in our index yet.