commit cb8314e0b3a94b9d3c8ce2368ec5b461fb61d117
Author: David Fifield <da...@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/5a55777ed9a9c1024c00b241
    
    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-indication-dvsni
-               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#http-challenge
+               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 mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to