1
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-16 21:56:40 -05:00

tls: Change struct fields to pointers, add nil checks; rate.Burst update

Making them pointers makes for cleaner JSON when adapting configs, if
the struct is empty now it will be omitted entirely.

The x/time/rate package was updated to support changing the burst, so
we've incorporated that here and removed a TODO.
This commit is contained in:
Matthew Holt 2019-09-30 09:07:43 -06:00
parent c12bf4054c
commit b249b45d10
No known key found for this signature in database
GPG key ID: 2A349DD577D586A5
6 changed files with 122 additions and 85 deletions

2
go.mod
View file

@ -26,5 +26,5 @@ require (
github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3 github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3
go.starlark.net v0.0.0-20190604130855-6ddc71c0ba77 go.starlark.net v0.0.0-20190604130855-6ddc71c0ba77
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 golang.org/x/net v0.0.0-20190603091049-60506f45cf65
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0
) )

2
go.sum
View file

@ -298,6 +298,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View file

@ -329,15 +329,18 @@ func (app *App) automaticHTTPS() error {
// to tell the TLS app to manage these certs by honoring // to tell the TLS app to manage these certs by honoring
// those port configurations // those port configurations
acmeManager := &caddytls.ACMEManagerMaker{ acmeManager := &caddytls.ACMEManagerMaker{
Challenges: caddytls.ChallengesConfig{ Challenges: &caddytls.ChallengesConfig{
HTTP: caddytls.HTTPChallengeConfig{ HTTP: &caddytls.HTTPChallengeConfig{
AlternatePort: app.HTTPPort, // we specifically want the user-configured port, if any AlternatePort: app.HTTPPort, // we specifically want the user-configured port, if any
}, },
TLSALPN: caddytls.TLSALPNChallengeConfig{ TLSALPN: &caddytls.TLSALPNChallengeConfig{
AlternatePort: app.HTTPSPort, // we specifically want the user-configured port, if any AlternatePort: app.HTTPSPort, // we specifically want the user-configured port, if any
}, },
}, },
} }
if tlsApp.Automation == nil {
tlsApp.Automation = new(caddytls.AutomationConfig)
}
tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, tlsApp.Automation.Policies = append(tlsApp.Automation.Policies,
caddytls.AutomationPolicy{ caddytls.AutomationPolicy{
Hosts: domainsForCerts, Hosts: domainsForCerts,

View file

@ -40,16 +40,16 @@ func init() {
// after you have configured this struct // after you have configured this struct
// to your liking. // to your liking.
type ACMEManagerMaker struct { type ACMEManagerMaker struct {
CA string `json:"ca,omitempty"` CA string `json:"ca,omitempty"`
Email string `json:"email,omitempty"` Email string `json:"email,omitempty"`
RenewAhead caddy.Duration `json:"renew_ahead,omitempty"` RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
KeyType string `json:"key_type,omitempty"` KeyType string `json:"key_type,omitempty"`
ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"` ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
MustStaple bool `json:"must_staple,omitempty"` MustStaple bool `json:"must_staple,omitempty"`
Challenges ChallengesConfig `json:"challenges,omitempty"` Challenges *ChallengesConfig `json:"challenges,omitempty"`
OnDemand bool `json:"on_demand,omitempty"` OnDemand bool `json:"on_demand,omitempty"`
Storage json.RawMessage `json:"storage,omitempty"` Storage json.RawMessage `json:"storage,omitempty"`
TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"` TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`
storage certmagic.Storage storage certmagic.Storage
rootPool *x509.CertPool rootPool *x509.CertPool
@ -72,7 +72,7 @@ func (m ACMEManagerMaker) NewManager(interactive bool) (certmagic.Manager, error
// Provision sets up m. // Provision sets up m.
func (m *ACMEManagerMaker) Provision(ctx caddy.Context) error { func (m *ACMEManagerMaker) Provision(ctx caddy.Context) error {
// DNS providers // DNS providers
if m.Challenges.DNSRaw != nil { if m.Challenges != nil && m.Challenges.DNSRaw != nil {
val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNSRaw) val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNSRaw)
if err != nil { if err != nil {
return fmt.Errorf("loading DNS provider module: %s", err) return fmt.Errorf("loading DNS provider module: %s", err)
@ -125,7 +125,7 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
if m.OnDemand { if m.OnDemand {
var onDemand *OnDemandConfig var onDemand *OnDemandConfig
appVal, err := ctx.App("tls") appVal, err := ctx.App("tls")
if err == nil { if err == nil && appVal.(*TLS).Automation != nil {
onDemand = appVal.(*TLS).Automation.OnDemand onDemand = appVal.(*TLS).Automation.OnDemand
} }
@ -153,24 +153,33 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
} }
} }
return certmagic.Config{ cfg := certmagic.Config{
CA: m.CA, CA: m.CA,
Email: m.Email, Email: m.Email,
Agreed: true, Agreed: true,
DisableHTTPChallenge: m.Challenges.HTTP.Disabled, RenewDurationBefore: time.Duration(m.RenewAhead),
DisableTLSALPNChallenge: m.Challenges.TLSALPN.Disabled, KeyType: supportedCertKeyTypes[m.KeyType],
RenewDurationBefore: time.Duration(m.RenewAhead), CertObtainTimeout: time.Duration(m.ACMETimeout),
AltHTTPPort: m.Challenges.HTTP.AlternatePort, OnDemand: ond,
AltTLSALPNPort: m.Challenges.TLSALPN.AlternatePort, MustStaple: m.MustStaple,
DNSProvider: m.Challenges.DNS, Storage: storage,
KeyType: supportedCertKeyTypes[m.KeyType], TrustedRoots: m.rootPool,
CertObtainTimeout: time.Duration(m.ACMETimeout),
OnDemand: ond,
MustStaple: m.MustStaple,
Storage: storage,
TrustedRoots: m.rootPool,
// TODO: listenHost // TODO: listenHost
} }
if m.Challenges != nil {
if m.Challenges.HTTP != nil {
cfg.DisableHTTPChallenge = m.Challenges.HTTP.Disabled
cfg.AltHTTPPort = m.Challenges.HTTP.AlternatePort
}
if m.Challenges.TLSALPN != nil {
cfg.DisableTLSALPNChallenge = m.Challenges.TLSALPN.Disabled
cfg.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort
}
cfg.DNSProvider = m.Challenges.DNS
}
return cfg
} }
// onDemandAskRequest makes a request to the ask URL // onDemandAskRequest makes a request to the ask URL

View file

@ -155,17 +155,19 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
} }
// session tickets support // session tickets support
cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled if tlsApp.SessionTickets != nil {
cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled
// session ticket key rotation // session ticket key rotation
tlsApp.SessionTickets.register(cfg) tlsApp.SessionTickets.register(cfg)
ctx.OnCancel(func() { ctx.OnCancel(func() {
// do cleanup when the context is cancelled because, // do cleanup when the context is cancelled because,
// though unlikely, it is possible that a context // though unlikely, it is possible that a context
// needing a TLS server config could exist for less // needing a TLS server config could exist for less
// than the lifetime of the whole app // than the lifetime of the whole app
tlsApp.SessionTickets.unregister(cfg) tlsApp.SessionTickets.unregister(cfg)
}) })
}
// TODO: Clean up session ticket active locks in storage if app (or process) is being closed! // TODO: Clean up session ticket active locks in storage if app (or process) is being closed!

View file

@ -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"