fleet/server/mdm/nanomdm
Magnus Jensen 34d0620f80
restrict unbouded goroutine spawning, fix panic on nil pushInfo for multi push (#44397)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #42898

I was waiting for my [upstream
PR](https://github.com/micromdm/nanomdm/pull/250) to be merged, but I've
waited for 2+ weeks now, so I'll go ahead and do the same change here,
and then if the maintainer requests change I can update this fix
retrospectively

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information. **None, since it's unused in our codebase at this
point**

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Prevented nil push entries from causing panics during concurrent push
processing
* Adjusted worker allocation so concurrency scales down when batch sizes
are smaller
* Clamped configured worker count to a minimum of 1 (documented default
behavior)

* **Tests**
* Added regression test ensuring safe handling of nil entries in
concurrent push inputs and updated test harness to exercise
reduced-worker scenarios
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 13:46:14 -06:00
..
certverify Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00
cli slog migration: platform/mysql and related logic (#40072) 2026-02-19 08:27:24 -06:00
cmd Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00
cryptoutil Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00
docs Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00
http update nanomdm types to avoid confusion (#41877) 2026-03-18 13:10:56 -05:00
mdm Show configuration profile name and more fine-grained status (#42126) 2026-04-09 12:46:11 -05:00
push restrict unbouded goroutine spawning, fix panic on nil pushInfo for multi push (#44397) 2026-04-29 13:46:14 -06:00
service Implement clear passcode backend (#43072) 2026-04-07 15:23:59 -05:00
storage don't clear bootstrap token when doing MDM cert renewals (#43098) 2026-04-13 14:37:05 -06:00
test update nanomdm types to avoid confusion (#41877) 2026-03-18 13:10:56 -05:00
tools Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00
docker-compose.yml Bump supported MySQL versions (#40892) 2026-03-04 12:25:20 -06:00
LICENSE Move nanomdm dependency in monorepo (#16015) 2024-01-11 23:28:48 -03:00
README.md Update nanomdm dependency with latest bug fixes and improvements. (#23906) 2024-11-20 11:47:11 -06:00

NanoMDM

The contents of this directory were copied (on January 2024) from https://github.com/fleetdm/nanomdm (the apple-mdm branch) which was forked from https://github.com/micromdm/nanomdm. They were updated in November 2024 with changes up to github.com/micromdm/nanomdm@825f2979a2dc28c6cc57bb62aff16737978bd90e

NanoMDM is a minimalist Apple MDM server heavily inspired by MicroMDM.

Getting started & Documentation

  • Quickstart A quick guide to get NanoMDM up and running using ngrok.

  • Operations Guide A brief overview of the various command-line switches and HTTP endpoints and APIs available to NanoMDM.

Features

  • Horizontal scaling: zero/minimal local state. Persistence in storage layers. MySQL and PostgreSQL backends provided in the box.
  • Multiple APNs topics: potentially multi-tenant.
  • Multi-command targeting: send the same command (or pushes) to multiple enrollments without individually queuing commands.
  • Migration endpoint: allow migrating MDM enrollments between storage backends or (supported) MDM servers
  • Otherwise we share many features between MicroMDM and NanoMDM, such as:
    • A MicroMDM-emulating HTTP webhook/callback.
    • Enrollment-certificate authorization
    • API-driven interaction (queuing of commands, APNs pushes, etc.)

$x not included

NanoMDM is but one component for a functioning MDM server. At a minimum you need a SCEP server and TLS termination, for example. If you've used MicroMDM before you might be interested to know what NanoMDM does not include, by way of comparison.

  • SCEP.
    • Spin up your own scep server. Or bring your own.
  • TLS.
    • You'll need to provide your own reverse proxy/load balancer that terminates TLS.
  • ADE (DEP) API access.
    • While ADE/DEP enrollments are supported there is no DEP API access.
  • Enrollment (Profiles).
    • You'll need to create and serve your own enrollment profiles to devices.
  • Blueprints.
    • No 'automatic' command sending upon enrollment. Entirely driven by webhook or other integrations.
  • JSON command API.
    • Commands are submitted in raw Plist form only. See the cmdr.py tool that helps generate raw commands
    • The micro2nano project provides an API translation server between MicroMDM's JSON command API and NanoMDM's raw Plist API.
  • VPP.
  • Enrollment (device) APIs.
    • No ability, yet, to inspect enrollment details or state.
    • This is partly mitigated by the fact that both the file and mysql storage backends are "easy" to inspect and query.

Architecture Overview

NanoMDM, at its core, is a thin composable layer between HTTP handlers and a set of storage abstractions.

  • The "front-end" is a set of standard Golang HTTP handlers that handle MDM and API requests. The core MDM handlers adapt the requests to the service layer. These handlers exist in the http package.
  • The service layer is a composable interface for processing and handling MDM requests. The main NanoMDM service dispatches to the storage layer. These services exist under the service package.
  • The storage layer is a set of interfaces and implementations that store & retrieve MDM enrollment and command data. These exist under the storage package.

You can read more about the architecture in the blog post Introducing NanoMDM.

Running unit tests locally

  1. Start up MySQL docker compose up
  2. Load schema: mysql --user=fleet -pinsecure --host=127.0.0.1 --port=3800 --protocol=TCP fleet < ./storage/mysql/schema.sql
  3. export NANOMDM_MYSQL_STORAGE_TEST_DSN=fleet:insecure@tcp(127.0.0.1:3800)/fleet
  4. go test -v -parallel 8 -race=true ./...

Clean up MySQL after running tests

SET FOREIGN_KEY_CHECKS = 0;
truncate table nano_cert_auth_associations;
truncate table nano_command_results;
truncate table nano_commands;
truncate table nano_devices;
truncate table nano_enrollment_queue;
truncate table nano_enrollments;
truncate table nano_push_certs;
truncate table nano_users;
SET FOREIGN_KEY_CHECKS = 1;