Skip to content

Commit 421b2ec

Browse files
authored
Merge pull request #22025 from ahrtr/20260701_crl_3.6
[release-3.6] Reject the client request if the client certificate has been revoked
2 parents 1663b36 + 8221ae8 commit 421b2ec

3 files changed

Lines changed: 47 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 := os.ReadFile(crlPath)

server/embed/serve.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,14 @@ func (sctx *serveCtx) serve(
233233
return tlsErr
234234
}
235235

236+
// In gRPC-only mode the gRPC stack owns the TLS handshake (via grpc.Creds).
237+
// Wrapping sctx.l with a second TLS listener would cause a double-TLS failure,
238+
// so inject CRL checking into the TLS config before the gRPC server is created
239+
// (gRPC clones the config at creation time).
240+
if onlyGRPC {
241+
tlsinfo.ConfigureCRLVerification(tlscfg)
242+
}
243+
236244
if grpcEnabled {
237245
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
238246
v3electionpb.RegisterElectionServer(gs, servElection)

tests/e2e/ctl_v3_kv_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ func TestCtlV3GetRevokedCRL(t *testing.T) {
5555
testCtl(t, testGetRevokedCRL, withCfg(*cfg))
5656
}
5757

58+
// TestCtlV3GetRevokedCRLGRPCOnly is the same as TestCtlV3GetRevokedCRL but uses
59+
// --listen-client-http-urls to split HTTP and gRPC onto separate ports. In that
60+
// mode the gRPC server owns the TLS handshake directly (onlyGRPC=true in
61+
// serve.go) and CRL checking must be injected via ConfigureCRLVerification
62+
// rather than through the wrapping TLS listener used in the combined path.
63+
func TestCtlV3GetRevokedCRLGRPCOnly(t *testing.T) {
64+
cfg := e2e.NewConfig(
65+
e2e.WithClusterSize(1),
66+
e2e.WithClientConnType(e2e.ClientTLS),
67+
e2e.WithClientRevokeCerts(true),
68+
e2e.WithClientCertAuthority(true),
69+
e2e.WithClientHTTPSeparate(true),
70+
)
71+
testCtl(t, testGetRevokedCRL, withCfg(*cfg))
72+
}
73+
5874
func testGetRevokedCRL(cx ctlCtx) {
5975
// test reject
6076
require.ErrorContains(cx.t, ctlV3Put(cx, "k", "v", ""), "context deadline exceeded")

0 commit comments

Comments
 (0)