diff --git a/caddy/caddymain/run.go b/caddy/caddymain/run.go index 03c11a128..8de692bda 100644 --- a/caddy/caddymain/run.go +++ b/caddy/caddymain/run.go @@ -46,6 +46,7 @@ func init() { flag.BoolVar(&certmagic.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement") flag.StringVar(&certmagic.CA, "ca", certmagic.CA, "URL to certificate authority's ACME server directory") + flag.StringVar(&certmagic.DefaultServerName, "default-sni", certmagic.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate") flag.BoolVar(&certmagic.DisableHTTPChallenge, "disable-http-challenge", certmagic.DisableHTTPChallenge, "Disable the ACME HTTP challenge") flag.BoolVar(&certmagic.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge") flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable") @@ -108,7 +109,7 @@ func Run() { } } - //Load all additional envs as soon as possible + // load all additional envs as soon as possible if err := LoadEnvFromFile(envFile); err != nil { mustLogFatalf("%v", err) } diff --git a/caddytls/config.go b/caddytls/config.go index ec1b93c06..c8318b929 100644 --- a/caddytls/config.go +++ b/caddytls/config.go @@ -93,7 +93,7 @@ type Config struct { } // NewConfig returns a new Config with a pointer to the instance's -// certificate cache. You will usually need to set Other fields on +// certificate cache. You will usually need to set other fields on // the returned Config for successful practical use. func NewConfig(inst *caddy.Instance) *Config { inst.StorageMu.RLock() @@ -257,9 +257,13 @@ func MakeTLSConfig(configs []*Config) (*tls.Config, error) { // configs with the same hostname pattern; should // be OK since we already asserted they are roughly // the same); during TLS handshakes, configs are - // loaded based on the hostname pattern, according - // to client's SNI - configMap[cfg.Hostname] = cfg + // loaded based on the hostname pattern according + // to client's ServerName (SNI) value + if cfg.Hostname == "0.0.0.0" || cfg.Hostname == "::" { + configMap[""] = cfg + } else { + configMap[cfg.Hostname] = cfg + } } // Is TLS disabled? By now, we know that all diff --git a/caddytls/handshake.go b/caddytls/handshake.go index e3005e927..858a6cc57 100644 --- a/caddytls/handshake.go +++ b/caddytls/handshake.go @@ -17,6 +17,8 @@ package caddytls import ( "crypto/tls" "fmt" + "log" + "net" "strings" "github.com/mholt/caddy/telemetry" @@ -38,14 +40,31 @@ type configGroup map[string]*Config // This function follows nearly the same logic to lookup // a hostname as the getCertificate function uses. func (cg configGroup) getConfig(hello *tls.ClientHelloInfo) *Config { - name := certmagic.CertNameFromClientHello(hello) + name := certmagic.NormalizedName(hello.ServerName) + if name == "" { + name = certmagic.NormalizedName(certmagic.DefaultServerName) + } - // exact match? great, let's use it + // if SNI is empty, prefer matching IP address (it is + // more specific than a "catch-all" configuration) + if name == "" && hello.Conn != nil { + addr := hello.Conn.LocalAddr().String() + ip, _, err := net.SplitHostPort(addr) + if err == nil { + addr = ip + } + if config, ok := cg[addr]; ok { + return config + } + } + + // otherwise, try an exact match if config, ok := cg[name]; ok { return config } - // try replacing labels in the name with wildcards until we get a match + // then try replacing labels in the name with + // wildcards until we get a match labels := strings.Split(name, ".") for i := range labels { labels[i] = "*" @@ -55,14 +74,25 @@ func (cg configGroup) getConfig(hello *tls.ClientHelloInfo) *Config { } } - // try a config that serves all names (the above - // loop doesn't try empty string; for hosts defined - // with only a port, for instance, like ":443") - - // also known as the default config + // try a config that matches all names - this + // is needed to match configs defined without + // a specific host, like ":443", when SNI is + // a non-empty value if config, ok := cg[""]; ok { return config } + // failover with a random config: this is necessary + // because we might be needing to solve a TLS-ALPN + // ACME challenge for a name that we don't have a + // TLS configuration for; any config will do for + // this purpose + for _, config := range cg { + return config + } + + log.Printf("[ERROR] No TLS configuration available for ClientHello with ServerName: %s", hello.ServerName) + return nil } diff --git a/caddytls/setup.go b/caddytls/setup.go index 52c924f76..9ac89a07f 100644 --- a/caddytls/setup.go +++ b/caddytls/setup.go @@ -54,18 +54,6 @@ func setupTLS(c *caddy.Controller) error { config.Enabled = true - // a single certificate cache is used by the whole caddy.Instance; get a pointer to it - certCache, ok := c.Get(CertCacheInstStorageKey).(*certmagic.Cache) - if !ok || certCache == nil { - certCache = certmagic.NewCache(certmagic.DefaultStorage) - c.OnShutdown(func() error { - certCache.Stop() - return nil - }) - c.Set(CertCacheInstStorageKey, certCache) - } - config.Manager = certmagic.NewWithCache(certCache, certmagic.Config{}) - // we use certmagic events to collect metrics for telemetry config.Manager.OnEvent = func(event string, data interface{}) { switch event { diff --git a/caddytls/setup_test.go b/caddytls/setup_test.go index cb97587fb..e973eb2b4 100644 --- a/caddytls/setup_test.go +++ b/caddytls/setup_test.go @@ -47,11 +47,18 @@ func TestMain(m *testing.M) { } func TestSetupParseBasic(t *testing.T) { - cfg := &Config{Manager: &certmagic.Config{}} + tmpdir, err := ioutil.TempDir("", "caddytls_setup_test_") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + certCache := certmagic.NewCache(&certmagic.FileStorage{Path: tmpdir}) + cfg := &Config{Manager: certmagic.NewWithCache(certCache, certmagic.Config{})} RegisterConfigGetter("", func(c *caddy.Controller) *Config { return cfg }) c := caddy.NewTestController("", `tls `+certFile+` `+keyFile+``) - err := setupTLS(c) + err = setupTLS(c) if err != nil { t.Errorf("Expected no errors, got: %v", err) } @@ -126,11 +133,18 @@ func TestSetupParseWithOptionalParams(t *testing.T) { alpn http/1.1 }` - cfg := &Config{Manager: &certmagic.Config{}} + tmpdir, err := ioutil.TempDir("", "caddytls_setup_test_") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + certCache := certmagic.NewCache(&certmagic.FileStorage{Path: tmpdir}) + cfg := &Config{Manager: certmagic.NewWithCache(certCache, certmagic.Config{})} RegisterConfigGetter("", func(c *caddy.Controller) *Config { return cfg }) c := caddy.NewTestController("", params) - err := setupTLS(c) + err = setupTLS(c) if err != nil { t.Errorf("Expected no errors, got: %v", err) } diff --git a/vendor/github.com/mholt/certmagic/certificates.go b/vendor/github.com/mholt/certmagic/certificates.go index 02a175e67..a6be89177 100644 --- a/vendor/github.com/mholt/certmagic/certificates.go +++ b/vendor/github.com/mholt/certmagic/certificates.go @@ -315,7 +315,7 @@ func (cfg *Config) cacheCertificate(cert Certificate) Certificate { // (yes, if certs overlap in the names they serve, one will // overwrite another here, but that's just how it goes) for _, name := range cert.Names { - cfg.certificates[name] = cert.Hash + cfg.certificates[NormalizedName(name)] = cert.Hash } // store the certificate diff --git a/vendor/github.com/mholt/certmagic/certmagic.go b/vendor/github.com/mholt/certmagic/certmagic.go index 8339921ed..f3acc0421 100644 --- a/vendor/github.com/mholt/certmagic/certmagic.go +++ b/vendor/github.com/mholt/certmagic/certmagic.go @@ -484,6 +484,12 @@ var ( // the risk of rate limiting. CertObtainTimeout time.Duration + // Set the default server name for clients + // not indicating a server name using SNI. + // In most cases this will be the primary + // domain that is being served. + DefaultServerName string + // The state needed to operate on-demand TLS OnDemand *OnDemandConfig diff --git a/vendor/github.com/mholt/certmagic/client.go b/vendor/github.com/mholt/certmagic/client.go index 4356628fc..0a07b4880 100644 --- a/vendor/github.com/mholt/certmagic/client.go +++ b/vendor/github.com/mholt/certmagic/client.go @@ -215,7 +215,7 @@ func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) { // lockKey returns a key for a lock that is specific to the operation // named op being performed related to domainName and this config's CA. func (cfg *Config) lockKey(op, domainName string) string { - return fmt.Sprintf("%s:%s:%s", op, domainName, cfg.CA) + return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.CA) } // Obtain obtains a single certificate for name. It stores the certificate diff --git a/vendor/github.com/mholt/certmagic/config.go b/vendor/github.com/mholt/certmagic/config.go index 63f13aef0..77072d4a2 100644 --- a/vendor/github.com/mholt/certmagic/config.go +++ b/vendor/github.com/mholt/certmagic/config.go @@ -101,6 +101,11 @@ type Config struct { // the risk of rate limiting. CertObtainTimeout time.Duration + // DefaultServerName specifies a server name + // to use when choosing a certificate if the + // ClientHello's ServerName field is empty + DefaultServerName string + // The state needed to operate on-demand TLS OnDemand *OnDemandConfig @@ -207,6 +212,9 @@ func NewWithCache(certCache *Cache, cfg Config) *Config { if cfg.CertObtainTimeout == 0 { cfg.CertObtainTimeout = CertObtainTimeout } + if cfg.DefaultServerName == "" { + cfg.DefaultServerName = DefaultServerName + } if cfg.OnDemand == nil { cfg.OnDemand = OnDemand } diff --git a/vendor/github.com/mholt/certmagic/handshake.go b/vendor/github.com/mholt/certmagic/handshake.go index 736383660..11a086c7c 100644 --- a/vendor/github.com/mholt/certmagic/handshake.go +++ b/vendor/github.com/mholt/certmagic/handshake.go @@ -67,13 +67,8 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif } } - wrapped := wrappedClientHelloInfo{ - ClientHelloInfo: clientHello, - serverNameOrIP: CertNameFromClientHello(clientHello), - } - // get the certificate and serve it up - cert, err := cfg.getCertDuringHandshake(wrapped, true, true) + cert, err := cfg.getCertDuringHandshake(clientHello, true, true) if err == nil && cfg.OnEvent != nil { cfg.OnEvent("tls_handshake_completed", clientHello) } @@ -96,56 +91,81 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif // which is by the Go Authors. // // This function is safe for concurrent use. -func (cfg *Config) getCertificate(name string) (cert Certificate, matched, defaulted bool) { - var certKey string - var ok bool +func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) { + name := NormalizedName(hello.ServerName) cfg.certCache.mu.RLock() defer cfg.certCache.mu.RUnlock() - // exact match? great, let's use it - if certKey, ok = cfg.certificates[name]; ok { - cert = cfg.certCache.cache[certKey] - matched = true - return - } + var certKey string + var ok bool - // try replacing labels in the name with wildcards until we get a match - labels := strings.Split(name, ".") - for i := range labels { - labels[i] = "*" - candidate := strings.Join(labels, ".") - if certKey, ok = cfg.certificates[candidate]; ok { + if name == "" { + // if SNI is empty, prefer matching IP address + if hello.Conn != nil { + addr := hello.Conn.LocalAddr().String() + ip, _, err := net.SplitHostPort(addr) + if err == nil { + addr = ip + } + if certKey, ok = cfg.certificates[addr]; ok { + cert = cfg.certCache.cache[certKey] + matched = true + return + } + } + + // fall back to a "default" certificate, if specified + if cfg.DefaultServerName != "" { + normDefault := NormalizedName(cfg.DefaultServerName) + if certKey, ok := cfg.certificates[normDefault]; ok { + cert = cfg.certCache.cache[certKey] + defaulted = true + return + } + } + } else { + // if SNI is specified, try an exact match first + if certKey, ok = cfg.certificates[name]; ok { cert = cfg.certCache.cache[certKey] matched = true return } + + // try replacing labels in the name with + // wildcards until we get a match + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if certKey, ok = cfg.certificates[candidate]; ok { + cert = cfg.certCache.cache[certKey] + matched = true + return + } + } + + // check the certCache directly to see if the SNI name is + // already the key of the certificate it wants; this implies + // that the SNI can contain the hash of a specific cert + // (chain) it wants and we will still be able to serve it up + // (this behavior, by the way, could be controversial as to + // whether it complies with RFC 6066 about SNI, but I think + // it does, soooo...) + // (this is how we solved the former ACME TLS-SNI challenge) + if directCert, ok := cfg.certCache.cache[name]; ok { + cert = directCert + matched = true + return + } } - // check the certCache directly to see if the SNI name is - // already the key of the certificate it wants; this implies - // that the SNI can contain the hash of a specific cert - // (chain) it wants and we will still be able to serve it up - // (this behavior, by the way, could be controversial as to - // whether it complies with RFC 6066 about SNI, but I think - // it does, soooo...) - // (this is how we solved the former ACME TLS-SNI challenge) - if directCert, ok := cfg.certCache.cache[name]; ok { - cert = directCert - matched = true - return - } - - // if nothing matches, use a "default" certificate (See issues - // mholt/caddy#2035 and mholt/caddy#1303; any change to this - // behavior must account for hosts defined like ":443" or - // "0.0.0.0:443" where the hostname is empty or a catch-all - // IP or something.) - if certKey, ok := cfg.certificates[""]; ok { - cert = cfg.certCache.cache[certKey] - defaulted = true - return - } + // otherwise, we're bingo on ammo; see issues + // mholt/caddy#2035 and mholt/caddy#1303 (any + // change to certificate matching behavior must + // account for hosts defined where the hostname + // is empty or a catch-all, like ":443" or + // "0.0.0.0:443") return } @@ -161,9 +181,11 @@ func (cfg *Config) getCertificate(name string) (cert Certificate, matched, defau // certificate is available. // // This function is safe for concurrent use. -func (cfg *Config) getCertDuringHandshake(hello wrappedClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) { +func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) { + name := NormalizedName(hello.ServerName) + // First check our in-memory cache to see if we've already loaded it - cert, matched, defaulted := cfg.getCertificate(hello.serverNameOrIP) + cert, matched, defaulted := cfg.getCertificate(hello) if matched { return cert, nil } @@ -172,11 +194,11 @@ func (cfg *Config) getCertDuringHandshake(hello wrappedClientHelloInfo, loadIfNe // obtain a needed certificate if cfg.OnDemand != nil && loadIfNecessary { // Then check to see if we have one on disk - loadedCert, err := cfg.CacheManagedCertificate(hello.serverNameOrIP) + loadedCert, err := cfg.CacheManagedCertificate(name) if err == nil { loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert) if err != nil { - log.Printf("[ERROR] Maintaining newly-loaded certificate for %s: %v", hello.serverNameOrIP, err) + log.Printf("[ERROR] Maintaining newly-loaded certificate for %s: %v", name, err) } return loadedCert, nil } @@ -184,14 +206,14 @@ func (cfg *Config) getCertDuringHandshake(hello wrappedClientHelloInfo, loadIfNe // By this point, we need to ask the CA for a certificate // Make sure the certificate should be obtained based on config - err := cfg.checkIfCertShouldBeObtained(hello.serverNameOrIP) + err := cfg.checkIfCertShouldBeObtained(name) if err != nil { return Certificate{}, err } // Name has to qualify for a certificate - if !HostQualifies(hello.serverNameOrIP) { - return cert, fmt.Errorf("hostname '%s' does not qualify for certificate", hello.serverNameOrIP) + if !HostQualifies(name) { + return cert, fmt.Errorf("hostname '%s' does not qualify for certificate", name) } // Obtain certificate from the CA @@ -204,7 +226,7 @@ func (cfg *Config) getCertDuringHandshake(hello wrappedClientHelloInfo, loadIfNe return cert, nil } - return Certificate{}, fmt.Errorf("no certificate available for %s", hello.serverNameOrIP) + return Certificate{}, fmt.Errorf("no certificate available for '%s'", name) } // checkIfCertShouldBeObtained checks to see if an on-demand tls certificate @@ -222,10 +244,12 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string) error { // hello, it will wait and use what the other goroutine obtained. // // This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) obtainOnDemandCertificate(hello wrappedClientHelloInfo) (Certificate, error) { +func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certificate, error) { + name := NormalizedName(hello.ServerName) + // We must protect this process from happening concurrently, so synchronize. obtainCertWaitChansMu.Lock() - wait, ok := obtainCertWaitChans[hello.serverNameOrIP] + wait, ok := obtainCertWaitChans[name] if ok { // lucky us -- another goroutine is already obtaining the certificate. // wait for it to finish obtaining the cert and then we'll use it. @@ -237,32 +261,32 @@ func (cfg *Config) obtainOnDemandCertificate(hello wrappedClientHelloInfo) (Cert // looks like it's up to us to do all the work and obtain the cert. // make a chan others can wait on if needed wait = make(chan struct{}) - obtainCertWaitChans[hello.serverNameOrIP] = wait + obtainCertWaitChans[name] = wait obtainCertWaitChansMu.Unlock() // obtain the certificate - log.Printf("[INFO] Obtaining new certificate for %s", hello.serverNameOrIP) - err := cfg.ObtainCert(hello.serverNameOrIP, false) + log.Printf("[INFO] Obtaining new certificate for %s", name) + err := cfg.ObtainCert(name, false) // immediately unblock anyone waiting for it; doing this in // a defer would risk deadlock because of the recursive call // to getCertDuringHandshake below when we return! obtainCertWaitChansMu.Lock() close(wait) - delete(obtainCertWaitChans, hello.serverNameOrIP) + delete(obtainCertWaitChans, name) obtainCertWaitChansMu.Unlock() if err != nil { // Failed to solve challenge, so don't allow another on-demand // issue for this name to be attempted for a little while. failedIssuanceMu.Lock() - failedIssuance[hello.serverNameOrIP] = time.Now() + failedIssuance[name] = time.Now() go func(name string) { time.Sleep(5 * time.Minute) failedIssuanceMu.Lock() delete(failedIssuance, name) failedIssuanceMu.Unlock() - }(hello.serverNameOrIP) + }(name) failedIssuanceMu.Unlock() return Certificate{}, err } @@ -280,7 +304,7 @@ func (cfg *Config) obtainOnDemandCertificate(hello wrappedClientHelloInfo) (Cert // handshakeMaintenance performs a check on cert for expiration and OCSP validity. // // This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) handshakeMaintenance(hello wrappedClientHelloInfo, cert Certificate) (Certificate, error) { +func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certificate) (Certificate, error) { // Check cert expiration timeLeft := cert.NotAfter.Sub(time.Now().UTC()) if timeLeft < cfg.RenewDurationBefore { @@ -296,7 +320,7 @@ func (cfg *Config) handshakeMaintenance(hello wrappedClientHelloInfo, cert Certi if err != nil { // An error with OCSP stapling is not the end of the world, and in fact, is // quite common considering not all certs have issuer URLs that support it. - log.Printf("[ERROR] Getting OCSP for %s: %v", hello.serverNameOrIP, err) + log.Printf("[ERROR] Getting OCSP for %s: %v", hello.ServerName, err) } cfg.certCache.mu.Lock() cfg.certCache.cache[cert.Hash] = cert @@ -313,10 +337,11 @@ func (cfg *Config) handshakeMaintenance(hello wrappedClientHelloInfo, cert Certi // ClientHello. // // This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) renewDynamicCertificate(hello wrappedClientHelloInfo, currentCert Certificate) (Certificate, error) { +func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) { + name := NormalizedName(hello.ServerName) obtainCertWaitChansMu.Lock() - wait, ok := obtainCertWaitChans[hello.serverNameOrIP] + wait, ok := obtainCertWaitChans[name] if ok { // lucky us -- another goroutine is already renewing the certificate. // wait for it to finish, then we'll use the new one. @@ -327,24 +352,24 @@ func (cfg *Config) renewDynamicCertificate(hello wrappedClientHelloInfo, current // looks like it's up to us to do all the work and renew the cert wait = make(chan struct{}) - obtainCertWaitChans[hello.serverNameOrIP] = wait + obtainCertWaitChans[name] = wait obtainCertWaitChansMu.Unlock() // renew and reload the certificate - log.Printf("[INFO] Renewing certificate for %s", hello.serverNameOrIP) - err := cfg.RenewCert(hello.serverNameOrIP, false) + log.Printf("[INFO] Renewing certificate for %s", name) + err := cfg.RenewCert(name, false) if err == nil { // even though the recursive nature of the dynamic cert loading // would just call this function anyway, we do it here to // make the replacement as atomic as possible. - newCert, err := currentCert.configs[0].CacheManagedCertificate(hello.serverNameOrIP) + newCert, err := currentCert.configs[0].CacheManagedCertificate(name) if err != nil { - log.Printf("[ERROR] loading renewed certificate for %s: %v", hello.serverNameOrIP, err) + log.Printf("[ERROR] loading renewed certificate for %s: %v", name, err) } else { // replace the old certificate with the new one err = cfg.certCache.replaceCertificate(currentCert, newCert) if err != nil { - log.Printf("[ERROR] Replacing certificate for %s: %v", hello.serverNameOrIP, err) + log.Printf("[ERROR] Replacing certificate for %s: %v", name, err) } } } @@ -354,7 +379,7 @@ func (cfg *Config) renewDynamicCertificate(hello wrappedClientHelloInfo, current // to getCertDuringHandshake below when we return! obtainCertWaitChansMu.Lock() close(wait) - delete(obtainCertWaitChans, hello.serverNameOrIP) + delete(obtainCertWaitChans, name) obtainCertWaitChansMu.Unlock() if err != nil { @@ -370,7 +395,7 @@ func (cfg *Config) renewDynamicCertificate(hello wrappedClientHelloInfo, current // is present, it makes a certificate to solve this challenge and returns it. // A boolean true is returned if a valid certificate is returned. func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) { - tokenKey := distributedSolver{}.challengeTokensKey(clientHello.ServerName) + tokenKey := distributedSolver{config: cfg}.challengeTokensKey(clientHello.ServerName) chalInfoBytes, err := cfg.certCache.storage.Load(tokenKey) if err != nil { if _, ok := err.(ErrNotExist); ok { @@ -396,36 +421,10 @@ func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInf return Certificate{Certificate: *cert}, true, nil } -// CertNameFromClientHello returns a normalized name for which to -// look up a certificate given this ClientHelloInfo. If the client -// did not send a ServerName value, the connection's local IP is -// assumed. -func CertNameFromClientHello(hello *tls.ClientHelloInfo) string { - // Not going to trim trailing dots here since RFC 3546 says, - // "The hostname is represented ... without a trailing dot." - // Just normalize to lowercase and remove any leading or - // trailing whitespace n case the hello was sloppily made - name := strings.ToLower(strings.TrimSpace(hello.ServerName)) - - // if SNI is not set, assume IP of listener - if name == "" && hello.Conn != nil { - addr := hello.Conn.LocalAddr().String() - ip, _, err := net.SplitHostPort(addr) - if err == nil { - name = ip - } - } - - return name -} - -// wrappedClientHelloInfo is a type that allows us to -// attach a name with which to look for a certificate -// to a given ClientHelloInfo, since not all clients -// use SNI and some self-signed certificates use IP. -type wrappedClientHelloInfo struct { - *tls.ClientHelloInfo - serverNameOrIP string +// NormalizedName returns a cleaned form of serverName that is +// used for consistency when referring to a SNI value. +func NormalizedName(serverName string) string { + return strings.ToLower(strings.TrimSpace(serverName)) } // obtainCertWaitChans is used to coordinate obtaining certs for each hostname. diff --git a/vendor/github.com/mholt/certmagic/maintain.go b/vendor/github.com/mholt/certmagic/maintain.go index 9652aaaf2..a154b3105 100644 --- a/vendor/github.com/mholt/certmagic/maintain.go +++ b/vendor/github.com/mholt/certmagic/maintain.go @@ -35,6 +35,8 @@ func (certCache *Cache) maintainAssets() { renewalTicker := time.NewTicker(certCache.RenewInterval) ocspTicker := time.NewTicker(certCache.OCSPInterval) + log.Printf("[INFO][%s] Started certificate maintenance routine", certCache.storage) + for { select { case <-renewalTicker.C: diff --git a/vendor/github.com/mholt/certmagic/solvers.go b/vendor/github.com/mholt/certmagic/solvers.go index c62b692dc..1802c61db 100644 --- a/vendor/github.com/mholt/certmagic/solvers.go +++ b/vendor/github.com/mholt/certmagic/solvers.go @@ -50,7 +50,7 @@ func (s tlsALPNSolver) Present(domain, token, keyAuth string) error { // CleanUp removes the challenge certificate from the cache. func (s tlsALPNSolver) CleanUp(domain, token, keyAuth string) error { s.certCache.mu.Lock() - delete(s.certCache.cache, domain) + delete(s.certCache.cache, tlsALPNCertKeyName(domain)) s.certCache.mu.Unlock() return nil } diff --git a/vendor/manifest b/vendor/manifest index a2473037e..039f47b0e 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -138,7 +138,7 @@ "importpath": "github.com/mholt/certmagic", "repository": "https://github.com/mholt/certmagic", "vcs": "git", - "revision": "c1d472b46046ee329c099086d689ada0c44d56b0", + "revision": "a7f18a937c080b88693cd4e14d48e42cc067b268", "branch": "master", "notests": true },