package tls import ( "context" "crypto/tls" "time" "github.com/pkg/errors" "go.uber.org/zap" ) const ( defaultCertFile = "/var/secrets/mail/tls.crt" defaultKeyFile = "/var/secrets/mail/tls.key" ) type CertManager struct { cert *tls.Certificate Log *zap.Logger certFile string keyFile string } func NewCertManager(certFile, keyFile string) (*CertManager, error) { if certFile == "" { certFile = defaultCertFile } if keyFile == "" { keyFile = defaultKeyFile } cm := &CertManager{ certFile: certFile, keyFile: keyFile, } return cm, nil } func (cm *CertManager) Get(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { cm.Log.Info("received request for certificate") if cm.cert == nil { cm.Log.Error("no certificate available") return nil, errors.New("no certificate") } cm.Log.Info("returning certificate to requester") return cm.cert, nil } func (cm *CertManager) Load() error { cert, err := tls.LoadX509KeyPair(cm.certFile, cm.keyFile) if err != nil { return errors.Wrap(err, "failed to load certificate") } cm.cert = &cert return nil } func (cm *CertManager) Ready() bool { return cm.cert != nil } func (cm *CertManager) Watch(ctx context.Context, log *zap.Logger) error { cm.Log = log log.Info("loading certificate") if err := cm.Load(); err != nil { log.Error("failed to load initial certificate", zap.Error(err)) return err } log.Info("certificate loaded") for { select { case <-time.After(time.Hour): case <-ctx.Done(): return ctx.Err() } log.Info("reloading certificate") if err := cm.Load(); err != nil { log.Error("failed to reload new certificate", zap.Error(err)) continue } log.Info("certificate reloaded") } }