summaryrefslogtreecommitdiff
path: root/modules/caddyhttp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/caddyhttp')
-rw-r--r--modules/caddyhttp/caddyhttp.go30
-rw-r--r--modules/caddyhttp/fileserver/caddyfile.go2
-rw-r--r--modules/caddyhttp/fileserver/staticfiles.go44
-rw-r--r--modules/caddyhttp/replacer.go6
-rw-r--r--modules/caddyhttp/server.go28
5 files changed, 93 insertions, 17 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index 174e316..9dfdf36 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -75,6 +75,15 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.AutoHTTPS = new(AutoHTTPSConfig)
}
+ // disallow TLS client auth bypass which could
+ // otherwise be exploited by sending an unprotected
+ // SNI value during TLS handshake, then a protected
+ // Host header during HTTP request later on that
+ // connection
+ if srv.hasTLSClientAuth() {
+ srv.StrictSNIHost = true
+ }
+
// TODO: Test this function to ensure these replacements are performed
for i := range srv.Listen {
srv.Listen[i] = repl.ReplaceAll(srv.Listen[i], "")
@@ -159,8 +168,7 @@ func (app *App) Start() error {
return fmt.Errorf("%s: listening on %s: %v", network, addr, err)
}
- // enable HTTP/2 (and support for solving the
- // TLS-ALPN ACME challenge) by default
+ // enable HTTP/2 by default
for _, pol := range srv.TLSConnPolicies {
if len(pol.ALPN) == 0 {
pol.ALPN = append(pol.ALPN, defaultALPN...)
@@ -226,6 +234,8 @@ func (app *App) automaticHTTPS() error {
// skip if all listeners use the HTTP port
if !srv.listenersUseAnyPortOtherThan(app.HTTPPort) {
+ log.Printf("[INFO] Server %v is only listening on the HTTP port %d, so no automatic HTTPS will be applied to this server",
+ srv.Listen, app.HTTPPort)
continue
}
@@ -294,11 +304,11 @@ func (app *App) automaticHTTPS() error {
return fmt.Errorf("%s: managing certificate for %s: %s", srvName, domains, err)
}
- // tell the server to use TLS by specifying a TLS
- // connection policy (which supports HTTP/2 and the
- // TLS-ALPN ACME challenge as well)
- srv.TLSConnPolicies = caddytls.ConnectionPolicies{
- {ALPN: defaultALPN},
+ // tell the server to use TLS if it is not already doing so
+ if srv.TLSConnPolicies == nil {
+ srv.TLSConnPolicies = caddytls.ConnectionPolicies{
+ &caddytls.ConnectionPolicy{ALPN: defaultALPN},
+ }
}
if srv.AutoHTTPS.DisableRedir {
@@ -307,6 +317,12 @@ func (app *App) automaticHTTPS() error {
log.Printf("[INFO] Enabling automatic HTTP->HTTPS redirects for %v", domains)
+ // notify user if their config might override the HTTP->HTTPS redirects
+ if srv.listenersIncludePort(app.HTTPPort) {
+ log.Printf("[WARNING] Server %v is listening on HTTP port %d, so automatic HTTP->HTTPS redirects may be overridden by your own configuration",
+ srv.Listen, app.HTTPPort)
+ }
+
// create HTTP->HTTPS redirects
for _, addr := range srv.Listen {
netw, host, port, err := caddy.SplitNetworkAddress(addr)
diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go
index 4622af2..b7cb311 100644
--- a/modules/caddyhttp/fileserver/caddyfile.go
+++ b/modules/caddyhttp/fileserver/caddyfile.go
@@ -94,7 +94,7 @@ func parseTryFiles(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
}
handler := rewrite.Rewrite{
- URI: "{http.matchers.file.relative}{http.request.uri.query}",
+ URI: "{http.matchers.file.relative}{http.request.uri.query_string}",
}
matcherSet := map[string]json.RawMessage{
diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go
index cfb79f8..3e4cccc 100644
--- a/modules/caddyhttp/fileserver/staticfiles.go
+++ b/modules/caddyhttp/fileserver/staticfiles.go
@@ -41,10 +41,11 @@ func init() {
// FileServer implements a static file server responder for Caddy.
type FileServer struct {
- Root string `json:"root,omitempty"` // default is current directory
- Hide []string `json:"hide,omitempty"`
- IndexNames []string `json:"index_names,omitempty"`
- Browse *Browse `json:"browse,omitempty"`
+ Root string `json:"root,omitempty"` // default is current directory
+ Hide []string `json:"hide,omitempty"`
+ IndexNames []string `json:"index_names,omitempty"`
+ Browse *Browse `json:"browse,omitempty"`
+ CanonicalURIs *bool `json:"canonical_uris,omitempty"`
}
// CaddyModule returns the Caddy module information.
@@ -109,6 +110,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd
// if the request mapped to a directory, see if
// there is an index file we can serve
+ var implicitIndexFile bool
if info.IsDir() && len(fsrv.IndexNames) > 0 {
for _, indexPage := range fsrv.IndexNames {
indexPath := sanitizedPathJoin(filename, indexPage)
@@ -122,12 +124,17 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd
continue
}
- // we found an index file that might work,
- // so rewrite the request path
- r.URL.Path = path.Join(r.URL.Path, indexPage)
+ // don't rewrite the request path to append
+ // the index file, because we might need to
+ // do a canonical-URL redirect below based
+ // on the URL as-is
+ // we've chosen to use this index file,
+ // so replace the last file info and path
+ // with that of the index file
info = indexInfo
filename = indexPath
+ implicitIndexFile = true
break
}
}
@@ -149,10 +156,22 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, _ cadd
return caddyhttp.Error(http.StatusNotFound, nil)
}
+ // if URL canonicalization is enabled, we need to enforce trailing
+ // slash convention: if a directory, trailing slash; if a file, no
+ // trailing slash - not enforcing this can break relative hrefs
+ // in HTML (see https://github.com/caddyserver/caddy/issues/2741)
+ if fsrv.CanonicalURIs == nil || *fsrv.CanonicalURIs {
+ if implicitIndexFile && !strings.HasSuffix(r.URL.Path, "/") {
+ return redirect(w, r, r.URL.Path+"/")
+ } else if !implicitIndexFile && strings.HasSuffix(r.URL.Path, "/") {
+ return redirect(w, r, r.URL.Path[:len(r.URL.Path)-1])
+ }
+ }
+
// open the file
file, err := fsrv.openFile(filename, w)
if err != nil {
- return err
+ return err // error is already structured
}
defer file.Close()
@@ -309,6 +328,15 @@ func calculateEtag(d os.FileInfo) string {
return `"` + t + s + `"`
}
+func redirect(w http.ResponseWriter, r *http.Request, to string) error {
+ for strings.HasPrefix(to, "//") {
+ // prevent path-based open redirects
+ to = strings.TrimPrefix(to, "/")
+ }
+ http.Redirect(w, r, to, http.StatusPermanentRedirect)
+ return nil
+}
+
var defaultIndexNames = []string{"index.html", "index.txt"}
var bufPool = sync.Pool{
diff --git a/modules/caddyhttp/replacer.go b/modules/caddyhttp/replacer.go
index f7f69a4..e003259 100644
--- a/modules/caddyhttp/replacer.go
+++ b/modules/caddyhttp/replacer.go
@@ -89,6 +89,12 @@ func addHTTPVarsToReplacer(repl caddy.Replacer, req *http.Request, w http.Respon
return dir, true
case "http.request.uri.query":
return req.URL.RawQuery, true
+ case "http.request.uri.query_string":
+ qs := req.URL.Query().Encode()
+ if qs != "" {
+ qs = "?" + qs
+ }
+ return qs, true
}
// hostname labels
diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go
index 04935e6..42f7a5a 100644
--- a/modules/caddyhttp/server.go
+++ b/modules/caddyhttp/server.go
@@ -41,7 +41,7 @@ type Server struct {
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"`
MaxRehandles *int `json:"max_rehandles,omitempty"`
- StrictSNIHost bool `json:"strict_sni_host,omitempty"` // TODO: see if we can turn this on by default when clientauth is configured
+ StrictSNIHost bool `json:"strict_sni_host,omitempty"`
tlsApp *caddytls.TLS
}
@@ -183,6 +183,32 @@ func (s *Server) listenersUseAnyPortOtherThan(otherPort int) bool {
return false
}
+// listenersIncludePort returns true if there are any
+// listeners in s that use otherPort.
+func (s *Server) listenersIncludePort(otherPort int) bool {
+ for _, lnAddr := range s.Listen {
+ _, addrs, err := caddy.ParseListenAddr(lnAddr)
+ if err == nil {
+ for _, a := range addrs {
+ _, port, err := net.SplitHostPort(a)
+ if err == nil && port == strconv.Itoa(otherPort) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func (s *Server) hasTLSClientAuth() bool {
+ for _, cp := range s.TLSConnPolicies {
+ if cp.ClientAuthentication != nil && cp.ClientAuthentication.Active() {
+ return true
+ }
+ }
+ return false
+}
+
// AutoHTTPSConfig is used to disable automatic HTTPS
// or certain aspects of it for a specific server.
type AutoHTTPSConfig struct {