autocert (Let's Encrypt) for broker.

Replaces --cert and --key with --acme-hostnames and --acme-email.
This commit is contained in:
David Fifield 2017-07-14 20:25:46 -07:00
parent 1966612113
commit 2d89aa0b7b
2 changed files with 56 additions and 38 deletions

View file

@ -22,9 +22,29 @@ The Broker expects:
### Running your own ### Running your own
You can run your own Broker on localhost, you'll need to pass a TLS The server uses TLS by default.
certificate file using `--cert` option and the corresponding private key There is a `--disable-tls` option for testing purposes,
file using `--key` option. but you should use TLS in production.
The server automatically fetches certificates
from [Let's Encrypt](https://en.wikipedia.org/wiki/Let's_Encrypt) as needed.
Use the `--acme-hostnames` option to tell the server
what hostnames it may request certificates for.
You can optionally provide a contact email address,
using the `--acme-email` option,
so that Let's Encrypt can inform you of any problems.
In order to fetch certificates automatically,
the server needs to be listening on port 443 (the default).
On Linux, you can use the `setcap` program,
part of libcap2, to enable the broker to bind to low-numbered ports
without having to run as root:
```
setcap 'cap_net_bind_service=+ep' /usr/local/bin/broker
```
You can control the listening port with the --tlsPort
or --webPort options (--webPort is honored only when
also using --disable-tls).
You'll need to provide the URL of the custom broker You'll need to provide the URL of the custom broker
to the client plugin using the `--url $URL` flag. to the client plugin using the `--url $URL` flag.

View file

@ -7,15 +7,17 @@ package main
import ( import (
"container/heap" "container/heap"
"crypto/tls"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
"os" "strings"
"sync"
"time" "time"
"golang.org/x/crypto/acme/autocert"
) )
const ( const (
@ -230,26 +232,18 @@ func ipHandler(w http.ResponseWriter, r *http.Request) {
} }
func main() { func main() {
var cert, cert_key, http_port, https_port string var acmeEmail string
var acmeHostnamesCommas string
flag.StringVar(&cert, "cert", "", "TLS certificate file") var disableTLS bool
flag.StringVar(&cert_key, "key", "", "TLS key file") var http_port, https_port string
flag.StringVar(&acmeEmail, "acme-email", "", "optional contact email for Let's Encrypt notifications")
flag.StringVar(&acmeHostnamesCommas, "acme-hostnames", "", "comma-separated hostnames for TLS certificate")
flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
flag.StringVar(&http_port, "webPort", "80", "HTTP port number") flag.StringVar(&http_port, "webPort", "80", "HTTP port number")
flag.StringVar(&https_port, "tlsPort", "443", "HTTPS port number") flag.StringVar(&https_port, "tlsPort", "443", "HTTPS port number")
flag.Parse() flag.Parse()
if cert == "" || cert_key == "" {
log.Println("Missing options, exiting.")
fmt.Println("Usage:")
flag.PrintDefaults()
os.Exit(1)
}
log.Println("Using cert file:", cert)
log.Println("Using cert key file: ", cert_key)
ctx := NewBrokerContext() ctx := NewBrokerContext()
go ctx.Broker() go ctx.Broker()
@ -262,26 +256,30 @@ func main() {
http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers}) http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers})
http.Handle("/debug", SnowflakeHandler{ctx, debugHandler}) http.Handle("/debug", SnowflakeHandler{ctx, debugHandler})
var wg sync.WaitGroup var err error
wg.Add(2) var server http.Server
//Run HTTP server if acmeHostnamesCommas != "" {
go func() { acmeHostnames := strings.Split(acmeHostnamesCommas, ",")
defer wg.Done() log.Printf("ACME hostnames: %q", acmeHostnames)
err := http.ListenAndServe(":"+http_port, nil)
if err != nil { certManager := autocert.Manager{
log.Println("ListenAndServe: ", err) Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(acmeHostnames...),
Email: acmeEmail,
} }
}()
//Run HTTPS server server.Addr = net.JoinHostPort("", https_port)
go func() { server.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
defer wg.Done() err = server.ListenAndServeTLS("", "")
err := http.ListenAndServeTLS(":"+https_port, cert, cert_key, nil) } else if disableTLS {
if err != nil { server.Addr = net.JoinHostPort("", http_port)
log.Println("ListenAndServeTLS: ", err) err = server.ListenAndServe()
} } else {
}() log.Fatal("the --acme-hostnames or --disable-tls option is required")
}
wg.Wait() if err != nil {
log.Fatal(err)
}
} }