mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-10 07:37:33 +02:00
* envoy: support autocert * envoy: fallback to http host routing if sni fails to match * update comment * envoy: renew certs when necessary * fix tests
102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"sync"
|
|
|
|
"github.com/caddyserver/certmagic"
|
|
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
)
|
|
|
|
// AutocertManager manages Let's Encrypt certificates based on configuration options.
|
|
var AutocertManager = newAutocertManager()
|
|
|
|
type autocertManager struct {
|
|
mu sync.RWMutex
|
|
certmagic *certmagic.Config
|
|
acmeMgr *certmagic.ACMEManager
|
|
}
|
|
|
|
func newAutocertManager() *autocertManager {
|
|
mgr := &autocertManager{}
|
|
return mgr
|
|
}
|
|
|
|
func (mgr *autocertManager) getConfig(options *Options) (*certmagic.Config, error) {
|
|
mgr.mu.Lock()
|
|
defer mgr.mu.Unlock()
|
|
|
|
cm := mgr.certmagic
|
|
if cm == nil {
|
|
cm = certmagic.NewDefault()
|
|
}
|
|
|
|
cm.OnDemand = nil // disable on-demand
|
|
cm.Storage = &certmagic.FileStorage{Path: options.AutoCertFolder}
|
|
// add existing certs to the cache, and staple OCSP
|
|
for _, cert := range options.Certificates {
|
|
if err := cm.CacheUnmanagedTLSCertificate(cert, nil); err != nil {
|
|
return nil, fmt.Errorf("config: failed caching cert: %w", err)
|
|
}
|
|
}
|
|
acmeMgr := certmagic.NewACMEManager(cm, certmagic.DefaultACME)
|
|
acmeMgr.Agreed = true
|
|
if options.AutoCertUseStaging {
|
|
acmeMgr.CA = certmagic.LetsEncryptStagingCA
|
|
}
|
|
acmeMgr.DisableTLSALPNChallenge = true
|
|
cm.Issuer = acmeMgr
|
|
mgr.acmeMgr = acmeMgr
|
|
|
|
return cm, nil
|
|
}
|
|
|
|
func (mgr *autocertManager) update(options *Options) error {
|
|
if !options.AutoCert {
|
|
return nil
|
|
}
|
|
|
|
cm, err := mgr.getConfig(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, domain := range options.sourceHostnames() {
|
|
cert, err := cm.CacheManagedCertificate(domain)
|
|
if err != nil {
|
|
log.Info().Str("domain", domain).Msg("obtaining certificate")
|
|
err = cm.ObtainCert(context.Background(), domain, false)
|
|
if err != nil {
|
|
return fmt.Errorf("config: failed to obtain client certificate: %w", err)
|
|
}
|
|
cert, err = cm.CacheManagedCertificate(domain)
|
|
}
|
|
if err == nil && cert.NeedsRenewal(cm) {
|
|
log.Info().Str("domain", domain).Msg("renewing certificate")
|
|
err = cm.RenewCert(context.Background(), domain, false)
|
|
if err != nil {
|
|
return fmt.Errorf("config: failed to renew client certificate: %w", err)
|
|
}
|
|
cert, err = cm.CacheManagedCertificate(domain)
|
|
}
|
|
if err == nil {
|
|
options.Certificates = append(options.Certificates, cert.Certificate)
|
|
} else {
|
|
log.Error().Err(err).Msg("config: failed to obtain client certificate")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mgr *autocertManager) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool {
|
|
mgr.mu.RLock()
|
|
acmeMgr := mgr.acmeMgr
|
|
mgr.mu.RUnlock()
|
|
if acmeMgr == nil {
|
|
return false
|
|
}
|
|
return acmeMgr.HandleHTTPChallenge(w, r)
|
|
}
|