Certificate Portal
OIDC-authenticated web portal that issues a user certificate from Active Directory Certificate Services and delivers it as a signed Apple .mobileconfig.
This is Design B: the portal talks to the CA itself. There is no NDES and no SCEP. The device never contacts AD CS.
Sign in (OIDC) → LDAP objectSid → RSA key + CSR in memory
→ POST /certsrv/certfnsh.asp (NTLM) → PKCS#12 inside .mobileconfig
→ CMS-sign the profile → download over HTTPS
Tradeoff
The private key is generated on the server, packed into a PKCS#12, and sent to the device. It is kept in memory only for that request and is not written to disk. That is commonly accepted for user Wi-Fi certificates. Do not use this portal for keys that must never leave the device (SCEP / on-device generation is a different design).
Documentation
| Doc | Contents |
|---|---|
| docs/architecture.md | Request flow, the two PKCS#12s, why phones never talk to the CA |
| docs/setup.md | OIDC, LDAP, certsrv, TLS, Docker / python -m cert_portal |
| docs/gitea.md | Gitea Actions: build and publish to the container registry |
| docs/adcs.md | Web Enrollment, template flags, SID URI / strong mapping |
| docs/profile-signing.md | Signing identity, device trust, CMS format |
| docs/security.md | Key lifetime, logging, sessions, who can download |
| .env.example | Every setting, commented |
After sign-in, operators and users can also open Help in the app (/help) without cloning this repo.
Quick start
- Copy
.env.exampleto.envand fill in OIDC, LDAP, certsrv, and the profile-signing PKCS#12 path. - Python 3.12+:
pip install -e ".[dev]"thenpython -m cert_portal - Or pull
git.atlashorizon.net/nick/cert-portal:latestfrom the Gitea registry (see docs/gitea.md), ordocker build -t cert-portal .locally. Run with--env-file .env, publishing port 8000, mounting the signing P12.
Put a reverse proxy in front for TLS. Set PUBLIC_BASE_URL and OIDC_REDIRECT_URI to that HTTPS origin (/auth/callback).
What you need on the Windows side
- AD CS Web Enrollment (
https://ca.example.com/certsrv) reachable from the Linux host - A template the portal service account can Enroll, with subject and SAN supplied in the request
- A domain account for NTLM to certsrv (and a separate LDAP bind account, or the same one)
- A certificate (and private key) used only to sign
.mobileconfigfiles, already trusted on devices
Details: docs/adcs.md and docs/profile-signing.md.
Development
pip install -e ".[dev]"
pytest
Tests mock the CA. They do not need live AD CS, LDAP, or an IdP.