Default host now empty string; default port now depends on host

Hosts which are eligible for automatic HTTPS have default port "https" but other hosts (wildcards, loopback, etc.) have the default port 2015. The default host of empty string should be more IPv6-compatible.
This commit is contained in:
Matthew Holt 2015-11-18 10:05:13 -07:00
parent 580b50ea20
commit e17d43b58a
5 changed files with 72 additions and 29 deletions

View file

@ -83,7 +83,7 @@ var (
const (
// DefaultHost is the default host.
DefaultHost = "0.0.0.0"
DefaultHost = ""
// DefaultPort is the default port.
DefaultPort = "2015"
// DefaultRoot is the default root folder.

View file

@ -331,22 +331,18 @@ func validDirective(d string) bool {
return false
}
// NewDefault makes a default configuration, which
// is empty except for root, host, and port,
// which are essentials for serving the cwd.
func NewDefault() server.Config {
return server.Config{
Root: Root,
Host: Host,
Port: Port,
}
}
// DefaultInput returns the default Caddyfile input
// to use when it is otherwise empty or missing.
// It uses the default host and port (depends on
// host, e.g. localhost is 2015, otherwise https) and
// root.
func DefaultInput() CaddyfileInput {
port := Port
if letsencrypt.HostQualifies(Host) {
port = "https"
}
return CaddyfileInput{
Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, Port, Root)),
Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, port, Root)),
}
}

View file

@ -8,17 +8,26 @@ import (
"github.com/mholt/caddy/server"
)
func TestNewDefault(t *testing.T) {
config := NewDefault()
func TestDefaultInput(t *testing.T) {
if actual, expected := string(DefaultInput().Body()), ":2015\nroot ."; actual != expected {
t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual)
}
if actual, expected := config.Root, DefaultRoot; actual != expected {
t.Errorf("Root was %s but expected %s", actual, expected)
// next few tests simulate user providing -host flag
Host = "not-localhost.com"
if actual, expected := string(DefaultInput().Body()), "not-localhost.com:https\nroot ."; actual != expected {
t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual)
}
if actual, expected := config.Host, DefaultHost; actual != expected {
t.Errorf("Host was %s but expected %s", actual, expected)
Host = "[::1]"
if actual, expected := string(DefaultInput().Body()), "[::1]:2015\nroot ."; actual != expected {
t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual)
}
if actual, expected := config.Port, DefaultPort; actual != expected {
t.Errorf("Port was %s but expected %s", actual, expected)
Host = "127.0.1.1"
if actual, expected := string(DefaultInput().Body()), "127.0.1.1:2015\nroot ."; actual != expected {
t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual)
}
}

View file

@ -166,17 +166,29 @@ func configQualifies(allConfigs []server.Config, cfgIndex int) bool {
cfg.TLS.LetsEncryptEmail != "off" &&
// obviously we get can't certs for loopback or internal hosts
cfg.Host != "localhost" &&
cfg.Host != "" &&
cfg.Host != "0.0.0.0" &&
cfg.Host != "::1" &&
!strings.HasPrefix(cfg.Host, "127.") && // to use boulder on your own machine, add fake domain to hosts file
// not excluding 10.* and 192.168.* hosts for possibility of running internal Boulder instance
HostQualifies(cfg.Host) &&
// make sure another HTTPS version of this config doesn't exist in the list already
!otherHostHasScheme(allConfigs, cfgIndex, "https")
}
// HostQualifies returns true if the hostname alone
// appears eligible for automatic HTTPS. For example,
// localhost, empty hostname, and wildcard hosts are
// not eligible because we cannot obtain certificates
// for those names.
func HostQualifies(hostname string) bool {
return hostname != "localhost" &&
strings.TrimSpace(hostname) != "" &&
hostname != "0.0.0.0" &&
hostname != "[::]" && // before parsing
hostname != "::" && // after parsing
hostname != "[::1]" && // before parsing
hostname != "::1" && // after parsing
!strings.HasPrefix(hostname, "127.") // to use boulder on your own machine, add fake domain to hosts file
// not excluding 10.* and 192.168.* hosts for possibility of running internal Boulder instance
}
// groupConfigsByEmail groups configs by user email address. The returned map is
// a map of email address to the configs that are serviced under that account.
// If an email address is not available for an eligible config, the user will be
@ -273,12 +285,10 @@ func newClientPort(leEmail, port string) (*acme.Client, error) {
// obtainCertificates obtains certificates from the CA server for
// the configurations in serverConfigs using client.
func obtainCertificates(client *acme.Client, serverConfigs []server.Config) ([]acme.CertificateResource, map[string]error) {
// collect all the hostnames into one slice
var hosts []string
for _, cfg := range serverConfigs {
hosts = append(hosts, cfg.Host)
}
return client.ObtainCertificates(hosts, true)
}

View file

@ -8,6 +8,34 @@ import (
"github.com/mholt/caddy/server"
)
func TestHostQualifies(t *testing.T) {
for i, test := range []struct {
host string
expect bool
}{
{"localhost", false},
{"127.0.0.1", false},
{"127.0.1.5", false},
{"::1", false},
{"[::1]", false},
{"[::]", false},
{"::", false},
{"", false},
{" ", false},
{"0.0.0.0", false},
{"192.168.1.3", true},
{"10.0.2.1", true},
{"foobar.com", true},
} {
if HostQualifies(test.host) && !test.expect {
t.Errorf("Test %d: Expected '%s' to NOT qualify, but it did", i, test.host)
}
if !HostQualifies(test.host) && test.expect {
t.Errorf("Test %d: Expected '%s' to qualify, but it did NOT", i, test.host)
}
}
}
func TestRedirPlaintextHost(t *testing.T) {
cfg := redirPlaintextHost(server.Config{
Host: "example.com",