From 2015126c651702d347b545c76e2ef6f1bbb29a2b Mon Sep 17 00:00:00 2001 From: mosen Date: Fri, 7 Oct 2016 10:13:26 +1100 Subject: [PATCH] Enrollment (#13) * Add flag url (MICROMDM_PUBLIC_URL) for public facing mdm enrollment url. Add flag scep-url (MICROMDM_SCEP_URL) for scep payload in enrollment profile. (cherry picked from commit 1cc09e3) * enroll service allows scep challenge via flag --scep-challenge or env MICROMDM_SCEP_CHALLENGE remove reference to certificates and apps, those will be in other branches. (cherry picked from commit e417ba8) * Add MDM payload Add NewPayload method Profiles and payloads created by their factory methods automatically generate a UUID Add go-kit service for enroll. Merge rebase for enroll service (cherry picked from commit 6843b20) * Fleshed out a bit more of the enroll service. Rebase onto master (cherry picked from commit 69dc230) * Add endpoint and transport for Enroll service. Rebase develop onto master (cherry picked from commit 1501b2e) * Fix several compilation errors regarding syntax. Rebase develop onto master (cherry picked from commit fe3d039) * More consistent naming of Payloads. Merge develop onto master (cherry picked from commit 6f70231) * Fix error with plist encoding by dereferencing nested payloads. Add correct header for enrollment profile. Enrollment works with micromdm/scep server! Rebase develop onto master (cherry picked from commit 7e858c3) * Warnings are more helpful if you omit pieces of configuration. CA payload for SCEP includes identifier. CORS methods, app service, cert service omitted for this commit, will be cherry picked into a different branch. (cherry picked from commit 9219be2) * enroll package follows the style of go-kit (cherry picked from commit fb03d3c) * Fix variable naming of URL and SCEPURL. Remove duplicate enroll service. * Fix variable naming in main URL, SCEP, CORS * Fix errors returned in mdmEnrollResponse. --- enroll/endpoint.go | 25 +++++++++++------- enroll/service.go | 33 ++++++++++++++---------- enroll/transport.go | 20 +++++++-------- main.go | 62 +++++++++++++++++++++++++-------------------- 4 files changed, 80 insertions(+), 60 deletions(-) diff --git a/enroll/endpoint.go b/enroll/endpoint.go index 4009fa06..291161a8 100644 --- a/enroll/endpoint.go +++ b/enroll/endpoint.go @@ -5,19 +5,26 @@ import ( "golang.org/x/net/context" ) +type Endpoints struct { + GetEnrollEndpoint endpoint.Endpoint +} + type mdmEnrollRequest struct{} type mdmEnrollResponse struct { - Profile + Profile, + Err error `plist:"error,omitempty"` } -func makeEnrollEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - //req := request.(mdmEnrollRequest) - profile, err := svc.Enroll() - if err != nil { - return mdmEnrollResponse{}, err - } - return mdmEnrollResponse{profile}, nil +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + GetEnrollEndpoint: MakeGetEnrollEndpoint(s), + } +} + +func MakeGetEnrollEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + profile, err := s.Enroll(ctx) + return mdmEnrollResponse{profile, err}, nil } } diff --git a/enroll/service.go b/enroll/service.go index a15298d5..e744b33d 100644 --- a/enroll/service.go +++ b/enroll/service.go @@ -1,12 +1,15 @@ package enroll -import "io/ioutil" +import ( + "golang.org/x/net/context" + "io/ioutil" +) type Service interface { - Enroll() (Profile, error) + Enroll(ctx context.Context) (Profile, error) } -func NewService(pushCertPath string, pushCertPass string, caCertPath string, url string, scepUrl string) (Service, error) { +func NewService(pushCertPath string, pushCertPass string, caCertPath string, scepURL string, scepChallenge string, url string) (Service, error) { pushTopic, err := GetPushTopicFromPKCS12(pushCertPath, pushCertPass) if err != nil { return nil, err @@ -30,24 +33,25 @@ func NewService(pushCertPath string, pushCertPass string, caCertPath string, url } return &service{ - Url: url, - SCEPUrl: scepUrl, - SCEPSubject: scepSubject, - Topic: pushTopic, - CACert: caCert, + URL: url, + SCEPURL: scepURL, + SCEPSubject: scepSubject, + SCEPChallenge: scepChallenge, + Topic: pushTopic, + CACert: caCert, }, nil } type service struct { - Url string - SCEPUrl string + URL string + SCEPURL string SCEPChallenge string SCEPSubject [][][]string Topic string // APNS Topic for MDM notifications CACert []byte } -func (svc service) Enroll() (Profile, error) { +func (svc service) Enroll(ctx context.Context) (Profile, error) { profile := NewProfile() profile.PayloadIdentifier = "com.github.micromdm.micromdm.mdm" profile.PayloadOrganization = "MicroMDM" @@ -56,7 +60,7 @@ func (svc service) Enroll() (Profile, error) { scepContent := SCEPPayloadContent{ Challenge: svc.SCEPChallenge, - URL: svc.SCEPUrl, + URL: svc.SCEPURL, Keysize: 1024, KeyType: "RSA", KeyUsage: 0, @@ -78,9 +82,9 @@ func (svc service) Enroll() (Profile, error) { mdmPayloadContent := MDMPayloadContent{ Payload: *mdmPayload, AccessRights: 8191, - CheckInURL: svc.Url + "/mdm/checkin", + CheckInURL: svc.URL + "/mdm/checkin", CheckOutWhenRemoved: true, - ServerURL: svc.Url + "/mdm/connect", + ServerURL: svc.URL + "/mdm/connect", IdentityCertificateUUID: scepPayload.PayloadUUID, Topic: svc.Topic, } @@ -89,6 +93,7 @@ func (svc service) Enroll() (Profile, error) { caPayload := NewPayload("com.apple.ssl.certificate") caPayload.PayloadDisplayName = "Root certificate for MicroMDM" caPayload.PayloadDescription = "Installs the root CA certificate for MicroMDM" + caPayload.PayloadIdentifier = "com.github.micromdm.ssl.ca" caPayload.PayloadContent = svc.CACert profile.PayloadContent = []interface{}{*scepPayload, mdmPayloadContent, *caPayload} diff --git a/enroll/transport.go b/enroll/transport.go index 607c7703..36ed004e 100644 --- a/enroll/transport.go +++ b/enroll/transport.go @@ -5,28 +5,28 @@ import ( "golang.org/x/net/context" - kitlog "github.com/go-kit/kit/log" - kithttp "github.com/go-kit/kit/transport/http" + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" "github.com/groob/plist" ) // ServiceHandler returns an HTTP Handler for the enroll service -func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http.Handler { - opts := []kithttp.ServerOption{ - kithttp.ServerErrorLogger(logger), +func MakeHTTPHandler(ctx context.Context, svc Service, logger log.Logger) http.Handler { + r := mux.NewRouter() + e := MakeServerEndpoints(svc) + opts := []httptransport.ServerOption{ + httptransport.ServerErrorLogger(logger), } - connectHandler := kithttp.NewServer( + r.Methods("GET").Path("/mdm/enroll").Handler(httptransport.NewServer( ctx, - makeEnrollEndpoint(svc), + e.GetEnrollEndpoint, decodeMDMEnrollRequest, encodeResponse, opts..., - ) - r := mux.NewRouter() + )) - r.Handle("/mdm/enroll", connectHandler).Methods("GET") return r } diff --git a/main.go b/main.go index 5aa7816b..b07881e5 100644 --- a/main.go +++ b/main.go @@ -39,27 +39,28 @@ func main() { //flags var ( - flUrl = flag.String("url", envString("MICROMDM_URL", ""), "public facing url") - flPort = flag.String("port", envString("MICROMDM_HTTP_LISTEN_PORT", ""), "port to listen on") - flTLS = flag.Bool("tls", envBool("MICROMDM_USE_TLS"), "use https") - flTLSCert = flag.String("tls-cert", envString("MICROMDM_TLS_CERT", ""), "path to TLS certificate") - flTLSKey = flag.String("tls-key", envString("MICROMDM_TLS_KEY", ""), "path to TLS private key") - flTLSCACert = flag.String("tls-ca-cert", envString("MICROMDM_TLS_CA_CERT", ""), "path to CA certificate") - flScepUrl = flag.String("scep-url", envString("MICROMDM_SCEP_URL", ""), "url of SCEP server") - flPGconn = flag.String("postgres", envString("MICROMDM_POSTGRES_CONN_URL", ""), "postgres connection url") - flRedisconn = flag.String("redis", envString("MICROMDM_REDIS_CONN_URL", ""), "redis connection url") - flVersion = flag.Bool("version", false, "print version information") - flPushCert = flag.String("push-cert", envString("MICROMDM_PUSH_CERT", ""), "path to push certificate") - flPushPass = flag.String("push-pass", envString("MICROMDM_PUSH_PASS", ""), "push certificate password") - flEnrollment = flag.String("profile", envString("MICROMDM_ENROLL_PROFILE", ""), "path to enrollment profile") - flDEPCK = flag.String("dep-consumer-key", envString("DEP_CONSUMER_KEY", ""), "dep consumer key") - flDEPCS = flag.String("dep-consumer-secret", envString("DEP_CONSUMER_SECRET", ""), "dep consumer secret") - flDEPAT = flag.String("dep-access-token", envString("DEP_ACCESS_TOKEN", ""), "dep access token") - flDEPAS = flag.String("dep-access-secret", envString("DEP_ACCESS_SECRET", ""), "dep access secret") - flDEPsim = flag.Bool("depsim", envBool("DEP_USE_DEPSIM"), "use default depsim credentials") - flDEPServerURL = flag.String("dep-server-url", envString("DEP_SERVER_URL", ""), "dep server url. for testing. Use blank if not running against depsim") - flPkgRepo = flag.String("pkg-repo", envString("MICROMDM_PKG_REPO", ""), "path to pkg repo") - flCorsOrigin = flag.String("cors-origin", envString("MICROMDM_CORS_ORIGIN", ""), "allowed domain for cross origin resource sharing") + flURL = flag.String("url", envString("MICROMDM_URL", ""), "public facing url") + flPort = flag.String("port", envString("MICROMDM_HTTP_LISTEN_PORT", ""), "port to listen on") + flTLS = flag.Bool("tls", envBool("MICROMDM_USE_TLS"), "use https") + flTLSCert = flag.String("tls-cert", envString("MICROMDM_TLS_CERT", ""), "path to TLS certificate") + flTLSKey = flag.String("tls-key", envString("MICROMDM_TLS_KEY", ""), "path to TLS private key") + flTLSCACert = flag.String("tls-ca-cert", envString("MICROMDM_TLS_CA_CERT", ""), "path to CA certificate") + flSCEPURL = flag.String("scep-url", envString("MICROMDM_SCEP_URL", ""), "scep server url. If blank, enroll profile will not use a scep payload.") + flSCEPChallenge = flag.String("scep-challenge", envString("MICROMDM_SCEP_CHALLENGE", ""), "scep server challenge") + flPGconn = flag.String("postgres", envString("MICROMDM_POSTGRES_CONN_URL", ""), "postgres connection url") + flRedisconn = flag.String("redis", envString("MICROMDM_REDIS_CONN_URL", ""), "redis connection url") + flVersion = flag.Bool("version", false, "print version information") + flPushCert = flag.String("push-cert", envString("MICROMDM_PUSH_CERT", ""), "path to push certificate") + flPushPass = flag.String("push-pass", envString("MICROMDM_PUSH_PASS", ""), "push certificate password") + flEnrollment = flag.String("profile", envString("MICROMDM_ENROLL_PROFILE", ""), "path to enrollment profile") + flDEPCK = flag.String("dep-consumer-key", envString("DEP_CONSUMER_KEY", ""), "dep consumer key") + flDEPCS = flag.String("dep-consumer-secret", envString("DEP_CONSUMER_SECRET", ""), "dep consumer secret") + flDEPAT = flag.String("dep-access-token", envString("DEP_ACCESS_TOKEN", ""), "dep access token") + flDEPAS = flag.String("dep-access-secret", envString("DEP_ACCESS_SECRET", ""), "dep access secret") + flDEPsim = flag.Bool("depsim", envBool("DEP_USE_DEPSIM"), "use default depsim credentials") + flDEPServerURL = flag.String("dep-server-url", envString("DEP_SERVER_URL", ""), "dep server url. for testing. Use blank if not running against depsim") + flPkgRepo = flag.String("pkg-repo", envString("MICROMDM_PKG_REPO", ""), "path to pkg repo") + flCORSOrigin = flag.String("cors-origin", envString("MICROMDM_CORS_ORIGIN", ""), "allowed domain for cross origin resource sharing") ) // set tls to true by default. let user set it to false @@ -206,11 +207,18 @@ func main() { mux.Handle("/mdm/checkin", checkinHandler) mux.Handle("/mdm/connect", connectHandler) - if checkEmptyArgs(*flUrl, *flScepUrl) { - logger.Log("warn", "Enrollment endpoint /mdm/enroll will be disabled because you did not specify flags for the external URL or SCEP URL") + if checkEmptyArgs(*flURL, *flSCEPURL) { + logger.Log("warn", "Enrollment endpoint /mdm/enroll will be disabled because you did not specify flags/environment vars for the external URL (--url MICROMDM_URL) or SCEP URL (--scep-url/MICROMDM_SCEP_URL)") } else { - enrollSvc, _ := enroll.NewService(*flPushCert, *flPushPass, *flTLSCACert, *flUrl, *flScepUrl) - enrollHandler := enroll.ServiceHandler(ctx, enrollSvc, httpLogger) + if *flSCEPChallenge == "" { + logger.Log("warn", "You did not specify a SCEP challenge via --scep-challenge or MICROMDM_SCEP_CHALLENGE (this may not be what you intended).") + } + + if *flTLSCACert == "" { + logger.Log("warn", "You did not specify a CA Certificate to trust via --tls-ca-cert or MICROMDM_TLS_CA_CERT. If your certificates are self signed, devices may not be able to enroll.") + } + enrollSvc, _ := enroll.NewService(*flPushCert, *flPushPass, *flTLSCACert, *flSCEPURL, *flSCEPChallenge, *flURL) + enrollHandler := enroll.MakeHTTPHandler(ctx, enrollSvc, httpLogger) mux.Handle("/mdm/enroll", enrollHandler) } @@ -218,9 +226,9 @@ func main() { mux.Handle("/repo/", http.StripPrefix("/repo/", http.FileServer(http.Dir(*flPkgRepo)))) } - if *flCorsOrigin != "" { + if *flCORSOrigin != "" { c := cors.New(cors.Options{ - AllowedOrigins: []string{*flCorsOrigin}, + AllowedOrigins: []string{*flCORSOrigin}, AllowCredentials: true, AllowedMethods: []string{"GET", "POST", "PATCH", "DELETE"}, })