@marcel91200 / caddy Mirror · read-only / commits / e42514ad
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

caddyhttp: Clean up; move some code around

Matthew Holt committed Mar 15, 2020 at 21:28 UTC e42514ad4af007799374aeb6eeea3bd884aac55b
2 files changed +436 -409
modules/caddyhttp/app.go new
+433
@@ -0,0 +1,433 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package caddyhttp
16 +
17 +import (
18 + "context"
19 + "crypto/tls"
20 + "fmt"
21 + "net"
22 + "net/http"
23 + "strconv"
24 + "time"
25 +
26 + "github.com/caddyserver/caddy/v2"
27 + "github.com/caddyserver/caddy/v2/modules/caddytls"
28 + "github.com/lucas-clemente/quic-go/http3"
29 + "go.uber.org/zap"
30 +)
31 +
32 +func init() {
33 + err := caddy.RegisterModule(App{})
34 + if err != nil {
35 + caddy.Log().Fatal(err.Error())
36 + }
37 +}
38 +
39 +// App is a robust, production-ready HTTP server.
40 +//
41 +// HTTPS is enabled by default if host matchers with qualifying names are used
42 +// in any of routes; certificates are automatically provisioned and renewed.
43 +// Additionally, automatic HTTPS will also enable HTTPS for servers that listen
44 +// only on the HTTPS port but which do not have any TLS connection policies
45 +// defined by adding a good, default TLS connection policy.
46 +//
47 +// In HTTP routes, additional placeholders are available (replace any `*`):
48 +//
49 +// Placeholder | Description
50 +// ------------|---------------
51 +// `{http.request.cookie.*}` | HTTP request cookie
52 +// `{http.request.header.*}` | Specific request header field
53 +// `{http.request.host.labels.*}` | Request host labels (0-based from right); e.g. for foo.example.com: 0=com, 1=example, 2=foo
54 +// `{http.request.host}` | The host part of the request's Host header
55 +// `{http.request.hostport}` | The host and port from the request's Host header
56 +// `{http.request.method}` | The request method
57 +// `{http.request.orig_method}` | The request's original method
58 +// `{http.request.orig_uri.path.dir}` | The request's original directory
59 +// `{http.request.orig_uri.path.file}` | The request's original filename
60 +// `{http.request.orig_uri.path}` | The request's original path
61 +// `{http.request.orig_uri.query}` | The request's original query string (without `?`)
62 +// `{http.request.orig_uri}` | The request's original URI
63 +// `{http.request.port}` | The port part of the request's Host header
64 +// `{http.request.proto}` | The protocol of the request
65 +// `{http.request.remote.host}` | The host part of the remote client's address
66 +// `{http.request.remote.port}` | The port part of the remote client's address
67 +// `{http.request.remote}` | The address of the remote client
68 +// `{http.request.scheme}` | The request scheme
69 +// `{http.request.tls.version}` | The TLS version name
70 +// `{http.request.tls.cipher_suite}` | The TLS cipher suite
71 +// `{http.request.tls.resumed}` | The TLS connection resumed a previous connection
72 +// `{http.request.tls.proto}` | The negotiated next protocol
73 +// `{http.request.tls.proto_mutual}` | The negotiated next protocol was advertised by the server
74 +// `{http.request.tls.server_name}` | The server name requested by the client, if any
75 +// `{http.request.tls.client.fingerprint}` | The SHA256 checksum of the client certificate
76 +// `{http.request.tls.client.issuer}` | The issuer DN of the client certificate
77 +// `{http.request.tls.client.serial}` | The serial number of the client certificate
78 +// `{http.request.tls.client.subject}` | The subject DN of the client certificate
79 +// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
80 +// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
81 +// `{http.request.uri.path.file}` | The filename of the path, excluding directory
82 +// `{http.request.uri.path}` | The path component of the request URI
83 +// `{http.request.uri.query.*}` | Individual query string value
84 +// `{http.request.uri.query}` | The query string (without `?`)
85 +// `{http.request.uri}` | The full request URI
86 +// `{http.response.header.*}` | Specific response header field
87 +// `{http.vars.*}` | Custom variables in the HTTP handler chain
88 +type App struct {
89 + // HTTPPort specifies the port to use for HTTP (as opposed to HTTPS),
90 + // which is used when setting up HTTP->HTTPS redirects or ACME HTTP
91 + // challenge solvers. Default: 80.
92 + HTTPPort int `json:"http_port,omitempty"`
93 +
94 + // HTTPSPort specifies the port to use for HTTPS, which is used when
95 + // solving the ACME TLS-ALPN challenges, or whenever HTTPS is needed
96 + // but no specific port number is given. Default: 443.
97 + HTTPSPort int `json:"https_port,omitempty"`
98 +
99 + // GracePeriod is how long to wait for active connections when shutting
100 + // down the server. Once the grace period is over, connections will
101 + // be forcefully closed.
102 + GracePeriod caddy.Duration `json:"grace_period,omitempty"`
103 +
104 + // Servers is the list of servers, keyed by arbitrary names chosen
105 + // at your discretion for your own convenience; the keys do not
106 + // affect functionality.
107 + Servers map[string]*Server `json:"servers,omitempty"`
108 +
109 + servers []*http.Server
110 + h3servers []*http3.Server
111 + h3listeners []net.PacketConn
112 +
113 + ctx caddy.Context
114 + logger *zap.Logger
115 + tlsApp *caddytls.TLS
116 +
117 + // used temporarily between phases 1 and 2 of auto HTTPS
118 + allCertDomains []string
119 +}
120 +
121 +// CaddyModule returns the Caddy module information.
122 +func (App) CaddyModule() caddy.ModuleInfo {
123 + return caddy.ModuleInfo{
124 + ID: "http",
125 + New: func() caddy.Module { return new(App) },
126 + }
127 +}
128 +
129 +// Provision sets up the app.
130 +func (app *App) Provision(ctx caddy.Context) error {
131 + // store some references
132 + tlsAppIface, err := ctx.App("tls")
133 + if err != nil {
134 + return fmt.Errorf("getting tls app: %v", err)
135 + }
136 + app.tlsApp = tlsAppIface.(*caddytls.TLS)
137 + app.ctx = ctx
138 + app.logger = ctx.Logger(app)
139 +
140 + repl := caddy.NewReplacer()
141 +
142 + // this provisions the matchers for each route,
143 + // and prepares auto HTTP->HTTPS redirects, and
144 + // is required before we provision each server
145 + err = app.automaticHTTPSPhase1(ctx, repl)
146 + if err != nil {
147 + return err
148 + }
149 +
150 + // prepare each server
151 + for srvName, srv := range app.Servers {
152 + srv.tlsApp = app.tlsApp
153 + srv.logger = app.logger.Named("log")
154 + srv.errorLogger = app.logger.Named("log.error")
155 +
156 + // only enable access logs if configured
157 + if srv.Logs != nil {
158 + srv.accessLogger = app.logger.Named("log.access")
159 + }
160 +
161 + // if not explicitly configured by the user, disallow TLS
162 + // client auth bypass (domain fronting) which could
163 + // otherwise be exploited by sending an unprotected SNI
164 + // value during a TLS handshake, then putting a protected
165 + // domain in the Host header after establishing connection;
166 + // this is a safe default, but we allow users to override
167 + // it for example in the case of running a proxy where
168 + // domain fronting is desired and access is not restricted
169 + // based on hostname
170 + if srv.StrictSNIHost == nil && srv.hasTLSClientAuth() {
171 + app.logger.Info("enabling strict SNI-Host matching because TLS client auth is configured",
172 + zap.String("server_name", srvName),
173 + )
174 + trueBool := true
175 + srv.StrictSNIHost = &trueBool
176 + }
177 +
178 + // process each listener address
179 + for i := range srv.Listen {
180 + lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
181 + if err != nil {
182 + return fmt.Errorf("server %s, listener %d: %v",
183 + srvName, i, err)
184 + }
185 + srv.Listen[i] = lnOut
186 + }
187 +
188 + // set up each listener modifier
189 + if srv.ListenerWrappersRaw != nil {
190 + vals, err := ctx.LoadModule(srv, "ListenerWrappersRaw")
191 + if err != nil {
192 + return fmt.Errorf("loading listener wrapper modules: %v", err)
193 + }
194 + var hasTLSPlaceholder bool
195 + for i, val := range vals.([]interface{}) {
196 + if _, ok := val.(*tlsPlaceholderWrapper); ok {
197 + if i == 0 {
198 + // putting the tls placeholder wrapper first is nonsensical because
199 + // that is the default, implicit setting: without it, all wrappers
200 + // will go after the TLS listener anyway
201 + return fmt.Errorf("it is unnecessary to specify the TLS listener wrapper in the first position because that is the default")
202 + }
203 + if hasTLSPlaceholder {
204 + return fmt.Errorf("TLS listener wrapper can only be specified once")
205 + }
206 + hasTLSPlaceholder = true
207 + }
208 + srv.listenerWrappers = append(srv.listenerWrappers, val.(caddy.ListenerWrapper))
209 + }
210 + // if any wrappers were configured but the TLS placeholder wrapper is
211 + // absent, prepend it so all defined wrappers come after the TLS
212 + // handshake; this simplifies logic when starting the server, since we
213 + // can simply assume the TLS placeholder will always be there
214 + if !hasTLSPlaceholder && len(srv.listenerWrappers) > 0 {
215 + srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
216 + }
217 + }
218 +
219 + // pre-compile the primary handler chain, and be sure to wrap it in our
220 + // route handler so that important security checks are done, etc.
221 + primaryRoute := emptyHandler
222 + if srv.Routes != nil {
223 + err := srv.Routes.ProvisionHandlers(ctx)
224 + if err != nil {
225 + return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
226 + }
227 + primaryRoute = srv.Routes.Compile(emptyHandler)
228 + }
229 + srv.primaryHandlerChain = srv.wrapPrimaryRoute(primaryRoute)
230 +
231 + // pre-compile the error handler chain
232 + if srv.Errors != nil {
233 + err := srv.Errors.Routes.Provision(ctx)
234 + if err != nil {
235 + return fmt.Errorf("server %s: setting up server error handling routes: %v", srvName, err)
236 + }
237 + srv.errorHandlerChain = srv.Errors.Routes.Compile(errorEmptyHandler)
238 + }
239 +
240 + // prepare the TLS connection policies
241 + err = srv.TLSConnPolicies.Provision(ctx)
242 + if err != nil {
243 + return fmt.Errorf("server %s: setting up TLS connection policies: %v", srvName, err)
244 + }
245 + }
246 +
247 + return nil
248 +}
249 +
250 +// Validate ensures the app's configuration is valid.
251 +func (app *App) Validate() error {
252 + // each server must use distinct listener addresses
253 + lnAddrs := make(map[string]string)
254 + for srvName, srv := range app.Servers {
255 + for _, addr := range srv.Listen {
256 + listenAddr, err := caddy.ParseNetworkAddress(addr)
257 + if err != nil {
258 + return fmt.Errorf("invalid listener address '%s': %v", addr, err)
259 + }
260 + // check that every address in the port range is unique to this server;
261 + // we do not use <= here because PortRangeSize() adds 1 to EndPort for us
262 + for i := uint(0); i < listenAddr.PortRangeSize(); i++ {
263 + addr := caddy.JoinNetworkAddress(listenAddr.Network, listenAddr.Host, strconv.Itoa(int(listenAddr.StartPort+i)))
264 + if sn, ok := lnAddrs[addr]; ok {
265 + return fmt.Errorf("server %s: listener address repeated: %s (already claimed by server '%s')", srvName, addr, sn)
266 + }
267 + lnAddrs[addr] = srvName
268 + }
269 + }
270 + }
271 +
272 + return nil
273 +}
274 +
275 +// Start runs the app. It finishes automatic HTTPS if enabled,
276 +// including management of certificates.
277 +func (app *App) Start() error {
278 + for srvName, srv := range app.Servers {
279 + s := &http.Server{
280 + ReadTimeout: time.Duration(srv.ReadTimeout),
281 + ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
282 + WriteTimeout: time.Duration(srv.WriteTimeout),
283 + IdleTimeout: time.Duration(srv.IdleTimeout),
284 + MaxHeaderBytes: srv.MaxHeaderBytes,
285 + Handler: srv,
286 + }
287 +
288 + for _, lnAddr := range srv.Listen {
289 + listenAddr, err := caddy.ParseNetworkAddress(lnAddr)
290 + if err != nil {
291 + return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
292 + }
293 + for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
294 + // create the listener for this socket
295 + hostport := listenAddr.JoinHostPort(portOffset)
296 + ln, err := caddy.Listen(listenAddr.Network, hostport)
297 + if err != nil {
298 + return fmt.Errorf("%s: listening on %s: %v", listenAddr.Network, hostport, err)
299 + }
300 +
301 + // wrap listener before TLS (up to the TLS placeholder wrapper)
302 + var lnWrapperIdx int
303 + for i, lnWrapper := range srv.listenerWrappers {
304 + if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok {
305 + lnWrapperIdx = i + 1 // mark the next wrapper's spot
306 + break
307 + }
308 + ln = lnWrapper.WrapListener(ln)
309 + }
310 +
311 + // enable TLS if there is a policy and if this is not the HTTP port
312 + useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
313 + if useTLS {
314 + // create TLS listener
315 + tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
316 + ln = tls.NewListener(ln, tlsCfg)
317 +
318 + /////////
319 + // TODO: HTTP/3 support is experimental for now
320 + if srv.ExperimentalHTTP3 {
321 + app.logger.Info("enabling experimental HTTP/3 listener",
322 + zap.String("addr", hostport),
323 + )
324 + h3ln, err := caddy.ListenPacket("udp", hostport)
325 + if err != nil {
326 + return fmt.Errorf("getting HTTP/3 UDP listener: %v", err)
327 + }
328 + h3srv := &http3.Server{
329 + Server: &http.Server{
330 + Addr: hostport,
331 + Handler: srv,
332 + TLSConfig: tlsCfg,
333 + },
334 + }
335 + go h3srv.Serve(h3ln)
336 + app.h3servers = append(app.h3servers, h3srv)
337 + app.h3listeners = append(app.h3listeners, h3ln)
338 + srv.h3server = h3srv
339 + }
340 + /////////
341 + }
342 +
343 + // finish wrapping listener where we left off before TLS
344 + for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ {
345 + ln = srv.listenerWrappers[i].WrapListener(ln)
346 + }
347 +
348 + app.logger.Debug("starting server loop",
349 + zap.String("address", lnAddr),
350 + zap.Bool("http3", srv.ExperimentalHTTP3),
351 + zap.Bool("tls", useTLS),
352 + )
353 +
354 + go s.Serve(ln)
355 + app.servers = append(app.servers, s)
356 + }
357 + }
358 + }
359 +
360 + // finish automatic HTTPS by finally beginning
361 + // certificate management
362 + err := app.automaticHTTPSPhase2()
363 + if err != nil {
364 + return fmt.Errorf("finalizing automatic HTTPS: %v", err)
365 + }
366 +
367 + return nil
368 +}
369 +
370 +// Stop gracefully shuts down the HTTP server.
371 +func (app *App) Stop() error {
372 + ctx := context.Background()
373 + if app.GracePeriod > 0 {
374 + var cancel context.CancelFunc
375 + ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod))
376 + defer cancel()
377 + }
378 + for _, s := range app.servers {
379 + err := s.Shutdown(ctx)
380 + if err != nil {
381 + return err
382 + }
383 + }
384 +
385 + // close the http3 servers; it's unclear whether the bug reported in
386 + // https://github.com/caddyserver/caddy/pull/2727#issuecomment-526856566
387 + // was ever truly fixed, since it seemed racey/nondeterministic; but
388 + // recent tests in 2020 were unable to replicate the issue again after
389 + // repeated attempts (the bug manifested after a config reload; i.e.
390 + // reusing a http3 server or listener was problematic), but it seems
391 + // to be working fine now
392 + for _, s := range app.h3servers {
393 + // TODO: CloseGracefully, once implemented upstream
394 + // (see https://github.com/lucas-clemente/quic-go/issues/2103)
395 + err := s.Close()
396 + if err != nil {
397 + return err
398 + }
399 + }
400 +
401 + // closing an http3.Server does not close their underlying listeners
402 + // since apparently the listener can be used both by servers and
403 + // clients at the same time; so we need to manually call Close()
404 + // on the underlying h3 listeners (see lucas-clemente/quic-go#2103)
405 + for _, pc := range app.h3listeners {
406 + err := pc.Close()
407 + if err != nil {
408 + return err
409 + }
410 + }
411 + return nil
412 +}
413 +
414 +func (app *App) httpPort() int {
415 + if app.HTTPPort == 0 {
416 + return DefaultHTTPPort
417 + }
418 + return app.HTTPPort
419 +}
420 +
421 +func (app *App) httpsPort() int {
422 + if app.HTTPSPort == 0 {
423 + return DefaultHTTPSPort
424 + }
425 + return app.HTTPSPort
426 +}
427 +
428 +// Interface guards
429 +var (
430 + _ caddy.App = (*App)(nil)
431 + _ caddy.Provisioner = (*App)(nil)
432 + _ caddy.Validator = (*App)(nil)
433 +)
modules/caddyhttp/caddyhttp.go
+3 -409
@@ -16,10 +16,7 @@ package caddyhttp
16
17 import (
18 "bytes"
19 - "context"
20 - "crypto/tls"
19 "encoding/json"
22 - "fmt"
20 "io"
21 weakrand "math/rand"
22 "net"
@@ -28,412 +25,15 @@ import (
25 "time"
26
27 "github.com/caddyserver/caddy/v2"
31 - "github.com/caddyserver/caddy/v2/modules/caddytls"
32 - "github.com/lucas-clemente/quic-go/http3"
33 - "go.uber.org/zap"
28 )
29
30 func init() {
31 weakrand.Seed(time.Now().UnixNano())
32
39 - err := caddy.RegisterModule(App{})
33 + err := caddy.RegisterModule(tlsPlaceholderWrapper{})
34 if err != nil {
35 caddy.Log().Fatal(err.Error())
36 }
43 -
44 - err = caddy.RegisterModule(tlsPlaceholderWrapper{})
45 - if err != nil {
46 - caddy.Log().Fatal(err.Error())
47 - }
48 -}
49 -
50 -// App is a robust, production-ready HTTP server.
51 -//
52 -// HTTPS is enabled by default if host matchers with qualifying names are used
53 -// in any of routes; certificates are automatically provisioned and renewed.
54 -// Additionally, automatic HTTPS will also enable HTTPS for servers that listen
55 -// only on the HTTPS port but which do not have any TLS connection policies
56 -// defined by adding a good, default TLS connection policy.
57 -//
58 -// In HTTP routes, additional placeholders are available (replace any `*`):
59 -//
60 -// Placeholder | Description
61 -// ------------|---------------
62 -// `{http.request.cookie.*}` | HTTP request cookie
63 -// `{http.request.header.*}` | Specific request header field
64 -// `{http.request.host.labels.*}` | Request host labels (0-based from right); e.g. for foo.example.com: 0=com, 1=example, 2=foo
65 -// `{http.request.host}` | The host part of the request's Host header
66 -// `{http.request.hostport}` | The host and port from the request's Host header
67 -// `{http.request.method}` | The request method
68 -// `{http.request.orig_method}` | The request's original method
69 -// `{http.request.orig_uri.path.dir}` | The request's original directory
70 -// `{http.request.orig_uri.path.file}` | The request's original filename
71 -// `{http.request.orig_uri.path}` | The request's original path
72 -// `{http.request.orig_uri.query}` | The request's original query string (without `?`)
73 -// `{http.request.orig_uri}` | The request's original URI
74 -// `{http.request.port}` | The port part of the request's Host header
75 -// `{http.request.proto}` | The protocol of the request
76 -// `{http.request.remote.host}` | The host part of the remote client's address
77 -// `{http.request.remote.port}` | The port part of the remote client's address
78 -// `{http.request.remote}` | The address of the remote client
79 -// `{http.request.scheme}` | The request scheme
80 -// `{http.request.tls.version}` | The TLS version name
81 -// `{http.request.tls.cipher_suite}` | The TLS cipher suite
82 -// `{http.request.tls.resumed}` | The TLS connection resumed a previous connection
83 -// `{http.request.tls.proto}` | The negotiated next protocol
84 -// `{http.request.tls.proto_mutual}` | The negotiated next protocol was advertised by the server
85 -// `{http.request.tls.server_name}` | The server name requested by the client, if any
86 -// `{http.request.tls.client.fingerprint}` | The SHA256 checksum of the client certificate
87 -// `{http.request.tls.client.issuer}` | The issuer DN of the client certificate
88 -// `{http.request.tls.client.serial}` | The serial number of the client certificate
89 -// `{http.request.tls.client.subject}` | The subject DN of the client certificate
90 -// `{http.request.uri.path.*}` | Parts of the path, split by `/` (0-based from left)
91 -// `{http.request.uri.path.dir}` | The directory, excluding leaf filename
92 -// `{http.request.uri.path.file}` | The filename of the path, excluding directory
93 -// `{http.request.uri.path}` | The path component of the request URI
94 -// `{http.request.uri.query.*}` | Individual query string value
95 -// `{http.request.uri.query}` | The query string (without `?`)
96 -// `{http.request.uri}` | The full request URI
97 -// `{http.response.header.*}` | Specific response header field
98 -// `{http.vars.*}` | Custom variables in the HTTP handler chain
99 -type App struct {
100 - // HTTPPort specifies the port to use for HTTP (as opposed to HTTPS),
101 - // which is used when setting up HTTP->HTTPS redirects or ACME HTTP
102 - // challenge solvers. Default: 80.
103 - HTTPPort int `json:"http_port,omitempty"`
104 -
105 - // HTTPSPort specifies the port to use for HTTPS, which is used when
106 - // solving the ACME TLS-ALPN challenges, or whenever HTTPS is needed
107 - // but no specific port number is given. Default: 443.
108 - HTTPSPort int `json:"https_port,omitempty"`
109 -
110 - // GracePeriod is how long to wait for active connections when shutting
111 - // down the server. Once the grace period is over, connections will
112 - // be forcefully closed.
113 - GracePeriod caddy.Duration `json:"grace_period,omitempty"`
114 -
115 - // Servers is the list of servers, keyed by arbitrary names chosen
116 - // at your discretion for your own convenience; the keys do not
117 - // affect functionality.
118 - Servers map[string]*Server `json:"servers,omitempty"`
119 -
120 - servers []*http.Server
121 - h3servers []*http3.Server
122 - h3listeners []net.PacketConn
123 -
124 - ctx caddy.Context
125 - logger *zap.Logger
126 - tlsApp *caddytls.TLS
127 -
128 - // used temporarily between phases 1 and 2 of auto HTTPS
129 - allCertDomains []string
130 -}
131 -
132 -// CaddyModule returns the Caddy module information.
133 -func (App) CaddyModule() caddy.ModuleInfo {
134 - return caddy.ModuleInfo{
135 - ID: "http",
136 - New: func() caddy.Module { return new(App) },
137 - }
138 -}
139 -
140 -// Provision sets up the app.
141 -func (app *App) Provision(ctx caddy.Context) error {
142 - // store some references
143 - tlsAppIface, err := ctx.App("tls")
144 - if err != nil {
145 - return fmt.Errorf("getting tls app: %v", err)
146 - }
147 - app.tlsApp = tlsAppIface.(*caddytls.TLS)
148 - app.ctx = ctx
149 - app.logger = ctx.Logger(app)
150 -
151 - repl := caddy.NewReplacer()
152 -
153 - // this provisions the matchers for each route,
154 - // and prepares auto HTTP->HTTPS redirects, and
155 - // is required before we provision each server
156 - err = app.automaticHTTPSPhase1(ctx, repl)
157 - if err != nil {
158 - return err
159 - }
160 -
161 - // prepare each server
162 - for srvName, srv := range app.Servers {
163 - srv.tlsApp = app.tlsApp
164 - srv.logger = app.logger.Named("log")
165 - srv.errorLogger = app.logger.Named("log.error")
166 -
167 - // only enable access logs if configured
168 - if srv.Logs != nil {
169 - srv.accessLogger = app.logger.Named("log.access")
170 - }
171 -
172 - // if not explicitly configured by the user, disallow TLS
173 - // client auth bypass (domain fronting) which could
174 - // otherwise be exploited by sending an unprotected SNI
175 - // value during a TLS handshake, then putting a protected
176 - // domain in the Host header after establishing connection;
177 - // this is a safe default, but we allow users to override
178 - // it for example in the case of running a proxy where
179 - // domain fronting is desired and access is not restricted
180 - // based on hostname
181 - if srv.StrictSNIHost == nil && srv.hasTLSClientAuth() {
182 - app.logger.Info("enabling strict SNI-Host matching because TLS client auth is configured",
183 - zap.String("server_name", srvName),
184 - )
185 - trueBool := true
186 - srv.StrictSNIHost = &trueBool
187 - }
188 -
189 - // process each listener address
190 - for i := range srv.Listen {
191 - lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
192 - if err != nil {
193 - return fmt.Errorf("server %s, listener %d: %v",
194 - srvName, i, err)
195 - }
196 - srv.Listen[i] = lnOut
197 - }
198 -
199 - // set up each listener modifier
200 - if srv.ListenerWrappersRaw != nil {
201 - vals, err := ctx.LoadModule(srv, "ListenerWrappersRaw")
202 - if err != nil {
203 - return fmt.Errorf("loading listener wrapper modules: %v", err)
204 - }
205 - var hasTLSPlaceholder bool
206 - for i, val := range vals.([]interface{}) {
207 - if _, ok := val.(*tlsPlaceholderWrapper); ok {
208 - if i == 0 {
209 - // putting the tls placeholder wrapper first is nonsensical because
210 - // that is the default, implicit setting: without it, all wrappers
211 - // will go after the TLS listener anyway
212 - return fmt.Errorf("it is unnecessary to specify the TLS listener wrapper in the first position because that is the default")
213 - }
214 - if hasTLSPlaceholder {
215 - return fmt.Errorf("TLS listener wrapper can only be specified once")
216 - }
217 - hasTLSPlaceholder = true
218 - }
219 - srv.listenerWrappers = append(srv.listenerWrappers, val.(caddy.ListenerWrapper))
220 - }
221 - // if any wrappers were configured but the TLS placeholder wrapper is
222 - // absent, prepend it so all defined wrappers come after the TLS
223 - // handshake; this simplifies logic when starting the server, since we
224 - // can simply assume the TLS placeholder will always be there
225 - if !hasTLSPlaceholder && len(srv.listenerWrappers) > 0 {
226 - srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
227 - }
228 - }
229 -
230 - // pre-compile the primary handler chain, and be sure to wrap it in our
231 - // route handler so that important security checks are done, etc.
232 - primaryRoute := emptyHandler
233 - if srv.Routes != nil {
234 - err := srv.Routes.ProvisionHandlers(ctx)
235 - if err != nil {
236 - return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
237 - }
238 - primaryRoute = srv.Routes.Compile(emptyHandler)
239 - }
240 - srv.primaryHandlerChain = srv.wrapPrimaryRoute(primaryRoute)
241 -
242 - // pre-compile the error handler chain
243 - if srv.Errors != nil {
244 - err := srv.Errors.Routes.Provision(ctx)
245 - if err != nil {
246 - return fmt.Errorf("server %s: setting up server error handling routes: %v", srvName, err)
247 - }
248 - srv.errorHandlerChain = srv.Errors.Routes.Compile(errorEmptyHandler)
249 - }
250 -
251 - // prepare the TLS connection policies
252 - err = srv.TLSConnPolicies.Provision(ctx)
253 - if err != nil {
254 - return fmt.Errorf("server %s: setting up TLS connection policies: %v", srvName, err)
255 - }
256 - }
257 -
258 - return nil
259 -}
260 -
261 -// Validate ensures the app's configuration is valid.
262 -func (app *App) Validate() error {
263 - // each server must use distinct listener addresses
264 - lnAddrs := make(map[string]string)
265 - for srvName, srv := range app.Servers {
266 - for _, addr := range srv.Listen {
267 - listenAddr, err := caddy.ParseNetworkAddress(addr)
268 - if err != nil {
269 - return fmt.Errorf("invalid listener address '%s': %v", addr, err)
270 - }
271 - // check that every address in the port range is unique to this server;
272 - // we do not use <= here because PortRangeSize() adds 1 to EndPort for us
273 - for i := uint(0); i < listenAddr.PortRangeSize(); i++ {
274 - addr := caddy.JoinNetworkAddress(listenAddr.Network, listenAddr.Host, strconv.Itoa(int(listenAddr.StartPort+i)))
275 - if sn, ok := lnAddrs[addr]; ok {
276 - return fmt.Errorf("server %s: listener address repeated: %s (already claimed by server '%s')", srvName, addr, sn)
277 - }
278 - lnAddrs[addr] = srvName
279 - }
280 - }
281 - }
282 -
283 - return nil
284 -}
285 -
286 -// Start runs the app. It finishes automatic HTTPS if enabled,
287 -// including management of certificates.
288 -func (app *App) Start() error {
289 - for srvName, srv := range app.Servers {
290 - s := &http.Server{
291 - ReadTimeout: time.Duration(srv.ReadTimeout),
292 - ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout),
293 - WriteTimeout: time.Duration(srv.WriteTimeout),
294 - IdleTimeout: time.Duration(srv.IdleTimeout),
295 - MaxHeaderBytes: srv.MaxHeaderBytes,
296 - Handler: srv,
297 - }
298 -
299 - for _, lnAddr := range srv.Listen {
300 - listenAddr, err := caddy.ParseNetworkAddress(lnAddr)
301 - if err != nil {
302 - return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
303 - }
304 - for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
305 - // create the listener for this socket
306 - hostport := listenAddr.JoinHostPort(portOffset)
307 - ln, err := caddy.Listen(listenAddr.Network, hostport)
308 - if err != nil {
309 - return fmt.Errorf("%s: listening on %s: %v", listenAddr.Network, hostport, err)
310 - }
311 -
312 - // wrap listener before TLS (up to the TLS placeholder wrapper)
313 - var lnWrapperIdx int
314 - for i, lnWrapper := range srv.listenerWrappers {
315 - if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok {
316 - lnWrapperIdx = i + 1 // mark the next wrapper's spot
317 - break
318 - }
319 - ln = lnWrapper.WrapListener(ln)
320 - }
321 -
322 - // enable TLS if there is a policy and if this is not the HTTP port
323 - useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
324 - if useTLS {
325 - // create TLS listener
326 - tlsCfg := srv.TLSConnPolicies.TLSConfig(app.ctx)
327 - ln = tls.NewListener(ln, tlsCfg)
328 -
329 - /////////
330 - // TODO: HTTP/3 support is experimental for now
331 - if srv.ExperimentalHTTP3 {
332 - app.logger.Info("enabling experimental HTTP/3 listener",
333 - zap.String("addr", hostport),
334 - )
335 - h3ln, err := caddy.ListenPacket("udp", hostport)
336 - if err != nil {
337 - return fmt.Errorf("getting HTTP/3 UDP listener: %v", err)
338 - }
339 - h3srv := &http3.Server{
340 - Server: &http.Server{
341 - Addr: hostport,
342 - Handler: srv,
343 - TLSConfig: tlsCfg,
344 - },
345 - }
346 - go h3srv.Serve(h3ln)
347 - app.h3servers = append(app.h3servers, h3srv)
348 - app.h3listeners = append(app.h3listeners, h3ln)
349 - srv.h3server = h3srv
350 - }
351 - /////////
352 - }
353 -
354 - // finish wrapping listener where we left off before TLS
355 - for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ {
356 - ln = srv.listenerWrappers[i].WrapListener(ln)
357 - }
358 -
359 - app.logger.Debug("starting server loop",
360 - zap.String("address", lnAddr),
361 - zap.Bool("http3", srv.ExperimentalHTTP3),
362 - zap.Bool("tls", useTLS),
363 - )
364 -
365 - go s.Serve(ln)
366 - app.servers = append(app.servers, s)
367 - }
368 - }
369 - }
370 -
371 - // finish automatic HTTPS by finally beginning
372 - // certificate management
373 - err := app.automaticHTTPSPhase2()
374 - if err != nil {
375 - return fmt.Errorf("finalizing automatic HTTPS: %v", err)
376 - }
377 -
378 - return nil
379 -}
380 -
381 -// Stop gracefully shuts down the HTTP server.
382 -func (app *App) Stop() error {
383 - ctx := context.Background()
384 - if app.GracePeriod > 0 {
385 - var cancel context.CancelFunc
386 - ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod))
387 - defer cancel()
388 - }
389 - for _, s := range app.servers {
390 - err := s.Shutdown(ctx)
391 - if err != nil {
392 - return err
393 - }
394 - }
395 -
396 - // close the http3 servers; it's unclear whether the bug reported in
397 - // https://github.com/caddyserver/caddy/pull/2727#issuecomment-526856566
398 - // was ever truly fixed, since it seemed racey/nondeterministic; but
399 - // recent tests in 2020 were unable to replicate the issue again after
400 - // repeated attempts (the bug manifested after a config reload; i.e.
401 - // reusing a http3 server or listener was problematic), but it seems
402 - // to be working fine now
403 - for _, s := range app.h3servers {
404 - // TODO: CloseGracefully, once implemented upstream
405 - // (see https://github.com/lucas-clemente/quic-go/issues/2103)
406 - err := s.Close()
407 - if err != nil {
408 - return err
409 - }
410 - }
411 -
412 - // closing an http3.Server does not close their underlying listeners
413 - // since apparently the listener can be used both by servers and
414 - // clients at the same time; so we need to manually call Close()
415 - // on the underlying h3 listeners (see lucas-clemente/quic-go#2103)
416 - for _, pc := range app.h3listeners {
417 - err := pc.Close()
418 - if err != nil {
419 - return err
420 - }
421 - }
422 - return nil
423 -}
424 -
425 -func (app *App) httpPort() int {
426 - if app.HTTPPort == 0 {
427 - return DefaultHTTPPort
428 - }
429 - return app.HTTPPort
430 -}
431 -
432 -func (app *App) httpsPort() int {
433 - if app.HTTPSPort == 0 {
434 - return DefaultHTTPSPort
435 - }
436 - return app.HTTPSPort
37 }
38
39 // RequestMatcher is a type that can match to a request.
@@ -618,11 +218,5 @@ const (
218 DefaultHTTPSPort = 443
219 )
220
621 -// Interface guards
622 -var (
623 - _ caddy.App = (*App)(nil)
624 - _ caddy.Provisioner = (*App)(nil)
625 - _ caddy.Validator = (*App)(nil)
626 -
627 - _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
628 -)
221 +// Interface guard
222 +var _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)