Files
cert-portal/docs/architecture.md
T
2026-09-18 19:45:17 -07:00

3.0 KiB

Architecture

The portal is a Linux FastAPI app. It is the enrollment client. Apple devices only download a finished, signed configuration profile.

Flow

  1. OIDC. The browser is redirected to a generic OpenID Provider (Keycloak, Okta, Authentik, Entra ID, …). Authorization code + PKCE. A small user dict (sub, username, email, name) is stored in a signed session cookie. Access tokens are not kept.
  2. LDAP. Using the username claim (default email, then preferred_username), the portal binds as a service account and searches Active Directory. It reads objectSid, userPrincipalName, and displayName. An optional group gate uses LDAP_MATCHING_RULE_IN_CHAIN (nested membership).
  3. Key + CSR (memory only). On Download, not at login, the portal generates RSA-2048 (configurable), and a CSR:
    • Subject CN=<UPN>
    • SAN otherName 1.3.6.1.4.1.311.20.2.3 = UPN
    • SAN URI tag:microsoft.com,2022-09-14:sid:<SID> (Microsoft strong mapping / KB5014754)
  4. certsrv. NTLM POST to /certsrv/certfnsh.asp with CertificateTemplate:<name>. On issued, GET /certsrv/certnew.cer?ReqID=&Enc=b64. Pending or denied is an error; this portal does not wait for manager approval.
  5. Identity PKCS#12. Private key + issued cert (+ optional CA) encrypted with a one-time password. Not written to disk.
  6. Unsigned plist. A com.apple.security.pkcs12 payload holds the identity P12 and that password. Optional CA payload if PROFILE_CA_CERT_PATH is set.
  7. Sign. The completed plist is wrapped in attached CMS (not detached), equivalent to openssl smime -sign -nodetach -outform der, using the profile-signing PKCS#12.
  8. HTTPS download. Content-Type: application/x-apple-aspen-config.

The CA database requester is the portal NTLM account. The user is bound by SAN (UPN + SID URI), not by Windows impersonation. OIDC does not yield a Kerberos ticket for the end user, so there is no Enrollment Agent / on-behalf-of in this version.

Two PKCS#12 files

File Lifetime Role
Identity P12 One download, RAM only Embedded in the profile as com.apple.security.pkcs12. User cert + private key.
Profile-signing P12 Long-lived file on the server CMS-signs the .mobileconfig. Must already be trusted on the device.

Do not reuse the signing P12 as a user identity, and do not save identity P12s.

Why the phone never talks to the CA

SCEP/NDES would have the device generate a key and speak SCEP to NDES. This portal instead returns the cert and key inside the profile. After install, nothing on the phone needs certsrv or NDES reachability.

Code map

Module Responsibility
auth_oidc.py OIDC client registration, claim → username
directory.py LDAPS, objectSid decode, group gate
enrollment.py RSA key, CSR SAN, PKCS#12 export
certsrv.py NTLM submit/fetch, HTML disposition parser
profile.py Unsigned plist + CMS sign
issue.py Orchestrates one download
app.py HTTP routes, session, templates