// Copyright 2015 Matthew Holt // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package certmagic automates the obtaining and renewal of TLS certificates, // including TLS & HTTPS best practices such as robust OCSP stapling, caching, // HTTP->HTTPS redirects, and more. // // Its high-level API serves your HTTP handlers over HTTPS if you simply give // the domain name(s) and the http.Handler; CertMagic will create and run // the HTTPS server for you, fully managing certificates during the lifetime // of the server. Similarly, it can be used to start TLS listeners or return // a ready-to-use tls.Config -- whatever layer you need TLS for, CertMagic // makes it easy. See the HTTPS, Listen, and TLS functions for that. // // If you need more control, create a Cache using NewCache() and then make // a Config using New(). You can then call Manage() on the config. But if // you use this lower-level API, you'll have to be sure to solve the HTTP // and TLS-ALPN challenges yourself (unless you disabled them or use the // DNS challenge) by using the provided Config.GetCertificate function // in your tls.Config and/or Config.HTTPChallangeHandler in your HTTP // handler. // // See the package's README for more instruction. package certmagic import ( "context" "crypto" "crypto/tls" "crypto/x509" "fmt" "log" "net" "net/http" "sort" "strings" "sync" "time" ) // HTTPS serves mux for all domainNames using the HTTP // and HTTPS ports, redirecting all HTTP requests to HTTPS. // It uses the Default config. // // This high-level convenience function is opinionated and // applies sane defaults for production use, including // timeouts for HTTP requests and responses. To allow very // long-lived connections, you should make your own // http.Server values and use this package's Listen(), TLS(), // or Config.TLSConfig() functions to customize to your needs. // For example, servers which need to support large uploads or // downloads with slow clients may need to use longer timeouts, // thus this function is not suitable. // // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func HTTPS(domainNames []string, mux http.Handler) error { if mux == nil { mux = http.DefaultServeMux } DefaultACME.Agreed = true cfg := NewDefault() err := cfg.ManageSync(domainNames) if err != nil { return err } httpWg.Add(1) defer httpWg.Done() // if we haven't made listeners yet, do so now, // and clean them up when all servers are done lnMu.Lock() if httpLn == nil && httpsLn == nil { httpLn, err = net.Listen("tcp", fmt.Sprintf(":%d", HTTPPort)) if err != nil { lnMu.Unlock() return err } httpsLn, err = tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) if err != nil { httpLn.Close() httpLn = nil lnMu.Unlock() return err } go func() { httpWg.Wait() lnMu.Lock() httpLn.Close() httpsLn.Close() lnMu.Unlock() }() } hln, hsln := httpLn, httpsLn lnMu.Unlock() // create HTTP/S servers that are configured // with sane default timeouts and appropriate // handlers (the HTTP server solves the HTTP // challenge and issues redirects to HTTPS, // while the HTTPS server simply serves the // user's handler) httpServer := &http.Server{ ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: 5 * time.Second, } if am, ok := cfg.Issuer.(*ACMEManager); ok { httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)) } httpsServer := &http.Server{ ReadHeaderTimeout: 10 * time.Second, ReadTimeout: 30 * time.Second, WriteTimeout: 2 * time.Minute, IdleTimeout: 5 * time.Minute, Handler: mux, } log.Printf("%v Serving HTTP->HTTPS on %s and %s", domainNames, hln.Addr(), hsln.Addr()) go httpServer.Serve(hln) return httpsServer.Serve(hsln) } func httpRedirectHandler(w http.ResponseWriter, r *http.Request) { toURL := "https://" // since we redirect to the standard HTTPS port, we // do not need to include it in the redirect URL requestHost := hostOnly(r.Host) toURL += requestHost toURL += r.URL.RequestURI() // get rid of this disgusting unencrypted HTTP connection 🤢 w.Header().Set("Connection", "close") http.Redirect(w, r, toURL, http.StatusMovedPermanently) } // TLS enables management of certificates for domainNames // and returns a valid tls.Config. It uses the Default // config. // // Because this is a convenience function that returns // only a tls.Config, it does not assume HTTP is being // served on the HTTP port, so the HTTP challenge is // disabled (no HTTPChallengeHandler is necessary). The // package variable Default is modified so that the // HTTP challenge is disabled. // // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func TLS(domainNames []string) (*tls.Config, error) { DefaultACME.Agreed = true DefaultACME.DisableHTTPChallenge = true cfg := NewDefault() return cfg.TLSConfig(), cfg.ManageSync(domainNames) } // Listen manages certificates for domainName and returns a // TLS listener. It uses the Default config. // // Because this convenience function returns only a TLS-enabled // listener and does not presume HTTP is also being served, // the HTTP challenge will be disabled. The package variable // Default is modified so that the HTTP challenge is disabled. // // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func Listen(domainNames []string) (net.Listener, error) { DefaultACME.Agreed = true DefaultACME.DisableHTTPChallenge = true cfg := NewDefault() err := cfg.ManageSync(domainNames) if err != nil { return nil, err } return tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) } // ManageSync obtains certificates for domainNames and keeps them // renewed using the Default config. // // This is a slightly lower-level function; you will need to // wire up support for the ACME challenges yourself. You can // obtain a Config to help you do that by calling NewDefault(). // // You will need to ensure that you use a TLS config that gets // certificates from this Config and that the HTTP and TLS-ALPN // challenges can be solved. The easiest way to do this is to // use NewDefault().TLSConfig() as your TLS config and to wrap // your HTTP handler with NewDefault().HTTPChallengeHandler(). // If you don't have an HTTP server, you will need to disable // the HTTP challenge. // // If you already have a TLS config you want to use, you can // simply set its GetCertificate field to // NewDefault().GetCertificate. // // Calling this function signifies your acceptance to // the CA's Subscriber Agreement and/or Terms of Service. func ManageSync(domainNames []string) error { DefaultACME.Agreed = true return NewDefault().ManageSync(domainNames) } // ManageAsync is the same as ManageSync, except that // certificates are managed asynchronously. This means // that the function will return before certificates // are ready, and errors that occur during certificate // obtain or renew operations are only logged. It is // vital that you monitor the logs if using this method, // which is only recommended for automated/non-interactive // environments. func ManageAsync(ctx context.Context, domainNames []string) error { DefaultACME.Agreed = true return NewDefault().ManageAsync(ctx, domainNames) } // OnDemandConfig configures on-demand TLS (certificate // operations as-needed, like during TLS handshakes, // rather than immediately). // // When this package's high-level convenience functions // are used (HTTPS, Manage, etc., where the Default // config is used as a template), this struct regulates // certificate operations using an implicit whitelist // containing the names passed into those functions if // no DecisionFunc is set. This ensures some degree of // control by default to avoid certificate operations for // aribtrary domain names. To override this whitelist, // manually specify a DecisionFunc. To impose rate limits, // specify your own DecisionFunc. type OnDemandConfig struct { // If set, this function will be called to determine // whether a certificate can be obtained or renewed // for the given name. If an error is returned, the // request will be denied. DecisionFunc func(name string) error // List of whitelisted hostnames (SNI values) for // deferred (on-demand) obtaining of certificates. // Used only by higher-level functions in this // package to persist the list of hostnames that // the config is supposed to manage. This is done // because it seems reasonable that if you say // "Manage [domain names...]", then only those // domain names should be able to have certs; // we don't NEED this feature, but it makes sense // for higher-level convenience functions to be // able to retain their convenience (alternative // is: the user manually creates a DecisionFunc // that whitelists the same names it already // passed into Manage) and without letting clients // have their run of any domain names they want. // Only enforced if len > 0. hostWhitelist []string } func (o *OnDemandConfig) whitelistContains(name string) bool { for _, n := range o.hostWhitelist { if strings.EqualFold(n, name) { return true } } return false } // isLoopback returns true if the hostname of addr looks // explicitly like a common local hostname. addr must only // be a host or a host:port combination. func isLoopback(addr string) bool { host := hostOnly(addr) return host == "localhost" || strings.Trim(host, "[]") == "::1" || strings.HasPrefix(host, "127.") } // isInternal returns true if the IP of addr // belongs to a private network IP range. addr // must only be an IP or an IP:port combination. // Loopback addresses are considered false. func isInternal(addr string) bool { privateNetworks := []string{ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7", } host := hostOnly(addr) ip := net.ParseIP(host) if ip == nil { return false } for _, privateNetwork := range privateNetworks { _, ipnet, _ := net.ParseCIDR(privateNetwork) if ipnet.Contains(ip) { return true } } return false } // hostOnly returns only the host portion of hostport. // If there is no port or if there is an error splitting // the port off, the whole input string is returned. func hostOnly(hostport string) string { host, _, err := net.SplitHostPort(hostport) if err != nil { return hostport // OK; probably had no port to begin with } return host } // PreChecker is an interface that can be optionally implemented by // Issuers. Pre-checks are performed before each call (or batch of // identical calls) to Issue(), giving the issuer the option to ensure // it has all the necessary information/state. type PreChecker interface { PreCheck(names []string, interactive bool) error } // Issuer is a type that can issue certificates. type Issuer interface { // Issue obtains a certificate for the given CSR. It // must honor context cancellation if it is long-running. // It can also use the context to find out if the current // call is part of a retry, via AttemptsCtxKey. Issue(ctx context.Context, request *x509.CertificateRequest) (*IssuedCertificate, error) // IssuerKey must return a string that uniquely identifies // this particular configuration of the Issuer such that // any certificates obtained by this Issuer will be treated // as identical if they have the same SANs. // // Certificates obtained from Issuers with the same IssuerKey // will overwrite others with the same SANs. For example, an // Issuer might be able to obtain certificates from different // CAs, say A and B. It is likely that the CAs have different // use cases and purposes (e.g. testing and production), so // their respective certificates should not overwrite eaach // other. IssuerKey() string } // Revoker can revoke certificates. type Revoker interface { Revoke(ctx context.Context, cert CertificateResource) error } // KeyGenerator can generate a private key. type KeyGenerator interface { // GenerateKey generates a private key. The returned // PrivateKey must be able to expose its associated // public key. GenerateKey() (crypto.PrivateKey, error) } // IssuedCertificate represents a certificate that was just issued. type IssuedCertificate struct { // The PEM-encoding of DER-encoded ASN.1 data. Certificate []byte // Any extra information to serialize alongside the // certificate in storage. Metadata interface{} } // CertificateResource associates a certificate with its private // key and other useful information, for use in maintaining the // certificate. type CertificateResource struct { // The list of names on the certificate; // for convenience only. SANs []string `json:"sans,omitempty"` // The PEM-encoding of DER-encoded ASN.1 data // for the cert or chain. CertificatePEM []byte `json:"-"` // The PEM-encoding of the certificate's private key. PrivateKeyPEM []byte `json:"-"` // Any extra information associated with the certificate, // usually provided by the issuer implementation. IssuerData interface{} `json:"issuer_data,omitempty"` } // NamesKey returns the list of SANs as a single string, // truncated to some ridiculously long size limit. It // can act as a key for the set of names on the resource. func (cr *CertificateResource) NamesKey() string { sort.Strings(cr.SANs) result := strings.Join(cr.SANs, ",") if len(result) > 1024 { const trunc = "_trunc" result = result[:1024-len(trunc)] + trunc } return result } // Default contains the package defaults for the // various Config fields. This is used as a template // when creating your own Configs with New(), and it // is also used as the Config by all the high-level // functions in this package. // // The fields of this value will be used for Config // fields which are unset. Feel free to modify these // defaults, but do not use this Config by itself: it // is only a template. Valid configurations can be // obtained by calling New() (if you have your own // certificate cache) or NewDefault() (if you only // need a single config and want to use the default // cache). This is the only Config which can access // the default certificate cache. var Default = Config{ RenewalWindowRatio: DefaultRenewalWindowRatio, Storage: defaultFileStorage, KeySource: DefaultKeyGenerator, } const ( // HTTPChallengePort is the officially-designated port for // the HTTP challenge according to the ACME spec. HTTPChallengePort = 80 // TLSALPNChallengePort is the officially-designated port for // the TLS-ALPN challenge according to the ACME spec. TLSALPNChallengePort = 443 ) // Port variables must remain their defaults unless you // forward packets from the defaults to whatever these // are set to; otherwise ACME challenges will fail. var ( // HTTPPort is the port on which to serve HTTP // and, by extension, the HTTP challenge (unless // Default.AltHTTPPort is set). HTTPPort = 80 // HTTPSPort is the port on which to serve HTTPS // and, by extension, the TLS-ALPN challenge // (unless Default.AltTLSALPNPort is set). HTTPSPort = 443 ) // Variables for conveniently serving HTTPS. var ( httpLn, httpsLn net.Listener lnMu sync.Mutex httpWg sync.WaitGroup )