Skip to content

Commit fed7930

Browse files
authored
Merge pull request #22021 from ahrtr/20260701_crl_3.5
[release-3.5] Reject the client request if the client certificate has been revoked
2 parents f7e018b + 24838af commit fed7930

3 files changed

Lines changed: 48 additions & 0 deletions

File tree

client/pkg/transport/listener_tls.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,29 @@ func (l *tlsListener) acceptLoop() {
166166
}
167167
}
168168

169+
// ConfigureCRLVerification appends a VerifyConnection hook to cfg that
170+
// rejects any peer certificate whose serial number appears in the CRL file
171+
// configured on info. It is a no-op when CRLFile is empty. Any existing
172+
// VerifyConnection hook is called first and its error short-circuits.
173+
func (info TLSInfo) ConfigureCRLVerification(cfg *tls.Config) {
174+
if len(info.CRLFile) == 0 {
175+
return
176+
}
177+
crlFile := info.CRLFile
178+
prev := cfg.VerifyConnection
179+
cfg.VerifyConnection = func(cs tls.ConnectionState) error {
180+
if prev != nil {
181+
if err := prev(cs); err != nil {
182+
return err
183+
}
184+
}
185+
if len(cs.PeerCertificates) == 0 {
186+
return nil
187+
}
188+
return checkCRL(crlFile, cs.PeerCertificates)
189+
}
190+
}
191+
169192
func checkCRL(crlPath string, cert []*x509.Certificate) error {
170193
// TODO: cache
171194
crlBytes, err := ioutil.ReadFile(crlPath)

server/embed/serve.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ func (sctx *serveCtx) serve(
223223
return tlsErr
224224
}
225225

226+
// In gRPC-only mode the gRPC stack owns the TLS handshake (via grpc.Creds).
227+
// Wrapping sctx.l with a second TLS listener would cause a double-TLS failure,
228+
// so inject CRL checking into the TLS config before the gRPC server is created
229+
// (gRPC clones the config at creation time).
230+
if onlyGRPC {
231+
tlsinfo.ConfigureCRLVerification(tlscfg)
232+
}
233+
226234
if grpcEnabled {
227235
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
228236
v3electionpb.RegisterElectionServer(gs, servElection)

tests/e2e/ctl_v3_kv_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ func TestCtlV3GetRevokedCRL(t *testing.T) {
6969
testCtl(t, testGetRevokedCRL, withCfg(cfg))
7070
}
7171

72+
// TestCtlV3GetRevokedCRLGRPCOnly is the same as TestCtlV3GetRevokedCRL but uses
73+
// --listen-client-http-urls to split HTTP and gRPC onto separate ports. In that
74+
// mode the gRPC server owns the TLS handshake directly (onlyGRPC=true in
75+
// serve.go) and CRL checking must be injected via ConfigureCRLVerification
76+
// rather than through the wrapping TLS listener used in the combined path.
77+
func TestCtlV3GetRevokedCRLGRPCOnly(t *testing.T) {
78+
cfg := e2e.EtcdProcessClusterConfig{
79+
ClusterSize: 1,
80+
InitialToken: "new",
81+
ClientTLS: e2e.ClientTLS,
82+
IsClientCRL: true,
83+
ClientCertAuthEnabled: true,
84+
ClientHttpSeparate: true,
85+
}
86+
testCtl(t, testGetRevokedCRL, withCfg(cfg))
87+
}
88+
7289
func testGetRevokedCRL(cx ctlCtx) {
7390
// test reject
7491
if err := ctlV3Put(cx, "k", "v", ""); err == nil || !strings.Contains(err.Error(), "Error:") {

0 commit comments

Comments
 (0)