Use Manager.HTTPHandler for automatic TLS support in the server.

As with commit fcc274ac68 for the broker,
we need to start using the HTTP-01 challenge type in the Snowflake
websocket server transport plugin.
https://bugs.torproject.org/25346
This commit is contained in:
David Fifield 2018-03-05 15:25:48 -08:00
parent c62111c507
commit d0686b1c8d
2 changed files with 24 additions and 30 deletions

View file

@ -7,10 +7,10 @@ and the proxy connects to the server (this program) using WebSocket.
# Setup # Setup
The server needs to be able to listen on port 443 The server needs to be able to listen on port 80
in order to generate its TLS certificates. in order to generate its TLS certificates.
On Linux, use the `setcap` program to enable On Linux, use the `setcap` program to enable
the server to listen on port 443 without running as root: the server to listen on port 80 without running as root:
``` ```
setcap 'cap_net_bind_service=+ep' /usr/local/bin/snowflake-server setcap 'cap_net_bind_service=+ep' /usr/local/bin/snowflake-server
``` ```
@ -48,12 +48,11 @@ The server will cache TLS certificate data in the directory
`pt_state/snowflake-certificate-cache` inside the tor state directory. `pt_state/snowflake-certificate-cache` inside the tor state directory.
In order to fetch certificates automatically, In order to fetch certificates automatically,
the server needs to listen on port 443. the server needs to listen on port 80,
in addition to whatever ports it is listening on
for WebSocket connections.
This is a requirement of the ACME protocol used by Let's Encrypt. This is a requirement of the ACME protocol used by Let's Encrypt.
If your `ServerTransportListenAddr` is not on port 443, The program will exit if it can't bind to port 80.
the server will open an listener on port 443 in addition
to the port you requested.
The program will exit if it can't bind to port 443.
On Linux, you can use the `setcap` program, On Linux, you can use the `setcap` program,
part of libcap2, to enable the server to bind to low-numbered ports part of libcap2, to enable the server to bind to low-numbered ports
without having to run as root: without having to run as root:

View file

@ -41,8 +41,8 @@ func usage() {
WebSocket server pluggable transport for Snowflake. Works only as a managed WebSocket server pluggable transport for Snowflake. Works only as a managed
proxy. Uses TLS with ACME (Let's Encrypt) by default. Set the certificate proxy. Uses TLS with ACME (Let's Encrypt) by default. Set the certificate
hostnames with the --acme-hostnames option. Use ServerTransportListenAddr in hostnames with the --acme-hostnames option. Use ServerTransportListenAddr in
torrc to choose the listening port. When using TLS, if the port is not 443, this torrc to choose the listening port. When using TLS, this program will open an
program will open an additional listening port on 443 to work with ACME. additional HTTP listener on port 80 to work with ACME.
`, os.Args[0]) `, os.Args[0])
flag.PrintDefaults() flag.PrintDefaults()
@ -297,19 +297,11 @@ func main() {
} }
} }
// The ACME responder only works when it is running on port 443. In case // The ACME HTTP-01 responder only works when it is running on port 80.
// there is not already going to be a TLS listener on port 443, we need // We actually open the port in the loop below, so that any errors can
// to open an additional one. The port is actually opened in the loop // be reported in the SMETHOD-ERROR of some bindaddr.
// below, so that any errors can be reported in the SMETHOD-ERROR of // https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http-challenge
// another bindaddr. needHTTP01Listener := !disableTLS
// https://letsencrypt.github.io/acme-spec/#domain-validation-with-server-name-indication-dvsni
need443Listener := !disableTLS
for _, bindaddr := range ptInfo.Bindaddrs {
if !disableTLS && bindaddr.Addr.Port == 443 {
need443Listener = false
break
}
}
listeners := make([]net.Listener, 0) listeners := make([]net.Listener, 0)
for _, bindaddr := range ptInfo.Bindaddrs { for _, bindaddr := range ptInfo.Bindaddrs {
@ -318,18 +310,21 @@ func main() {
continue continue
} }
if need443Listener { if needHTTP01Listener {
addr := *bindaddr.Addr addr := *bindaddr.Addr
addr.Port = 443 addr.Port = 80
log.Printf("opening additional ACME listener on %s", addr.String()) log.Printf("Starting HTTP-01 ACME listener")
ln443, err := startListenerTLS("tcp", &addr, certManager) lnHTTP01, err := net.ListenTCP("tcp", &addr)
if err != nil { if err != nil {
log.Printf("error opening ACME listener: %s", err) log.Printf("error opening HTTP-01 ACME listener: %s", err)
pt.SmethodError(bindaddr.MethodName, "ACME listener: "+err.Error()) pt.SmethodError(bindaddr.MethodName, "HTTP-01 ACME listener: "+err.Error())
continue continue
} }
listeners = append(listeners, ln443) go func() {
need443Listener = false log.Fatal(http.Serve(lnHTTP01, certManager.HTTPHandler(nil)))
}()
listeners = append(listeners, lnHTTP01)
needHTTP01Listener = false
} }
var ln net.Listener var ln net.Listener