diff --git a/caddyhttp/bind/bind.go b/caddyhttp/bind/bind.go index 7ab0a645e..0b7a4496e 100644 --- a/caddyhttp/bind/bind.go +++ b/caddyhttp/bind/bind.go @@ -29,10 +29,17 @@ func init() { func setupBind(c *caddy.Controller) error { config := httpserver.GetConfig(c) for c.Next() { - if !c.Args(&config.ListenHost) { - return c.ArgErr() + args := c.RemainingArgs() + + if len(args) == 0 { + return c.Errf("Expected at least one address") } - config.TLS.ListenHost = config.ListenHost // necessary for ACME challenges, see issue #309 + + for _, addr := range args { + config.ListenHosts = append(config.ListenHosts, addr) + } + + config.TLS.ListenHost = config.ListenHosts[0] // necessary for ACME challenges, see issue #309 } return nil } diff --git a/caddyhttp/bind/bind_test.go b/caddyhttp/bind/bind_test.go index 70221a060..e4172b66b 100644 --- a/caddyhttp/bind/bind_test.go +++ b/caddyhttp/bind/bind_test.go @@ -15,6 +15,7 @@ package bind import ( + "reflect" "testing" "github.com/mholt/caddy" @@ -22,17 +23,34 @@ import ( ) func TestSetupBind(t *testing.T) { - c := caddy.NewTestController("http", `bind 1.2.3.4`) - err := setupBind(c) - if err != nil { - t.Fatalf("Expected no errors, but got: %v", err) - } + for _, testcase := range []struct { + Bind string + Hosts []string + TLSHost string + }{ + { + Bind: "bind 1.2.3.4", + Hosts: []string{"1.2.3.4"}, + TLSHost: "1.2.3.4", + }, + { + Bind: "bind 1.2.3.4 5.6.7.8", + Hosts: []string{"1.2.3.4", "5.6.7.8"}, + TLSHost: "1.2.3.4", + }, + } { + c := caddy.NewTestController("http", testcase.Bind) + err := setupBind(c) + if err != nil { + t.Fatalf("Expected no errors, but got: %v", err) + } - cfg := httpserver.GetConfig(c) - if got, want := cfg.ListenHost, "1.2.3.4"; got != want { - t.Errorf("Expected the config's ListenHost to be %s, was %s", want, got) - } - if got, want := cfg.TLS.ListenHost, "1.2.3.4"; got != want { - t.Errorf("Expected the TLS config's ListenHost to be %s, was %s", want, got) + cfg := httpserver.GetConfig(c) + if got, want := cfg.ListenHosts, testcase.Hosts; !reflect.DeepEqual(got, want) { + t.Errorf("Expected the config's ListenHost to be %s, was %s", want, got) + } + if got, want := cfg.TLS.ListenHost, testcase.TLSHost; got != want { + t.Errorf("Expected the TLS config's ListenHost to be %s, was %s", want, got) + } } } diff --git a/caddyhttp/httpserver/https.go b/caddyhttp/httpserver/https.go index a037a86d0..7b0052be3 100644 --- a/caddyhttp/httpserver/https.go +++ b/caddyhttp/httpserver/https.go @@ -204,10 +204,10 @@ func redirPlaintextHost(cfg *SiteConfig) *SiteConfig { 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, AltTLSSNIPort: cfg.TLS.AltTLSSNIPort}, - Timeouts: cfg.Timeouts, + Addr: Address{Original: addr, Host: host, Port: port}, + ListenHosts: cfg.ListenHosts, + middleware: []Middleware{redirMiddleware}, + 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 043249445..550d701f6 100644 --- a/caddyhttp/httpserver/https_test.go +++ b/caddyhttp/httpserver/https_test.go @@ -19,6 +19,7 @@ import ( "net" "net/http" "net/http/httptest" + "reflect" "testing" "github.com/mholt/caddy/caddytls" @@ -28,7 +29,7 @@ func TestRedirPlaintextHost(t *testing.T) { for i, testcase := range []struct { Host string // used for the site config Port string - ListenHost string + ListenHosts []string RequestHost string // if different from Host }{ { @@ -43,13 +44,17 @@ func TestRedirPlaintextHost(t *testing.T) { Port: "1234", }, { - Host: "foohost", - ListenHost: "93.184.216.34", + Host: "foohost", + ListenHosts: []string{"93.184.216.34"}, }, { - Host: "foohost", - Port: "1234", - ListenHost: "93.184.216.34", + Host: "foohost", + Port: "1234", + ListenHosts: []string{"93.184.216.34"}, + }, + { + Host: "foohost", + ListenHosts: []string{"127.0.0.1", "127.0.0.2"}, }, { Host: "foohost", @@ -70,15 +75,15 @@ func TestRedirPlaintextHost(t *testing.T) { Host: testcase.Host, Port: testcase.Port, }, - ListenHost: testcase.ListenHost, - TLS: new(caddytls.Config), + ListenHosts: testcase.ListenHosts, + TLS: new(caddytls.Config), }) // Check host and port if actual, expected := cfg.Addr.Host, testcase.Host; actual != expected { t.Errorf("Test %d: Expected redir config to have host %s but got %s", i, expected, actual) } - if actual, expected := cfg.ListenHost, testcase.ListenHost; actual != expected { + if actual, expected := cfg.ListenHosts, testcase.ListenHosts; !reflect.DeepEqual(actual, expected) { t.Errorf("Test %d: Expected redir config to have bindhost %s but got %s", i, expected, actual) } if actual, expected := cfg.Addr.Port, HTTPPort; actual != expected { diff --git a/caddyhttp/httpserver/plugin.go b/caddyhttp/httpserver/plugin.go index 4abcbbb6d..b1d6b8acf 100644 --- a/caddyhttp/httpserver/plugin.go +++ b/caddyhttp/httpserver/plugin.go @@ -239,9 +239,18 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) { // see if all the addresses (both sites and // listeners) are loopback to help us determine // if this is a "production" instance or not - if !atLeastOneSiteLooksLikeProduction { + for _, listenHost := range cfg.ListenHosts { + if !atLeastOneSiteLooksLikeProduction { + if !caddy.IsLoopback(cfg.Addr.Host) && + !caddy.IsLoopback(listenHost) && + (caddytls.QualifiesForManagedTLS(cfg) || + caddytls.HostQualifies(cfg.Addr.Host)) { + atLeastOneSiteLooksLikeProduction = true + } + } + } + if !atLeastOneSiteLooksLikeProduction && len(cfg.ListenHosts) == 0 { if !caddy.IsLoopback(cfg.Addr.Host) && - !caddy.IsLoopback(cfg.ListenHost) && (caddytls.QualifiesForManagedTLS(cfg) || caddytls.HostQualifies(cfg.Addr.Host)) { atLeastOneSiteLooksLikeProduction = true @@ -368,19 +377,30 @@ func groupSiteConfigsByListenAddr(configs []*SiteConfig) (map[string][]*SiteConf for _, conf := range configs { // We would add a special case here so that localhost addresses - // bind to 127.0.0.1 if conf.ListenHost is not already set, which + // bind to 127.0.0.1 if conf.ListenHosts is not already set, which // would prevent outsiders from even connecting; but that was problematic: // https://caddy.community/t/wildcard-virtual-domains-with-wildcard-roots/221/5?u=matt if conf.Addr.Port == "" { conf.Addr.Port = Port } - addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.ListenHost, conf.Addr.Port)) - if err != nil { - return nil, err + + if len(conf.ListenHosts) == 0 { + addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort("", conf.Addr.Port)) + if err != nil { + return nil, err + } + addrstr := addr.String() + groups[addrstr] = append(groups[addrstr], conf) + } + for _, host := range conf.ListenHosts { + addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(host, conf.Addr.Port)) + if err != nil { + return nil, err + } + addrstr := addr.String() + groups[addrstr] = append(groups[addrstr], conf) } - addrstr := addr.String() - groups[addrstr] = append(groups[addrstr], conf) } return groups, nil diff --git a/caddyhttp/httpserver/plugin_test.go b/caddyhttp/httpserver/plugin_test.go index bf922dfc4..b3365253a 100644 --- a/caddyhttp/httpserver/plugin_test.go +++ b/caddyhttp/httpserver/plugin_test.go @@ -15,6 +15,7 @@ package httpserver import ( + "reflect" "strings" "testing" @@ -347,3 +348,28 @@ func TestHideCaddyfile(t *testing.T) { } t.Fatal("Caddyfile missing from HiddenFiles") } + +func TestGroupSiteConfigsByListenAddr(t *testing.T) { + cfg := []*SiteConfig{ + { + ListenHosts: []string{"127.0.0.1", "::1"}, + }, + { + Addr: Address{ + Port: "80", + }, + }, + } + groups, err := groupSiteConfigsByListenAddr(cfg) + if err != nil { + t.Fatal("Failed to group SiteConfigs by listen address") + } + actual := []string{} + for k := range groups { + actual = append(actual, k) + } + expected := []string{"127.0.0.1:" + Port, "[::1]:" + Port, ":80"} + if !reflect.DeepEqual(actual, expected) { + t.Errorf("Expected listen on %#v, but got %#v", expected, actual) + } +} diff --git a/caddyhttp/httpserver/server.go b/caddyhttp/httpserver/server.go index 800f921de..fff100653 100644 --- a/caddyhttp/httpserver/server.go +++ b/caddyhttp/httpserver/server.go @@ -419,7 +419,7 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) (int, error) // we still check for ACME challenge if the vhost exists, // because we must apply its HTTP challenge config settings - if caddytls.HTTPChallengeHandler(w, r, vhost.ListenHost) { + if caddytls.HTTPChallengeHandler(w, r, vhost.ListenHosts[0]) { return 0, nil } diff --git a/caddyhttp/httpserver/siteconfig.go b/caddyhttp/httpserver/siteconfig.go index 3b29f911a..b9903f336 100644 --- a/caddyhttp/httpserver/siteconfig.go +++ b/caddyhttp/httpserver/siteconfig.go @@ -31,7 +31,7 @@ type SiteConfig struct { // The hostname to bind listener to; // defaults to Addr.Host - ListenHost string + ListenHosts []string // TLS configuration TLS *caddytls.Config