mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 10:37:24 -04:00 
			
		
		
		
	* introduced own ChallengeProvider type, based on acme.ChallengeProvider to avoid vendoring/version mismatches in Caddy plugins; see Caddy issue #1697 * fixed up comments for ChallengeProvider * moved ChallengeProvider to caddytls/tls.go
		
			
				
	
	
		
			194 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package caddytls facilitates the management of TLS assets and integrates
 | |
| // Let's Encrypt functionality into Caddy with first-class support for
 | |
| // creating and renewing certificates automatically. It also implements
 | |
| // the tls directive.
 | |
| //
 | |
| // This package is meant to be used by Caddy server types. To use the
 | |
| // tls directive, a server type must import this package and call
 | |
| // RegisterConfigGetter(). The server type must make and keep track of
 | |
| // the caddytls.Config structs that this package produces. It must also
 | |
| // add tls to its list of directives. When it comes time to make the
 | |
| // server instances, the server type can call MakeTLSConfig() to convert
 | |
| // a []caddytls.Config to a single tls.Config for use in tls.NewListener().
 | |
| // It is also recommended to call RotateSessionTicketKeys() when
 | |
| // starting a new listener.
 | |
| package caddytls
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/mholt/caddy"
 | |
| 	"github.com/xenolf/lego/acme"
 | |
| )
 | |
| 
 | |
| // HostQualifies returns true if the hostname alone
 | |
| // appears eligible for automatic HTTPS. For example,
 | |
| // localhost, empty hostname, and IP addresses are
 | |
| // not eligible because we cannot obtain certificates
 | |
| // for those names.
 | |
| func HostQualifies(hostname string) bool {
 | |
| 	return hostname != "localhost" && // localhost is ineligible
 | |
| 
 | |
| 		// hostname must not be empty
 | |
| 		strings.TrimSpace(hostname) != "" &&
 | |
| 
 | |
| 		// must not contain wildcard (*) characters (until CA supports it)
 | |
| 		!strings.Contains(hostname, "*") &&
 | |
| 
 | |
| 		// must not start or end with a dot
 | |
| 		!strings.HasPrefix(hostname, ".") &&
 | |
| 		!strings.HasSuffix(hostname, ".") &&
 | |
| 
 | |
| 		// cannot be an IP address, see
 | |
| 		// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
 | |
| 		net.ParseIP(hostname) == nil
 | |
| }
 | |
| 
 | |
| // saveCertResource saves the certificate resource to disk. This
 | |
| // includes the certificate file itself, the private key, and the
 | |
| // metadata file.
 | |
