From fe191eb7cc02583d5c92a2517dc3e07866c6bdfe Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Mon, 11 Sep 2017 17:14:12 +0300 Subject: [PATCH] Added micromdm/go4 as a dependency. (#229) Improves http debug logging by also logging the sent response. Converts regular http logs to key-value format. Changes the http server options to httputil.ListenAndServe, which is antended to be a standard ListenAndServe method for all micromdm project. Adds environment variables as configuration options for sensitive values. Closes #196 --- CHANGELOG.md | 5 ++ Gopkg.lock | 46 ++++++----- Makefile | 32 ++++---- cmd/mdmctl/mdmdctl.go | 2 +- main.go | 2 +- serve.go | 177 ++++++++++++------------------------------ version/version.go | 69 ---------------- 7 files changed, 103 insertions(+), 230 deletions(-) delete mode 100644 version/version.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 037977f8..69445fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# TBD + +* Added ENV variables for sensitive flags: `MICROMDM_APNS_KEY_PASSWORD`,`MICROMDM_API_KEY` +* Removed the `-redir-addr` flag. Redirect to HTTPS is only enabled when the 443 port is used. + # v1.1.0 June 05 2017 * Import and sign pkgs, generate appmanifest on import. diff --git a/Gopkg.lock b/Gopkg.lock index 4b6af24e..1d040076 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -10,8 +10,8 @@ [[projects]] name = "github.com/boltdb/bolt" packages = ["."] - revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9" - version = "v1.3.0" + revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8" + version = "v1.3.1" [[projects]] branch = "master" @@ -23,11 +23,11 @@ branch = "master" name = "github.com/garyburd/go-oauth" packages = ["oauth"] - revision = "d9dbc37b1a7acf4923050e614fc7861e3fbf42f9" + revision = "166ce8d672783fbb5a72247c3cf459267717e1ec" [[projects]] name = "github.com/go-kit/kit" - packages = ["endpoint","log","metrics","transport/http"] + packages = ["endpoint","log","log/level","metrics","transport/http"] revision = "fadad6fffe0466b19df9efd9acde5c9a52df5fa4" version = "v0.4.0" @@ -40,8 +40,8 @@ [[projects]] name = "github.com/go-stack/stack" packages = ["."] - revision = "7a2f19628aabfe68f0766b59e74d6315f8347d22" - version = "v1.5.3" + revision = "817915b46b97fd7bb80e8ab6b69f01a53ac3eebf" + version = "v1.6.0" [[projects]] name = "github.com/gogo/protobuf" @@ -53,7 +53,7 @@ branch = "master" name = "github.com/golang/protobuf" packages = ["proto"] - revision = "e325f446bebc2998605911c0a2650d9920361d4a" + revision = "17ce1425424ab154092bbb43af630bd647f3bb0d" [[projects]] name = "github.com/gorilla/context" @@ -61,18 +61,18 @@ revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" version = "v1.1" -[[projects]] - name = "github.com/gorilla/handlers" - packages = ["."] - revision = "a4043c62cc2329bacda331d33fc908ab11ef0ec3" - version = "v1.2.1" - [[projects]] name = "github.com/gorilla/mux" packages = ["."] revision = "bcd8bc72b08df0f70df986b97f95590779502d31" version = "v1.4.0" +[[projects]] + branch = "master" + name = "github.com/groob/finalizer" + packages = [".","logutil"] + revision = "4c2ed49aabdaa038f247d72d183e410757cdbe9c" + [[projects]] branch = "master" name = "github.com/groob/plist" @@ -91,11 +91,17 @@ packages = ["."] revision = "3e565f89e59266df10c92da54118bf13ec312905" +[[projects]] + branch = "master" + name = "github.com/micromdm/go4" + packages = ["httputil","version"] + revision = "486ea5f130c78a9c058374eb159df08296caea9d" + [[projects]] branch = "master" name = "github.com/micromdm/mdm" packages = ["."] - revision = "1b8ad711b42f5510344c657a195d7eede8479728" + revision = "97c19c7edf3498cf43b378042317d9d9caa84dc6" [[projects]] name = "github.com/micromdm/scep" @@ -119,35 +125,35 @@ branch = "master" name = "golang.org/x/crypto" packages = ["acme","acme/autocert","pkcs12","pkcs12/internal/rc2"] - revision = "850760c427c516be930bc91280636328f1a62286" + revision = "9ba3862cf6a5452ae579de98f9364dd2e544844c" [[projects]] branch = "master" name = "golang.org/x/net" packages = ["context","context/ctxhttp","http2","http2/hpack","idna","lex/httplex"] - revision = "dfe83d419c9403b40b19d08cdba2afec27b002f7" + revision = "66aacef3dd8a676686c7ae3716979581e8b03c47" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "0b25a408a50076fbbcae6b7ac0ea5fbb0b085e79" + revision = "a5054c7c1385fd50d9394475365355a87a7873ec" [[projects]] branch = "master" name = "golang.org/x/text" packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] - revision = "3491b61b9edc56653ad4333e605e2908e46a036b" + revision = "bd91bbf73e9a4a801adbfb97133c992678533126" [[projects]] branch = "master" name = "golang.org/x/tools" packages = ["go/ast/astutil","imports"] - revision = "b4e0c5ecfb3125b1fbb235b1d381898d2b2a16d6" + revision = "3b1faeda9afbcba128c2d794b38ffe7982141139" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "9502148927d630aed51089b18e46395f56995ed144a986d44a960379d1bff4b8" + inputs-digest = "6ef27b152096e36c87702ca8c72c8fe0121399d323dc59d8b539dd4bc3ba4e9a" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Makefile b/Makefile index bd090409..4c6f0611 100644 --- a/Makefile +++ b/Makefile @@ -18,13 +18,13 @@ SHELL = /bin/bash BUILD_VERSION = "\ - -X github.com/micromdm/micromdm/version.appName=${APP_NAME} \ - -X github.com/micromdm/micromdm/version.version=${VERSION} \ - -X github.com/micromdm/micromdm/version.branch=${BRANCH} \ - -X github.com/micromdm/micromdm/version.buildUser=${USER} \ - -X github.com/micromdm/micromdm/version.buildDate=${NOW} \ - -X github.com/micromdm/micromdm/version.revision=${REVISION} \ - -X github.com/micromdm/micromdm/version.goVersion=${GOVERSION}" + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.appName=${APP_NAME} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.version=${VERSION} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.branch=${BRANCH} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.buildUser=${USER} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.buildDate=${NOW} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.revision=${REVISION} \ + -X github.com/micromdm/micromdm/vendor/github.com/micromdm/go4/version.goVersion=${GOVERSION}" WORKSPACE = ${GOPATH}/src/github.com/micromdm/micromdm check-deps: @@ -63,21 +63,27 @@ INSTALL_STEPS := \ install-local: $(INSTALL_STEPS) -APP_NAME = mdmctl -mdmctl: .pre-build +.pre-mdmctl: + $(eval APP_NAME = mdmctl) + +mdmctl: .pre-build .pre-mdmctl go build -i -o build/darwin/mdmctl -ldflags ${BUILD_VERSION} ./cmd/mdmctl -install-mdmctl: +install-mdmctl: .pre-mdmctl go install -ldflags ${BUILD_VERSION} ./cmd/mdmctl APP_NAME = micromdm -micromdm: .pre-build + +.pre-micromdm: + $(eval APP_NAME = micromdm) + +micromdm: .pre-build .pre-micromdm go build -i -o build/darwin/micromdm -ldflags ${BUILD_VERSION} ./cmd/mdmctl -install-micromdm: +install-micromdm: .pre-micromdm go install -ldflags ${BUILD_VERSION} -xp-micromdm: .pre-build +xp-micromdm: .pre-build .pre-micromdm GOOS=darwin go build -i -o build/darwin/micromdm -ldflags ${BUILD_VERSION} GOOS=linux CGO_ENABLED=0 go build -i -o build/linux/micromdm -ldflags ${BUILD_VERSION} diff --git a/cmd/mdmctl/mdmdctl.go b/cmd/mdmctl/mdmdctl.go index 31da03cf..c6be1458 100644 --- a/cmd/mdmctl/mdmdctl.go +++ b/cmd/mdmctl/mdmdctl.go @@ -7,7 +7,7 @@ import ( "strings" "text/tabwriter" - "github.com/micromdm/micromdm/version" + "github.com/micromdm/go4/version" ) func main() { diff --git a/main.go b/main.go index a0ac53cd..e86877df 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( "text/tabwriter" "time" - "github.com/micromdm/micromdm/version" + "github.com/micromdm/go4/version" ) func init() { diff --git a/serve.go b/serve.go index 1c47a2e4..e4733534 100644 --- a/serve.go +++ b/serve.go @@ -14,16 +14,14 @@ import ( "io/ioutil" stdlog "log" "net/http" - "net/http/httputil" "net/url" "os" - "os/signal" "path/filepath" "strings" - "syscall" - "time" "github.com/fullsailor/pkcs7" + "github.com/groob/finalizer/logutil" + "github.com/micromdm/go4/env" "golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/pkcs12" @@ -32,11 +30,11 @@ import ( "github.com/go-kit/kit/endpoint" "github.com/go-kit/kit/log" httptransport "github.com/go-kit/kit/transport/http" - "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/micromdm/dep" + "github.com/micromdm/go4/httputil" boltdepot "github.com/micromdm/scep/depot/bolt" scep "github.com/micromdm/scep/server" @@ -84,15 +82,14 @@ func serve(args []string) error { var ( flConfigPath = flagset.String("config-path", "/var/db/micromdm", "path to configuration directory") flServerURL = flagset.String("server-url", "", "public HTTPS url of your server") - flAPIKey = flagset.String("api-key", "", "API Token for mdmctl command") + flAPIKey = flagset.String("api-key", env.String("MICROMDM_API_KEY", ""), "API Token for mdmctl command") flAPNSCertPath = flagset.String("apns-cert", "", "path to APNS certificate") - flAPNSKeyPass = flagset.String("apns-password", "", "password for your p12 APNS cert file (if using)") + flAPNSKeyPass = flagset.String("apns-password", env.String("MICROMDM_APNS_KEY_PASSWORD", ""), "password for your p12 APNS cert file (if using)") flAPNSKeyPath = flagset.String("apns-key", "", "path to key file if using .pem push cert") flTLS = flagset.Bool("tls", true, "use https") flTLSCert = flagset.String("tls-cert", "", "path to TLS certificate") flTLSKey = flagset.String("tls-key", "", "path to TLS private key") flHTTPAddr = flagset.String("http-addr", ":https", "http(s) listen address of mdm server. defaults to :8080 if tls is false") - flRedirAddr = flagset.String("redir-addr", ":http", "http redirect to https listen address") flHTTPDebug = flagset.Bool("http-debug", false, "enable debug for http(dumps full request)") flRepoPath = flagset.String("filerepo", "", "path to http file repo") flDepSim = flagset.Bool("depsim", false, "use depsim config") @@ -348,67 +345,61 @@ func serve(args []string) error { var handler http.Handler if *flHTTPDebug { - handler = debugHTTPmiddleware(r) + handler = httputil.HTTPDebugMiddleware(os.Stdout, true, logger.Log)(r) } else { handler = r } - handler = handlers.CombinedLoggingHandler(os.Stdout, handler) - srv := &http.Server{ - Addr: *flHTTPAddr, - Handler: handler, - ReadTimeout: 60 * time.Second, - WriteTimeout: 60 * time.Second, - ReadHeaderTimeout: 10 * time.Second, - IdleTimeout: 10 * time.Minute, - MaxHeaderBytes: 1 << 18, // 0.25 MB - TLSConfig: tlsConfig(), - } + handler = logutil.NewHTTPLogger(httpLogger).Middleware(handler) srvURL, err := url.Parse(sm.ServerPublicURL) if err != nil { - stdlog.Fatal(err) + return errors.Wrapf(err, "parsing serverURL %q", sm.ServerPublicURL) } - errs := make(chan error, 2) - go func() { - sig := make(chan os.Signal) - signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) - <-sig // block on signal then gracefully shutdown. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - errs <- srv.Shutdown(ctx) - }() + serveOpts := serveOptions( + handler, + *flHTTPAddr, + srvURL.Hostname(), + logger, + *flTLSCert, + *flTLSKey, + sm.configPath, + *flTLS, + ) + err = httputil.ListenAndServe(serveOpts...) + return errors.Wrap(err, "calling ListenAndServe") +} - go func() { - logger := log.With(logger, "transport", "HTTP") - if !*flTLS { - var httpAddr string - if *flHTTPAddr == ":https" { - httpAddr = ":8080" - } else { - httpAddr = *flHTTPAddr - } - logger.Log("addr", httpAddr) - errs <- http.ListenAndServe(httpAddr, handler) - return - } - - tlsFromFile := (*flTLSCert != "" && *flTLSKey != "") - if tlsFromFile { - logger.Log("addr", srv.Addr) - redirectTLS(*flRedirAddr, sm.ServerPublicURL) - errs <- serveTLS(srv, *flTLSCert, *flTLSKey) - return - } else { - logger.Log("addr", srv.Addr) - redirectTLS(*flRedirAddr, sm.ServerPublicURL) - errs <- serveACME(srv, srvURL.Hostname(), filepath.Join(sm.configPath, "le-certificates")) - return - } - }() - - mainLogger.Log("terminated", <-errs) - return nil +// serveOptions configures the []httputil.Options for ListenAndServe +func serveOptions( + handler http.Handler, + addr string, + hostname string, + logger log.Logger, + certPath string, + keyPath string, + configPath string, + tls bool, +) []httputil.Option { + tlsFromFile := (certPath != "" && keyPath != "") + serveOpts := []httputil.Option{ + httputil.WithACMEHosts([]string{hostname}), + httputil.WithLogger(logger), + httputil.WithHTTPHandler(handler), + } + if tlsFromFile { + serveOpts = append(serveOpts, httputil.WithKeyPair(certPath, keyPath)) + } + if !tls && addr == ":https" { + serveOpts = append(serveOpts, httputil.WithAddress(":8080")) + } + if tls { + serveOpts = append(serveOpts, httputil.WithAutocertCache(autocert.DirCache(filepath.Join(configPath, "le-certificates")))) + } + if addr != ":https" { + serveOpts = append(serveOpts, httputil.WithAddress(addr)) + } + return serveOpts } func printExamples() { @@ -424,57 +415,6 @@ func printExamples() { fmt.Println(exampleText) } -func serveTLS(server *http.Server, certPath, keyPath string) error { - err := server.ListenAndServeTLS(certPath, keyPath) - return err -} - -func serveACME(server *http.Server, domain, cachePath string) error { - m := autocert.Manager{ - Prompt: autocert.AcceptTOS, - HostPolicy: autocert.HostWhitelist(domain), - Cache: autocert.DirCache(cachePath), - } - server.TLSConfig.GetCertificate = m.GetCertificate - err := server.ListenAndServeTLS("", "") - return err -} - -// redirects port 80 to port 443 -func redirectTLS(addr, serverUrl string) { - srv := &http.Server{ - Addr: addr, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Connection", "close") - url := serverUrl + 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 { configPath string depsim bool @@ -792,21 +732,6 @@ func (c *config) setupSCEP(logger log.Logger) { } } -func debugHTTPmiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body := io.TeeReader(r.Body, os.Stderr) - r.Body = ioutil.NopCloser(body) - out, err := httputil.DumpRequest(r, true) - if err != nil { - stdlog.Println(err) - } - fmt.Println("") - fmt.Println(string(out)) - fmt.Println("") - next.ServeHTTP(w, r) - }) -} - // TODO: move to separate package/library func mdmAuthSignMessageMiddleware(db *boltdepot.Depot, next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/version/version.go b/version/version.go deleted file mode 100644 index 9a875469..00000000 --- a/version/version.go +++ /dev/null @@ -1,69 +0,0 @@ -// Package version provides utilities for displaying version information. -package version - -import ( - "encoding/json" - "fmt" - "net/http" -) - -// go build -ldflags "-X github.com/micromdm/micromdm/version.version=1.0.0" -var ( - version = "unknown" - branch = "unknown" - revision = "unknown" - goVersion = "unknown" - buildDate = "unknown" - buildUser = "unknown" - appName = "micromdm" -) - -// Info holds version and build info about the app. -type Info struct { - Version string `json:"version"` - Branch string `json:"branch"` - Revision string `json:"revision"` - GoVersion string `json:"go_version"` - BuildDate string `json:"build_date"` - BuildUser string `json:"build_user"` -} - -// Version returns a struct with the current version information. -func Version() Info { - return Info{ - Version: version, - Branch: branch, - Revision: revision, - GoVersion: goVersion, - BuildDate: buildDate, - BuildUser: buildUser, - } -} - -// Print outputs the app name and version string. -func Print() { - v := Version() - fmt.Printf("%s version %s\n", appName, v.Version) -} - -// PrintFull outputs the app name and detailed version information. -func PrintFull() { - v := Version() - fmt.Printf("%s - version %s\n", appName, v.Version) - fmt.Printf(" branch: \t%s\n", v.Branch) - fmt.Printf(" revision: \t%s\n", v.Revision) - fmt.Printf(" build date: \t%s\n", v.BuildDate) - fmt.Printf(" build user: \t%s\n", v.BuildUser) - fmt.Printf(" go version: \t%s\n", v.GoVersion) -} - -// Handler provides an HTTP Handler which returns JSON formatted version info. -func Handler() http.Handler { - v := Version() - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.Encode(v) - }) -}