mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 20:11:19 -04:00
Automatically fetch certificates from Let's Encrypt.
This removes the --tls-cert and --tls-keys options and replaces them with --acme-hostname and (optional) --acme-email. It uses https://godoc.org/golang.org/x/crypto/acme/autocert, which is kind of a successor to https://godoc.org/rsc.io/letsencrypt. The autocert package only works when the listener runs on port 443. For that reason, if TOR_PT_SERVER_BINDADDR asks for a port other than 443, the program will open an *additional* listening port on 443. If there is an error opening the listener, it is reported through an SMETHOD-ERROR for the requested address. The inspiration for this code came from George Tankersley's patch for meek-server: https://bugs.torproject.org/18655#comment:8 https://github.com/gtank/meek/tree/letsencrypt
This commit is contained in:
parent
af70d49e96
commit
61310600c3
2 changed files with 50 additions and 24 deletions
|
@ -19,12 +19,14 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||
"git.torproject.org/pluggable-transports/websocket.git/websocket"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
const ptMethodName = "snowflake"
|
||||
|
@ -150,7 +152,7 @@ func webSocketHandler(ws *websocket.WebSocket) {
|
|||
proxy(or, &conn)
|
||||
}
|
||||
|
||||
func listenTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
|
||||
func listenTLS(network string, addr *net.TCPAddr, m *autocert.Manager) (net.Listener, error) {
|
||||
// This is cribbed from the source of net/http.Server.ListenAndServeTLS.
|
||||
// We have to separate the Listen and Serve parts because we need to
|
||||
// report the listening address before entering Serve (which is an
|
||||
|
@ -158,13 +160,7 @@ func listenTLS(network string, addr *net.TCPAddr, certFilename, keyFilename stri
|
|||
// https://groups.google.com/d/msg/Golang-nuts/3F1VRCCENp8/3hcayZiwYM8J
|
||||
config := &tls.Config{}
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFilename, keyFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.GetCertificate = m.GetCertificate
|
||||
|
||||
conn, err := net.ListenTCP(network, addr)
|
||||
if err != nil {
|
||||
|
@ -190,8 +186,8 @@ func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
|
|||
return startServer(ln)
|
||||
}
|
||||
|
||||
func startListenerTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
|
||||
ln, err := listenTLS(network, addr, certFilename, keyFilename)
|
||||
func startListenerTLS(network string, addr *net.TCPAddr, m *autocert.Manager) (net.Listener, error) {
|
||||
ln, err := listenTLS(network, addr, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -217,14 +213,13 @@ func startServer(ln net.Listener) (net.Listener, error) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
var acmeHostnamesCommas string
|
||||
var disableTLS bool
|
||||
var certFilename, keyFilename string
|
||||
var logFilename string
|
||||
|
||||
flag.Usage = usage
|
||||
flag.StringVar(&acmeHostnamesCommas, "acme-hostnames", "", "comma-separated hostnames for TLS certificate")
|
||||
flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
|
||||
flag.StringVar(&certFilename, "cert", "", "TLS certificate file (required without --disable-tls)")
|
||||
flag.StringVar(&keyFilename, "key", "", "TLS private key file (required without --disable-tls)")
|
||||
flag.StringVar(&logFilename, "log", "", "log file to write to")
|
||||
flag.Parse()
|
||||
|
||||
|
@ -237,15 +232,10 @@ func main() {
|
|||
log.SetOutput(f)
|
||||
}
|
||||
|
||||
if disableTLS {
|
||||
if certFilename != "" || keyFilename != "" {
|
||||
log.Fatalf("the --cert and --key options are not allowed with --disable-tls")
|
||||
}
|
||||
} else {
|
||||
if certFilename == "" || keyFilename == "" {
|
||||
log.Fatalf("the --cert and --key options are required")
|
||||
}
|
||||
if !disableTLS && acmeHostnamesCommas == "" {
|
||||
log.Fatal("the --acme-hostnames option is required")
|
||||
}
|
||||
acmeHostnames := strings.Split(acmeHostnamesCommas, ",")
|
||||
|
||||
log.Printf("starting")
|
||||
var err error
|
||||
|
@ -254,6 +244,28 @@ func main() {
|
|||
log.Fatalf("error in setup: %s", err)
|
||||
}
|
||||
|
||||
if !disableTLS {
|
||||
log.Printf("ACME hostnames: %q", acmeHostnames)
|
||||
}
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(acmeHostnames...),
|
||||
}
|
||||
|
||||
// The ACME responder only works when it is running on port 443. In case
|
||||
// there is not already going to be a TLS listener on port 443, we need
|
||||
// to open an additional one. The port is actually opened in the loop
|
||||
// below, so that any errors can be reported in the SMETHOD-ERROR of
|
||||
// another bindaddr.
|
||||
// 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)
|
||||
for _, bindaddr := range ptInfo.Bindaddrs {
|
||||
if bindaddr.MethodName != ptMethodName {
|
||||
|
@ -261,6 +273,20 @@ func main() {
|
|||
continue
|
||||
}
|
||||
|
||||
if need443Listener {
|
||||
addr := *bindaddr.Addr
|
||||
addr.Port = 443
|
||||
log.Printf("opening additional ACME listener on %s", addr.String())
|
||||
ln443, err := startListenerTLS("tcp", &addr, &certManager)
|
||||
if err != nil {
|
||||
log.Printf("error opening ACME listener: %s", err)
|
||||
pt.SmethodError(bindaddr.MethodName, "ACME listener: "+err.Error())
|
||||
continue
|
||||
}
|
||||
listeners = append(listeners, ln443)
|
||||
need443Listener = false
|
||||
}
|
||||
|
||||
var ln net.Listener
|
||||
args := pt.Args{}
|
||||
if disableTLS {
|
||||
|
@ -268,7 +294,7 @@ func main() {
|
|||
ln, err = startListener("tcp", bindaddr.Addr)
|
||||
} else {
|
||||
args.Add("tls", "yes")
|
||||
ln, err = startListenerTLS("tcp", bindaddr.Addr, certFilename, keyFilename)
|
||||
ln, err = startListenerTLS("tcp", bindaddr.Addr, &certManager)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("error opening listener: %s", err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue