Files
certctl/docs
shankar0123 92c50d9e19 harden(oidc): relax alg-downgrade IdP-bind check to intersection-empty (Keycloak compat)
Phase-10 live-IdP smoke (Keycloak 26.x via testcontainers-go) revealed
the IdP-bind alg-downgrade check was too strict for real-world IdPs.
6 of the integration tests in internal/auth/oidc/integration_keycloak*_test.go
were failing with:

  oidc: IdP advertises weak signing algorithms (HS*/none);
  refusing to use as defense against downgrade attacks: HS256

Keycloak 26.x (and several other real-world IdPs — Auth0 when HS-mode is
enabled, some Authentik configs) advertise EVERY alg they're capable of
in the discovery doc's id_token_signing_alg_values_supported field, even
when the realm only signs with RS256 in practice. Pre-fix the IdP-bind
check refused on ANY HS* or 'none' advertisement → no real Keycloak deploy
could ever bind a provider row, hence the integration-test failures.

The strict-deny check was defense-in-depth on top of the load-bearing
per-token alg-pin at sig-verify time (isDisallowedAlg, service.go L1177):
that check rejects every ID token whose JWS header carries an alg outside
DefaultAllowedAlgs, regardless of what the discovery doc advertises.
A forged HS256 token signed with the IdP's RS256 pubkey as HMAC secret
is rejected at sig-verify time → the actual algorithm-confusion attack
is closed by the per-token pin, NOT by the discovery-doc check.

Fix: relax the IdP-bind check to refuse only when the intersection of
advertised vs DefaultAllowedAlgs is EMPTY (the pathological all-weak-alg
IdP case). Keycloak (RS256 + HS256 advertised) now binds successfully;
an HS-only IdP still fails closed.

Changes:
- internal/auth/oidc/service.go: rewrite the alg-check loop at L1067 in
  getOrLoad / RefreshKeys to compute the intersection set; refuse only
  when no acceptable alg is advertised. ErrIdPDowngradeAdvertised
  docstring updated to reflect new contract. DefaultAllowedAlgs
  docstring + the package-level design-comment block at L40-72 updated
  with v2.1.0-relaxed semantics callouts.
- internal/auth/oidc/test_discovery.go: TestDiscovery dry-run validator
  rewritten to surface HS*/none alongside RS* as an informational note
  ('note: IdP advertises weak algorithms %v alongside acceptable ones')
  rather than a hard-fail error. HS-only / none-only still hard-fails.
- internal/auth/oidc/service_test.go: TestService_IdPDowngradeDefense_*
  tests updated. Renamed:
  - RejectsHSAdvertised → RS256PlusHS256_BindsSuccessfully (positive)
  - RejectsNoneAdvertised → RejectsHSOnlyAdvertised (intersection-empty)
  - RefreshKeys_CatchesPostLoadDowngrade rotated to HS-only post-load
- internal/auth/oidc/coverage_fill_test.go: TestTestDiscovery_AlgDowngradeDetected
  split into _HS256AlongsideRS256_BindsWithNote (positive, asserts note
  but no hard-fail) + _HSOnly_StillTrips_HardFail (intersection-empty).
- docs/operator/auth-threat-model.md: OIDC token-validation alg-allow-list
  section rewritten to call out the load-bearing-defense hierarchy
  (per-token pin first, IdP-bind check defense-in-depth) and document
  the v2.1.0 relaxation rationale.
- CHANGELOG.md: ### Security entry under Unreleased.

Verify: go test ./internal/auth/oidc/ -short PASS; gofmt clean; go vet
clean. The Keycloak integration tests should now pass when the operator
re-runs 'make keycloak-integration-test'.
2026-05-11 15:34:59 +00:00
..

certctl Documentation

Last reviewed: 2026-05-05

The full docs index, organized by audience. Pick the section that matches what you need to do; each link below opens a focused doc rather than a wall of text.

For the elevator pitch and quickstart commands, see the repo README.md at the root. For the marketing site, see certctl.io.


Getting Started

You're new to certctl, just cloned the repo, or want to understand what it does before installing.

Doc What it covers
Concepts TLS certificates explained for beginners — CAs, ACME, EST, private keys, the full glossary
Quickstart Five-minute setup with Docker Compose, dashboard tour, API tour
Examples Five turnkey scenarios — ACME+NGINX, wildcard DNS-01, private CA+Traefik, step-ca+HAProxy, multi-issuer
Advanced demo End-to-end certificate lifecycle with technical depth at each step
Why certctl Positioning vs ACME clients, agent-based SaaS, enterprise platforms; when to look elsewhere

Reference

You're operating certctl in production or building integrations and need authoritative technical detail.

Doc What it covers
Architecture System design, data flow, security model, deployment topologies
Profiles CertificateProfile policy object — issuer wiring, EKUs, RequiresApproval gate (Phase 9 closure)
API OpenAPI 3.1 spec, integration patterns, client SDK generation
CLI certctl-cli command reference and CI/CD integration patterns
Configuration CERTCTL_* environment variable reference (scheduler, rate limits, deploy verify, audit, agent)
MCP server Model Context Protocol integration for AI assistants
Release verification Cosign / SLSA / SBOM verification procedure
Intermediate CA hierarchy Multi-level CA tree management — RFC 5280 §3.2/§4.2.1.9/§4.2.1.10 enforcement
Auth standards implemented RFC + CWE evidence for the Auth Bundle 1 + 2 surface (NOT a compliance-mapping doc)
Deployment model Atomic write, post-deploy verify, rollback semantics across all targets
Vendor matrix Tested vendor versions per target connector

