178 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
package letsencrypt // import "rsc.io/letsencrypt"
 | 
						|
 | 
						|
Package letsencrypt obtains TLS certificates from LetsEncrypt.org.
 | 
						|
 | 
						|
LetsEncrypt.org is a service that issues free SSL/TLS certificates to
 | 
						|
servers that can prove control over the given domain's DNS records or the
 | 
						|
servers pointed at by those records.
 | 
						|
 | 
						|
 | 
						|
Warning
 | 
						|
 | 
						|
Like any other random code you find on the internet, this package should not
 | 
						|
be relied upon in important, production systems without thorough testing to
 | 
						|
ensure that it meets your needs.
 | 
						|
 | 
						|
In the long term you should be using
 | 
						|
https://golang.org/x/crypto/acme/autocert instead of this package. Send
 | 
						|
improvements there, not here.
 | 
						|
 | 
						|
This is a package that I wrote for my own personal web sites (swtch.com,
 | 
						|
rsc.io) in a hurry when my paid-for SSL certificate was expiring. It has no
 | 
						|
tests, has barely been used, and there is some anecdotal evidence that it
 | 
						|
does not properly renew certificates in a timely fashion, so servers that
 | 
						|
run for more than 3 months may run into trouble. I don't run this code
 | 
						|
anymore: to simplify maintenance, I moved the sites off of Ubuntu VMs and
 | 
						|
onto Google App Engine, configured with inexpensive long-term certificates
 | 
						|
purchased from cheapsslsecurity.com.
 | 
						|
 | 
						|
This package was interesting primarily as an example of how simple the API
 | 
						|
for using LetsEncrypt.org could be made, in contrast to the low-level
 | 
						|
implementations that existed at the time. In that respect, it helped inform
 | 
						|
the design of the golang.org/x/crypto/acme/autocert package.
 | 
						|
 | 
						|
 | 
						|
Quick Start
 | 
						|
 | 
						|
A complete HTTP/HTTPS web server using TLS certificates from
 | 
						|
LetsEncrypt.org, redirecting all HTTP access to HTTPS, and maintaining TLS
 | 
						|
certificates in a file letsencrypt.cache across server restarts.
 | 
						|
 | 
						|
    package main
 | 
						|
 | 
						|
    import (
 | 
						|
    	"fmt"
 | 
						|
    	"log"
 | 
						|
    	"net/http"
 | 
						|
    	"rsc.io/letsencrypt"
 | 
						|
    )
 | 
						|
 | 
						|
    func main() {
 | 
						|
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 | 
						|
    		fmt.Fprintf(w, "Hello, TLS!\n")
 | 
						|
    	})
 | 
						|
    	var m letsencrypt.Manager
 | 
						|
    	if err := m.CacheFile("letsencrypt.cache"); err != nil {
 | 
						|
    		log.Fatal(err)
 | 
						|
    	}
 | 
						|
    	log.Fatal(m.Serve())
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
Overview
 | 
						|
 | 
						|
The fundamental type in this package is the Manager, which manages obtaining
 | 
						|
and refreshing a collection of TLS certificates, typically for use by an
 | 
						|
HTTPS server. The example above shows the most basic use of a Manager. The
 | 
						|
use can be customized by calling additional methods of the Manager.
 | 
						|
 | 
						|
 | 
						|
Registration
 | 
						|
 | 
						|
A Manager m registers anonymously with LetsEncrypt.org, including agreeing
 | 
						|
to the letsencrypt.org terms of service, the first time it needs to obtain a
 | 
						|
certificate. To register with a particular email address and with the option
 | 
						|
of a prompt for agreement with the terms of service, call m.Register.
 | 
						|
 | 
						|
 | 
						|
GetCertificate
 | 
						|
 | 
						|
The Manager's GetCertificate method returns certificates from the Manager's
 | 
						|
cache, filling the cache by requesting certificates from LetsEncrypt.org. In
 | 
						|
this way, a server with a tls.Config.GetCertificate set to m.GetCertificate
 | 
						|
will demand load a certificate for any host name it serves. To force loading
 | 
						|
of certificates ahead of time, install m.GetCertificate as before but then
 | 
						|
call m.Cert for each host name.
 | 
						|
 | 
						|
A Manager can only obtain a certificate for a given host name if it can
 | 
						|
prove control of that host name to LetsEncrypt.org. By default it proves
 | 
						|
control by answering an HTTPS-based challenge: when the LetsEncrypt.org
 | 
						|
servers connect to the named host on port 443 (HTTPS), the TLS SNI handshake
 | 
						|
must use m.GetCertificate to obtain a per-host certificate. The most common
 | 
						|
way to satisfy this requirement is for the host name to resolve to the IP
 | 
						|
address of a (single) computer running m.ServeHTTPS, or at least running a
 | 
						|
