mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-07 18:15:47 +08:00
Move the server type to it's own package (#458)
This commit is contained in:
committed by
Victor Vrantchan
parent
8f5838ceea
commit
d7f07bb022
@@ -1,17 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -20,51 +16,39 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/RobotsAndPencils/buford/push"
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/fullsailor/pkcs7"
|
"github.com/fullsailor/pkcs7"
|
||||||
"github.com/go-kit/kit/auth/basic"
|
"github.com/go-kit/kit/auth/basic"
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
"github.com/go-kit/kit/log/level"
|
|
||||||
httptransport "github.com/go-kit/kit/transport/http"
|
httptransport "github.com/go-kit/kit/transport/http"
|
||||||
"github.com/groob/finalizer/logutil"
|
"github.com/groob/finalizer/logutil"
|
||||||
"github.com/micromdm/dep"
|
|
||||||
"github.com/micromdm/go4/env"
|
"github.com/micromdm/go4/env"
|
||||||
"github.com/micromdm/go4/httputil"
|
"github.com/micromdm/go4/httputil"
|
||||||
"github.com/micromdm/go4/version"
|
"github.com/micromdm/go4/version"
|
||||||
boltdepot "github.com/micromdm/scep/depot/bolt"
|
|
||||||
scep "github.com/micromdm/scep/server"
|
scep "github.com/micromdm/scep/server"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"golang.org/x/crypto/pkcs12"
|
|
||||||
|
|
||||||
"github.com/micromdm/micromdm/dep/depsync"
|
"github.com/micromdm/micromdm/dep/depsync"
|
||||||
"github.com/micromdm/micromdm/mdm"
|
"github.com/micromdm/micromdm/mdm"
|
||||||
"github.com/micromdm/micromdm/mdm/enroll"
|
"github.com/micromdm/micromdm/mdm/enroll"
|
||||||
"github.com/micromdm/micromdm/pkg/crypto"
|
|
||||||
httputil2 "github.com/micromdm/micromdm/pkg/httputil"
|
httputil2 "github.com/micromdm/micromdm/pkg/httputil"
|
||||||
"github.com/micromdm/micromdm/platform/apns"
|
"github.com/micromdm/micromdm/platform/apns"
|
||||||
apnsbuiltin "github.com/micromdm/micromdm/platform/apns/builtin"
|
|
||||||
"github.com/micromdm/micromdm/platform/appstore"
|
"github.com/micromdm/micromdm/platform/appstore"
|
||||||
appsbuiltin "github.com/micromdm/micromdm/platform/appstore/builtin"
|
appsbuiltin "github.com/micromdm/micromdm/platform/appstore/builtin"
|
||||||
"github.com/micromdm/micromdm/platform/blueprint"
|
"github.com/micromdm/micromdm/platform/blueprint"
|
||||||
blueprintbuiltin "github.com/micromdm/micromdm/platform/blueprint/builtin"
|
blueprintbuiltin "github.com/micromdm/micromdm/platform/blueprint/builtin"
|
||||||
"github.com/micromdm/micromdm/platform/command"
|
"github.com/micromdm/micromdm/platform/command"
|
||||||
"github.com/micromdm/micromdm/platform/config"
|
"github.com/micromdm/micromdm/platform/config"
|
||||||
configbuiltin "github.com/micromdm/micromdm/platform/config/builtin"
|
|
||||||
depapi "github.com/micromdm/micromdm/platform/dep"
|
depapi "github.com/micromdm/micromdm/platform/dep"
|
||||||
"github.com/micromdm/micromdm/platform/device"
|
"github.com/micromdm/micromdm/platform/device"
|
||||||
devicebuiltin "github.com/micromdm/micromdm/platform/device/builtin"
|
devicebuiltin "github.com/micromdm/micromdm/platform/device/builtin"
|
||||||
"github.com/micromdm/micromdm/platform/profile"
|
"github.com/micromdm/micromdm/platform/profile"
|
||||||
profilebuiltin "github.com/micromdm/micromdm/platform/profile/builtin"
|
|
||||||
"github.com/micromdm/micromdm/platform/pubsub"
|
|
||||||
"github.com/micromdm/micromdm/platform/pubsub/inmem"
|
|
||||||
"github.com/micromdm/micromdm/platform/queue"
|
|
||||||
block "github.com/micromdm/micromdm/platform/remove"
|
block "github.com/micromdm/micromdm/platform/remove"
|
||||||
blockbuiltin "github.com/micromdm/micromdm/platform/remove/builtin"
|
|
||||||
"github.com/micromdm/micromdm/platform/user"
|
"github.com/micromdm/micromdm/platform/user"
|
||||||
userbuiltin "github.com/micromdm/micromdm/platform/user/builtin"
|
userbuiltin "github.com/micromdm/micromdm/platform/user/builtin"
|
||||||
"github.com/micromdm/micromdm/workflow/webhook"
|
"github.com/micromdm/micromdm/server"
|
||||||
|
boltdepot "github.com/micromdm/scep/depot/bolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const homePage = `<!doctype html>
|
const homePage = `<!doctype html>
|
||||||
@@ -137,17 +121,17 @@ func serve(args []string) error {
|
|||||||
if err := os.MkdirAll(*flConfigPath, 0755); err != nil {
|
if err := os.MkdirAll(*flConfigPath, 0755); err != nil {
|
||||||
return errors.Wrapf(err, "creating config directory %s", *flConfigPath)
|
return errors.Wrapf(err, "creating config directory %s", *flConfigPath)
|
||||||
}
|
}
|
||||||
sm := &server{
|
sm := &server.Server{
|
||||||
configPath: *flConfigPath,
|
ConfigPath: *flConfigPath,
|
||||||
ServerPublicURL: strings.TrimRight(*flServerURL, "/"),
|
ServerPublicURL: strings.TrimRight(*flServerURL, "/"),
|
||||||
APNSCertificatePath: *flAPNSCertPath,
|
APNSCertificatePath: *flAPNSCertPath,
|
||||||
APNSPrivateKeyPass: *flAPNSKeyPass,
|
APNSPrivateKeyPass: *flAPNSKeyPass,
|
||||||
APNSPrivateKeyPath: *flAPNSKeyPath,
|
APNSPrivateKeyPath: *flAPNSKeyPath,
|
||||||
depsim: *flDepSim,
|
Depsim: *flDepSim,
|
||||||
tlsCertPath: *flTLSCert,
|
TLSCertPath: *flTLSCert,
|
||||||
CommandWebhookURL: *flCommandWebhookURL,
|
CommandWebhookURL: *flCommandWebhookURL,
|
||||||
|
|
||||||
webhooksHTTPClient: &http.Client{Timeout: time.Second * 30},
|
WebhooksHTTPClient: &http.Client{Timeout: time.Second * 30},
|
||||||
|
|
||||||
// TODO: we have a static SCEP challenge password here to prevent
|
// TODO: we have a static SCEP challenge password here to prevent
|
||||||
// being prompted for the SCEP challenge which happens in a "normal"
|
// being prompted for the SCEP challenge which happens in a "normal"
|
||||||
@@ -156,78 +140,60 @@ func serve(args []string) error {
|
|||||||
SCEPChallenge: "micromdm",
|
SCEPChallenge: "micromdm",
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.setupPubSub()
|
if err := sm.Setup(logger); err != nil {
|
||||||
sm.setupBolt()
|
stdlog.Fatal(err)
|
||||||
sm.setupRemoveService()
|
}
|
||||||
sm.setupConfigStore()
|
syncer, err := sm.CreateDEPSyncer(logger)
|
||||||
sm.loadPushCerts()
|
if err != nil {
|
||||||
sm.setupSCEP(logger)
|
stdlog.Fatal(err)
|
||||||
sm.setupPushService(logger)
|
|
||||||
sm.setupCommandService()
|
|
||||||
sm.setupWebhooks(logger)
|
|
||||||
sm.setupCommandQueue(logger)
|
|
||||||
sm.setupDepClient()
|
|
||||||
syncer := sm.setupDEPSync(logger)
|
|
||||||
if sm.err != nil {
|
|
||||||
stdlog.Fatal(sm.err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeService block.Service
|
var removeService block.Service
|
||||||
{
|
{
|
||||||
svc, err := block.New(sm.removeDB)
|
svc, err := block.New(sm.RemoveDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdlog.Fatal(err)
|
stdlog.Fatal(err)
|
||||||
}
|
}
|
||||||
removeService = block.LoggingMiddleware(logger)(svc)
|
removeService = block.LoggingMiddleware(logger)(svc)
|
||||||
}
|
}
|
||||||
|
|
||||||
devDB, err := devicebuiltin.NewDB(sm.db)
|
devDB, err := devicebuiltin.NewDB(sm.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdlog.Fatal(err)
|
stdlog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
devWorker := device.NewWorker(devDB, sm.pubclient, logger)
|
devWorker := device.NewWorker(devDB, sm.PubClient, logger)
|
||||||
go devWorker.Run(context.Background())
|
go devWorker.Run(context.Background())
|
||||||
|
|
||||||
userDB, err := userbuiltin.NewDB(sm.db)
|
userDB, err := userbuiltin.NewDB(sm.DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdlog.Fatal(err)
|
stdlog.Fatal(err)
|
||||||
}
|
}
|
||||||
userWorker := user.NewWorker(userDB, sm.pubclient, logger)
|
userWorker := user.NewWorker(userDB, sm.PubClient, logger)
|
||||||
go userWorker.Run(context.Background())
|
go userWorker.Run(context.Background())
|
||||||
|
|
||||||
sm.profileDB, err = profilebuiltin.NewDB(sm.db)
|
bpDB, err := blueprintbuiltin.NewDB(sm.DB, sm.ProfileDB, userDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdlog.Fatal(err)
|
stdlog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.setupEnrollmentService()
|
if err := bpDB.StartListener(sm.PubClient, sm.CommandService); err != nil {
|
||||||
if sm.err != nil {
|
|
||||||
stdlog.Fatalf("enrollment service: %s", sm.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bpDB, err := blueprintbuiltin.NewDB(sm.db, sm.profileDB, userDB)
|
|
||||||
if err != nil {
|
|
||||||
stdlog.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bpDB.StartListener(sm.pubclient, sm.commandService); err != nil {
|
|
||||||
stdlog.Fatal(err)
|
stdlog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
httpLogger := log.With(logger, "transport", "http")
|
httpLogger := log.With(logger, "transport", "http")
|
||||||
|
|
||||||
dc := sm.depClient
|
dc := sm.DEPClient
|
||||||
appDB := &appsbuiltin.Repo{Path: *flRepoPath}
|
appDB := &appsbuiltin.Repo{Path: *flRepoPath}
|
||||||
|
|
||||||
scepEndpoints := scep.MakeServerEndpoints(sm.scepService)
|
scepEndpoints := scep.MakeServerEndpoints(sm.SCEPService)
|
||||||
scepComponentLogger := log.With(logger, "component", "scep")
|
scepComponentLogger := log.With(logger, "component", "scep")
|
||||||
scepEndpoints.GetEndpoint = scep.EndpointLoggingMiddleware(scepComponentLogger)(scepEndpoints.GetEndpoint)
|
scepEndpoints.GetEndpoint = scep.EndpointLoggingMiddleware(scepComponentLogger)(scepEndpoints.GetEndpoint)
|
||||||
scepEndpoints.PostEndpoint = scep.EndpointLoggingMiddleware(scepComponentLogger)(scepEndpoints.PostEndpoint)
|
scepEndpoints.PostEndpoint = scep.EndpointLoggingMiddleware(scepComponentLogger)(scepEndpoints.PostEndpoint)
|
||||||
scepHandler := scep.MakeHTTPHandler(scepEndpoints, sm.scepService, scepComponentLogger)
|
scepHandler := scep.MakeHTTPHandler(scepEndpoints, sm.SCEPService, scepComponentLogger)
|
||||||
|
|
||||||
enrollHandlers := enroll.MakeHTTPHandlers(ctx, enroll.MakeServerEndpoints(sm.enrollService, sm.scepDepot), httptransport.ServerErrorLogger(httpLogger))
|
enrollHandlers := enroll.MakeHTTPHandlers(ctx, enroll.MakeServerEndpoints(sm.EnrollService, sm.SCEPDepot), httptransport.ServerErrorLogger(httpLogger))
|
||||||
|
|
||||||
r, options := httputil2.NewRouter(logger)
|
r, options := httputil2.NewRouter(logger)
|
||||||
|
|
||||||
@@ -242,26 +208,26 @@ func serve(args []string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
signatureVerifier := &mdmSignatureVerifier{db: sm.scepDepot}
|
signatureVerifier := &mdmSignatureVerifier{db: sm.SCEPDepot}
|
||||||
mdmEndpoints := mdm.MakeServerEndpoints(sm.mdmService)
|
mdmEndpoints := mdm.MakeServerEndpoints(sm.MDMService)
|
||||||
mdm.RegisterHTTPHandlers(r, mdmEndpoints, signatureVerifier, logger)
|
mdm.RegisterHTTPHandlers(r, mdmEndpoints, signatureVerifier, logger)
|
||||||
|
|
||||||
// API commands. Only handled if the user provides an api key.
|
// API commands. Only handled if the user provides an api key.
|
||||||
if *flAPIKey != "" {
|
if *flAPIKey != "" {
|
||||||
basicAuthEndpointMiddleware := basic.AuthMiddleware("micromdm", *flAPIKey, "micromdm")
|
basicAuthEndpointMiddleware := basic.AuthMiddleware("micromdm", *flAPIKey, "micromdm")
|
||||||
|
|
||||||
configsvc := config.New(sm.configDB)
|
configsvc := config.New(sm.ConfigDB)
|
||||||
configEndpoints := config.MakeServerEndpoints(configsvc, basicAuthEndpointMiddleware)
|
configEndpoints := config.MakeServerEndpoints(configsvc, basicAuthEndpointMiddleware)
|
||||||
config.RegisterHTTPHandlers(r, configEndpoints, options...)
|
config.RegisterHTTPHandlers(r, configEndpoints, options...)
|
||||||
|
|
||||||
apnsEndpoints := apns.MakeServerEndpoints(sm.pushService, basicAuthEndpointMiddleware)
|
apnsEndpoints := apns.MakeServerEndpoints(sm.APNSPushService, basicAuthEndpointMiddleware)
|
||||||
apns.RegisterHTTPHandlers(r, apnsEndpoints, options...)
|
apns.RegisterHTTPHandlers(r, apnsEndpoints, options...)
|
||||||
|
|
||||||
devicesvc := device.New(devDB)
|
devicesvc := device.New(devDB)
|
||||||
deviceEndpoints := device.MakeServerEndpoints(devicesvc, basicAuthEndpointMiddleware)
|
deviceEndpoints := device.MakeServerEndpoints(devicesvc, basicAuthEndpointMiddleware)
|
||||||
device.RegisterHTTPHandlers(r, deviceEndpoints, options...)
|
device.RegisterHTTPHandlers(r, deviceEndpoints, options...)
|
||||||
|
|
||||||
profilesvc := profile.New(sm.profileDB)
|
profilesvc := profile.New(sm.ProfileDB)
|
||||||
profileEndpoints := profile.MakeServerEndpoints(profilesvc, basicAuthEndpointMiddleware)
|
profileEndpoints := profile.MakeServerEndpoints(profilesvc, basicAuthEndpointMiddleware)
|
||||||
profile.RegisterHTTPHandlers(r, profileEndpoints, options...)
|
profile.RegisterHTTPHandlers(r, profileEndpoints, options...)
|
||||||
|
|
||||||
@@ -280,10 +246,10 @@ func serve(args []string) error {
|
|||||||
appEndpoints := appstore.MakeServerEndpoints(appsvc, basicAuthEndpointMiddleware)
|
appEndpoints := appstore.MakeServerEndpoints(appsvc, basicAuthEndpointMiddleware)
|
||||||
appstore.RegisterHTTPHandlers(r, appEndpoints, options...)
|
appstore.RegisterHTTPHandlers(r, appEndpoints, options...)
|
||||||
|
|
||||||
commandEndpoints := command.MakeServerEndpoints(sm.commandService, basicAuthEndpointMiddleware)
|
commandEndpoints := command.MakeServerEndpoints(sm.CommandService, basicAuthEndpointMiddleware)
|
||||||
command.RegisterHTTPHandlers(r, commandEndpoints, options...)
|
command.RegisterHTTPHandlers(r, commandEndpoints, options...)
|
||||||
|
|
||||||
depsvc := depapi.New(dc, sm.pubclient)
|
depsvc := depapi.New(dc, sm.PubClient)
|
||||||
depEndpoints := depapi.MakeServerEndpoints(depsvc, basicAuthEndpointMiddleware)
|
depEndpoints := depapi.MakeServerEndpoints(depsvc, basicAuthEndpointMiddleware)
|
||||||
depapi.RegisterHTTPHandlers(r, depEndpoints, options...)
|
depapi.RegisterHTTPHandlers(r, depEndpoints, options...)
|
||||||
|
|
||||||
@@ -320,7 +286,7 @@ func serve(args []string) error {
|
|||||||
logger,
|
logger,
|
||||||
*flTLSCert,
|
*flTLSCert,
|
||||||
*flTLSKey,
|
*flTLSKey,
|
||||||
sm.configPath,
|
sm.ConfigPath,
|
||||||
*flTLS,
|
*flTLS,
|
||||||
)
|
)
|
||||||
err = httputil.ListenAndServe(serveOpts...)
|
err = httputil.ListenAndServe(serveOpts...)
|
||||||
@@ -372,390 +338,6 @@ func printExamples() {
|
|||||||
fmt.Println(exampleText)
|
fmt.Println(exampleText)
|
||||||
}
|
}
|
||||||
|
|
||||||
type server struct {
|
|
||||||
configPath string
|
|
||||||
depsim string
|
|
||||||
pubclient pubsub.PublishSubscriber
|
|
||||||
db *bolt.DB
|
|
||||||
pushCert pushServiceCert
|
|
||||||
ServerPublicURL string
|
|
||||||
SCEPChallenge string
|
|
||||||
APNSPrivateKeyPath string
|
|
||||||
APNSCertificatePath string
|
|
||||||
APNSPrivateKeyPass string
|
|
||||||
tlsCertPath string
|
|
||||||
scepDepot *boltdepot.Depot
|
|
||||||
profileDB profile.Store
|
|
||||||
configDB config.Store
|
|
||||||
removeDB block.Store
|
|
||||||
CommandWebhookURL string
|
|
||||||
depClient dep.Client
|
|
||||||
|
|
||||||
// TODO: refactor enroll service and remove the need to reference
|
|
||||||
// this on-disk cert. but it might be useful to keep the PEM
|
|
||||||
// around for anyone who will need to export the CA.
|
|
||||||
scepCACertPath string
|
|
||||||
|
|
||||||
PushService *push.Service // bufford push
|
|
||||||
pushService apns.Service
|
|
||||||
mdmService mdm.Service
|
|
||||||
enrollService enroll.Service
|
|
||||||
scepService scep.Service
|
|
||||||
commandService command.Service
|
|
||||||
configService config.Service
|
|
||||||
|
|
||||||
webhooksHTTPClient *http.Client
|
|
||||||
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupPubSub() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.pubclient = inmem.NewPubSub()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupCommandService() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.commandService, c.err = command.New(c.pubclient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupWebhooks(logger log.Logger) {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CommandWebhookURL == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
ww := webhook.New(c.CommandWebhookURL, c.pubclient, webhook.WithLogger(logger), webhook.WithHTTPClient(c.webhooksHTTPClient))
|
|
||||||
go ww.Run(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupRemoveService() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
removeDB, err := blockbuiltin.NewDB(c.db)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.removeDB = removeDB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupCommandQueue(logger log.Logger) {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
q, err := queue.NewQueue(c.db, c.pubclient)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var mdmService mdm.Service
|
|
||||||
{
|
|
||||||
svc := mdm.NewService(c.pubclient, q)
|
|
||||||
mdmService = svc
|
|
||||||
mdmService = block.RemoveMiddleware(c.removeDB)(mdmService)
|
|
||||||
}
|
|
||||||
c.mdmService = mdmService
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupBolt() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dbPath := filepath.Join(c.configPath, "micromdm.db")
|
|
||||||
db, err := bolt.Open(dbPath, 0644, &bolt.Options{Timeout: time.Second})
|
|
||||||
if err != nil {
|
|
||||||
c.err = errors.Wrap(err, "opening boltdb")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.db = db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) loadPushCerts() {
|
|
||||||
if c.APNSCertificatePath == "" && c.APNSPrivateKeyPass == "" && c.APNSPrivateKeyPath == "" {
|
|
||||||
// this is optional, config could also be provided with mdmctl
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.APNSPrivateKeyPath == "" {
|
|
||||||
var pkcs12Data []byte
|
|
||||||
pkcs12Data, c.err = ioutil.ReadFile(c.APNSCertificatePath)
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.pushCert.PrivateKey, c.pushCert.Certificate, c.err =
|
|
||||||
pkcs12.Decode(pkcs12Data, c.APNSPrivateKeyPass)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.pushCert.Certificate, c.err = crypto.ReadPEMCertificateFile(c.APNSCertificatePath)
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var pemData []byte
|
|
||||||
pemData, c.err = ioutil.ReadFile(c.APNSPrivateKeyPath)
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pkeyBlock := new(bytes.Buffer)
|
|
||||||
pemBlock, _ := pem.Decode(pemData)
|
|
||||||
if pemBlock == nil {
|
|
||||||
c.err = errors.New("invalid PEM data for privkey")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if x509.IsEncryptedPEMBlock(pemBlock) {
|
|
||||||
b, err := x509.DecryptPEMBlock(pemBlock, []byte(c.APNSPrivateKeyPass))
|
|
||||||
if err != nil {
|
|
||||||
c.err = fmt.Errorf("decrypting DES private key %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pkeyBlock.Write(b)
|
|
||||||
} else {
|
|
||||||
pkeyBlock.Write(pemBlock.Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, err := x509.ParsePKCS1PrivateKey(pkeyBlock.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
c.err = fmt.Errorf("parsing pkcs1 private key: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.pushCert.PrivateKey = priv
|
|
||||||
}
|
|
||||||
|
|
||||||
type pushServiceCert struct {
|
|
||||||
*x509.Certificate
|
|
||||||
PrivateKey interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupConfigStore() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
db, err := configbuiltin.NewDB(c.db, c.pubclient)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.configDB = db
|
|
||||||
c.configService = config.New(db)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupPushService(logger log.Logger) {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts []apns.Option
|
|
||||||
{
|
|
||||||
cert, _ := c.configDB.PushCertificate()
|
|
||||||
if c.pushCert.Certificate != nil && cert == nil {
|
|
||||||
cert = &tls.Certificate{
|
|
||||||
Certificate: [][]byte{c.pushCert.Certificate.Raw},
|
|
||||||
PrivateKey: c.pushCert.PrivateKey,
|
|
||||||
Leaf: c.pushCert.Certificate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cert == nil {
|
|
||||||
goto after
|
|
||||||
}
|
|
||||||
client, err := push.NewClient(*cert)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
svc := push.NewService(client, push.Production)
|
|
||||||
opts = append(opts, apns.WithPushService(svc))
|
|
||||||
}
|
|
||||||
after:
|
|
||||||
|
|
||||||
db, err := apnsbuiltin.NewDB(c.db, c.pubclient)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
service, err := apns.New(db, c.configDB, c.pubclient, opts...)
|
|
||||||
if err != nil {
|
|
||||||
c.err = errors.Wrap(err, "starting micromdm push service")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.pushService = apns.LoggingMiddleware(
|
|
||||||
log.With(level.Info(logger), "component", "apns"),
|
|
||||||
)(service)
|
|
||||||
|
|
||||||
pushinfoWorker := apns.NewWorker(db, c.pubclient, logger)
|
|
||||||
go pushinfoWorker.Run(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupEnrollmentService() {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var topicProvider enroll.TopicProvider
|
|
||||||
if c.pushCert.Certificate != nil {
|
|
||||||
pushTopic, err := crypto.TopicFromCert(c.pushCert.Certificate)
|
|
||||||
if err != nil {
|
|
||||||
c.err = errors.Wrap(err, "get apns topic from certificate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
topicProvider = staticTopicProvider{topic: pushTopic}
|
|
||||||
} else {
|
|
||||||
topicProvider = c.configDB
|
|
||||||
}
|
|
||||||
|
|
||||||
var SCEPCertificateSubject string
|
|
||||||
// TODO: clean up order of inputs. Maybe pass *SCEPConfig as an arg?
|
|
||||||
// but if you do, the packages are coupled, better not.
|
|
||||||
c.enrollService, c.err = enroll.NewService(
|
|
||||||
topicProvider,
|
|
||||||
c.pubclient,
|
|
||||||
c.scepCACertPath,
|
|
||||||
c.ServerPublicURL+"/scep",
|
|
||||||
c.SCEPChallenge,
|
|
||||||
c.ServerPublicURL,
|
|
||||||
c.tlsCertPath,
|
|
||||||
SCEPCertificateSubject,
|
|
||||||
c.profileDB,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the apns-cert flags are specified this provider will be used in the enroll service.
|
|
||||||
type staticTopicProvider struct{ topic string }
|
|
||||||
|
|
||||||
func (p staticTopicProvider) PushTopic() (string, error) {
|
|
||||||
return p.topic, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupDepClient() (dep.Client, error) {
|
|
||||||
if c.err != nil {
|
|
||||||
return nil, c.err
|
|
||||||
}
|
|
||||||
// depsim config
|
|
||||||
depsim := c.depsim
|
|
||||||
var conf *dep.Config
|
|
||||||
|
|
||||||
// try getting the oauth config from bolt
|
|
||||||
tokens, err := c.configDB.DEPTokens()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(tokens) >= 1 {
|
|
||||||
conf = new(dep.Config)
|
|
||||||
conf.ConsumerSecret = tokens[0].ConsumerSecret
|
|
||||||
conf.ConsumerKey = tokens[0].ConsumerKey
|
|
||||||
conf.AccessSecret = tokens[0].AccessSecret
|
|
||||||
conf.AccessToken = tokens[0].AccessToken
|
|
||||||
// TODO: handle expiration
|
|
||||||
}
|
|
||||||
|
|
||||||
// override with depsim keys if specified on CLI
|
|
||||||
if depsim != "" {
|
|
||||||
conf = &dep.Config{
|
|
||||||
ConsumerKey: "CK_48dd68d198350f51258e885ce9a5c37ab7f98543c4a697323d75682a6c10a32501cb247e3db08105db868f73f2c972bdb6ae77112aea803b9219eb52689d42e6",
|
|
||||||
ConsumerSecret: "CS_34c7b2b531a600d99a0e4edcf4a78ded79b86ef318118c2f5bcfee1b011108c32d5302df801adbe29d446eb78f02b13144e323eb9aad51c79f01e50cb45c3a68",
|
|
||||||
AccessToken: "AT_927696831c59ba510cfe4ec1a69e5267c19881257d4bca2906a99d0785b785a6f6fdeb09774954fdd5e2d0ad952e3af52c6d8d2f21c924ba0caf4a031c158b89",
|
|
||||||
AccessSecret: "AS_c31afd7a09691d83548489336e8ff1cb11b82b6bca13f793344496a556b1f4972eaff4dde6deb5ac9cf076fdfa97ec97699c34d515947b9cf9ed31c99dded6ba",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
depServerURL := "https://mdmenrollment.apple.com"
|
|
||||||
if depsim != "" {
|
|
||||||
depServerURL = depsim
|
|
||||||
}
|
|
||||||
client, err := dep.NewClient(conf, dep.ServerURL(depServerURL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.depClient = client
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupDEPSync(logger log.Logger) depsync.Syncer {
|
|
||||||
if c.err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
client := c.depClient
|
|
||||||
opts := []depsync.Option{
|
|
||||||
depsync.WithLogger(log.With(logger, "component", "depsync")),
|
|
||||||
}
|
|
||||||
if client != nil {
|
|
||||||
opts = append(opts, depsync.WithClient(client))
|
|
||||||
}
|
|
||||||
|
|
||||||
var syncer depsync.Syncer
|
|
||||||
syncer, c.err = depsync.New(c.pubclient, c.db, logger, opts...)
|
|
||||||
if c.err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return syncer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *server) setupSCEP(logger log.Logger) {
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
depot, err := boltdepot.NewBoltDepot(c.db)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := depot.CreateOrLoadKey(2048)
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
caCert, err := depot.CreateOrLoadCA(key, 5, "MicroMDM", "US")
|
|
||||||
if err != nil {
|
|
||||||
c.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.scepCACertPath = filepath.Join(c.configPath, "SCEPCACert.pem")
|
|
||||||
|
|
||||||
c.err = crypto.WritePEMCertificateFile(caCert, c.scepCACertPath)
|
|
||||||
if c.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := []scep.ServiceOption{
|
|
||||||
scep.ClientValidity(365),
|
|
||||||
scep.ChallengePassword(c.SCEPChallenge),
|
|
||||||
}
|
|
||||||
c.scepDepot = depot
|
|
||||||
c.scepService, c.err = scep.NewService(depot, opts...)
|
|
||||||
if c.err == nil {
|
|
||||||
c.scepService = scep.NewLoggingService(logger, c.scepService)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type mdmSignatureVerifier struct {
|
type mdmSignatureVerifier struct {
|
||||||
db *boltdepot.Depot
|
db *boltdepot.Depot
|
||||||
}
|
}
|
||||||
|
|||||||
456
server/server.go
Normal file
456
server/server.go
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/RobotsAndPencils/buford/push"
|
||||||
|
"github.com/boltdb/bolt"
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/go-kit/kit/log/level"
|
||||||
|
"github.com/micromdm/dep"
|
||||||
|
boltdepot "github.com/micromdm/scep/depot/bolt"
|
||||||
|
scep "github.com/micromdm/scep/server"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/crypto/pkcs12"
|
||||||
|
|
||||||
|
"github.com/micromdm/micromdm/dep/depsync"
|
||||||
|
"github.com/micromdm/micromdm/mdm"
|
||||||
|
"github.com/micromdm/micromdm/mdm/enroll"
|
||||||
|
"github.com/micromdm/micromdm/pkg/crypto"
|
||||||
|
"github.com/micromdm/micromdm/platform/apns"
|
||||||
|
apnsbuiltin "github.com/micromdm/micromdm/platform/apns/builtin"
|
||||||
|
"github.com/micromdm/micromdm/platform/command"
|
||||||
|
"github.com/micromdm/micromdm/platform/config"
|
||||||
|
configbuiltin "github.com/micromdm/micromdm/platform/config/builtin"
|
||||||
|
"github.com/micromdm/micromdm/platform/profile"
|
||||||
|
profilebuiltin "github.com/micromdm/micromdm/platform/profile/builtin"
|
||||||
|
"github.com/micromdm/micromdm/platform/pubsub"
|
||||||
|
"github.com/micromdm/micromdm/platform/pubsub/inmem"
|
||||||
|
"github.com/micromdm/micromdm/platform/queue"
|
||||||
|
block "github.com/micromdm/micromdm/platform/remove"
|
||||||
|
blockbuiltin "github.com/micromdm/micromdm/platform/remove/builtin"
|
||||||
|
"github.com/micromdm/micromdm/workflow/webhook"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
ConfigPath string
|
||||||
|
Depsim string
|
||||||
|
PubClient pubsub.PublishSubscriber
|
||||||
|
DB *bolt.DB
|
||||||
|
PushCert pushServiceCert
|
||||||
|
ServerPublicURL string
|
||||||
|
SCEPChallenge string
|
||||||
|
APNSPrivateKeyPath string
|
||||||
|
APNSCertificatePath string
|
||||||
|
APNSPrivateKeyPass string
|
||||||
|
TLSCertPath string
|
||||||
|
SCEPDepot *boltdepot.Depot
|
||||||
|
ProfileDB profile.Store
|
||||||
|
ConfigDB config.Store
|
||||||
|
RemoveDB block.Store
|
||||||
|
CommandWebhookURL string
|
||||||
|
DEPClient dep.Client
|
||||||
|
|
||||||
|
// TODO: refactor enroll service and remove the need to reference
|
||||||
|
// this on-disk cert. but it might be useful to keep the PEM
|
||||||
|
// around for anyone who will need to export the CA.
|
||||||
|
SCEPCACertPath string
|
||||||
|
|
||||||
|
PushService *push.Service // bufford push
|
||||||
|
APNSPushService apns.Service
|
||||||
|
CommandService command.Service
|
||||||
|
MDMService mdm.Service
|
||||||
|
EnrollService enroll.Service
|
||||||
|
SCEPService scep.Service
|
||||||
|
ConfigService config.Service
|
||||||
|
|
||||||
|
WebhooksHTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) Setup(logger log.Logger) error {
|
||||||
|
if err := c.setupPubSub(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupBolt(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupRemoveService(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupConfigStore(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.loadPushCerts(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupSCEP(logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupPushService(logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupCommandService(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupWebhooks(logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupCommandQueue(logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupDepClient(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupProfileDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.setupEnrollmentService(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupProfileDB() error {
|
||||||
|
profileDB, err := profilebuiltin.NewDB(c.DB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.ProfileDB = profileDB
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupPubSub() error {
|
||||||
|
c.PubClient = inmem.NewPubSub()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupWebhooks(logger log.Logger) error {
|
||||||
|
if c.CommandWebhookURL == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ww := webhook.New(c.CommandWebhookURL, c.PubClient, webhook.WithLogger(logger), webhook.WithHTTPClient(c.WebhooksHTTPClient))
|
||||||
|
go ww.Run(ctx)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupRemoveService() error {
|
||||||
|
removeDB, err := blockbuiltin.NewDB(c.DB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.RemoveDB = removeDB
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupCommandService() error {
|
||||||
|
commandService, err := command.New(c.PubClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.CommandService = commandService
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupCommandQueue(logger log.Logger) error {
|
||||||
|
q, err := queue.NewQueue(c.DB, c.PubClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mdmService mdm.Service
|
||||||
|
{
|
||||||
|
svc := mdm.NewService(c.PubClient, q)
|
||||||
|
mdmService = svc
|
||||||
|
mdmService = block.RemoveMiddleware(c.RemoveDB)(mdmService)
|
||||||
|
}
|
||||||
|
c.MDMService = mdmService
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupBolt() error {
|
||||||
|
dbPath := filepath.Join(c.ConfigPath, "micromdm.db")
|
||||||
|
db, err := bolt.Open(dbPath, 0644, &bolt.Options{Timeout: time.Second})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "opening boltdb")
|
||||||
|
}
|
||||||
|
c.DB = db
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) loadPushCerts() error {
|
||||||
|
if c.APNSCertificatePath == "" && c.APNSPrivateKeyPass == "" && c.APNSPrivateKeyPath == "" {
|
||||||
|
// this is optional, config could also be provided with mdmctl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.APNSPrivateKeyPath == "" {
|
||||||
|
var pkcs12Data []byte
|
||||||
|
|
||||||
|
pkcs12Data, err = ioutil.ReadFile(c.APNSCertificatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.PushCert.PrivateKey, c.PushCert.Certificate, err =
|
||||||
|
pkcs12.Decode(pkcs12Data, c.APNSPrivateKeyPass)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.PushCert.Certificate, err = crypto.ReadPEMCertificateFile(c.APNSCertificatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pemData []byte
|
||||||
|
pemData, err = ioutil.ReadFile(c.APNSPrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkeyBlock := new(bytes.Buffer)
|
||||||
|
pemBlock, _ := pem.Decode(pemData)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return errors.New("invalid PEM data for privkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
if x509.IsEncryptedPEMBlock(pemBlock) {
|
||||||
|
b, err := x509.DecryptPEMBlock(pemBlock, []byte(c.APNSPrivateKeyPass))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decrypting DES private key %s", err)
|
||||||
|
}
|
||||||
|
pkeyBlock.Write(b)
|
||||||
|
} else {
|
||||||
|
pkeyBlock.Write(pemBlock.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := x509.ParsePKCS1PrivateKey(pkeyBlock.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing pkcs1 private key: %s", err)
|
||||||
|
}
|
||||||
|
c.PushCert.PrivateKey = priv
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pushServiceCert struct {
|
||||||
|
*x509.Certificate
|
||||||
|
PrivateKey interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupConfigStore() error {
|
||||||
|
db, err := configbuiltin.NewDB(c.DB, c.PubClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.ConfigDB = db
|
||||||
|
c.ConfigService = config.New(db)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupPushService(logger log.Logger) error {
|
||||||
|
var opts []apns.Option
|
||||||
|
{
|
||||||
|
cert, _ := c.ConfigDB.PushCertificate()
|
||||||
|
if c.PushCert.Certificate != nil && cert == nil {
|
||||||
|
cert = &tls.Certificate{
|
||||||
|
Certificate: [][]byte{c.PushCert.Certificate.Raw},
|
||||||
|
PrivateKey: c.PushCert.PrivateKey,
|
||||||
|
Leaf: c.PushCert.Certificate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
goto after
|
||||||
|
}
|
||||||
|
client, err := push.NewClient(*cert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
svc := push.NewService(client, push.Production)
|
||||||
|
opts = append(opts, apns.WithPushService(svc))
|
||||||
|
}
|
||||||
|
after:
|
||||||
|
|
||||||
|
db, err := apnsbuiltin.NewDB(c.DB, c.PubClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
service, err := apns.New(db, c.ConfigDB, c.PubClient, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "starting micromdm push service")
|
||||||
|
}
|
||||||
|
c.APNSPushService = apns.LoggingMiddleware(
|
||||||
|
log.With(level.Info(logger), "component", "apns"),
|
||||||
|
)(service)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupEnrollmentService() error {
|
||||||
|
var topicProvider enroll.TopicProvider
|
||||||
|
var err error
|
||||||
|
if c.PushCert.Certificate != nil {
|
||||||
|
pushTopic, err := crypto.TopicFromCert(c.PushCert.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "get apns topic from certificate")
|
||||||
|
}
|
||||||
|
topicProvider = staticTopicProvider{topic: pushTopic}
|
||||||
|
} else {
|
||||||
|
topicProvider = c.ConfigDB
|
||||||
|
}
|
||||||
|
|
||||||
|
var SCEPCertificateSubject string
|
||||||
|
// TODO: clean up order of inputs. Maybe pass *SCEPConfig as an arg?
|
||||||
|
// but if you do, the packages are coupled, better not.
|
||||||
|
c.EnrollService, err = enroll.NewService(
|
||||||
|
topicProvider,
|
||||||
|
c.PubClient,
|
||||||
|
c.SCEPCACertPath,
|
||||||
|
c.ServerPublicURL+"/scep",
|
||||||
|
c.SCEPChallenge,
|
||||||
|
c.ServerPublicURL,
|
||||||
|
c.TLSCertPath,
|
||||||
|
SCEPCertificateSubject,
|
||||||
|
c.ProfileDB,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the apns-cert flags are specified this provider will be used in the enroll service.
|
||||||
|
type staticTopicProvider struct{ topic string }
|
||||||
|
|
||||||
|
func (p staticTopicProvider) PushTopic() (string, error) {
|
||||||
|
return p.topic, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupDepClient() error {
|
||||||
|
// depsim config
|
||||||
|
depsim := c.Depsim
|
||||||
|
var conf *dep.Config
|
||||||
|
|
||||||
|
// try getting the oauth config from bolt
|
||||||
|
tokens, err := c.ConfigDB.DEPTokens()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tokens) >= 1 {
|
||||||
|
conf = new(dep.Config)
|
||||||
|
conf.ConsumerSecret = tokens[0].ConsumerSecret
|
||||||
|
conf.ConsumerKey = tokens[0].ConsumerKey
|
||||||
|
conf.AccessSecret = tokens[0].AccessSecret
|
||||||
|
conf.AccessToken = tokens[0].AccessToken
|
||||||
|
// TODO: handle expiration
|
||||||
|
}
|
||||||
|
|
||||||
|
// override with depsim keys if specified on CLI
|
||||||
|
if depsim != "" {
|
||||||
|
conf = &dep.Config{
|
||||||
|
ConsumerKey: "CK_48dd68d198350f51258e885ce9a5c37ab7f98543c4a697323d75682a6c10a32501cb247e3db08105db868f73f2c972bdb6ae77112aea803b9219eb52689d42e6",
|
||||||
|
ConsumerSecret: "CS_34c7b2b531a600d99a0e4edcf4a78ded79b86ef318118c2f5bcfee1b011108c32d5302df801adbe29d446eb78f02b13144e323eb9aad51c79f01e50cb45c3a68",
|
||||||
|
AccessToken: "AT_927696831c59ba510cfe4ec1a69e5267c19881257d4bca2906a99d0785b785a6f6fdeb09774954fdd5e2d0ad952e3af52c6d8d2f21c924ba0caf4a031c158b89",
|
||||||
|
AccessSecret: "AS_c31afd7a09691d83548489336e8ff1cb11b82b6bca13f793344496a556b1f4972eaff4dde6deb5ac9cf076fdfa97ec97699c34d515947b9cf9ed31c99dded6ba",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
depServerURL := "https://mdmenrollment.apple.com"
|
||||||
|
if depsim != "" {
|
||||||
|
depServerURL = depsim
|
||||||
|
}
|
||||||
|
client, err := dep.NewClient(conf, dep.ServerURL(depServerURL))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.DEPClient = client
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) CreateDEPSyncer(logger log.Logger) (depsync.Syncer, error) {
|
||||||
|
client := c.DEPClient
|
||||||
|
opts := []depsync.Option{
|
||||||
|
depsync.WithLogger(log.With(logger, "component", "depsync")),
|
||||||
|
}
|
||||||
|
if client != nil {
|
||||||
|
opts = append(opts, depsync.WithClient(client))
|
||||||
|
}
|
||||||
|
|
||||||
|
var syncer depsync.Syncer
|
||||||
|
syncer, err := depsync.New(c.PubClient, c.DB, logger, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return syncer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Server) setupSCEP(logger log.Logger) error {
|
||||||
|
depot, err := boltdepot.NewBoltDepot(c.DB)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := depot.CreateOrLoadKey(2048)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err := depot.CreateOrLoadCA(key, 5, "MicroMDM", "US")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SCEPCACertPath = filepath.Join(c.ConfigPath, "SCEPCACert.pem")
|
||||||
|
|
||||||
|
err = crypto.WritePEMCertificateFile(caCert, c.SCEPCACertPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []scep.ServiceOption{
|
||||||
|
scep.ClientValidity(365),
|
||||||
|
scep.ChallengePassword(c.SCEPChallenge),
|
||||||
|
}
|
||||||
|
c.SCEPDepot = depot
|
||||||
|
c.SCEPService, err = scep.NewService(depot, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.SCEPService = scep.NewLoggingService(logger, c.SCEPService)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package server
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
@@ -8,24 +8,24 @@ func TestLoadPushCerts(t *testing.T) {
|
|||||||
p12path := "testdata/pushcert.p12"
|
p12path := "testdata/pushcert.p12"
|
||||||
keysecret := "secret"
|
keysecret := "secret"
|
||||||
|
|
||||||
cfg := &server{
|
s := &Server{
|
||||||
APNSPrivateKeyPath: keypath,
|
APNSPrivateKeyPath: keypath,
|
||||||
APNSCertificatePath: certpath,
|
APNSCertificatePath: certpath,
|
||||||
APNSPrivateKeyPass: keysecret,
|
APNSPrivateKeyPass: keysecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
// test separate key and cert
|
// test separate key and cert
|
||||||
cfg.loadPushCerts()
|
err := s.loadPushCerts()
|
||||||
if cfg.err != nil {
|
if err != nil {
|
||||||
t.Fatal(cfg.err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test p12 with secret
|
// test p12 with secret
|
||||||
cfg.APNSCertificatePath = p12path
|
s.APNSCertificatePath = p12path
|
||||||
cfg.APNSPrivateKeyPath = ""
|
s.APNSPrivateKeyPath = ""
|
||||||
cfg.loadPushCerts()
|
err = s.loadPushCerts()
|
||||||
if cfg.err != nil {
|
if err != nil {
|
||||||
t.Fatal(cfg.err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user