Connectors

The connector index is the canonical catalog (interfaces, registry, scanners, plus an inline reference per built-in). Per-connector deep-dive siblings cover operator-grade material — vendor edges, troubleshooting, rotation playbooks, when-to-use vs alternatives.

Issuers (13 deep-dives): ACME · ADCS · AWS ACM Private CA · DigiCert · EJBCA / Keyfactor · Entrust · GlobalSign Atlas HVCA · Google CAS · Local CA · OpenSSL / Custom CA · Sectigo SCM · step-ca / Smallstep · Vault PKI

Targets (15 deep-dives): Apache · AWS Certificate Manager · Azure Key Vault · Caddy · Envoy · F5 BIG-IP · HAProxy · IIS · Java Keystore · Kubernetes Secrets · NGINX · Postfix / Dovecot · SSH (agentless) · Traefik · Windows Certificate Store

Protocols

Doc What it covers
ACME server Run certctl as an RFC 8555 + RFC 9773 ARI ACME server
ACME server threat model Security posture for the ACME server endpoint
SCEP server RFC 8894 native SCEP server — RA cert config, multi-profile dispatch, must-staple, mTLS sibling route
SCEP for Microsoft Intune Intune-specific deployment guide — NDES replacement playbook
EST server RFC 7030 EST server — 802.1X / Wi-Fi enrollment, IoT bootstrap, channel binding
CRL & OCSP RFC 5280 CRL + RFC 6960 OCSP responder for relying parties
Async CA polling Bounded polling for async-CA issuer connectors

Operator

You're running certctl in production and need operational guidance.

Doc What it covers
Security posture Auth, rate limits, encryption at rest, key rotation, RBAC primitive (Bundle 1), bootstrap
RBAC operator reference Roles, permissions, scopes, scope-down + bootstrap flow (Bundle 1)
Auth threat model API-key compromise, role-grant abuse, bootstrap-token leak, audit-mutation, compliance mapping (Bundle 1)
OIDC / SSO runbooks Per-IdP setup guides — Keycloak, Authentik, Okta, Auth0, Entra ID, Google Workspace (Bundle 2)
Control plane TLS Self-signed bootstrap, operator-supplied Secret, cert-manager Certificate CR
Database TLS PostgreSQL transport encryption
Approval workflow Two-person integrity gate for high-stakes issuance + Phase 9 profile-edit closure
Helm deployment Kubernetes installation via the bundled chart
Performance baselines Operator-runnable benchmarks for regression spot checks
Auth benchmarks Session + OIDC validation p99 targets and measured baselines (Bundle 2 Phase 14)
Legacy clients (TLS 1.2) Reverse-proxy runbook for embedded EST/SCEP clients on TLS 1.2

Runbooks

Runbook When
Cloud targets AWS ACM + Azure Key Vault deployment, debugging, rollback
Expiry alerts Per-policy multi-channel routing matrix, severity tiers
Disaster recovery CRL cache, OCSP responder cert, CA private-key rotation, Postgres restore

Migration

You're moving from another cert-management tool to certctl, or running both in parallel.

From Doc
Certbot migration/from-certbot.md
acme.sh migration/from-acmesh.md
cert-manager (coexistence, not replacement) migration/cert-manager-coexistence.md
Caddy ACME (point Caddy at certctl) migration/acme-from-caddy.md
cert-manager ACME (point cert-manager at certctl) migration/acme-from-cert-manager.md
Traefik ACME (point Traefik at certctl) migration/acme-from-traefik.md
API keys → RBAC (v2.0.x → v2.1.0) migration/api-keys-to-rbac.mdAUDIT YOUR API KEYS post-upgrade
Enable OIDC SSO on a Bundle-1-merged deployment migration/oidc-enable.md — step-by-step Bundle 2 OIDC onboarding

Contributor

You're contributing to certctl, running tests locally, or trying to understand the CI pipeline.

Doc What it covers
Testing strategy What we test and why; per-PR fast gates vs daily deep-scan
Test environment Local environment with real CAs (Pebble, step-ca, etc.)
QA prerequisites Before running QA: stack boot, demo data baseline, env vars
QA test suite qa_test.go reference for release QA
GUI QA checklist Manual GUI verification pass for release
Release sign-off Release-day checklist — code state, automated gates, manual QA, artefact verification
CI pipeline CI shape, regression guards, adding new checks

Archive

Historical docs preserved for reference. Most operators don't need these.

Doc Why archived
Upgrade to TLS (v2.2) Pre-v2.2 HTTPS-everywhere upgrade procedure
Upgrade past v2 JWT removal G-1 milestone JWT auth removal procedure

Reading order by role

First-time operator: ConceptsQuickstartExamples. About 90 minutes end to end.

Production operator: ArchitectureSecurity postureControl plane TLSDisaster recovery runbook. About 4 hours end to end.

PKI engineer: ACME serverSCEP serverEST serverIntermediate CA hierarchy. About 6 hours end to end.

Contributor: ArchitectureTesting strategyTest environmentCI pipeline. About 3 hours end to end.