Go TLS server with tls.Config.GetCertificate set to m.GetCertificate.
 | 
						|
However, other configurations are possible. For example, a group of machines
 | 
						|
could use an implementation of tls.Config.GetCertificate that cached
 | 
						|
certificates but handled cache misses by making RPCs to a Manager m on an
 | 
						|
elected leader machine.
 | 
						|
 | 
						|
In typical usage, then, the setting of tls.Config.GetCertificate to
 | 
						|
m.GetCertificate serves two purposes: it provides certificates to the TLS
 | 
						|
server for ordinary serving, and it also answers challenges to prove
 | 
						|
ownership of the domains in order to obtain those certificates.
 | 
						|
 | 
						|
To force the loading of a certificate for a given host into the Manager's
 | 
						|
cache, use m.Cert.
 | 
						|
 | 
						|
 | 
						|
Persistent Storage
 | 
						|
 | 
						|
If a server always starts with a zero Manager m, the server effectively
 | 
						|
fetches a new certificate for each of its host name from LetsEncrypt.org on
 | 
						|
each restart. This is unfortunate both because the server cannot start if
 | 
						|
LetsEncrypt.org is unavailable and because LetsEncrypt.org limits how often
 | 
						|
it will issue a certificate for a given host name (at time of writing, the
 | 
						|
limit is 5 per week for a given host name). To save server state proactively
 | 
						|
to a cache file and to reload the server state from that same file when
 | 
						|
creating a new manager, call m.CacheFile with the name of the file to use.
 | 
						|
 | 
						|
For alternate storage uses, m.Marshal returns the current state of the
 | 
						|
Manager as an opaque string, m.Unmarshal sets the state of the Manager using
 | 
						|
a string previously returned by m.Marshal (usually a different m), and
 | 
						|
m.Watch returns a channel that receives notifications about state changes.
 | 
						|
 | 
						|
 | 
						|
Limits
 | 
						|
 | 
						|
To avoid hitting basic rate limits on LetsEncrypt.org, a given Manager
 | 
						|
limits all its interactions to at most one request every minute, with an
 | 
						|
initial allowed burst of 20 requests.
 | 
						|
 | 
						|
By default, if GetCertificate is asked for a certificate it does not have,
 | 
						|
it will in turn ask LetsEncrypt.org for that certificate. This opens a
 | 
						|
potential attack where attackers connect to a server by IP address and
 | 
						|
pretend to be asking for an incorrect host name. Then GetCertificate will
 | 
						|
attempt to obtain a certificate for that host, incorrectly, eventually
 | 
						|
hitting LetsEncrypt.org's rate limit for certificate requests and making it
 | 
						|
impossible to obtain actual certificates. Because servers hold certificates
 | 
						|
for months at a time, however, an attack would need to be sustained over a
 | 
						|
time period of at least a month in order to cause real problems.
 | 
						|
 | 
						|
To mitigate this kind of attack, a given Manager limits itself to an average
 | 
						|
of one certificate request for a new host every three hours, with an initial
 | 
						|
allowed burst of up to 20 requests. Long-running servers will therefore stay
 | 
						|
within the LetsEncrypt.org limit of 300 failed requests per month.
 | 
						|
Certificate refreshes are not subject to this limit.
 | 
						|
 | 
						|
To eliminate the attack entirely, call m.SetHosts to enumerate the exact set
 | 
						|
of hosts that are allowed in certificate requests.
 | 
						|
 | 
						|
 | 
						|
Web Servers
 | 
						|
 | 
						|
The basic requirement for use of a Manager is that there be an HTTPS server
 | 
						|
running on port 443 and calling m.GetCertificate to obtain TLS certificates.
 | 
						|
Using standard primitives, the way to do this is:
 | 
						|
 | 
						|
    srv := &http.Server{
 | 
						|
    	Addr: ":https",
 | 
						|
    	TLSConfig: &tls.Config{
 | 
						|
    		GetCertificate: m.GetCertificate,
 | 
						|
    	},
 | 
						|
    }
 | 
						|
    srv.ListenAndServeTLS("", "")
 | 
						|
 | 
						|
However, this pattern of serving HTTPS with demand-loaded TLS certificates
 | 
						|
comes up enough to wrap into a single method m.ServeHTTPS.
 | 
						|
 | 
						|
Similarly, many HTTPS servers prefer to redirect HTTP clients to the HTTPS
 | 
						|
URLs. That functionality is provided by RedirectHTTP.
 | 
						|
 | 
						|
The combination of serving HTTPS with demand-loaded TLS certificates and
 | 
						|
serving HTTPS redirects to HTTP clients is provided by m.Serve, as used in
 | 
						|
the original example above.
 | 
						|
 | 
						|
func RedirectHTTP(w http.ResponseWriter, r *http.Request)
 | 
						|
type Manager struct { ... }
 |