From 7418de0991958eabe5bf1c84c568e1fb8003c79c Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Mon, 27 Mar 2017 18:50:38 -0400 Subject: [PATCH] ADD authentication to mdmctl and server (#127) uses basic auth to set/read token --- cmd/mdmctl/list.go | 14 ++++++-------- core/list/client.go | 12 ++++++++++-- serve.go | 27 ++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/cmd/mdmctl/list.go b/cmd/mdmctl/list.go index 5d70fe6b..c620e9fd 100644 --- a/cmd/mdmctl/list.go +++ b/cmd/mdmctl/list.go @@ -21,13 +21,6 @@ func (out *tableOutput) BasicFooter() { out.w.Flush() } -func (out *tableOutput) BasicLine(line ...string) { - for _, l := range line { - fmt.Fprintf(out.w, l+"\t") - } - fmt.Fprintf(out.w, "\n") -} - func listDevices(args []string) error { if len(args) == 0 || args[0] != "devices" { return errors.New("must specify resource name as an argument (ex: devices)") @@ -42,7 +35,12 @@ func listDevices(args []string) error { if instance == "" { return errors.New("MICROMDM_SERVER_URL not set") } - svc, err := list.NewClient(instance, logger) + + token := os.Getenv("MICROMDM_API_TOKEN") + if token == "" { + return errors.New("MICROMDM_API_TOKEN not set") + } + svc, err := list.NewClient(instance, logger, token) if err != nil { return err } diff --git a/core/list/client.go b/core/list/client.go index d6b1bf72..30d5b90e 100644 --- a/core/list/client.go +++ b/core/list/client.go @@ -1,6 +1,8 @@ package list import ( + "context" + "net/http" "net/url" "github.com/go-kit/kit/endpoint" @@ -8,7 +10,7 @@ import ( httptransport "github.com/go-kit/kit/transport/http" ) -func NewClient(instance string, logger log.Logger) (Service, error) { +func NewClient(instance string, logger log.Logger, token string) (Service, error) { u, err := url.Parse(instance) if err != nil { return nil, err @@ -19,7 +21,7 @@ func NewClient(instance string, logger log.Logger) (Service, error) { listDevicesEndpoint = httptransport.NewClient( "GET", copyURL(u, "/v1/devices"), - EncodeHTTPGenericRequest, + encodeRequestWithToken(token, EncodeHTTPGenericRequest), DecodeDevicesResponse, ).Endpoint() } @@ -29,6 +31,12 @@ func NewClient(instance string, logger log.Logger) (Service, error) { }, nil } +func encodeRequestWithToken(token string, next httptransport.EncodeRequestFunc) httptransport.EncodeRequestFunc { + return func(ctx context.Context, r *http.Request, request interface{}) error { + r.SetBasicAuth("micromdm", token) + return next(ctx, r, request) + } +} func copyURL(base *url.URL, path string) *url.URL { next := *base next.Path = path diff --git a/serve.go b/serve.go index 95654007..7b45b337 100644 --- a/serve.go +++ b/serve.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/asn1" + "encoding/base64" "encoding/json" "encoding/pem" "flag" @@ -76,6 +77,7 @@ func serve(args []string) error { flagset := flag.NewFlagSet("serve", flag.ExitOnError) var ( flServerURL = flagset.String("server-url", "", "public HTTPS url of your server") + flAPIKey = flagset.String("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)") flAPNSKeyPath = flagset.String("apns-key", "", "path to key file if using .pem push cert") @@ -208,11 +210,15 @@ func serve(args []string) error { r.Handle("/scep", scepHandler) r.Handle("/push/{udid}", pushHandlers.PushHandler) r.Handle("/v1/commands", commandHandlers.NewCommandHandler).Methods("POST") - r.Handle("/v1/devices", listAPIHandlers).Methods("GET") r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, homePage) }) + // API commands. Only handled if the user provides an api key. + if *flAPIKey != "" { + r.Handle("/v1/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers)).Methods("GET") + } + if *flRepoPath != "" { r.PathPrefix("/repo/").Handler(http.StripPrefix("/repo/", http.FileServer(http.Dir(*flRepoPath)))) } @@ -721,3 +727,22 @@ func debugHTTPmiddleware(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } + +func basicAuth(password string) string { + const authUsername = "micromdm" + auth := authUsername + ":" + password + return base64.StdEncoding.EncodeToString([]byte(auth)) +} + +func apiAuthMiddleware(token string, next http.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, password, ok := r.BasicAuth() + if !ok || password != token { + w.Header().Set("WWW-Authenticate", `Basic realm="micromdm"`) + http.Error(w, `{"error": "you need to log in"}`, http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + + } +}