diff --git a/caddyhttp/httpserver/context.go b/caddyhttp/httpserver/context.go index 196337d9..050e1830 100644 --- a/caddyhttp/httpserver/context.go +++ b/caddyhttp/httpserver/context.go @@ -134,7 +134,7 @@ func (c Context) Port() (string, error) { if err != nil { if !strings.Contains(c.Req.Host, ":") { // common with sites served on the default port 80 - return "80", nil + return HTTPPort, nil } return "", err } diff --git a/caddyhttp/httpserver/https.go b/caddyhttp/httpserver/https.go index 60b0c242..fcbd2d90 100644 --- a/caddyhttp/httpserver/https.go +++ b/caddyhttp/httpserver/https.go @@ -93,7 +93,7 @@ func enableAutoHTTPS(configs []*SiteConfig, loadCertificates bool) error { cfg.TLS.Enabled && (!cfg.TLS.Manual || cfg.TLS.OnDemand) && cfg.Addr.Host != "localhost" { - cfg.Addr.Port = "443" + cfg.Addr.Port = HTTPSPort } } return nil @@ -108,8 +108,8 @@ func enableAutoHTTPS(configs []*SiteConfig, loadCertificates bool) error { func makePlaintextRedirects(allConfigs []*SiteConfig) []*SiteConfig { for i, cfg := range allConfigs { if cfg.TLS.Managed && - !hostHasOtherPort(allConfigs, i, "80") && - (cfg.Addr.Port == "443" || !hostHasOtherPort(allConfigs, i, "443")) { + !hostHasOtherPort(allConfigs, i, HTTPPort) && + (cfg.Addr.Port == HTTPSPort || !hostHasOtherPort(allConfigs, i, HTTPSPort)) { allConfigs = append(allConfigs, redirPlaintextHost(cfg)) } } @@ -135,18 +135,19 @@ func hostHasOtherPort(allConfigs []*SiteConfig, thisConfigIdx int, otherPort str // redirPlaintextHost returns a new plaintext HTTP configuration for // a virtualHost that simply redirects to cfg, which is assumed to // be the HTTPS configuration. The returned configuration is set -// to listen on port 80. The TLS field of cfg must not be nil. +// to listen on HTTPPort. The TLS field of cfg must not be nil. func redirPlaintextHost(cfg *SiteConfig) *SiteConfig { redirPort := cfg.Addr.Port - if redirPort == "443" { - // default port is redundant - redirPort = "" + if redirPort == DefaultHTTPSPort { + redirPort = "" // default port is redundant } redirMiddleware := func(next Handler) Handler { return HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { - toURL := "https://" + r.Host - if redirPort != "" { - toURL += ":" + redirPort + toURL := "https://" + if redirPort == "" { + toURL += cfg.Addr.Host // don't use r.Host as it may have a port included + } else { + toURL += net.JoinHostPort(cfg.Addr.Host, redirPort) } toURL += r.URL.RequestURI() w.Header().Set("Connection", "close") @@ -155,12 +156,13 @@ func redirPlaintextHost(cfg *SiteConfig) *SiteConfig { }) } host := cfg.Addr.Host - port := "80" + port := HTTPPort addr := net.JoinHostPort(host, port) return &SiteConfig{ Addr: Address{Original: addr, Host: host, Port: port}, ListenHost: cfg.ListenHost, middleware: []Middleware{redirMiddleware}, - TLS: &caddytls.Config{AltHTTPPort: cfg.TLS.AltHTTPPort}, + TLS: &caddytls.Config{AltHTTPPort: cfg.TLS.AltHTTPPort, AltTLSSNIPort: cfg.TLS.AltTLSSNIPort}, + Timeouts: cfg.Timeouts, } } diff --git a/caddyhttp/httpserver/https_test.go b/caddyhttp/httpserver/https_test.go index 04a4db1e..04dcbaea 100644 --- a/caddyhttp/httpserver/https_test.go +++ b/caddyhttp/httpserver/https_test.go @@ -11,7 +11,7 @@ import ( func TestRedirPlaintextHost(t *testing.T) { cfg := redirPlaintextHost(&SiteConfig{ Addr: Address{ - Host: "example.com", + Host: "foohost", Port: "1234", }, ListenHost: "93.184.216.34", @@ -19,7 +19,7 @@ func TestRedirPlaintextHost(t *testing.T) { }) // Check host and port - if actual, expected := cfg.Addr.Host, "example.com"; actual != expected { + if actual, expected := cfg.Addr.Host, "foohost"; actual != expected { t.Errorf("Expected redir config to have host %s but got %s", expected, actual) } if actual, expected := cfg.ListenHost, "93.184.216.34"; actual != expected { @@ -38,7 +38,7 @@ func TestRedirPlaintextHost(t *testing.T) { // Check redirect for correctness rec := httptest.NewRecorder() - req, err := http.NewRequest("GET", "http://foo/bar?q=1", nil) + req, err := http.NewRequest("GET", "http://foohost/bar?q=1", nil) if err != nil { t.Fatal(err) } @@ -52,17 +52,17 @@ func TestRedirPlaintextHost(t *testing.T) { if rec.Code != http.StatusMovedPermanently { t.Errorf("Expected status %d but got %d", http.StatusMovedPermanently, rec.Code) } - if got, want := rec.Header().Get("Location"), "https://foo:1234/bar?q=1"; got != want { + if got, want := rec.Header().Get("Location"), "https://foohost:1234/bar?q=1"; got != want { t.Errorf("Expected Location: '%s' but got '%s'", want, got) } // browsers can infer a default port from scheme, so make sure the port // doesn't get added in explicitly for default ports like 443 for https. - cfg = redirPlaintextHost(&SiteConfig{Addr: Address{Host: "example.com", Port: "443"}, TLS: new(caddytls.Config)}) + cfg = redirPlaintextHost(&SiteConfig{Addr: Address{Host: "foohost", Port: "443"}, TLS: new(caddytls.Config)}) handler = cfg.middleware[0](nil) rec = httptest.NewRecorder() - req, err = http.NewRequest("GET", "http://foo/bar?q=1", nil) + req, err = http.NewRequest("GET", "http://foohost/bar?q=1", nil) if err != nil { t.Fatal(err) } @@ -76,7 +76,7 @@ func TestRedirPlaintextHost(t *testing.T) { if rec.Code != http.StatusMovedPermanently { t.Errorf("Expected status %d but got %d", http.StatusMovedPermanently, rec.Code) } - if got, want := rec.Header().Get("Location"), "https://foo/bar?q=1"; got != want { + if got, want := rec.Header().Get("Location"), "https://foohost/bar?q=1"; got != want { t.Errorf("Expected Location: '%s' but got '%s'", want, got) } } diff --git a/caddyhttp/httpserver/plugin.go b/caddyhttp/httpserver/plugin.go index 7736fcf3..4c7caa3e 100644 --- a/caddyhttp/httpserver/plugin.go +++ b/caddyhttp/httpserver/plugin.go @@ -19,6 +19,8 @@ import ( const serverType = "http" func init() { + flag.StringVar(&HTTPPort, "http-port", HTTPPort, "Default port to use for HTTP") + flag.StringVar(&HTTPSPort, "https-port", HTTPSPort, "Default port to use for HTTPS") flag.StringVar(&Host, "host", DefaultHost, "Default host") flag.StringVar(&Port, "port", DefaultPort, "Default port") flag.StringVar(&Root, "root", DefaultRoot, "Root path of default site") @@ -119,11 +121,25 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd addr.Port = Port } + // If default HTTP or HTTPS ports have been customized, + // make sure the ACME challenge ports match + var altHTTPPort, altTLSSNIPort string + if HTTPPort != DefaultHTTPPort { + altHTTPPort = HTTPPort + } + if HTTPSPort != DefaultHTTPSPort { + altTLSSNIPort = HTTPSPort + } + // Save the config to our master list, and key it for lookups cfg := &SiteConfig{ - Addr: addr, - Root: Root, - TLS: &caddytls.Config{Hostname: addr.Host}, + Addr: addr, + Root: Root, + TLS: &caddytls.Config{ + Hostname: addr.Host, + AltHTTPPort: altHTTPPort, + AltTLSSNIPort: altTLSSNIPort, + }, originCaddyfile: sourceFile, } h.saveConfig(key, cfg) @@ -154,7 +170,7 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) { if !cfg.TLS.Enabled { continue } - if cfg.Addr.Port == "80" || cfg.Addr.Scheme == "http" { + if cfg.Addr.Port == HTTPPort || cfg.Addr.Scheme == "http" { cfg.TLS.Enabled = false log.Printf("[WARNING] TLS disabled for %s", cfg.Addr) } else if cfg.Addr.Scheme == "" { @@ -169,7 +185,7 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) { // this is vital, otherwise the function call below that // sets the listener address will use the default port // instead of 443 because it doesn't know about TLS. - cfg.Addr.Port = "443" + cfg.Addr.Port = HTTPSPort } } @@ -270,7 +286,7 @@ func (a Address) String() string { } scheme := a.Scheme if scheme == "" { - if a.Port == "443" { + if a.Port == HTTPSPort { scheme = "https" } else { scheme = "http" @@ -282,8 +298,8 @@ func (a Address) String() string { } s += a.Host if a.Port != "" && - ((scheme == "https" && a.Port != "443") || - (scheme == "http" && a.Port != "80")) { + ((scheme == "https" && a.Port != DefaultHTTPSPort) || + (scheme == "http" && a.Port != DefaultHTTPPort)) { s += ":" + a.Port } if a.Path != "" { @@ -327,9 +343,9 @@ func standardizeAddress(str string) (Address, error) { // see if we can set port based off scheme if port == "" { if u.Scheme == "http" { - port = "80" + port = HTTPPort } else if u.Scheme == "https" { - port = "443" + port = HTTPSPort } } @@ -339,17 +355,17 @@ func standardizeAddress(str string) (Address, error) { } // error if scheme and port combination violate convention - if (u.Scheme == "http" && port == "443") || (u.Scheme == "https" && port == "80") { + if (u.Scheme == "http" && port == HTTPSPort) || (u.Scheme == "https" && port == HTTPPort) { return Address{}, fmt.Errorf("[%s] scheme and port violate convention", input) } // standardize http and https ports to their respective port numbers if port == "http" { u.Scheme = "http" - port = "80" + port = HTTPPort } else if port == "https" { u.Scheme = "https" - port = "443" + port = HTTPSPort } return Address{Original: input, Scheme: u.Scheme, Host: host, Port: port, Path: u.Path}, err @@ -477,6 +493,10 @@ const ( DefaultPort = "2015" // DefaultRoot is the default root folder. DefaultRoot = "." + // DefaultHTTPPort is the default port for HTTP. + DefaultHTTPPort = "80" + // DefaultHTTPSPort is the default port for HTTPS. + DefaultHTTPSPort = "443" ) // These "soft defaults" are configurable by @@ -499,4 +519,10 @@ var ( // QUIC indicates whether QUIC is enabled or not. QUIC bool + + // HTTPPort is the port to use for HTTP. + HTTPPort = DefaultHTTPPort + + // HTTPSPort is the port to use for HTTPS. + HTTPSPort = DefaultHTTPSPort ) diff --git a/caddytls/client.go b/caddytls/client.go index 852fbe42..d1c3295e 100644 --- a/caddytls/client.go +++ b/caddytls/client.go @@ -112,33 +112,39 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error) // Use HTTP and TLS-SNI challenges by default // See if HTTP challenge needs to be proxied - useHTTPPort := "" // empty port value will use challenge default - if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, HTTPChallengePort)) { + useHTTPPort := HTTPChallengePort + if config.AltHTTPPort != "" { useHTTPPort = config.AltHTTPPort - if useHTTPPort == "" { - useHTTPPort = DefaultHTTPAlternatePort - } + } + if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, useHTTPPort)) { + useHTTPPort = DefaultHTTPAlternatePort + } + + // See which port TLS-SNI challenges will be accomplished on + useTLSSNIPort := TLSSNIChallengePort + if config.AltTLSSNIPort != "" { + useTLSSNIPort = config.AltTLSSNIPort } // Always respect user's bind preferences by using config.ListenHost. - // NOTE(Sep'16): At time of writing, SetHTTPAddress() and SetTLSaddress() + // NOTE(Sep'16): At time of writing, SetHTTPAddress() and SetTLSAddress() // must be called before SetChallengeProvider(), since they reset the // challenge provider back to the default one! err := c.acmeClient.SetHTTPAddress(net.JoinHostPort(config.ListenHost, useHTTPPort)) if err != nil { return nil, err } - err = c.acmeClient.SetTLSAddress(net.JoinHostPort(config.ListenHost, "")) + err = c.acmeClient.SetTLSAddress(net.JoinHostPort(config.ListenHost, useTLSSNIPort)) if err != nil { return nil, err } // See if TLS challenge needs to be handled by our own facilities - if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, TLSSNIChallengePort)) { + if caddy.HasListenerWithAddress(net.JoinHostPort(config.ListenHost, useTLSSNIPort)) { c.acmeClient.SetChallengeProvider(acme.TLSSNI01, tlsSniSolver{}) } } else { - // Otherwise, DNS challenge it is + // Otherwise, use DNS challenge exclusively // Load provider constructor function provFn, ok := dnsProviders[config.DNSProvider] diff --git a/caddytls/config.go b/caddytls/config.go index d6d77999..a1be44ae 100644 --- a/caddytls/config.go +++ b/caddytls/config.go @@ -84,6 +84,12 @@ type Config struct { // coming in on port 80 to this alternate port AltHTTPPort string + // The alternate port (ONLY port, not host) + // to use for the ACME TLS-SNI challenge. + // The system must forward the standard port + // for the TLS-SNI challenge to this port. + AltTLSSNIPort string + // The string identifier of the DNS provider // to use when solving the ACME DNS challenge DNSProvider string @@ -479,11 +485,11 @@ var defaultCurves = []tls.CurveID{ const ( // HTTPChallengePort is the officially designated port for - // the HTTP challenge. + // the HTTP challenge according to the ACME spec. HTTPChallengePort = "80" // TLSSNIChallengePort is the officially designated port for - // the TLS-SNI challenge. + // the TLS-SNI challenge according to the ACME spec. TLSSNIChallengePort = "443" // DefaultHTTPAlternatePort is the port on which the ACME