From af8e19aeaceb15b4021fd5e6edf49675aecf01ff Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Thu, 16 Mar 2017 05:51:42 +0000 Subject: [PATCH] add ACME certificate support --- .gitignore | 2 ++ lock.json | 33 +++++++++++------ main.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 117 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 48b8bf90..628b68fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ vendor/ +SCEPCACert.pem +mdm.db diff --git a/lock.json b/lock.json index 7f291761..1a18e29d 100644 --- a/lock.json +++ b/lock.json @@ -1,5 +1,5 @@ { - "memo": "d051154535b215b85193b9d45b252749d3ee8caa79ccd69e88feedc5490e7416", + "memo": "8ec26b54331ccbcbe914255b2e955a9f4aa1769f53accd011025f5ae2e986e4c", "projects": [ { "name": "github.com/RobotsAndPencils/buford", @@ -7,6 +7,7 @@ "revision": "d6d71aff1447ced56016d09a5d74f4ea488a2d75", "packages": [ "payload", + "payload/badge", "push" ] }, @@ -25,6 +26,7 @@ "packages": [ "endpoint", "log", + "metrics", "transport/http" ] }, @@ -46,8 +48,8 @@ }, { "name": "github.com/gogo/protobuf", - "version": "v0.3", - "revision": "909568be09de550ed094403c2bf8a261b5bb730a", + "version": "v0.4", + "revision": "100ba4e885062801d56799d78530b73b178a78f3", "packages": [ "proto" ] @@ -55,7 +57,7 @@ { "name": "github.com/golang/protobuf", "branch": "master", - "revision": "8ee79997227bf9b34611aee7946ae64735e6fd93", + "revision": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317", "packages": [ "proto" ] @@ -79,7 +81,7 @@ { "name": "github.com/groob/plist", "branch": "master", - "revision": "a4f34fbabd9152a2140daa4a93606ee2ce868b98", + "revision": "43f06cf03cbc5bd95dd0ba102ac68efab25e1c65", "packages": [ "." ] @@ -103,9 +105,12 @@ { "name": "github.com/micromdm/scep", "branch": "master", - "revision": "083238ccae1d8bf63e82cf4f6d1c198feeb5294a", + "revision": "1b9e502944245d1d526ad49d39e7d7d3afd5e927", "packages": [ + "depot", "depot/bolt", + "scep", + "scep/internal/pkcs7", "server" ] }, @@ -128,25 +133,31 @@ { "name": "golang.org/x/crypto", "branch": "master", - "revision": "854ae91cdcbf914b499b1d7641d07859f3653481", + "revision": "728b753d0135da6801d45a38e6f43ff55779c5c2", "packages": [ - "pkcs12" + "acme", + "acme/autocert", + "pkcs12", + "pkcs12/internal/rc2" ] }, { "name": "golang.org/x/net", "branch": "master", - "revision": "f2499483f923065a842d38eb4c7f1927e6fc6e6d", + "revision": "a6577fac2d73be281a500b310739095313165611", "packages": [ "context", "context/ctxhttp", - "http2" + "http2", + "http2/hpack", + "idna", + "lex/httplex" ] }, { "name": "golang.org/x/sys", "branch": "master", - "revision": "d75a52659825e75fff6158388dddc6a5b04f9ba5", + "revision": "99f16d856c9836c42d24e7ab64ea72916925fa97", "packages": [ "unix" ] diff --git a/main.go b/main.go index c2aa271b..4cf2c8ce 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "syscall" "time" + "golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/pkcs12" "github.com/RobotsAndPencils/buford/push" @@ -48,10 +49,13 @@ func init() { func main() { var ( - flServerURL = flag.String("server.url", "", "public HTTPS url of your server") - flAPNSCertPath = flag.String("apns.certificate", "mdm.p12", "path to APNS certificate") - flAPNSKeyPass = flag.String("apns.password", "secret", "password for your APNS cert file.") - flAPNSKeyPath = flag.String("apns.key", "", "path to key file if using .pem push cert") + flServerURL = flag.String("server-url", "", "public HTTPS url of your server") + flAPNSCertPath = flag.String("apns-certificate", "mdm.p12", "path to APNS certificate") + flAPNSKeyPass = flag.String("apns-password", "secret", "password for your APNS cert file.") + flAPNSKeyPath = flag.String("apns-key", "", "path to key file if using .pem push cert") + flTLS = flag.Bool("tls", true, "use https") + flTLSCert = flag.String("tls-cert", "", "path to TLS certificate") + flTLSKey = flag.String("tls-key", "", "path to TLS private key") ) flag.Parse() @@ -120,6 +124,21 @@ func main() { r.Handle("/scep", scepHandler) r.Handle("/push/{udid}", pushHandlers.PushHandler) r.Handle("/v1/commands", commandHandlers.NewCommandHandler).Methods("POST") + srv := &http.Server{ + Addr: ":https", + Handler: r, + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + ReadHeaderTimeout: 10 * time.Second, + IdleTimeout: 10 * time.Minute, + MaxHeaderBytes: 1 << 18, // 0.25 MB + TLSConfig: tlsConfig(), + } + + srvURL, err := url.Parse(sm.ServerPublicURL) + if err != nil { + stdlog.Fatal(err) + } errs := make(chan error, 2) go func() { @@ -129,15 +148,81 @@ func main() { }() go func() { - var httpAddr = "0.0.0.0:8080" logger := log.NewContext(logger).With("transport", "HTTP") - logger.Log("addr", httpAddr) - errs <- http.ListenAndServe( - httpAddr, r) + if !*flTLS { + var httpAddr = "0.0.0.0:8080" + logger.Log("addr", httpAddr) + errs <- http.ListenAndServe(httpAddr, r) + return + } + + tlsFromFile := (*flTLSCert != "" && *flTLSKey != "") + if tlsFromFile { + logger.Log("addr", srv.Addr) + errs <- serveTLS(srv, *flTLSCert, *flTLSKey) + return + } else { + logger.Log("addr", srv.Addr) + errs <- serveACME(srv, srvURL.Hostname()) + return + } }() + mainLogger.Log("terminated", <-errs) } +func serveTLS(server *http.Server, certPath, keyPath string) error { + redirectTLS() + err := server.ListenAndServeTLS(certPath, keyPath) + return err +} + +func serveACME(server *http.Server, domain string) error { + m := autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(domain), + Cache: autocert.DirCache("/var/db/le-certificates"), + } + server.TLSConfig.GetCertificate = m.GetCertificate + redirectTLS() + err := server.ListenAndServeTLS("", "") + return err +} + +// redirects port 80 to port 443 +func redirectTLS() { + srv := &http.Server{ + ReadTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Connection", "close") + url := "https://" + req.Host + req.URL.String() + http.Redirect(w, req, url, http.StatusMovedPermanently) + }), + } + go func() { stdlog.Fatal(srv.ListenAndServe()) }() +} + +func tlsConfig() *tls.Config { + cfg := &tls.Config{ + PreferServerCipherSuites: true, + CurvePreferences: []tls.CurveID{ + tls.CurveP256, + tls.X25519, + }, + MinVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + } + return cfg +} + type config struct { pubclient *pubsub.Inmem db *bolt.DB