mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-14 17:41:43 +08:00
first commit in new package
This commit is contained in:
30
enroll/endpoint.go
Normal file
30
enroll/endpoint.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package enroll
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
GetEnrollEndpoint endpoint.Endpoint
|
||||
}
|
||||
|
||||
type mdmEnrollRequest struct{}
|
||||
|
||||
type mdmEnrollResponse struct {
|
||||
Profile
|
||||
Err error `plist:"error,omitempty"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
78
enroll/profile.go
Normal file
78
enroll/profile.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package enroll
|
||||
|
||||
import (
|
||||
"github.com/satori/go.uuid"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Payload struct {
|
||||
PayloadType string `json:"type" db:"type"`
|
||||
PayloadVersion int `json:"version" db:"version"`
|
||||
PayloadIdentifier string `json:"identifier" db:"identifier"`
|
||||
PayloadUUID string `json:"uuid" db:"uuid"`
|
||||
PayloadDisplayName string `json:"displayname" db:"displayname"`
|
||||
PayloadDescription string `json:"description,omitempty" db:"description"`
|
||||
PayloadOrganization string `json:"organization,omitempty" db:"organization"`
|
||||
PayloadScope string `json:"scope" db:"scope" plist:",omitempty"`
|
||||
PayloadContent interface{} `json:"content,omitempty" plist:"PayloadContent,omitempty"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
PayloadContent []interface{} `json:"content,omitempty" db:"content"`
|
||||
PayloadDescription string `json:"description,omitempty" db:"description"`
|
||||
PayloadDisplayName string `json:"displayname,omitempty" db:"displayname"`
|
||||
PayloadExpirationDate *time.Time `json:"expiration_date,omitempty" db:"expiration_date" plist:",omitempty"`
|
||||
PayloadIdentifier string `json:"identifier" db:"identifier"`
|
||||
PayloadOrganization string `json:"organization,omitempty" db:"organization"`
|
||||
PayloadUUID string `json:"uuid" db:"uuid"`
|
||||
PayloadRemovalDisallowed bool `json:"removal_disallowed" db:"removal_disallowed" plist:",omitempty"`
|
||||
PayloadType string `json:"type" db:"type"`
|
||||
PayloadVersion int `json:"version" db:"version"`
|
||||
PayloadScope string `json:"scope" db:"scope" plist:",omitempty"`
|
||||
RemovalDate *time.Time `json:"removal_date" db:"removal_date" plist:"-" plist:",omitempty"`
|
||||
DurationUntilRemoval float32 `json:"duration_until_removal" db:"duration_until_removal" plist:",omitempty"`
|
||||
ConsentText map[string]string `json:"consent_text" db:"consent_text" plist:",omitempty"`
|
||||
}
|
||||
|
||||
func NewProfile() *Profile {
|
||||
payloadUuid := uuid.NewV4()
|
||||
|
||||
return &Profile{
|
||||
PayloadVersion: 1,
|
||||
PayloadType: "Configuration",
|
||||
PayloadUUID: payloadUuid.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPayload(payloadType string) *Payload {
|
||||
payloadUuid := uuid.NewV4()
|
||||
|
||||
return &Payload{
|
||||
PayloadVersion: 1,
|
||||
PayloadType: payloadType,
|
||||
PayloadUUID: payloadUuid.String(),
|
||||
}
|
||||
}
|
||||
|
||||
type SCEPPayloadContent struct {
|
||||
CAFingerprint []byte `plist:"CAFingerprint,omitempty"` // NSData
|
||||
Challenge string `plist:"Challenge,omitempty"`
|
||||
Keysize int
|
||||
KeyType string `plist:"Key Type"`
|
||||
KeyUsage int `plist:"Key Usage"`
|
||||
Name string
|
||||
Subject [][][]string `plist:"Subject,omitempty"`
|
||||
URL string
|
||||
}
|
||||
|
||||
// TODO: Actually this is one of those non-nested payloads that doesnt respect the PayloadContent key.
|
||||
type MDMPayloadContent struct {
|
||||
Payload
|
||||
AccessRights int
|
||||
CheckInURL string
|
||||
CheckOutWhenRemoved bool
|
||||
IdentityCertificateUUID string
|
||||
ServerCapabilities []string `plist:"ServerCapabilities,omitempty"`
|
||||
ServerURL string
|
||||
Topic string
|
||||
}
|
||||
146
enroll/service.go
Normal file
146
enroll/service.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package enroll
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Enroll(ctx context.Context) (Profile, error)
|
||||
}
|
||||
|
||||
func NewService(pushTopic, caCertPath, scepURL, scepChallenge, url, tlsCertPath, scepSubject string) (Service, error) {
|
||||
var caCert, tlsCert []byte
|
||||
var err error
|
||||
|
||||
if caCertPath != "" {
|
||||
caCert, err = ioutil.ReadFile(caCertPath)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tlsCertPath != "" {
|
||||
tlsCert, err = ioutil.ReadFile(tlsCertPath)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if scepSubject == "" {
|
||||
scepSubject = "/O=MicroMDM/CN=MicroMDM Identity (%ComputerName%)"
|
||||
}
|
||||
|
||||
subjectElements := strings.Split(scepSubject, "/")
|
||||
var subject [][][]string
|
||||
|
||||
for _, element := range subjectElements {
|
||||
if element == "" {
|
||||
continue
|
||||
}
|
||||
subjectKeyValue := strings.Split(element, "=")
|
||||
subject = append(subject, [][]string{[]string{subjectKeyValue[0], subjectKeyValue[1]}})
|
||||
}
|
||||
|
||||
return &service{
|
||||
URL: url,
|
||||
SCEPURL: scepURL,
|
||||
SCEPSubject: subject,
|
||||
SCEPChallenge: scepChallenge,
|
||||
Topic: pushTopic,
|
||||
CACert: caCert,
|
||||
TLSCert: tlsCert,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
URL string
|
||||
SCEPURL string
|
||||
SCEPChallenge string
|
||||
SCEPSubject [][][]string
|
||||
Topic string // APNS Topic for MDM notifications
|
||||
CACert []byte
|
||||
TLSCert []byte
|
||||
}
|
||||
|
||||
func (svc service) Enroll(ctx context.Context) (Profile, error) {
|
||||
profile := NewProfile()
|
||||
profile.PayloadIdentifier = "com.github.micromdm.micromdm.mdm"
|
||||
profile.PayloadOrganization = "MicroMDM"
|
||||
profile.PayloadDisplayName = "Enrollment Profile"
|
||||
profile.PayloadDescription = "The server may alter your settings"
|
||||
profile.PayloadScope = "System"
|
||||
|
||||
mdmPayload := NewPayload("com.apple.mdm")
|
||||
mdmPayload.PayloadDescription = "Enrolls with the MDM server"
|
||||
mdmPayload.PayloadOrganization = "MicroMDM"
|
||||
mdmPayload.PayloadIdentifier = "com.github.micromdm.mdm"
|
||||
mdmPayload.PayloadScope = "System"
|
||||
|
||||
mdmPayloadContent := MDMPayloadContent{
|
||||
Payload: *mdmPayload,
|
||||
AccessRights: 8191,
|
||||
CheckInURL: svc.URL + "/mdm/checkin",
|
||||
CheckOutWhenRemoved: true,
|
||||
ServerURL: svc.URL + "/mdm/connect",
|
||||
Topic: svc.Topic,
|
||||
}
|
||||
|
||||
payloadContent := []interface{}{}
|
||||
|
||||
if svc.SCEPURL != "" {
|
||||
scepContent := SCEPPayloadContent{
|
||||
URL: svc.SCEPURL,
|
||||
Keysize: 1024,
|
||||
KeyType: "RSA",
|
||||
KeyUsage: 0,
|
||||
Name: "Device Management Identity Certificate",
|
||||
Subject: svc.SCEPSubject,
|
||||
}
|
||||
|
||||
if svc.SCEPChallenge != "" {
|
||||
scepContent.Challenge = svc.SCEPChallenge
|
||||
}
|
||||
|
||||
scepPayload := NewPayload("com.apple.security.scep")
|
||||
scepPayload.PayloadDescription = "Configures SCEP"
|
||||
scepPayload.PayloadDisplayName = "SCEP"
|
||||
scepPayload.PayloadIdentifier = "com.github.micromdm.scep"
|
||||
scepPayload.PayloadOrganization = "MicroMDM"
|
||||
scepPayload.PayloadContent = scepContent
|
||||
scepPayload.PayloadScope = "System"
|
||||
|
||||
payloadContent = append(payloadContent, *scepPayload)
|
||||
mdmPayloadContent.IdentityCertificateUUID = scepPayload.PayloadUUID
|
||||
}
|
||||
|
||||
payloadContent = append(payloadContent, mdmPayloadContent)
|
||||
|
||||
if len(svc.CACert) > 0 {
|
||||
caPayload := NewPayload("com.apple.security.root")
|
||||
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
|
||||
|
||||
payloadContent = append(payloadContent, *caPayload)
|
||||
}
|
||||
|
||||
// Client needs to trust us at this point if we are using a self signed certificate.
|
||||
if len(svc.TLSCert) > 0 {
|
||||
tlsPayload := NewPayload("com.apple.security.pkcs1")
|
||||
tlsPayload.PayloadDisplayName = "Self-signed TLS certificate for MicroMDM"
|
||||
tlsPayload.PayloadDescription = "Installs the TLS certificate for MicroMDM"
|
||||
tlsPayload.PayloadIdentifier = "com.github.micromdm.tls"
|
||||
tlsPayload.PayloadContent = svc.TLSCert
|
||||
|
||||
payloadContent = append(payloadContent, *tlsPayload)
|
||||
}
|
||||
|
||||
profile.PayloadContent = payloadContent
|
||||
|
||||
return *profile, nil
|
||||
}
|
||||
47
enroll/transport.go
Normal file
47
enroll/transport.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package enroll
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"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 log.Logger) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
e := MakeServerEndpoints(svc)
|
||||
opts := []httptransport.ServerOption{
|
||||
httptransport.ServerErrorLogger(logger),
|
||||
}
|
||||
|
||||
r.Methods("GET").Path("/mdm/enroll").Handler(httptransport.NewServer(
|
||||
ctx,
|
||||
e.GetEnrollEndpoint,
|
||||
decodeMDMEnrollRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeMDMEnrollRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
resp := response.(mdmEnrollResponse)
|
||||
|
||||
w.Header().Set("Content-Type", "application/x-apple-aspen-config")
|
||||
|
||||
if err := plist.NewEncoder(w).Encode(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user