# 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](docs/architecture.md) | Request flow, the two PKCS#12s, why phones never talk to the CA | | [docs/setup.md](docs/setup.md) | OIDC, LDAP, certsrv, TLS, Docker / `python -m cert_portal` | | [docs/gitea.md](docs/gitea.md) | Gitea Actions: build and publish to the container registry | | [docs/adcs.md](docs/adcs.md) | Web Enrollment, template flags, SID URI / strong mapping | | [docs/profile-signing.md](docs/profile-signing.md) | Signing identity, device trust, CMS format | | [docs/security.md](docs/security.md) | Key lifetime, logging, sessions, who can download | | [.env.example](.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 1. Copy `.env.example` to `.env` and fill in OIDC, LDAP, certsrv, and the profile-signing PKCS#12 path. 2. Python 3.12+: `pip install -e ".[dev]"` then `python -m cert_portal` 3. Or pull `git.atlashorizon.net/nick/cert-portal:latest` from the Gitea registry (see [docs/gitea.md](docs/gitea.md)), or `docker 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** `.mobileconfig` files, already trusted on devices Details: [docs/adcs.md](docs/adcs.md) and [docs/profile-signing.md](docs/profile-signing.md). ## Development ```bash pip install -e ".[dev]" pytest ``` Tests mock the CA. They do not need live AD CS, LDAP, or an IdP.