Skip to content

Commit

Permalink
feat: provide dynamic client CA matching
Browse files Browse the repository at this point in the history
Use a "hack" with cloned tls.Config to allow dynamic reloading of client
CA certs.

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
  • Loading branch information
smira committed Mar 18, 2024
1 parent 2f4f911 commit c240482
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
24 changes: 21 additions & 3 deletions tls/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ import (

// CertificateProvider describes an interface by which TLS certificates may be managed.
type CertificateProvider interface {
// GetCA returns the active root CA.
// GetCA returns the active root CA certificate(s).
GetCA() ([]byte, error)

// GetCACertPool returns the active root CA certificates as a certificate pool.
GetCACertPool() (*x509.CertPool, error)

// GetCertificate returns the current certificate matching the given client request.
GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error)

Expand All @@ -40,8 +43,9 @@ type certificateProvider struct {

generator Generator

ca []byte
crt *tls.Certificate
ca []byte
caCertPool *x509.CertPool
crt *tls.Certificate

csrOptions []talosx509.Option
}
Expand Down Expand Up @@ -98,6 +102,17 @@ func (p *certificateProvider) GetCA() ([]byte, error) {
return p.ca, nil
}

func (p *certificateProvider) GetCACertPool() (*x509.CertPool, error) {
if p == nil {
return nil, errors.New("no provider")
}

p.rw.RLock()
defer p.rw.RUnlock()

return p.caCertPool, nil
}

func (p *certificateProvider) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
if p == nil {
return nil, errors.New("no provider")
Expand All @@ -119,6 +134,9 @@ func (p *certificateProvider) updateCertificates(ca []byte, cert *tls.Certificat

p.ca = ca
p.crt = cert

p.caCertPool = x509.NewCertPool()
p.caCertPool.AppendCertsFromPEM(ca)
}

func (p *certificateProvider) manageUpdates(ctx context.Context) error {
Expand Down
23 changes: 23 additions & 0 deletions tls/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,29 @@ func WithCACertPEM(ca []byte) func(*tls.Config) error {
}
}

// WithDynamicClientCA declares a dynamic client CA which will be updated on each client request.
//
// This methods is for server-side TLS.
// For client TLS, build the new `tls.Config` on each request.
func WithDynamicClientCA(p CertificateProvider) func(*tls.Config) error {
return func(cfg *tls.Config) error {
cfg.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
newCfg := cfg.Clone()

var err error

newCfg.ClientCAs, err = p.GetCACertPool()
if err != nil {
return nil, err
}

return newCfg, nil
}

return nil
}
}

func defaultConfig() *tls.Config {
return &tls.Config{
RootCAs: x509.NewCertPool(),
Expand Down

0 comments on commit c240482

Please sign in to comment.