oauth2-proxy/pkg/requests/dynamic_ca.go

70 lines
2.1 KiB
Go

package requests
import (
"crypto/x509"
"sync/atomic"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/watcher"
)
// DynamicCALoader provides hot-reloadable CA certificates.
// It caches the certificate pool and reloads from disk when triggered
// by file system events via the watcher.
type DynamicCALoader struct {
caFiles []string
useSystemTrustStore bool
certPool atomic.Pointer[x509.CertPool]
}
// NewDynamicCALoader creates a new DynamicCALoader that loads CA certificates
// from the specified files. The certificates are reloaded automatically when
// files change on disk.
func NewDynamicCALoader(caFiles []string, useSystemTrustStore bool) (*DynamicCALoader, error) {
d := &DynamicCALoader{
caFiles: caFiles,
useSystemTrustStore: useSystemTrustStore,
}
if err := d.reload(); err != nil {
return nil, err
}
return d, nil
}
// GetCertPool returns the current CA certificate pool.
func (d *DynamicCALoader) GetCertPool() (*x509.CertPool, error) {
return d.certPool.Load(), nil
}
// reload loads CA certificates from disk and updates the cached pool.
func (d *DynamicCALoader) reload() error {
pool, err := util.GetCertPool(d.caFiles, d.useSystemTrustStore)
if err != nil {
return err
}
d.certPool.Store(pool)
logger.Printf("CA certificates reloaded from %v", d.caFiles)
return nil
}
// ForceReload forces immediate reload of CA certificates.
// This is called by file watcher when CA files change on disk.
func (d *DynamicCALoader) ForceReload() {
if err := d.reload(); err != nil {
logger.Errorf("CA reload failed: %v", err)
}
}
// StartWatching sets up file watchers for all CA files.
// When any CA file changes, the certificates are automatically reloaded.
// This supports Kubernetes ConfigMap/Secret mounts which use symlink replacement.
func (d *DynamicCALoader) StartWatching(done <-chan bool) error {
for _, caFile := range d.caFiles {
if err := watcher.WatchFileForUpdates(caFile, done, d.ForceReload); err != nil {
return err
}
}
return nil
}