b22c6b7a4e
Adds READMEs and enforces vendor is done at repository root Signed-off-by: Derek McGowan <derek@mcgstyle.net>
177 lines
7.3 KiB
Text
177 lines
7.3 KiB
Text
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 { ... }
|