Documentation
¶
Overview ¶
Package httpapi exposes MDM check-in and command handlers and identity-certificate middleware.
Design ¶
Handler dispatches PUT plist requests by content type, allowing check-in and server URLs to share a route. Certificate middleware extracts identity from Mdm-Signature, TLS or a configured proxy header for service authorization. Trust roots and proxy access controls must be configured; a header alone does not prove key possession.
Traditional MDM errors avoid 401. Recognized account-driven sessions can return Apple's typed reauthentication challenge so the device can retry. Unknown enrollments can receive the configured unrecognized-device response. Enrollment profile delivery belongs to mdmprotocol/enroll and server/internal/app.
References ¶
- Decision record 0004: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0004-checkin-and-command-core.md
- Decision record 0006: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0006-mdm-signature-verification.md
- Threat model: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/security/threat-model.md (/checkin and /connect rows)
- End-to-end scenarios: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/testing/e2e-scenarios.md (E2E-001 to E2E-005)
- Apple: https://developer.apple.com/documentation/devicemanagement/check-in
- Apple: https://developer.apple.com/documentation/devicemanagement/managing-connections
- Schema: third_party/device-management/mdm/checkin/*.yaml
- Schema: third_party/device-management/mdm/errors/*.yaml
Index ¶
- Constants
- func CertFromContext(ctx context.Context) *x509.Certificate
- func CertFromHeader(name string, opts ...HeaderOption) func(http.Handler) http.Handler
- func CertFromMdmSignature(o cms.VerifyOptions, maxBytes int64) func(http.Handler) http.Handler
- func CertFromTLS(next http.Handler) http.Handler
- func CheckinHandler(cfg Config) http.Handler
- func ConnectHandler(cfg Config) http.Handler
- func Handler(cfg Config) http.Handler
- func IsCertMissing(err error) bool
- func WithCert(ctx context.Context, cert *x509.Certificate) context.Context
- type Checkiner
- type Config
- type Connecter
- type HeaderOption
Constants ¶
const ( ContentTypeCheckin = "application/x-apple-aspen-mdm-checkin" ContentTypeConnect = "application/x-apple-aspen-mdm" )
Content types Apple devices send.
Variables ¶
This section is empty.
Functions ¶
func CertFromContext ¶
func CertFromContext(ctx context.Context) *x509.Certificate
CertFromContext returns the device certificate stored by a middleware, or nil.
func CertFromHeader ¶
CertFromHeader takes the device certificate from a header set by a TLS terminating proxy. Two encodings are accepted: RFC 9440 (":<base64 DER>:") and URL-escaped PEM, as nginx and Apache produce. A malformed header, or one that fails WithHeaderRoots, is rejected with 400; an absent header passes through.
Both WithHeaderRoots and WithHeaderPeers are required to accept an assertion.
func CertFromMdmSignature ¶
CertFromMdmSignature verifies the Mdm-Signature header over the request body and takes the signer as the device certificate. The body is read (bounded by maxBytes, 0 for the plist default) and handed on to the next handler. A missing header passes through; an invalid signature is 400.
func CertFromTLS ¶
CertFromTLS takes the device certificate from the TLS peer certificates (mutual TLS terminated by this process). Requests without one pass through unchanged; the service decides whether that is acceptable.
func CheckinHandler ¶
CheckinHandler serves the check-in URL.
func ConnectHandler ¶
ConnectHandler serves the server URL (command channel).
func IsCertMissing ¶
IsCertMissing reports whether err came from a missing certificate.
Types ¶
type Checkiner ¶
type Checkiner interface {
Checkin(ctx context.Context, r *mdm.Request, ck *mdm.Checkin) (*service.CheckinResult, error)
}
Checkiner is the check-in half of the service.
type Config ¶
type Config struct {
Checkin Checkiner
Connect Connecter
// Logger defaults to slog.Default.
Logger *slog.Logger
// Decoder sets plist limits; zero means the defaults.
Decoder plist.Decoder
// UnenrollUnknown answers unknown enrollments with Apple's
// ErrorUnrecognizedDevice body, which makes the device unenroll. Off
// by default because it is irreversible for the device.
UnenrollUnknown bool
// Now defaults to time.Now.
Now func() time.Time
}
Config builds handlers.
type Connecter ¶
type Connecter interface {
Connect(ctx context.Context, r *mdm.Request, resp *mdm.Response) (*mdm.Command, error)
}
Connecter is the command half of the service.
type HeaderOption ¶
type HeaderOption func(*headerConfig)
HeaderOption configures CertFromHeader.
func WithHeaderPeers ¶
func WithHeaderPeers(peers ...netip.Prefix) HeaderOption
WithHeaderPeers permits assertions only from these socket peer networks. X-Forwarded-For and similar headers do not establish proxy trust.
func WithHeaderRoots ¶
func WithHeaderRoots(roots *x509.CertPool) HeaderOption
WithHeaderRoots verifies the header certificate chains to roots before it becomes the device identity.
A header carries no proof of possession, so on its own it is a statement by whatever sent the request. A device certificate is not secret: it travels to this server on every check-in and appears in the SCEP CertRep, so anyone who reaches the listener past the proxy can present another device's. Checking the chain narrows that to holders of a certificate the enrollment CA issued, and does not replace the proxy's responsibility to prove possession.