Commit 2d89aa0b authored by David Fifield's avatar David Fifield
Browse files

autocert (Let's Encrypt) for broker.

Replaces --cert and --key with --acme-hostnames and --acme-email.
parent 19666121
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -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.
+33 −35
Original line number Diff line number Diff line
@@ -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)
	}
}