commit cb8314e0b3a94b9d3c8ce2368ec5b461fb61d117 Author: David Fifield david@bamsoftware.com Date: Tue Mar 6 18:39:08 2018 -0800
Use autocert Manager.HTTPHandler (ACME HTTP-01 challenge)
The former TLS-SNI challenge type is gone. https://letsencrypt.status.io/pages/incident/55957a99e800baa4470002da/5a5577...
The new HTTP-01 challenge type requires a listener on port 80. The former TLS-SNI challenge just piggybacked on an additional HTTPS listener on port 443, if necessary. The new listener on port 80 just handles ACME business and nothing else.
https://bugs.torproject.org/24928 --- doc/meek-server.1.txt | 16 +++++++++------- meek-server/README | 6 ++++-- meek-server/meek-server.go | 48 +++++++++++++++++++++++++++------------------- 3 files changed, 41 insertions(+), 29 deletions(-)
diff --git a/doc/meek-server.1.txt b/doc/meek-server.1.txt index 97bfe5d..6d705e1 100644 --- a/doc/meek-server.1.txt +++ b/doc/meek-server.1.txt @@ -23,8 +23,8 @@ up certificates:
* **--acme-hostnames**=__HOSTNAME__ (with optional **--acme-email**=__EMAIL__) will automatically get certificates for - __HOSTNAME__ using Let's Encrypt. This only works when meek-server is - running on port 443. + __HOSTNAME__ using Let's Encrypt. When you use this option, + meek-server will need to be able to listen on port 80. * **--cert**=__FILENAME__ and **--key**=__FILENAME__ allow use to use your own externally acquired certificate.
@@ -42,7 +42,7 @@ ServerTransportListenAddr meek 0.0.0.0:8443 ServerTransportPlugin meek exec ./meek-server 8443 --cert cert.pem --key key.pem --log meek-server.log ----
-To listen on port 443 without needed to run as root, on Linux, +To listen on ports 80 and 443 without needed to run as root, on Linux, you can use the `setcap` program, part of libcap2: ---- setcap 'cap_net_bind_service=+ep' /usr/local/bin/meek-server @@ -56,10 +56,12 @@ OPTIONS
**--acme-hostnames**=__HOSTNAME__[,__HOSTNAME__]...:: Comma-separated list of hostnames to honor when getting automatic - certificates from Let's Encrypt. meek-server has to be running on - port 443 in order for the **--acme-hostnames** option to work. The - certificates will be cached in the pt_state/meek-certificate-cache - directory inside tor state directory. + certificates from Let's Encrypt. meek-server will open a special + listener on port 80 in order to handle ACME messages; this listener + is separate from the one specified by `ServerTransportListenAddr`. + The certificates will be cached in the + pt_state/meek-certificate-cache directory inside tor state + directory.
**--cert**=__FILENAME__:: Name of a PEM-encoded TLS certificate file. Required unless diff --git a/meek-server/README b/meek-server/README index 867816c..c4f1883 100644 --- a/meek-server/README +++ b/meek-server/README @@ -1,7 +1,9 @@ # How to run a meek-server (meek bridge):
You need a server with a DNS name pointing to it. -You need to be able to run a service on port 443. +You need to be able to run a service on ports 443 and 80. +Port 443 is for receiving meek-tunneled HTTPS from the CDN; +port 80 is for automatic certificates from Let's Encrypt.
Let's say the server's DNS name is meek.example.com.
@@ -10,7 +12,7 @@ Let's say the server's DNS name is meek.example.com. cd meek-server go build
-- Install meek-server under /usr/local/bin and give it permission to bind to port 443. +- Install meek-server under /usr/local/bin and give it permission to bind to ports 443 and 80.
cp meek-server /usr/local/bin setcap 'cap_net_bind_service=+ep' /usr/local/bin/meek-server diff --git a/meek-server/meek-server.go b/meek-server/meek-server.go index 73f3f7b..eef3dee 100644 --- a/meek-server/meek-server.go +++ b/meek-server/meek-server.go @@ -13,9 +13,10 @@ // ServerTransportPlugin meek exec ./meek-server --disable-tls --log meek-server.log // // The server runs in HTTPS mode by default, getting certificates from Let's -// Encrypt automatically. The server must be listening on port 443 for the -// automatic certificates to work. If you have your own certificate, use the -// --cert and --key options. Use --disable-tls option to run with plain HTTP. +// Encrypt automatically. The server opens an auxiliary ACME listener on port 80 +// in order for the automatic certificates to work. If you have your own +// certificate, use the --cert and --key options. Use --disable-tls option to +// run with plain HTTP. package main
import ( @@ -404,8 +405,9 @@ func main() { // --cert and --key together // --disable-tls // The outputs of this block of code are the disableTLS, - // need443Listener, and getCertificate variables. - var need443Listener = false + // needHTTP01Listener, certManager, and getCertificate variables. + var needHTTP01Listener = false + var certManager *autocert.Manager var getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error) if disableTLS { if acmeEmail != "" || acmeHostnamesCommas != "" || certFilename != "" || keyFilename != "" { @@ -424,9 +426,10 @@ func main() { acmeHostnames := strings.Split(acmeHostnamesCommas, ",") log.Printf("ACME hostnames: %q", acmeHostnames)
- // The ACME responder only works when it is running on port 443. - // https://letsencrypt.github.io/acme-spec/#domain-validation-with-server-name-... - need443Listener = true + // The ACME HTTP-01 responder only works when it is running on + // port 80. + // https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#htt... + needHTTP01Listener = true
var cache autocert.Cache cacheDir, err := getCertificateCacheDir() @@ -437,7 +440,7 @@ func main() { log.Printf("disabling ACME certificate cache: %s", err) }
- certManager := &autocert.Manager{ + certManager = &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(acmeHostnames...), Email: acmeEmail, @@ -450,20 +453,32 @@ func main() {
log.Printf("starting version %s (%s)", programVersion, runtime.Version()) servers := make([]*http.Server, 0) - have443Listener := false for _, bindaddr := range ptInfo.Bindaddrs { if port != 0 { bindaddr.Addr.Port = port } switch bindaddr.MethodName { case ptMethodName: + if needHTTP01Listener { + needHTTP01Listener = false + addr := *bindaddr.Addr + addr.Port = 80 + log.Printf("starting HTTP-01 ACME listener on %s", addr.String()) + lnHTTP01, err := net.ListenTCP("tcp", &addr) + if err != nil { + log.Printf("error opening HTTP-01 ACME listener: %s", err) + pt.SmethodError(bindaddr.MethodName, "HTTP-01 ACME listener: "+err.Error()) + continue + } + go func() { + log.Fatal(http.Serve(lnHTTP01, certManager.HTTPHandler(nil))) + }() + } + var server *http.Server if disableTLS { server, err = startServer(bindaddr.Addr) } else { - if bindaddr.Addr.Port == 443 { - have443Listener = true - } server, err = startServerTLS(bindaddr.Addr, getCertificate) } if err != nil { @@ -478,13 +493,6 @@ func main() { } pt.SmethodsDone()
- // Emit a warning if we're using ACME certificates and don't have a 443 - // listener. Don't quit, in case the user has made other provisions for - // forwarding port 443. - if need443Listener && !have443Listener { - log.Printf("warning: the --acme-hostnames option requires one of the bindaddrs to be on port 443.") - } - var numHandlers int = 0 var sig os.Signal sigChan := make(chan os.Signal, 1)
tor-commits@lists.torproject.org