diff --git a/cmd/micromdm/serve.go b/cmd/micromdm/serve.go index bc518cec..b6f97a90 100644 --- a/cmd/micromdm/serve.go +++ b/cmd/micromdm/serve.go @@ -125,6 +125,7 @@ func serve(args []string) error { flValidateSCEPExpiration = flagset.Bool("validate-scep-expiration", env.Bool("MICROMDM_VALIDATE_SCEP_EXPIRATION", false), "validate that the SCEP certificate is still valid") flPrintArgs = flagset.Bool("print-flags", false, "Print all flags and their values") flQueue = flagset.String("queue", env.String("MICROMDM_QUEUE", "builtin"), "command queue type") + flDMURL = flagset.String("dm", env.String("DM", ""), "URL to send Declarative Management requests to") ) flagset.Usage = usageFor(flagset, "micromdm serve [flags]") if err := flagset.Parse(args); err != nil { @@ -177,6 +178,7 @@ func serve(args []string) error { SCEPClientValidity: *flSCEPClientValidity, Queue: *flQueue, + DMURL: *flDMURL, } if !sm.UseDynSCEPChallenge { // TODO: we have a static SCEP challenge password here to prevent diff --git a/mdm/checkin.go b/mdm/checkin.go index 7a31218c..b2d78519 100644 --- a/mdm/checkin.go +++ b/mdm/checkin.go @@ -2,6 +2,8 @@ package mdm import ( "context" + stderrors "errors" + "fmt" "net/http" "time" @@ -16,6 +18,10 @@ type BootstrapToken struct { BootstrapToken []byte } +type DeclarativeManagement interface { + DeclarativeManagement(ctx context.Context, id, endpoint string, data []byte) ([]byte, error) +} + func (svc *MDMService) Checkin(ctx context.Context, event CheckinEvent) ([]byte, error) { // reject user settings at the loginwindow. // https://github.com/micromdm/micromdm/pull/379 @@ -33,15 +39,14 @@ func (svc *MDMService) Checkin(ctx context.Context, event CheckinEvent) ([]byte, return nil, errors.Wrap(err, "get checkin topic from message") } - if topic == AuthenticateTopic { + var resp []byte + + switch topic { + case AuthenticateTopic: if err := svc.queue.Clear(ctx, event); err != nil { return nil, errors.Wrap(err, "clearing queue on enrollment attempt") } - } - - var resp []byte - - if topic == GetBootstrapTokenTopic { + case GetBootstrapTokenTopic: udid := event.Command.UDID btBytes, err := svc.dev.GetBootstrapToken(ctx, udid) @@ -55,6 +60,23 @@ func (svc *MDMService) Checkin(ctx context.Context, event CheckinEvent) ([]byte, if err != nil { return nil, errors.Wrap(err, "marshal bootstrap token") } + case DeclarativeManagementTopic: + if svc.dm == nil { + return nil, stderrors.New("no Declarative Management handler") + } + + udid := event.Command.UDID + if event.Command.UserID != "" { + udid = event.Command.UserID + } + if event.Command.EnrollmentID != "" { + udid = event.Command.EnrollmentID + } + + resp, err = svc.dm.DeclarativeManagement(ctx, udid, event.Command.Endpoint, event.Command.Data) + if err != nil { + return resp, fmt.Errorf("declarative management: %w", err) + } } err = svc.pub.Publish(ctx, topic, msg) @@ -73,6 +95,8 @@ func topicFromMessage(messageType string) (string, error) { return GetBootstrapTokenTopic, nil case "SetBootstrapToken": return SetBootstrapTokenTopic, nil + case "DeclarativeManagement": + return DeclarativeManagementTopic, nil default: return "", errors.Errorf("unknown checkin message type %s", messageType) } diff --git a/mdm/checkin_event.go b/mdm/checkin_event.go index 2dbfba84..42eb5dad 100644 --- a/mdm/checkin_event.go +++ b/mdm/checkin_event.go @@ -30,6 +30,7 @@ type CheckinCommand struct { update getBootstrap setBootstrap + declarativeManagementMessage } // Authenticate Message Type @@ -74,6 +75,12 @@ type setBootstrap struct { BootstrapToken []byte } +// DeclarativeManagement Message Type +type declarativeManagementMessage struct { + Data []byte + Endpoint string +} + // data decodes to []byte, // we can then attach a string method to the type // Tokens are encoded as Hex Strings @@ -125,6 +132,11 @@ func MarshalCheckinEvent(e *CheckinEvent) ([]byte, error) { BootstrapToken: e.Command.BootstrapToken, SetAwaitingConfiguration: e.Command.SetAwaitingConfiguration, } + case "DeclarativeManagament": + command.DeclarativeManagement = &checkinproto.DeclarativeManagement{ + Data: e.Command.Data, + Endpoint: e.Command.Endpoint, + } } return proto.Marshal(&checkinproto.Event{ Id: e.ID, @@ -179,6 +191,9 @@ func UnmarshalCheckinEvent(data []byte, e *CheckinEvent) error { case "SetBootstrapToken": e.Command.BootstrapToken = pb.Command.SetBootstrapToken.BootstrapToken e.Command.SetAwaitingConfiguration = pb.Command.SetBootstrapToken.SetAwaitingConfiguration + case "DeclarativeManagement": + e.Command.Data = pb.Command.DeclarativeManagement.Data + e.Command.Endpoint = pb.Command.DeclarativeManagement.Endpoint } e.Raw = pb.GetRaw() e.Params = pb.GetParams() diff --git a/mdm/internal/checkinproto/checkin.pb.go b/mdm/internal/checkinproto/checkin.pb.go index c59ebd6f..e7255a63 100644 --- a/mdm/internal/checkinproto/checkin.pb.go +++ b/mdm/internal/checkinproto/checkin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.21.2 +// protoc-gen-go v1.28.1 +// protoc v3.20.2 // source: checkin.proto package checkinproto @@ -104,14 +104,15 @@ type Command struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MessageType string `protobuf:"bytes,1,opt,name=message_type,json=messageType,proto3" json:"message_type,omitempty"` - Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"` - Udid string `protobuf:"bytes,3,opt,name=udid,proto3" json:"udid,omitempty"` - Authenticate *Authenticate `protobuf:"bytes,4,opt,name=authenticate,proto3" json:"authenticate,omitempty"` - TokenUpdate *TokenUpdate `protobuf:"bytes,5,opt,name=token_update,json=tokenUpdate,proto3" json:"token_update,omitempty"` - EnrollmentId string `protobuf:"bytes,6,opt,name=enrollment_id,json=enrollmentId,proto3" json:"enrollment_id,omitempty"` - GetBootstrapToken *GetBootstrapToken `protobuf:"bytes,7,opt,name=get_bootstrap_token,json=getBootstrapToken,proto3" json:"get_bootstrap_token,omitempty"` - SetBootstrapToken *SetBootstrapToken `protobuf:"bytes,8,opt,name=set_bootstrap_token,json=setBootstrapToken,proto3" json:"set_bootstrap_token,omitempty"` + MessageType string `protobuf:"bytes,1,opt,name=message_type,json=messageType,proto3" json:"message_type,omitempty"` + Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"` + Udid string `protobuf:"bytes,3,opt,name=udid,proto3" json:"udid,omitempty"` + Authenticate *Authenticate `protobuf:"bytes,4,opt,name=authenticate,proto3" json:"authenticate,omitempty"` + TokenUpdate *TokenUpdate `protobuf:"bytes,5,opt,name=token_update,json=tokenUpdate,proto3" json:"token_update,omitempty"` + EnrollmentId string `protobuf:"bytes,6,opt,name=enrollment_id,json=enrollmentId,proto3" json:"enrollment_id,omitempty"` + GetBootstrapToken *GetBootstrapToken `protobuf:"bytes,7,opt,name=get_bootstrap_token,json=getBootstrapToken,proto3" json:"get_bootstrap_token,omitempty"` + SetBootstrapToken *SetBootstrapToken `protobuf:"bytes,8,opt,name=set_bootstrap_token,json=setBootstrapToken,proto3" json:"set_bootstrap_token,omitempty"` + DeclarativeManagement *DeclarativeManagement `protobuf:"bytes,9,opt,name=declarative_management,json=declarativeManagement,proto3" json:"declarative_management,omitempty"` } func (x *Command) Reset() { @@ -202,6 +203,13 @@ func (x *Command) GetSetBootstrapToken() *SetBootstrapToken { return nil } +func (x *Command) GetDeclarativeManagement() *DeclarativeManagement { + if x != nil { + return x.DeclarativeManagement + } + return nil +} + type Authenticate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -526,6 +534,61 @@ func (x *SetBootstrapToken) GetSetAwaitingConfiguration() bool { return false } +type DeclarativeManagement struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` +} + +func (x *DeclarativeManagement) Reset() { + *x = DeclarativeManagement{} + if protoimpl.UnsafeEnabled { + mi := &file_checkin_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeclarativeManagement) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeclarativeManagement) ProtoMessage() {} + +func (x *DeclarativeManagement) ProtoReflect() protoreflect.Message { + mi := &file_checkin_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeclarativeManagement.ProtoReflect.Descriptor instead. +func (*DeclarativeManagement) Descriptor() ([]byte, []int) { + return file_checkin_proto_rawDescGZIP(), []int{6} +} + +func (x *DeclarativeManagement) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *DeclarativeManagement) GetEndpoint() string { + if x != nil { + return x.Endpoint + } + return "" +} + var File_checkin_proto protoreflect.FileDescriptor var file_checkin_proto_rawDesc = []byte{ @@ -545,7 +608,7 @@ var file_checkin_proto_rawDesc = []byte{ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x9b, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x21, + 0x38, 0x01, 0x22, 0xf7, 0x03, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, @@ -571,62 +634,72 @@ var file_checkin_proto_rawDesc = []byte{ 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x11, 0x73, 0x65, 0x74, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x22, 0xb6, 0x02, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, - 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, - 0x04, 0x69, 0x6d, 0x65, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6d, 0x65, - 0x69, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6d, 0x65, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, - 0x6e, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, - 0x65, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xa9, 0x02, 0x0a, 0x0b, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x73, 0x68, 0x4d, 0x61, 0x67, 0x69, 0x63, 0x12, 0x21, - 0x0a, 0x0c, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x35, 0x0a, 0x16, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x15, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x4c, - 0x6f, 0x6e, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x4f, 0x6e, 0x43, 0x6f, - 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x22, 0x51, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x6f, 0x74, - 0x73, 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x67, 0x65, - 0x74, 0x5f, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, - 0x67, 0x65, 0x74, 0x41, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x42, - 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x27, 0x0a, - 0x0f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, - 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x77, - 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x65, 0x74, 0x41, + 0x12, 0x5a, 0x0a, 0x16, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x15, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xb6, 0x02, 0x0a, + 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6d, 0x65, + 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6d, 0x65, 0x69, 0x12, 0x12, 0x0a, + 0x04, 0x6d, 0x65, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x69, + 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, + 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xa9, 0x02, 0x0a, 0x0b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, + 0x75, 0x73, 0x68, 0x5f, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x70, 0x75, 0x73, 0x68, 0x4d, 0x61, 0x67, 0x69, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x35, 0x0a, + 0x16, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x64, 0x6d, 0x2f, 0x6d, 0x69, 0x63, 0x72, - 0x6f, 0x6d, 0x64, 0x6d, 0x2f, 0x6d, 0x64, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x24, 0x0a, + 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x6f, 0x6e, 0x67, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x68, 0x6f, 0x72, + 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x73, + 0x65, 0x72, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, + 0x6f, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x4f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, + 0x65, 0x22, 0x51, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, + 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x77, + 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x67, 0x65, 0x74, 0x41, + 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x42, 0x6f, 0x6f, 0x74, 0x73, + 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x6f, 0x6f, + 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x12, 0x3c, 0x0a, 0x1a, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x77, 0x61, 0x69, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x73, 0x65, 0x74, 0x41, 0x77, 0x61, 0x69, 0x74, + 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x64, 0x6d, + 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x64, 0x6d, 0x2f, 0x6d, 0x64, 0x6d, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -641,28 +714,30 @@ func file_checkin_proto_rawDescGZIP() []byte { return file_checkin_proto_rawDescData } -var file_checkin_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_checkin_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_checkin_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: checkinproto.Event - (*Command)(nil), // 1: checkinproto.Command - (*Authenticate)(nil), // 2: checkinproto.Authenticate - (*TokenUpdate)(nil), // 3: checkinproto.TokenUpdate - (*GetBootstrapToken)(nil), // 4: checkinproto.GetBootstrapToken - (*SetBootstrapToken)(nil), // 5: checkinproto.SetBootstrapToken - nil, // 6: checkinproto.Event.ParamsEntry + (*Event)(nil), // 0: checkinproto.Event + (*Command)(nil), // 1: checkinproto.Command + (*Authenticate)(nil), // 2: checkinproto.Authenticate + (*TokenUpdate)(nil), // 3: checkinproto.TokenUpdate + (*GetBootstrapToken)(nil), // 4: checkinproto.GetBootstrapToken + (*SetBootstrapToken)(nil), // 5: checkinproto.SetBootstrapToken + (*DeclarativeManagement)(nil), // 6: checkinproto.DeclarativeManagement + nil, // 7: checkinproto.Event.ParamsEntry } var file_checkin_proto_depIdxs = []int32{ 1, // 0: checkinproto.Event.command:type_name -> checkinproto.Command - 6, // 1: checkinproto.Event.params:type_name -> checkinproto.Event.ParamsEntry + 7, // 1: checkinproto.Event.params:type_name -> checkinproto.Event.ParamsEntry 2, // 2: checkinproto.Command.authenticate:type_name -> checkinproto.Authenticate 3, // 3: checkinproto.Command.token_update:type_name -> checkinproto.TokenUpdate 4, // 4: checkinproto.Command.get_bootstrap_token:type_name -> checkinproto.GetBootstrapToken 5, // 5: checkinproto.Command.set_bootstrap_token:type_name -> checkinproto.SetBootstrapToken - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 6, // 6: checkinproto.Command.declarative_management:type_name -> checkinproto.DeclarativeManagement + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_checkin_proto_init() } @@ -743,6 +818,18 @@ func file_checkin_proto_init() { return nil } } + file_checkin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeclarativeManagement); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -750,7 +837,7 @@ func file_checkin_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_checkin_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/mdm/internal/checkinproto/checkin.proto b/mdm/internal/checkinproto/checkin.proto index b99bd92d..cbd4e6dc 100644 --- a/mdm/internal/checkinproto/checkin.proto +++ b/mdm/internal/checkinproto/checkin.proto @@ -21,6 +21,7 @@ message Command { string enrollment_id = 6; GetBootstrapToken get_bootstrap_token = 7; SetBootstrapToken set_bootstrap_token = 8; + DeclarativeManagement declarative_management = 9; } message Authenticate { @@ -55,3 +56,8 @@ message SetBootstrapToken { bytes bootstrap_token = 1; bool set_awaiting_configuration = 2; } + +message DeclarativeManagement { + bytes data = 1; + string endpoint = 2; +} \ No newline at end of file diff --git a/mdm/service.go b/mdm/service.go index 6bb1ef1e..f7687afd 100644 --- a/mdm/service.go +++ b/mdm/service.go @@ -21,12 +21,13 @@ type Service interface { type Middleware func(Service) Service const ( - ConnectTopic = "mdm.Connect" - AuthenticateTopic = "mdm.Authenticate" - TokenUpdateTopic = "mdm.TokenUpdate" - CheckoutTopic = "mdm.CheckOut" - GetBootstrapTokenTopic = "mdm.GetBootstrapToken" - SetBootstrapTokenTopic = "mdm.SetBootstrapToken" + ConnectTopic = "mdm.Connect" + AuthenticateTopic = "mdm.Authenticate" + TokenUpdateTopic = "mdm.TokenUpdate" + CheckoutTopic = "mdm.CheckOut" + GetBootstrapTokenTopic = "mdm.GetBootstrapToken" + SetBootstrapTokenTopic = "mdm.SetBootstrapToken" + DeclarativeManagementTopic = "mdm.DeclarativeManagement" ) // BootBootstrapTokenRetriever retrieves BootStrap Tokens for devices @@ -45,12 +46,14 @@ type MDMService struct { dev BootstrapTokenRetriever pub pubsub.Publisher queue Queue + dm DeclarativeManagement } -func NewService(pub pubsub.Publisher, queue Queue, dev BootstrapTokenRetriever) *MDMService { +func NewService(pub pubsub.Publisher, queue Queue, dev BootstrapTokenRetriever, dm DeclarativeManagement) *MDMService { return &MDMService{ dev: dev, pub: pub, queue: queue, + dm: dm, } } diff --git a/platform/command/server.go b/platform/command/server.go index 35d719a0..74c6cb68 100644 --- a/platform/command/server.go +++ b/platform/command/server.go @@ -30,8 +30,8 @@ func RegisterHTTPHandlers(r *mux.Router, e Endpoints, options ...httptransport.S options..., )) - // POST /v1/commands/udid Add new MDM Command with raw plist to device queue. - r.Methods("POST").Path("/v1/commands/{udid}").Handler(httptransport.NewServer( + // POST,PUT /v1/commands/udid Add new MDM Command with raw plist to device queue. + r.Methods("POST", "PUT").Path("/v1/commands/{udid}").Handler(httptransport.NewServer( e.NewRawCommandEndpoint, decodeNewRawCommandRequest, httputil.EncodeJSONResponse, diff --git a/platform/device/udidauth.go b/platform/device/udidauth.go index f7ac4c03..b2bc604b 100644 --- a/platform/device/udidauth.go +++ b/platform/device/udidauth.go @@ -94,23 +94,19 @@ func (mw *udidCertAuthMiddleware) Checkin(ctx context.Context, req mdm.CheckinEv if err != nil { return nil, errors.Wrap(err, "error retrieving device certificate") } - switch req.Command.MessageType { - case "Authenticate": + if req.Command.MessageType == "Authenticate" { // unconditionally save the cert hash on Authenticate message if err := mw.store.SaveUDIDCertHash([]byte(req.Command.UDID), hashCertRaw(devcert.Raw)); err != nil { return nil, err } return mw.next.Checkin(ctx, req) - case "TokenUpdate", "CheckOut", "GetBootstrapToken", "SetBootstrapToken": - matched, err := mw.validateUDIDCertAuth([]byte(req.Command.UDID), hashCertRaw(devcert.Raw)) - if err != nil { - return nil, err - } - if !matched && !mw.warnOnly { - return nil, errors.New("device certifcate UDID mismatch") - } - return mw.next.Checkin(ctx, req) - default: - return nil, errors.Errorf("unknown checkin message type %s", req.Command.MessageType) } + matched, err := mw.validateUDIDCertAuth([]byte(req.Command.UDID), hashCertRaw(devcert.Raw)) + if err != nil { + return nil, err + } + if !matched && !mw.warnOnly { + return nil, errors.New("device certifcate UDID mismatch") + } + return mw.next.Checkin(ctx, req) } diff --git a/server/dm.go b/server/dm.go new file mode 100644 index 00000000..9298cb48 --- /dev/null +++ b/server/dm.go @@ -0,0 +1,78 @@ +package server + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" +) + +const enrollmentIDHeader = "X-Enrollment-ID" + +type DeclarativeManagementHTTPCaller struct { + url *url.URL + client *http.Client +} + +// NewDeclarativeManagementHTTPCaller creates a new DeclarativeManagementHTTPCaller +func NewDeclarativeManagementHTTPCaller(urlPrefix string, client *http.Client) (*DeclarativeManagementHTTPCaller, error) { + url, err := url.Parse(urlPrefix) + return &DeclarativeManagementHTTPCaller{url: url, client: client}, err +} + +type HTTPStatusError struct { + Err error + Status int +} + +func (e HTTPStatusError) Error() string { + return e.Err.Error() +} + +func (e HTTPStatusError) StatusCode() int { + return e.Status +} + +// DeclarativeManagement calls out to an HTTP URL to handle the actual Declarative Management protocol +func (c *DeclarativeManagementHTTPCaller) DeclarativeManagement(ctx context.Context, id, endpoint string, data []byte) ([]byte, error) { + if c.url == nil { + return nil, errors.New("missing URL") + } + endpointURL, err := url.Parse(endpoint) + if err != nil { + return nil, fmt.Errorf("parsing endpoint URL: %w", err) + } + u := c.url.ResolveReference(endpointURL) + method := http.MethodGet + if len(data) > 0 { + method = http.MethodPut + } + req, err := http.NewRequestWithContext(ctx, method, u.String(), bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + req.Header.Set(enrollmentIDHeader, id) + if len(data) > 0 { + req.Header.Set("Content-Type", "application/json") + } + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + // return the same HTTP status with a Go-kit StatusCoder + return bodyBytes, HTTPStatusError{ + Err: fmt.Errorf("unexpected HTTP status: %d", resp.StatusCode), + Status: resp.StatusCode, + } + } + return bodyBytes, nil +} diff --git a/server/server.go b/server/server.go index 976a7528..e65b3ebc 100644 --- a/server/server.go +++ b/server/server.go @@ -66,6 +66,7 @@ type Server struct { ValidateSCEPExpiration bool UDIDCertAuthWarnOnly bool Queue string + DMURL string APNSPushService apns.Service CommandService command.Service @@ -202,7 +203,15 @@ func (c *Server) setupCommandQueue(logger log.Logger) error { var mdmService mdm.Service { - svc := mdm.NewService(c.PubClient, q, devDB) + var dm mdm.DeclarativeManagement + if c.DMURL != "" { + dm, err = NewDeclarativeManagementHTTPCaller(c.DMURL, http.DefaultClient) + if err != nil { + return fmt.Errorf("setting up declarative management: %w", err) + } + } + + svc := mdm.NewService(c.PubClient, q, devDB, dm) mdmService = svc mdmService = block.RemoveMiddleware(c.RemoveDB)(mdmService)