@marcel91200 / caddy Mirror · read-only / commits / 269b1e9a
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: Improve (and fix) on-demand configuration

Matthew Holt committed Jun 20, 2019 at 20:36 UTC 269b1e9aa34b2b02911f8746e7b6a162cd8222cf
5 files changed +104 -11
go.mod
+2 -1
@@ -16,12 +16,13 @@ require (
16 github.com/imdario/mergo v0.3.7 // indirect
17 github.com/klauspost/compress v1.7.1-0.20190613161414-0b31f265a57b
18 github.com/klauspost/cpuid v1.2.1
19 - github.com/mholt/certmagic v0.6.0
19 + github.com/mholt/certmagic v0.6.2-0.20190621004807-be4f86a2eb60
20 github.com/rs/cors v1.6.0
21 github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
22 github.com/starlight-go/starlight v0.0.0-20181207205707-b06f321544f3
23 go.starlark.net v0.0.0-20190604130855-6ddc71c0ba77
24 golang.org/x/net v0.0.0-20190603091049-60506f45cf65
25 + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
26 gopkg.in/russross/blackfriday.v2 v2.0.1
27 )
28
go.sum
+4
@@ -36,6 +36,8 @@ github.com/mholt/certmagic v0.5.2-0.20190605043235-e49d0d405641 h1:wNqOQ0DFxcZDN
36 github.com/mholt/certmagic v0.5.2-0.20190605043235-e49d0d405641/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
37 github.com/mholt/certmagic v0.6.0 h1:gZwuBuONw2v8/fZh2nd39kvjFNjnSF2uIR4GzKaaryw=
38 github.com/mholt/certmagic v0.6.0/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
39 +github.com/mholt/certmagic v0.6.2-0.20190621004807-be4f86a2eb60 h1:SALetD3LrWGNvna2JIwY1dG4W6rBKtoBehQtHjEKTpo=
40 +github.com/mholt/certmagic v0.6.2-0.20190621004807-be4f86a2eb60/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
41 github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM=
42 github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
43 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -66,5 +68,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv
68 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
69 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
70 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
71 +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
72 +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
73 gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
74 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
modules/caddytls/acmemanager.go
+60 -6
@@ -3,6 +3,7 @@ package caddytls
3 import (
4 "encoding/json"
5 "fmt"
6 + "net/url"
7 "time"
8
9 "github.com/go-acme/lego/certcrypto"
@@ -30,12 +31,12 @@ func init() {
31 type ACMEManagerMaker struct {
32 CA string `json:"ca,omitempty"`
33 Email string `json:"email,omitempty"`
33 - RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
34 + RenewAhead caddy.Duration `json:"renew_ahead,omitempty"`
35 KeyType string `json:"key_type,omitempty"`
35 - ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
36 + ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
37 MustStaple bool `json:"must_staple,omitempty"`
37 - Challenges ChallengesConfig `json:"challenges"`
38 - OnDemand *OnDemandConfig `json:"on_demand,omitempty"`
38 + Challenges ChallengesConfig `json:"challenges,omitempty"`
39 + OnDemand bool `json:"on_demand,omitempty"`
40 Storage json.RawMessage `json:"storage,omitempty"`
41
42 storage certmagic.Storage
@@ -109,9 +110,34 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
110 }
111
112 var ond *certmagic.OnDemandConfig
112 - if m.OnDemand != nil {
113 + if m.OnDemand {
114 + var onDemand *OnDemandConfig
115 + appVal, err := ctx.App("tls")
116 + if err == nil {
117 + onDemand = appVal.(*TLS).Automation.OnDemand
118 + }
119 +
120 ond = &certmagic.OnDemandConfig{
114 - // TODO: fill this out
121 + DecisionFunc: func(name string) error {
122 + if onDemand != nil {
123 + if onDemand.Ask != "" {
124 + err := onDemandAskRequest(onDemand.Ask, name)
125 + if err != nil {
126 + return err
127 + }
128 + }
129 + // check the rate limiter last, since
130 + // even checking consumes a token; so
131 + // don't even bother checking if the
132 + // other regulations fail anyway
133 + if onDemand.RateLimit != nil {
134 + if !onDemandRateLimiter.Allow() {
135 + return fmt.Errorf("on-demand rate limit exceeded")
136 + }
137 + }
138 + }
139 + return nil
140 + },
141 }
142 }
143
@@ -134,6 +160,34 @@ func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Conf
160 }
161 }
162
163 +// onDemandAskRequest makes a request to the ask URL
164 +// to see if a certificate can be obtained for name.
165 +// The certificate request should be denied if this
166 +// returns an error.
167 +func onDemandAskRequest(ask string, name string) error {
168 + askURL, err := url.Parse(ask)
169 + if err != nil {
170 + return fmt.Errorf("parsing ask URL: %v", err)
171 + }
172 + qs := askURL.Query()
173 + qs.Set("domain", name)
174 + askURL.RawQuery = qs.Encode()
175 +
176 + resp, err := onDemandAskClient.Get(askURL.String())
177 + if err != nil {
178 + return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v",
179 + ask, name, err)
180 + }
181 + resp.Body.Close()
182 +
183 + if resp.StatusCode < 200 || resp.StatusCode > 299 {
184 + return fmt.Errorf("certificate for hostname '%s' not allowed; non-2xx status code %d returned from %v",
185 + name, resp.StatusCode, ask)
186 + }
187 +
188 + return nil
189 +}
190 +
191 // supportedCertKeyTypes is all the key types that are supported
192 // for certificates that are obtained through ACME.
193 var supportedCertKeyTypes = map[string]certcrypto.KeyType{
modules/caddytls/connpolicy.go
+3
@@ -86,6 +86,7 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) (*tls.Config, error) {
86 }
87 return pol.stdTLSConfig, nil
88 }
89 +
90 return nil, fmt.Errorf("no server TLS configuration available for ClientHello: %+v", hello)
91 },
92 }, nil
@@ -148,6 +149,8 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
149 tlsApp.SessionTickets.unregister(cfg)
150 })
151
152 + // TODO: Clean up active locks if app (or process) is being closed!
153 +
154 // add all the cipher suites in order, without duplicates
155 cipherSuitesAdded := make(map[uint16]struct{})
156 for _, csName := range p.CipherSuites {
modules/caddytls/tls.go
+35 -4
@@ -5,10 +5,12 @@ import (
5 "encoding/json"
6 "fmt"
7 "net/http"
8 + "time"
9
10 "github.com/caddyserver/caddy"
11 "github.com/go-acme/lego/challenge"
12 "github.com/mholt/certmagic"
13 + "golang.org/x/time/rate"
14 )
15
16 func init() {
@@ -71,6 +73,16 @@ func (t *TLS) Provision(ctx caddy.Context) error {
73 return fmt.Errorf("provisioning session tickets configuration: %v", err)
74 }
75
76 + // on-demand rate limiting
77 + if t.Automation.OnDemand != nil && t.Automation.OnDemand.RateLimit != nil {
78 + limit := rate.Every(time.Duration(t.Automation.OnDemand.RateLimit.Interval))
79 + // TODO: Burst size is not updated, see https://github.com/golang/go/issues/23575
80 + onDemandRateLimiter.SetLimit(limit)
81 + } else {
82 + // if no rate limit is specified, be sure to remove any existing limit
83 + onDemandRateLimiter.SetLimit(0)
84 + }
85 +
86 return nil
87 }
88
@@ -178,6 +190,7 @@ type CertificateLoader interface {
190 // construction and use of ACME clients.
191 type AutomationConfig struct {
192 Policies []AutomationPolicy `json:"policies,omitempty"`
193 + OnDemand *OnDemandConfig `json:"on_demand,omitempty"`
194 }
195
196 // AutomationPolicy designates the policy for automating the
@@ -189,6 +202,9 @@ type AutomationPolicy struct {
202 Management managerMaker `json:"-"`
203 }
204
205 +// makeCertMagicConfig converts ap into a CertMagic config. Passing onDemand
206 +// is necessary because the automation policy does not have convenient access
207 +// to the TLS app's global on-demand policies;
208 func (ap AutomationPolicy) makeCertMagicConfig(ctx caddy.Context) certmagic.Config {
209 // default manager (ACME) is a special case because of how CertMagic is designed
210 // TODO: refactor certmagic so that ACME manager is not a special case by extracting
@@ -226,10 +242,14 @@ type TLSALPNChallengeConfig struct {
242 // OnDemandConfig configures on-demand TLS, for obtaining
243 // needed certificates at handshake-time.
244 type OnDemandConfig struct {
229 - // TODO: MaxCertificates state might not endure reloads...
230 - // MaxCertificates int `json:"max_certificates,omitempty"`
231 - AskURL string `json:"ask_url,omitempty"`
232 - AskStarlark string `json:"ask_starlark,omitempty"`
245 + RateLimit *RateLimit `json:"rate_limit,omitempty"`
246 + Ask string `json:"ask,omitempty"`
247 +}
248 +
249 +// RateLimit specifies an interval with optional burst size.
250 +type RateLimit struct {
251 + Interval caddy.Duration `json:"interval,omitempty"`
252 + Burst int `json:"burst,omitempty"`
253 }
254
255 // managerMaker makes a certificate manager.
@@ -237,4 +257,15 @@ type managerMaker interface {
257 newManager(interactive bool) (certmagic.Manager, error)
258 }
259
260 +// These perpetual values are used for on-demand TLS.
261 +var (
262 + onDemandRateLimiter = rate.NewLimiter(0, 1)
263 + onDemandAskClient = &http.Client{
264 + Timeout: 10 * time.Second,
265 + CheckRedirect: func(req *http.Request, via []*http.Request) error {
266 + return fmt.Errorf("following http redirects is not allowed")
267 + },
268 + }
269 +)
270 +
271 const automateKey = "automate"