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"