mTLS Admin Setup
mTLS Admin Setup
The RA protects its admin (/api/v1/*) and client (/api/client/*) endpoints with mutual TLS (mTLS). Both the RA and the caller must present valid certificates signed by the same CA.
Overview
Admin tool / uPKI CLI
│
│ HTTPS (client cert = admin cert, signed by uPKI CA)
▼
uPKI RA :8000
│ verifies client cert against ca.crt
│ (rejects if not signed by the same CA)
Step 1 — Obtain an admin client certificate
Using uPKI CLI:
upki-cli generate \
--host 127.0.0.1 \
--cn "admin.example.internal" \
--profile admin
This issues a certificate with clientAuth extended key usage, signed by the CA.
Alternatively, request it via ZMQ directly:
{
"TASK": "generate",
"params": {
"cn": "admin.example.internal",
"profile": "admin"
}
}
Step 2 — Configure the HTTP client
For curl:
curl --cert admin.crt \
--key admin.key \
--cacert ca.crt \
https://upki-ra:8000/api/v1/health
For Python (httpx / requests):
import httpx
client = httpx.Client(
cert=("admin.crt", "admin.key"),
verify="ca.crt"
)
response = client.get("https://upki-ra:8000/api/v1/health")
Step 3 — Verify access
# Should return 200 OK
curl -s \
--cert admin.crt \
--key admin.key \
--cacert ca.crt \
https://upki-ra:8000/api/v1/health
Without a valid client certificate:
curl --cacert ca.crt https://upki-ra:8000/api/v1/health
# → 403 Forbidden or TLS handshake error
Certificate profiles
| Endpoint type | Recommended profile | Extended Key Usage |
|---|---|---|
| Admin API | admin | clientAuth |
| Client API | user or laptop | clientAuth |
| RA itself | ra (auto-issued) | serverAuth, clientAuth |
Revoking admin access
To revoke an admin certificate:
{
"TASK": "revoke",
"params": {
"dn": "/CN=admin.example.internal",
"reason": "cessationOfOperation"
}
}
Then regenerate the CRL:
{
"TASK": "generate_crl",
"params": {}
}
The RA reads the CRL on startup and on certificate validation — revoked certificates are rejected immediately after the CRL is updated.
Docker compose with mTLS admin
If running the RA behind Traefik and wanting to protect admin routes further, add a Traefik middleware for mTLS passthrough:
labels:
- "traefik.http.routers.upki-ra-admin.rule=Host(`ra.example.internal`) && PathPrefix(`/api/v1`)"
- "traefik.http.routers.upki-ra-admin.tls=true"
- "traefik.http.routers.upki-ra-admin.tls.certresolver=upki"
- "traefik.http.routers.upki-ra-admin.middlewares=strip-admin"
The RA itself handles mTLS verification for the /api/v1/* routes — Traefik only needs to forward the client certificate headers.