@marcel91200 / caddy Mirror · read-only / commits / b249b45d
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

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.

Matthew Holt committed Sep 30, 2019 at 09:07 UTC b249b45d109cdfef51b94cdeeb1ef7593e3b26ab
6 files changed +123 -86
go.mod
+1 -1
@@ -26,5 +26,5 @@ require (
26 github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3
27 go.starlark.net v0.0.0-20190604130855-6ddc71c0ba77
28 golang.org/x/net v0.0.0-20190603091049-60506f45cf65
29 - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
29 + golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0
30 )
go.sum
+2
@@ -298,6 +298,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
298 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
299 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
300 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
301 +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
302 +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
303 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
304 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
305 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
modules/caddyhttp/caddyhttp.go
+6 -3
@@ -329,15 +329,18 @@ func (app *App) automaticHTTPS() error {
329 // to tell the TLS app to manage these certs by honoring
330 // those port configurations
331 acmeManager := &caddytls.ACMEManagerMaker{
332 - Challenges: caddytls.ChallengesConfig{
333 - HTTP: caddytls.HTTPChallengeConfig{
332 + Challenges: &caddytls.ChallengesConfig{
333 + HTTP: &caddytls.HTTPChallengeConfig{
334 AlternatePort: app.HTTPPort, // we specifically want the user-configured port, if any
335 },
336 - TLSALPN: caddytls.TLSALPNChallengeConfig{
336 + TLSALPN: &caddytls.TLSALPNChallengeConfig{
337 AlternatePort: app.HTTPSPort, // we specifically want the user-configured port, if any
338 },
339 },
340 }
341 + if tlsApp.Automation == nil {
342 + tlsApp.Automation = new(caddytls.AutomationConfig)
343 + }
344 tlsApp.Automation.Policies = append(tlsApp.Automation.Policies,
345 caddytls.AutomationPolicy{
346 Hosts: domainsForCerts,
modules/caddytls/acmemanager.go
+37 -28
@@ -40,16 +40,16 @@ func init() {
40 // after you have configured this struct
41 // to your liking.
42 type ACMEManagerMaker struct {
43 - CA string `json:"ca,omitempty"`
44 - Email string `json:"email,omitempty"`
45 - RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
46 - KeyType string `json:"key_type,omitempty"`
47 - ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
48 - MustStaple bool `json:"must_staple,omitempty"`
49 - Challenges ChallengesConfig `json:"challenges,omitempty"`
50 - OnDemand bool `json:"on_demand,omitempty"`
51 - Storage json.RawMessage `json:"storage,omitempty"`
52 - TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`
43 + CA string `json:"ca,omitempty"`
44 + Email string `json:"email,omitempty"`
45 + RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
46 + KeyType string `json:"key_type,omitempty"`
47 + ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
48 + MustStaple bool `json:"must_staple,omitempty"`
49 + Challenges *ChallengesConfig `json:"challenges,omitempty"`
50 + OnDemand bool `json:"on_demand,omitempty"`
51 + Storage json.RawMessage `json:"storage,omitempty"`
52 + TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"`
53
54 storage certmagic.Storage
55 rootPool *x509.CertPool
@@ -72,7 +72,7 @@ func (m ACMEManagerMaker) NewManager(interactive bool) (certmagic.Manager, error
72 // Provision sets up m.
73 func (m *ACMEManagerMaker) Provision(ctx caddy.Context) error {
74 // DNS providers
75 - if m.Challenges.DNSRaw != nil {
75 + if m.Challenges != nil && m.Challenges.DNSRaw != nil {
76 val, err := ctx.LoadModuleInline("provider", "tls.dns", m.Challenges.DNSRaw)
77 if err != nil {
78 return fmt.Errorf("loading DNS provider module: %s", err)
@@ -125,7 +125,7 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
125 if m.OnDemand {
126 var onDemand *OnDemandConfig
127 appVal, err := ctx.App("tls")
128 - if err == nil {
128 + if err == nil && appVal.(*TLS).Automation != nil {
129 onDemand = appVal.(*TLS).Automation.OnDemand
130 }
131
@@ -153,24 +153,33 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
153 }
154 }
155
156 - return certmagic.Config{
157 - CA: m.CA,
158 - Email: m.Email,
159 - Agreed: true,
160 - DisableHTTPChallenge: m.Challenges.HTTP.Disabled,
161 - DisableTLSALPNChallenge: m.Challenges.TLSALPN.Disabled,
162 - RenewDurationBefore: time.Duration(m.RenewAhead),
163 - AltHTTPPort: m.Challenges.HTTP.AlternatePort,
164 - AltTLSALPNPort: m.Challenges.TLSALPN.AlternatePort,
165 - DNSProvider: m.Challenges.DNS,
166 - KeyType: supportedCertKeyTypes[m.KeyType],
167 - CertObtainTimeout: time.Duration(m.ACMETimeout),
168 - OnDemand: ond,
169 - MustStaple: m.MustStaple,
170 - Storage: storage,
171 - TrustedRoots: m.rootPool,
156 + cfg := certmagic.Config{
157 + CA: m.CA,
158 + Email: m.Email,
159 + Agreed: true,
160 + RenewDurationBefore: time.Duration(m.RenewAhead),
161 + KeyType: supportedCertKeyTypes[m.KeyType],
162 + CertObtainTimeout: time.Duration(m.ACMETimeout),
163 + OnDemand: ond,
164 + MustStaple: m.MustStaple,
165 + Storage: storage,
166 + TrustedRoots: m.rootPool,
167 // TODO: listenHost
168 }
169 +
170 + if m.Challenges != nil {
171 + if m.Challenges.HTTP != nil {
172 + cfg.DisableHTTPChallenge = m.Challenges.HTTP.Disabled
173 + cfg.AltHTTPPort = m.Challenges.HTTP.AlternatePort
174 + }
175 + if m.Challenges.TLSALPN != nil {
176 + cfg.DisableTLSALPNChallenge = m.Challenges.TLSALPN.Disabled
177 + cfg.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort
178 + }
179 + cfg.DNSProvider = m.Challenges.DNS
180 + }
181 +
182 + return cfg
183 }
184
185 // onDemandAskRequest makes a request to the ask URL
modules/caddytls/connpolicy.go
+13 -11
@@ -155,17 +155,19 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
155 }
156
157 // session tickets support
158 - cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled
159 -
160 - // session ticket key rotation
161 - tlsApp.SessionTickets.register(cfg)
162 - ctx.OnCancel(func() {
163 - // do cleanup when the context is cancelled because,
164 - // though unlikely, it is possible that a context
165 - // needing a TLS server config could exist for less
166 - // than the lifetime of the whole app
167 - tlsApp.SessionTickets.unregister(cfg)
168 - })
158 + if tlsApp.SessionTickets != nil {
159 + cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled
160 +
161 + // session ticket key rotation
162 + tlsApp.SessionTickets.register(cfg)
163 + ctx.OnCancel(func() {
164 + // do cleanup when the context is cancelled because,
165 + // though unlikely, it is possible that a context
166 + // needing a TLS server config could exist for less
167 + // than the lifetime of the whole app
168 + tlsApp.SessionTickets.unregister(cfg)
169 + })
170 + }
171
172 // TODO: Clean up session ticket active locks in storage if app (or process) is being closed!
173
modules/caddytls/tls.go
+64 -43
@@ -36,8 +36,8 @@ func init() {
36 // TLS represents a process-wide TLS configuration.
37 type TLS struct {
38 Certificates map[string]json.RawMessage `json:"certificates,omitempty"`
39 - Automation AutomationConfig `json:"automation"`
40 - SessionTickets SessionTicketService `json:"session_tickets"`
39 + Automation *AutomationConfig `json:"automation,omitempty"`
40 + SessionTickets *SessionTicketService `json:"session_tickets,omitempty"`
41
42 certificateLoaders []CertificateLoader
43 certCache *certmagic.Cache
@@ -58,26 +58,28 @@ func (TLS) CaddyModule() caddy.ModuleInfo {
58 func (t *TLS) Provision(ctx caddy.Context) error {
59 t.ctx = ctx
60
61 - // set up the certificate cache
62 - // TODO: this makes a new cache every time; better to only make a new
63 - // cache (or even better, add/remove only what is necessary) if the
64 - // certificates config has been updated
65 - t.certCache = certmagic.NewCache(certmagic.CacheOptions{
61 + // set up a new certificate cache; this (re)loads all certificates
62 + cacheOpts := certmagic.CacheOptions{
63 GetConfigForCert: func(cert certmagic.Certificate) (certmagic.Config, error) {
64 return t.getConfigForName(cert.Names[0])
65 },
69 - OCSPCheckInterval: time.Duration(t.Automation.OCSPCheckInterval),
70 - RenewCheckInterval: time.Duration(t.Automation.RenewCheckInterval),
71 - })
66 + }
67 + if t.Automation != nil {
68 + cacheOpts.OCSPCheckInterval = time.Duration(t.Automation.OCSPCheckInterval)
69 + cacheOpts.RenewCheckInterval = time.Duration(t.Automation.RenewCheckInterval)
70 + }
71 + t.certCache = certmagic.NewCache(cacheOpts)
72
73 // automation/management policies
74 - for i, ap := range t.Automation.Policies {
75 - val, err := ctx.LoadModuleInline("module", "tls.management", ap.ManagementRaw)
76 - if err != nil {
77 - return fmt.Errorf("loading TLS automation management module: %s", err)
74 + if t.Automation != nil {
75 + for i, ap := range t.Automation.Policies {
76 + val, err := ctx.LoadModuleInline("module", "tls.management", ap.ManagementRaw)
77 + if err != nil {
78 + return fmt.Errorf("loading TLS automation management module: %s", err)
79 + }
80 + t.Automation.Policies[i].Management = val.(ManagerMaker)
81 + t.Automation.Policies[i].ManagementRaw = nil // allow GC to deallocate
82 }
79 - t.Automation.Policies[i].Management = val.(ManagerMaker)
80 - t.Automation.Policies[i].ManagementRaw = nil // allow GC to deallocate
83 }
84
85 // certificate loaders
@@ -93,19 +95,22 @@ func (t *TLS) Provision(ctx caddy.Context) error {
95 }
96
97 // session ticket ephemeral keys (STEK) service and provider
96 - err := t.SessionTickets.provision(ctx)
97 - if err != nil {
98 - return fmt.Errorf("provisioning session tickets configuration: %v", err)
98 + if t.SessionTickets != nil {
99 + err := t.SessionTickets.provision(ctx)
100 + if err != nil {
101 + return fmt.Errorf("provisioning session tickets configuration: %v", err)
102 + }
103 }
104
105 // on-demand rate limiting
102 - if t.Automation.OnDemand != nil && t.Automation.OnDemand.RateLimit != nil {
106 + if t.Automation != nil && t.Automation.OnDemand != nil && t.Automation.OnDemand.RateLimit != nil {
107 limit := rate.Every(time.Duration(t.Automation.OnDemand.RateLimit.Interval))
104 - // TODO: Burst size is not updated, see https://github.com/golang/go/issues/23575
108 onDemandRateLimiter.SetLimit(limit)
109 + onDemandRateLimiter.SetBurst(t.Automation.OnDemand.RateLimit.Burst)
110 } else {
111 // if no rate limit is specified, be sure to remove any existing limit
112 onDemandRateLimiter.SetLimit(0)
113 + onDemandRateLimiter.SetBurst(0)
114 }
115
116 // load manual/static (unmanaged) certificates - we do this in
@@ -127,9 +132,6 @@ func (t *TLS) Provision(ctx caddy.Context) error {
132 }
133 }
134
130 - t.storageCleanTicker = time.NewTicker(storageCleanInterval)
131 - t.storageCleanStop = make(chan struct{})
132 -
135 return nil
136 }
137
@@ -156,17 +158,23 @@ func (t *TLS) Start() error {
158
159 // Stop stops the TLS module and cleans up any allocations.
160 func (t *TLS) Stop() error {
161 + // stop the storage cleaner goroutine and ticker
162 + close(t.storageCleanStop)
163 + t.storageCleanTicker.Stop()
164 + return nil
165 +}
166 +
167 +// Cleanup frees up resources allocated during Provision.
168 +func (t *TLS) Cleanup() error {
169 // stop the certificate cache
170 if t.certCache != nil {
171 t.certCache.Stop()
172 }
173
174 // stop the session ticket rotation goroutine
165 - t.SessionTickets.stop()
166 -
167 - // stop the storage cleaner goroutine and ticker
168 - close(t.storageCleanStop)
169 - t.storageCleanTicker.Stop()
175 + if t.SessionTickets != nil {
176 + t.SessionTickets.stop()
177 + }
178
179 return nil
180 }
@@ -202,15 +210,17 @@ func (t *TLS) getConfigForName(name string) (certmagic.Config, error) {
210 }
211
212 func (t *TLS) getAutomationPolicyForName(name string) AutomationPolicy {
205 - for _, ap := range t.Automation.Policies {
206 - if len(ap.Hosts) == 0 {
207 - // no host filter is an automatic match
208 - return ap
209 - }
210 - for _, h := range ap.Hosts {
211 - if h == name {
213 + if t.Automation != nil {
214 + for _, ap := range t.Automation.Policies {
215 + if len(ap.Hosts) == 0 {
216 + // no host filter is an automatic match
217 return ap
218 }
219 + for _, h := range ap.Hosts {
220 + if h == name {
221 + return ap
222 + }
223 + }
224 }
225 }
226
@@ -228,6 +238,8 @@ func (t *TLS) AllMatchingCertificates(san string) []certmagic.Certificate {
238 // if it was not recently done, and starts a goroutine that runs
239 // the operation at every tick from t.storageCleanTicker.
240 func (t *TLS) keepStorageClean() {
241 + t.storageCleanTicker = time.NewTicker(storageCleanInterval)
242 + t.storageCleanStop = make(chan struct{})
243 go func() {
244 for {
245 select {
@@ -259,10 +271,12 @@ func (t *TLS) cleanStorageUnits() {
271 certmagic.CleanStorage(t.ctx.Storage(), options)
272
273 // then clean each storage defined in ACME automation policies
262 - for _, ap := range t.Automation.Policies {
263 - if acmeMgmt, ok := ap.Management.(ACMEManagerMaker); ok {
264 - if acmeMgmt.storage != nil {
265 - certmagic.CleanStorage(acmeMgmt.storage, options)
274 + if t.Automation != nil {
275 + for _, ap := range t.Automation.Policies {
276 + if acmeMgmt, ok := ap.Management.(ACMEManagerMaker); ok {
277 + if acmeMgmt.storage != nil {
278 + certmagic.CleanStorage(acmeMgmt.storage, options)
279 + }
280 }
281 }
282 }
@@ -321,9 +335,9 @@ func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
335
336 // ChallengesConfig configures the ACME challenges.
337 type ChallengesConfig struct {
324 - HTTP HTTPChallengeConfig `json:"http"`
325 - TLSALPN TLSALPNChallengeConfig `json:"tls-alpn"`
326 - DNSRaw json.RawMessage `json:"dns,omitempty"`
338 + HTTP *HTTPChallengeConfig `json:"http,omitempty"`
339 + TLSALPN *TLSALPNChallengeConfig `json:"tls-alpn,omitempty"`
340 + DNSRaw json.RawMessage `json:"dns,omitempty"`
341
342 DNS challenge.Provider `json:"-"`
343 }
@@ -377,4 +391,11 @@ var (
391 storageCleanMu sync.Mutex
392 )
393
394 +// Interface guards
395 +var (
396 + _ caddy.App = (*TLS)(nil)
397 + _ caddy.Provisioner = (*TLS)(nil)
398 + _ caddy.CleanerUpper = (*TLS)(nil)
399 +)
400 +
401 const automateKey = "automate"