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
You can run your own Broker on localhost, you'll need to pass a TLS
certificate file using `--cert` option and the corresponding private key
file using `--key` option.
The server uses TLS by default.
There is a `--disable-tls` option for testing purposes,
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
to the client plugin using the `--url $URL` flag.

View file

@ -7,15 +7,17 @@ package main
import (
"container/heap"
"crypto/tls"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"sync"
"strings"
"time"
"golang.org/x/crypto/acme/autocert"
)
const (
@ -230,26 +232,18 @@ func ipHandler(w http.ResponseWriter, r *http.Request) {
}
func main() {
var cert, cert_key, http_port, https_port string
flag.StringVar(&cert, "cert", "", "TLS certificate file")
flag.StringVar(&cert_key, "key", "", "TLS key file")
var acmeEmail string
var acmeHostnamesCommas string
var disableTLS bool
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(&https_port, "tlsPort", "443", "HTTPS port number")
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()
go ctx.Broker()
@ -262,26 +256,30 @@ func main() {
http.Handle("/answer", SnowflakeHandler{ctx, proxyAnswers})
http.Handle("/debug", SnowflakeHandler{ctx, debugHandler})
var wg sync.WaitGroup
wg.Add(2)
var err error
var server http.Server
//Run HTTP server
go func() {
defer wg.Done()
err := http.ListenAndServe(":"+http_port, nil)
if err != nil {
log.Println("ListenAndServe: ", err)
if acmeHostnamesCommas != "" {
acmeHostnames := strings.Split(acmeHostnamesCommas, ",")
log.Printf("ACME hostnames: %q", acmeHostnames)
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(acmeHostnames...),
Email: acmeEmail,
}
}()
//Run HTTPS server
go func() {
defer wg.Done()
err := http.ListenAndServeTLS(":"+https_port, cert, cert_key, nil)
if err != nil {
log.Println("ListenAndServeTLS: ", err)
}
}()
server.Addr = net.JoinHostPort("", https_port)
server.TLSConfig = &tls.Config{GetCertificate: certManager.GetCertificate}
err = server.ListenAndServeTLS("", "")
} else if disableTLS {
server.Addr = net.JoinHostPort("", http_port)
err = server.ListenAndServe()
} else {
log.Fatal("the --acme-hostnames or --disable-tls option is required")
}
wg.Wait()
if err != nil {
log.Fatal(err)
}
}