4.6 KiB
4.6 KiB
Production PKI refuse matrix (501 vs lab 201)
Purpose: single place for what stays honest 501 in production vs what may return lab-only 201. Live production mint is not shipped. Lab path is never production.
Canonical detail: ENROLL.md · backlog: PKI-MINT-BACKLOG.md · lab design: TEST-CA-DESIGN.md · revoke design: CRL-OCSP-DESIGN.md.
Gate summary
| Gate | Production / default | Lab opt-in |
|---|---|---|
LAB_CA_ENABLE |
unset / off | =1 |
NODE_ENV=production |
refuses lab even if flag set | must not be production |
Lab PKI dir (LAB_PKI_DIR or ./lab-pki) |
ignored | must exist + init’d (ca.crt + key) |
| Signer | none | scripts/lab-test-ca.sh sign (openssl) |
| Response markers | no cert body | X-Lab-CA: 1, X-Lab-CA-Warning: NOT-FOR-PRODUCTION, body lab: true |
Fail closed: missing flag, missing lab-pki, missing script, production env, or sign failure → no silent success.
HTTP matrix — stays 501 vs lab 201
| Surface | Condition | HTTP | Body / headers | Production? |
|---|---|---|---|---|
GET /enroll/csr/schema |
always | 200 | fields only; mint: false |
OK (no crypto) |
GET /enroll/status |
default | 200 | csrPostStatus: **501**; labCa.enabled: false |
OK |
GET /enroll/status |
lab ready | 200 | csrPostStatus: **201** (probe only); labCa.enabled/ready |
Lab probe only — not a mint |
POST /enroll/csr |
missing / empty csr |
501 | error: not_implemented + nextSteps |
Stays 501 |
POST /enroll/csr |
invalid PEM shape | 400 | error: invalid_csr_pem |
Shape gate (no mint) |
POST /enroll/csr |
valid-looking PEM, lab off | 501 | not_implemented |
Stays 501 |
POST /enroll/csr |
valid PEM, lab on, pki missing / not ready | 501 | not_implemented |
Stays 501 |
POST /enroll/csr |
valid PEM, lab on, NODE_ENV=production |
501 | lab gated off → scaffold | Stays 501 (refuse) |
POST /enroll/csr |
valid PEM, lab on + pki ready + non-prod | 201 | certificate (+ optional chain); X-Lab-CA: 1 |
LAB ONLY |
POST /enroll/csr |
lab path, openssl sign fails | 502 | lab_ca_sign_failed + X-Lab-CA: 1 |
Lab failure (not production mint) |
| Login / My security UI | any | — | status chips / checklist only | No download-cert / mint button |
PUT /users/:username { mtlsEnrolled } |
admin | 200 | flag only | OK (no cert issue) |
| Proxy mTLS headers | edge | — | presence flags on status | External PKI SoT |
What always stays refused in production (no 201)
These must never return success cert material under production defaults:
| Capability | Status |
|---|---|
| Production CA / HSM / EST / ACME / SCEP mint | Not shipped → default POST 501 |
| Silent or default-on lab mint | Forbidden (LAB_CA_ENABLE default off; production env refuses) |
| Demo / stub / self-signed “real” staff cert from IAM | Forbidden |
| PKCS#12 / private key return | Never |
| Production CRL or OCSP from staff-iam | Not shipped (design only; lab empty crl offline) |
Dropping default 501 without real Signer |
Forbidden until live PKI checklist done |
Operator path (production)
- Issue workstation client certs in external reverse-proxy / internal PKI — not this app.
- Do not call
POST /enroll/csrexpecting a production cert — it returns 501. - Terminate mTLS at the proxy; forward verify/DN headers.
- Confirm
GET /enroll/status→proxyHeadersSeen/mtlsPresent. - Admin
mtlsEnrolled: true→ optionalSTAFF_MTLS_ENFORCE=1.
Lab path (explicit only)
./scripts/lab-test-ca.sh init --dir ./lab-pki
LAB_CA_ENABLE=1 npm start # never with NODE_ENV=production
# POST /enroll/csr { "csr": "-----BEGIN CERTIFICATE REQUEST-----..." }
# → 201 + X-Lab-CA: 1 OR 502 on sign failure OR 501 if pki not ready
Still left (live production PKI)
- Choose real CA model (external ACME/EST/HSM/delegated signer)
- AuthZ + full CSR ASN.1 / PoP / CN-SAN policy
- Bind cert → IAM user; metadata persist; renew/revoke
- Production CRL/OCSP (not lab empty CRL)
- Remove default 501 only after real
Signer; keep no-fake-crypto tests
Related
- PKI-MINT-BACKLOG.md — shipped surface + live checklist
- TEST-CA-DESIGN.md — lab script + gated HTTP
- CRL-OCSP-DESIGN.md — future revocation
- ENROLL.md — states, headers, nextSteps