| func saveCertResource(storage Storage, cert acme.CertificateResource) error {
 | |
| 	// Save cert, private key, and metadata
 | |
| 	siteData := &SiteData{
 | |
| 		Cert: cert.Certificate,
 | |
| 		Key:  cert.PrivateKey,
 | |
| 	}
 | |
| 	var err error
 | |
| 	siteData.Meta, err = json.MarshalIndent(&cert, "", "\t")
 | |
| 	if err == nil {
 | |
| 		err = storage.StoreSite(cert.Domain, siteData)
 | |
| 	}
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Revoke revokes the certificate for host via ACME protocol.
 | |
| // It assumes the certificate was obtained from the
 | |
| // CA at DefaultCAUrl.
 | |
| func Revoke(host string) error {
 | |
| 	client, err := newACMEClient(new(Config), true)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return client.Revoke(host)
 | |
| }
 | |
| 
 | |
| // tlsSniSolver is a type that can solve tls-sni challenges using
 | |
| // an existing listener and our custom, in-memory certificate cache.
 | |
| type tlsSniSolver struct{}
 | |
| 
 | |
| // Present adds the challenge certificate to the cache.
 | |
| func (s tlsSniSolver) Present(domain, token, keyAuth string) error {
 | |
| 	cert, acmeDomain, err := acme.TLSSNI01ChallengeCert(keyAuth)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	cacheCertificate(Certificate{
 | |
| 		Certificate: cert,
 | |
| 		Names:       []string{acmeDomain},
 | |
| 	})
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // CleanUp removes the challenge certificate from the cache.
 | |
| func (s tlsSniSolver) CleanUp(domain, token, keyAuth string) error {
 | |
| 	_, acmeDomain, err := acme.TLSSNI01ChallengeCert(keyAuth)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	uncacheCertificate(acmeDomain)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // ConfigHolder is any type that has a Config; it presumably is
 | |
| // connected to a hostname and port on which it is serving.
 | |
| type ConfigHolder interface {
 | |
| 	TLSConfig() *Config
 | |
| 	Host() string
 | |
| 	Port() string
 | |
| }
 | |
| 
 | |
| // QualifiesForManagedTLS returns true if c qualifies for
 | |
| // for managed TLS (but not on-demand TLS specifically).
 | |
| // It does NOT check to see if a cert and key already exist
 | |
| // for the config. If the return value is true, you should
 | |
| // be OK to set c.TLSConfig().Managed to true; then you should
 | |
| // check that value in the future instead, because the process
 | |
| // of setting up the config may make it look like it doesn't
 | |
| // qualify even though it originally did.
 | |
| func QualifiesForManagedTLS(c ConfigHolder) bool {
 | |
| 	if c == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	tlsConfig := c.TLSConfig()
 | |
| 	if tlsConfig == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	return (!tlsConfig.Manual || tlsConfig.OnDemand) && // user might provide own cert and key
 | |
| 
 | |
| 		// if self-signed, we've already generated one to use
 | |
| 		!tlsConfig.SelfSigned &&
 | |
| 
 | |
| 		// user can force-disable managed TLS
 | |
| 		c.Port() != "80" &&
 | |
| 		tlsConfig.ACMEEmail != "off" &&
 | |
| 
 | |
| 		// we get can't certs for some kinds of hostnames, but
 | |
| 		// on-demand TLS allows empty hostnames at startup
 | |
| 		(HostQualifies(c.Host()) || tlsConfig.OnDemand)
 | |
| }
 | |
| 
 | |
| // ChallengeProvider defines an own type that should be used in Caddy plugins
 | |
| // over acme.ChallengeProvider. Using acme.ChallengeProvider causes version mismatches
 | |
| // with vendored dependencies (see https://github.com/mattfarina/golang-broken-vendor)
 | |
| //
 | |
| // acme.ChallengeProvider is an interface that allows the implementation of custom
 | |
| // challenge providers. For more details, see:
 | |
| // https://godoc.org/github.com/xenolf/lego/acme#ChallengeProvider
 | |
| type ChallengeProvider acme.ChallengeProvider
 | |
| 
 | |
| // DNSProviderConstructor is a function that takes credentials and
 | |
| // returns a type that can solve the ACME DNS challenges.
 | |
| type DNSProviderConstructor func(credentials ...string) (ChallengeProvider, error)
 | |
| 
 | |
| // dnsProviders is the list of DNS providers that have been plugged in.
 | |
| var dnsProviders = make(map[string]DNSProviderConstructor)
 | |
| 
 | |
| // RegisterDNSProvider registers provider by name for solving the ACME DNS challenge.
 | |
| func RegisterDNSProvider(name string, provider DNSProviderConstructor) {
 | |
| 	dnsProviders[name] = provider
 | |
| 	caddy.RegisterPlugin("tls.dns."+name, caddy.Plugin{})
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// DefaultEmail represents the Let's Encrypt account email to use if none provided.
 | |
| 	DefaultEmail string
 | |
| 
 | |
| 	// Agreed indicates whether user has agreed to the Let's Encrypt SA.
 | |
| 	Agreed bool
 | |
| 
 | |
| 	// DefaultCAUrl is the default URL to the CA's ACME directory endpoint.
 | |
| 	// It's very important to set this unless you set it in every Config.
 | |
| 	DefaultCAUrl string
 | |
| 
 | |
| 	// DefaultKeyType is used as the type of key for new certificates
 | |
| 	// when no other key type is specified.
 | |
| 	DefaultKeyType = acme.RSA2048
 | |
| 
 | |
| 	// DisableHTTPChallenge will disable all HTTP challenges.
 | |
| 	DisableHTTPChallenge bool
 | |
| 
 | |
| 	// DisableTLSSNIChallenge will disable all TLS-SNI challenges.
 | |
| 	DisableTLSSNIChallenge bool
 | |
| )
 | |
| 
 | |
| var storageProviders = make(map[string]StorageConstructor)
 | |
| 
 | |
| // RegisterStorageProvider registers provider by name for storing tls data
 | |
| func RegisterStorageProvider(name string, provider StorageConstructor) {
 | |
| 	storageProviders[name] = provider
 | |
| 	caddy.RegisterPlugin("tls.storage."+name, caddy.Plugin{})
 | |
| }
 |