Skip to content

Commit f56e1c9

Browse files
authored
Merge pull request vmware-tanzu#1473 from bryanv/bryanv/webhook-verify-client-cert-fixes
🐛 Improve webhook CA CertPool handling
2 parents ec02d46 + c1cb830 commit f56e1c9

File tree

1 file changed

+48
-29
lines changed

1 file changed

+48
-29
lines changed

pkg/builder/auth.go

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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

2529
type 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

3842
func 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

Comments
 (0)