@@ -8,18 +8,22 @@ import (
88 "context"
99 "crypto/tls"
1010 "crypto/x509"
11+ "encoding/base64"
12+ "encoding/pem"
1113 "errors"
1214 "fmt"
1315 "net/http"
1416 "os"
1517 "path/filepath"
1618 "regexp"
1719 "strings"
20+ "sync/atomic"
1821
1922 authv1 "k8s.io/api/authentication/v1"
2023
2124 pkgcfg "github.com/vmware-tanzu/vm-operator/pkg/config"
2225 pkgctx "github.com/vmware-tanzu/vm-operator/pkg/context"
26+ pkglog "github.com/vmware-tanzu/vm-operator/pkg/log"
2327)
2428
2529type contextKey uint8
@@ -33,7 +37,7 @@ const (
3337 apiserverCN = "apiserver-webhook-client"
3438)
3539
36- var caCertPool * x509.CertPool
40+ var caCertPool atomic. Pointer [ x509.CertPool ]
3741
3842func IsPrivilegedAccount (
3943 ctx * pkgctx.WebhookContext ,
@@ -182,58 +186,73 @@ func verifyPeerCertificate(
182186 ctx context.Context ,
183187 connState * tls.ConnectionState ) error {
184188
185- certDir := pkgcfg .FromContext (ctx ).WebhookSecretVolumeMountPath
186- if certDir == "" {
187- certDir = pkgcfg .Default ().WebhookSecretVolumeMountPath
188- }
189- caFilePath := filepath .Join (certDir , "client-ca" , "ca.crt" )
190-
191- if err := loadCACert (caFilePath ); err != nil {
192- return err
193- }
194-
195189 if connState == nil || len (connState .PeerCertificates ) == 0 {
196190 return fmt .Errorf ("no client certificate provided" )
197191 }
198192
199193 // The first certificate is the leaf certificate.
200194 cert := connState .PeerCertificates [0 ]
201-
202195 if cert .Subject .CommonName != apiserverCN {
196+ // TODO: Check this after Verify()?
203197 return fmt .Errorf ("unauthorized client CN: %s" , cert .Subject .CommonName )
204198 }
205199
200+ pool , err := getCACertPool (ctx )
201+ if err != nil {
202+ return err
203+ }
204+
206205 opts := x509.VerifyOptions {
207- Roots : caCertPool ,
208- Intermediates : x509 .NewCertPool (),
209- KeyUsages : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
206+ Roots : pool ,
207+ KeyUsages : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
210208 }
211209
212- // Add intermediate if present
213- for _ , intermediate := range connState .PeerCertificates [1 :] {
214- opts .Intermediates .AddCert (intermediate )
210+ // Add intermediates if present.
211+ if len (connState .PeerCertificates ) > 1 {
212+ opts .Intermediates = x509 .NewCertPool ()
213+ for _ , intermediate := range connState .PeerCertificates [1 :] {
214+ opts .Intermediates .AddCert (intermediate )
215+ }
215216 }
216217
217- _ , err := cert .Verify (opts )
218- return err
218+ if _ , err = cert .Verify (opts ); err != nil {
219+ pemBlock := & pem.Block {Type : "CERTIFICATE" , Bytes : cert .Raw }
220+ pemBytes := pem .EncodeToMemory (pemBlock )
221+ pkglog .FromContextOrDefault (ctx ).Error (err , "error verifying client certificate" ,
222+ "clientCert" , base64 .StdEncoding .EncodeToString (pemBytes ),
223+ "peerCertCnt" , len (connState .PeerCertificates ))
224+ return err
225+ }
226+
227+ return nil
219228}
220229
221- func loadCACert ( path string ) error {
222- if caCertPool != nil {
223- return nil
230+ func getCACertPool ( ctx context. Context ) ( * x509. CertPool , error ) {
231+ if certPool := caCertPool . Load (); certPool != nil {
232+ return certPool , nil
224233 }
225234
226- caCertPEM , err := os .ReadFile (filepath .Clean (path ))
235+ certDir := pkgcfg .FromContext (ctx ).WebhookSecretVolumeMountPath
236+ if certDir == "" {
237+ certDir = pkgcfg .Default ().WebhookSecretVolumeMountPath
238+ }
239+ caFilePath := filepath .Clean (filepath .Join (certDir , "client-ca" , "ca.crt" ))
240+
241+ caCertPEM , err := os .ReadFile (caFilePath )
227242 if err != nil {
228- return err
243+ return nil , err
229244 }
230245
231- caCertPool = x509 .NewCertPool ()
232- if ok := caCertPool .AppendCertsFromPEM (caCertPEM ); ! ok {
233- return errors .New ("failed to append CA certificate" )
246+ certPool : = x509 .NewCertPool ()
247+ if ok := certPool .AppendCertsFromPEM (caCertPEM ); ! ok {
248+ return nil , errors .New ("failed to append CA certificate" )
234249 }
235250
236- return nil
251+ if caCertPool .CompareAndSwap (nil , certPool ) {
252+ return certPool , nil
253+ }
254+
255+ return caCertPool .Load (), nil
237256}
238257
239258// contextWithClientCert augments the given context with the request's client certs.
0 commit comments