@ -36,8 +36,8 @@ func init() {
// TLS represents a process-wide TLS configuration.
// TLS represents a process-wide TLS configuration.
type TLS struct {
type TLS struct {
Certificates map [ string ] json . RawMessage ` json:"certificates,omitempty" `
Certificates map [ string ] json . RawMessage ` json:"certificates,omitempty" `
Automation AutomationConfig ` json:"automation" `
Automation * AutomationConfig ` json:"automation,omitempty " `
SessionTickets SessionTicketService ` json:"session_tickets" `
SessionTickets * SessionTicketService ` json:"session_tickets,omitempty " `
certificateLoaders [ ] CertificateLoader
certificateLoaders [ ] CertificateLoader
certCache * certmagic . Cache
certCache * certmagic . Cache
@ -58,26 +58,28 @@ func (TLS) CaddyModule() caddy.ModuleInfo {
func ( t * TLS ) Provision ( ctx caddy . Context ) error {
func ( t * TLS ) Provision ( ctx caddy . Context ) error {
t . ctx = ctx
t . ctx = ctx
// set up the certificate cache
// set up a new certificate cache; this (re)loads all certificates
// TODO: this makes a new cache every time; better to only make a new
cacheOpts := certmagic . CacheOptions {
// cache (or even better, add/remove only what is necessary) if the
// certificates config has been updated
t . certCache = certmagic . NewCache ( certmagic . CacheOptions {
GetConfigForCert : func ( cert certmagic . Certificate ) ( certmagic . Config , error ) {
GetConfigForCert : func ( cert certmagic . Certificate ) ( certmagic . Config , error ) {
return t . getConfigForName ( cert . Names [ 0 ] )
return t . getConfigForName ( cert . Names [ 0 ] )
} ,
} ,
OCSPCheckInterval : time . Duration ( t . Automation . OCSPCheckInterval ) ,
}
RenewCheckInterval : time . Duration ( t . Automation . RenewCheckInterval ) ,
if t . Automation != nil {
} )
cacheOpts . OCSPCheckInterval = time . Duration ( t . Automation . OCSPCheckInterval )
cacheOpts . RenewCheckInterval = time . Duration ( t . Automation . RenewCheckInterval )
}
t . certCache = certmagic . NewCache ( cacheOpts )
// automation/management policies
// automation/management policies
for i , ap := range t . Automation . Policies {
if t . Automation != nil {
val , err := ctx . LoadModuleInline ( "module" , "tls.management" , ap . ManagementRaw )
for i , ap := range t . Automation . Policies {
if err != nil {
val , err := ctx . LoadModuleInline ( "module" , "tls.management" , ap . ManagementRaw )
return fmt . Errorf ( "loading TLS automation management module: %s" , err )
if err != nil {
return fmt . Errorf ( "loading TLS automation management module: %s" , err )
}
t . Automation . Policies [ i ] . Management = val . ( ManagerMaker )
t . Automation . Policies [ i ] . ManagementRaw = nil // allow GC to deallocate
}
}
t . Automation . Policies [ i ] . Management = val . ( ManagerMaker )
t . Automation . Policies [ i ] . ManagementRaw = nil // allow GC to deallocate
}
}
// certificate loaders
// certificate loaders
@ -93,19 +95,22 @@ func (t *TLS) Provision(ctx caddy.Context) error {
}
}
// session ticket ephemeral keys (STEK) service and provider
// session ticket ephemeral keys (STEK) service and provider
err := t . SessionTickets . provision ( ctx )
if t . SessionTickets != nil {
if err != nil {
err := t . SessionTickets . provision ( ctx )
return fmt . Errorf ( "provisioning session tickets configuration: %v" , err )
if err != nil {
return fmt . Errorf ( "provisioning session tickets configuration: %v" , err )
}
}
}
// on-demand rate limiting
// on-demand rate limiting
if t . Automation . OnDemand != nil && t . Automation . OnDemand . RateLimit != nil {
if t . Automation != nil && t . Automation . OnDemand != nil && t . Automation . OnDemand . RateLimit != nil {
limit := rate . Every ( time . Duration ( t . Automation . OnDemand . RateLimit . Interval ) )
limit := rate . Every ( time . Duration ( t . Automation . OnDemand . RateLimit . Interval ) )
// TODO: Burst size is not updated, see https://github.com/golang/go/issues/23575
onDemandRateLimiter . SetLimit ( limit )
onDemandRateLimiter . SetLimit ( limit )
onDemandRateLimiter . SetBurst ( t . Automation . OnDemand . RateLimit . Burst )
} else {
} else {
// if no rate limit is specified, be sure to remove any existing limit
// if no rate limit is specified, be sure to remove any existing limit
onDemandRateLimiter . SetLimit ( 0 )
onDemandRateLimiter . SetLimit ( 0 )
onDemandRateLimiter . SetBurst ( 0 )
}
}
// load manual/static (unmanaged) certificates - we do this in
// load manual/static (unmanaged) certificates - we do this in
@ -127,9 +132,6 @@ func (t *TLS) Provision(ctx caddy.Context) error {
}
}
}
}
t . storageCleanTicker = time . NewTicker ( storageCleanInterval )
t . storageCleanStop = make ( chan struct { } )
return nil
return nil
}
}
@ -156,17 +158,23 @@ func (t *TLS) Start() error {
// Stop stops the TLS module and cleans up any allocations.
// Stop stops the TLS module and cleans up any allocations.
func ( t * TLS ) Stop ( ) error {
func ( t * TLS ) Stop ( ) error {
// stop the storage cleaner goroutine and ticker
close ( t . storageCleanStop )
t . storageCleanTicker . Stop ( )
return nil
}
// Cleanup frees up resources allocated during Provision.
func ( t * TLS ) Cleanup ( ) error {
// stop the certificate cache
// stop the certificate cache
if t . certCache != nil {
if t . certCache != nil {
t . certCache . Stop ( )
t . certCache . Stop ( )
}
}
// stop the session ticket rotation goroutine
// stop the session ticket rotation goroutine
t . SessionTickets . stop ( )
if t . SessionTickets != nil {
t . SessionTickets . stop ( )
// stop the storage cleaner goroutine and ticker
}
close ( t . storageCleanStop )
t . storageCleanTicker . Stop ( )
return nil
return nil
}
}
@ -202,15 +210,17 @@ func (t *TLS) getConfigForName(name string) (certmagic.Config, error) {
}
}
func ( t * TLS ) getAutomationPolicyForName ( name string ) AutomationPolicy {
func ( t * TLS ) getAutomationPolicyForName ( name string ) AutomationPolicy {
for _ , ap := range t . Automation . Policies {
if t . Automation != nil {
if len ( ap . Hosts ) == 0 {
for _ , ap := range t . Automation . Policies {
// no host filter is an automatic match
if len ( ap . Hosts ) == 0 {
return ap
// no host filter is an automatic match
}
for _ , h := range ap . Hosts {
if h == name {
return ap
return ap
}
}
for _ , h := range ap . Hosts {
if h == name {
return ap
}
}
}
}
}
}
@ -228,6 +238,8 @@ func (t *TLS) AllMatchingCertificates(san string) []certmagic.Certificate {
// if it was not recently done, and starts a goroutine that runs
// if it was not recently done, and starts a goroutine that runs
// the operation at every tick from t.storageCleanTicker.
// the operation at every tick from t.storageCleanTicker.
func ( t * TLS ) keepStorageClean ( ) {
func ( t * TLS ) keepStorageClean ( ) {
t . storageCleanTicker = time . NewTicker ( storageCleanInterval )
t . storageCleanStop = make ( chan struct { } )
go func ( ) {
go func ( ) {
for {
for {
select {
select {
@ -259,10 +271,12 @@ func (t *TLS) cleanStorageUnits() {
certmagic . CleanStorage ( t . ctx . Storage ( ) , options )
certmagic . CleanStorage ( t . ctx . Storage ( ) , options )
// then clean each storage defined in ACME automation policies
// then clean each storage defined in ACME automation policies
for _ , ap := range t . Automation . Policies {
if t . Automation != nil {
if acmeMgmt , ok := ap . Management . ( ACMEManagerMaker ) ; ok {
for _ , ap := range t . Automation . Policies {
if acmeMgmt . storage != nil {
if acmeMgmt , ok := ap . Management . ( ACMEManagerMaker ) ; ok {
certmagic . CleanStorage ( acmeMgmt . storage , options )
if acmeMgmt . storage != nil {
certmagic . CleanStorage ( acmeMgmt . storage , options )
}
}
}
}
}
}
}
@ -321,9 +335,9 @@ func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
// ChallengesConfig configures the ACME challenges.
// ChallengesConfig configures the ACME challenges.
type ChallengesConfig struct {
type ChallengesConfig struct {
HTTP HTTPChallengeConfig ` json:"http" `
HTTP * HTTPChallengeConfig ` json:"http,omitempty " `
TLSALPN TLSALPNChallengeConfig ` json:"tls-alpn" `
TLSALPN * TLSALPNChallengeConfig ` json:"tls-alpn,omitempty " `
DNSRaw json . RawMessage ` json:"dns,omitempty" `
DNSRaw json . RawMessage ` json:"dns,omitempty" `
DNS challenge . Provider ` json:"-" `
DNS challenge . Provider ` json:"-" `
}
}
@ -377,4 +391,11 @@ var (
storageCleanMu sync . Mutex
storageCleanMu sync . Mutex
)
)
// Interface guards
var (
_ caddy . App = ( * TLS ) ( nil )
_ caddy . Provisioner = ( * TLS ) ( nil )
_ caddy . CleanerUpper = ( * TLS ) ( nil )
)
const automateKey = "automate"
const automateKey = "automate"