Пример #1
0
// LETSENCRYPTPROD returns a new Automatic TLS Listener using letsencrypt.org service
// receives two parameters, the first is the domain of the server
// and the second is optionally, the cache directory, if you skip it then the cache directory is "./certcache"
// if you want to disable cache directory then simple give it a value of empty string ""
//
// does NOT supports localhost domains for testing, use LETSENCRYPT instead.
//
// this is the recommended function to use when you're ready for production state
func LETSENCRYPTPROD(addr string, cacheDirOptional ...string) (net.Listener, error) {
	if portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {
		addr += ":443"
	}

	ln, err := TCP4(addr)
	if err != nil {
		return nil, err
	}

	cacheDir := "./certcache"
	if len(cacheDirOptional) > 0 {
		cacheDir = cacheDirOptional[0]
	}

	m := autocert.Manager{
		Prompt: autocert.AcceptTOS,
	} // HostPolicy is missing, if user wants it, then she/he should manually
	// configure the autocertmanager and use the `iris.Serve` to pass that listener

	if cacheDir == "" {
		// then the user passed empty by own will, then I guess she/he doesnt' want any cache directory
	} else {
		m.Cache = autocert.DirCache(cacheDir)
	}

	tlsConfig := &tls.Config{GetCertificate: m.GetCertificate}
	tlsLn := tls.NewListener(ln, tlsConfig)

	return tlsLn, nil
}
Пример #2
0
func setupTLS(ws *webserver.Server, conf *config) error {
	if conf.HTTPSCert != "" && conf.HTTPSKey != "" {
		ws.SetTLS(webserver.TLSSetup{
			CertFile: conf.HTTPSCert,
			KeyFile:  conf.HTTPSKey,
		})
		return nil
	}
	if !conf.CertManager {
		return nil
	}

	// As all requests to the publisher are proxied through Camlistore's app
	// handler, it makes sense to assume that both Camlistore and the publisher
	// are behind the same domain name. Therefore, it follows that
	// camlistored's autocert is the one actually getting a cert (and answering
	// the challenge) for the both of them. Plus, if they run on the same host
	// (default setup), they can't both listen on 443 to answer the TLS-SNI
	// challenge.
	// TODO(mpl): however, camlistored and publisher could be running on
	// different hosts, in which case we need to find a way for camlistored to
	// share its autocert cache with publisher. But I think that can wait a
	// later CL.
	hostname := os.Getenv("CAMLI_API_HOST")
	hostname = strings.TrimPrefix(hostname, "https://")
	hostname = strings.SplitN(hostname, "/", 2)[0]
	hostname = strings.SplitN(hostname, ":", 2)[0]
	if !netutil.IsFQDN(hostname) {
		return fmt.Errorf("cannot ask Let's Encrypt for a cert because %v is not a fully qualified domain name", hostname)
	}
	logger.Print("TLS enabled, with Let's Encrypt")

	// TODO(mpl): we only want publisher to use the same cache as
	// camlistored, and we don't actually need an autocert.Manager.
	// So we could just instantiate an autocert.DirCache, and generate
	// from there a *tls.Certificate, but it looks like it would mean
	// extracting quite a bit of code from the autocert pkg to do it properly.
	// Instead, I've opted for using an autocert.Manager (even though it's
	// never going to reply to any challenge), but with NOOP for Put and
	// Delete, just to be on the safe side. It's simple enough, but there's
	// still a catch: there's no ServerName in the clientHello we get, so we
	// reinject one so we can simply use the autocert.Manager.GetCertificate
	// method as the way to get the certificate from the cache. Is that OK?
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(hostname),
		Cache:      roCertCacheDir(osutil.DefaultLetsEncryptCache()),
	}
	ws.SetTLS(webserver.TLSSetup{
		CertManager: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
			if clientHello.ServerName == "" {
				clientHello.ServerName = hostname
			}
			return m.GetCertificate(clientHello)
		},
	})
	return nil
}
Пример #3
0
func serveHTTPS(httpServer *http.Server) error {
	log.Printf("Starting TLS server on %s", *httpsAddr)
	httpsServer := new(http.Server)
	*httpsServer = *httpServer
	httpsServer.Addr = *httpsAddr
	cacheDir := autocert.DirCache("letsencrypt.cache")
	var domain string
	if !inProd {
		if *tlsCertFile != "" && *tlsKeyFile != "" {
			return httpsServer.ListenAndServeTLS(*tlsCertFile, *tlsKeyFile)
		}
		// Otherwise use Let's Encrypt, i.e. same use case as in prod
		if strings.HasPrefix(*httpsAddr, ":") {
			return errors.New("for Let's Encrypt, -https needs to start with a host name")
		}
		host, _, err := net.SplitHostPort(*httpsAddr)
		if err != nil {
			return err
		}
		domain = host
	} else {
		domain = "camlistore.org"
		cacheDir = autocert.DirCache(prodLECacheDir)
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		HostPolicy: autocert.HostWhitelist(domain),
		Cache:      cacheDir,
	}
	if *adminEmail != "" {
		m.Email = *adminEmail
	}

	httpsServer.TLSConfig = &tls.Config{
		GetCertificate: m.GetCertificate,
	}
	log.Printf("Listening for HTTPS on %v", *httpsAddr)
	ln, err := net.Listen("tcp", *httpsAddr)
	if err != nil {
		return err
	}
	return httpsServer.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, httpsServer.TLSConfig))
}
Пример #4
0
func (srv *Server) newTLSConfig(cfg ServerConfig) *tls.Config {
	tlsConfig := utils.SecureTLSConfig()
	if cfg.AutocertDNSName == "" {
		// No official DNS name, no certificate.
		tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
			cert, _ := srv.localCertificate(clientHello.ServerName)
			return cert, nil
		}
		return tlsConfig
	}
	m := autocert.Manager{
		Prompt:     autocert.AcceptTOS,
		Cache:      srv.state.AutocertCache(),
		HostPolicy: autocert.HostWhitelist(cfg.AutocertDNSName),
	}
	if cfg.AutocertURL != "" {
		m.Client = &acme.Client{
			DirectoryURL: cfg.AutocertURL,
		}
	}
	tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
		logger.Infof("getting certificate for server name %q", clientHello.ServerName)
		// Get the locally created certificate and whether it's appropriate
		// for the SNI name. If not, we'll try to get an acme cert and
		// fall back to the local certificate if that fails.
		cert, shouldUse := srv.localCertificate(clientHello.ServerName)
		if shouldUse {
			return cert, nil
		}
		acmeCert, err := m.GetCertificate(clientHello)
		if err == nil {
			return acmeCert, nil
		}
		logger.Errorf("cannot get autocert certificate for %q: %v", clientHello.ServerName, err)
		return cert, nil
	}
	return tlsConfig
}