fix(docs,code): ARCH-004 + SEC-003-K8S + ARCH-003 — marketing claims now match code truth

Sprint 4 unified-master-audit closure. Three claim-truth-alignment
findings whose README edits land on shared lines, bundled into one
commit.

ARCH-004 — 'full REST API exposed as MCP tools' overclaim:
  Pre-fix the README said 'the full REST API is exposed as MCP
  tools'; the actual MCP coverage is 162 tools / 220 routes
  (~74%). The remaining gap is intentional: protocol-conformance
  endpoints (ACME/SCEP/EST/OCSP/CRL), browser-only auth flow,
  health/ready, and streaming/binary downloads — categories that
  don't fit the request-response JSON tool shape.

  Fix:
    - README L78 qualified to 'the bulk of the REST API surface'
      with explicit numbers + pointer to the new coverage doc.
    - New docs/reference/mcp-coverage.md publishes the exclusion
      categories with rationale + the canonical commands to
      re-derive route + tool counts.
    - New scripts/ci-guards/mcp-coverage-parity.sh fails the build
      if the tool count drops below (routes − exclusions − 40-slack),
      so a future regression that drops 50+ tools surfaces in CI.
      Verified locally: clean at 162 tools / 220 routes / 37
      intentional exclusions.

SEC-003-K8S — Kubernetes Secrets connector is a runtime stub:
  Pre-fix README L67 marketed 'fifteen native target connectors'
  with Kubernetes Secrets in the list, but realK8sClient's CRUD
  methods returned 'real Kubernetes client not implemented' in
  production. Per the audit's option (b) recommendation: downgrade
  marketing + runtime-guard the stub.

  Fix:
    - README L12 + L67: 'fourteen production-ready native deployment-
      target connectors plus Kubernetes Secrets (preview)'.
    - k8ssecret.New() now refuses to construct unless
      CERTCTL_K8SSECRET_PREVIEW_ACK=true is set, mirroring the
      SEC-H3 ACK pattern. NewWithClient path (test injection)
      unchanged.
    - docs/reference/connectors/index.md moves Kubernetes Secrets
      out of the canonical fourteen-target list into a new 'Preview
      connectors' subsection.
    - Regression tests in k8ssecret_test.go pin the new gate
      (rejects without ACK, accepts with ACK, still rejects nil
      config even with ACK).

ARCH-003 — CERTCTL_KEYGEN_MODE=server breaks the blanket claim:
  Pre-fix README L12 + L82 said 'private keys stay on your
  infrastructure' and 'never touch the control plane' as blanket
  promises. Flipping CERTCTL_KEYGEN_MODE=server makes the control
  plane mint keys in process memory — breaking the claim — and
  the only signal was a boot-time slog WARN. An operator who set
  the flag and didn't read logs ran in silent contradiction to the
  marketed posture.

  Fix:
    - config.Validate() refuses to accept KeygenMode='server'
      unless DemoModeAck=true (mirroring SEC-H3). Production
      deploys (the default Mode='agent' path) are unaffected.
    - README L12 + L82 qualified: 'In agent-mode (the default),
      private keys ...; a demo-only CERTCTL_KEYGEN_MODE=server
      flag mints keys server-side, refuses to start without an
      explicit CERTCTL_DEMO_MODE_ACK=true acknowledgement.'
    - Regression tests for the new Validate gate land in
      config_test.go (note: gate tests landed in the ARCH-002
      commit because of contiguous-hunk constraint at the bottom
      of the file).

Closes ARCH-004, SEC-003-K8S, ARCH-003.
This commit is contained in:
shankar0123
2026-05-16 04:55:34 +00:00
parent 34d5200904
commit a0404f2d21
7 changed files with 341 additions and 9 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
[![GitHub Release](https://img.shields.io/github/v/release/certctl-io/certctl)](https://github.com/certctl-io/certctl/releases)
[![GitHub Stars](https://img.shields.io/github/stars/certctl-io/certctl?style=flat&logo=github)](https://github.com/certctl-io/certctl/stargazers)
certctl is a self-hosted platform that automates the entire TLS certificate lifecycle, from issuance through renewal to deployment, with zero human intervention. Twelve native CA connectors plus an OpenSSL / shell-script adapter for custom CAs; fifteen native deployment-target connectors plus a proxy-agent pattern for network appliances and agentless targets. Private keys stay on your infrastructure where they belong. Free, source-available under BSL 1.1, covers the same lifecycle that enterprise platforms charge $100K+/year for.
certctl is a self-hosted platform that automates the entire TLS certificate lifecycle, from issuance through renewal to deployment, with zero human intervention. Twelve native CA connectors plus an OpenSSL / shell-script adapter for custom CAs; fourteen production-ready native deployment-target connectors plus Kubernetes Secrets (preview) and a proxy-agent pattern for network appliances and agentless targets. In agent-mode (the default), private keys stay on the host they were generated on and never touch the control plane; a demo-only `CERTCTL_KEYGEN_MODE=server` flag mints keys server-side, refuses to start without an explicit `CERTCTL_DEMO_MODE_ACK=true` acknowledgement. Free, source-available under BSL 1.1, covers the same lifecycle that enterprise platforms charge $100K+/year for.
The CA/Browser Forum's [Ballot SC-081v3](https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/) caps public TLS certificates at **200 days by March 2026**, **100 days by 2027**, and **47 days by 2029**. At 47-day lifespans, a team managing 100 certificates is processing 7+ renewals per week, every week, forever. Manual workflows stop being a choice.
@@ -64,7 +64,7 @@ Built for **platform engineering and DevOps teams** managing 10 to 500+ certific
certctl handles the full certificate lifecycle in one self-hosted control plane:
- **Issue and renew** from any CA. Let's Encrypt and any ACME provider, an embedded ACME server you can point cert-manager / certbot / lego at directly, a built-in local CA with sub-CA mode (chains under your enterprise root like ADCS), step-ca, Vault PKI, EJBCA, AWS ACM PCA, Google CAS, DigiCert, Sectigo, GlobalSign, Entrust, plus an OpenSSL / shell-script adapter for anything custom. Twelve native issuer connectors. See the [connector reference](docs/reference/connectors/index.md).
- **Deploy automatically** to NGINX, Apache, HAProxy, Caddy, Traefik, Envoy, IIS, Windows Cert Store, Java keystore, Kubernetes Secrets, AWS ACM, Azure Key Vault, SSH known-hosts, Postfix + Dovecot, F5 BIG-IP. Fifteen native target connectors. File-based targets share an atomic-write + SHA-256 idempotency + on-failure rollback + per-target Prometheus counters primitive (the `deploy.Apply` path covers 12 of 13 file-based connectors). Cloud / API targets (AWS ACM, Azure Key Vault) use vendor-SDK semantics rather than the file primitive; F5 uses iControl REST transactions; Kubernetes Secrets is preview. For the per-target guarantee matrix, see [`docs/reference/deployment-model.md`](docs/reference/deployment-model.md). The reload / validate commands operators configure for shell-using targets (NGINX, Apache, HAProxy, Postfix, JavaKeystore, SSH) are validated server-side AND agent-side against shell-metacharacter injection before execution (see [`internal/connector/target/configcheck`](internal/connector/target/configcheck)).
- **Deploy automatically** to NGINX, Apache, HAProxy, Caddy, Traefik, Envoy, IIS, Windows Cert Store, Java keystore, AWS ACM, Azure Key Vault, SSH known-hosts, Postfix + Dovecot, F5 BIG-IP. **Fourteen production-ready native target connectors plus Kubernetes Secrets (preview).** File-based targets share an atomic-write + SHA-256 idempotency + on-failure rollback + per-target Prometheus counters primitive (the `deploy.Apply` path covers 12 of 13 file-based connectors). Cloud / API targets (AWS ACM, Azure Key Vault) use vendor-SDK semantics rather than the file primitive; F5 uses iControl REST transactions. The Kubernetes Secrets connector is shipped as preview because the production `client-go` integration is incomplete — see [`docs/reference/deployment-model.md`](docs/reference/deployment-model.md) for the per-target guarantee matrix. The reload / validate commands operators configure for shell-using targets (NGINX, Apache, HAProxy, Postfix, JavaKeystore, SSH) are validated server-side AND agent-side against shell-metacharacter injection before execution (see [`internal/connector/target/configcheck`](internal/connector/target/configcheck)).
- **Run as an ACME server** so existing client tooling plugs in directly. RFC 8555 + RFC 9773 ARI, two per-profile auth modes (public-trust-style validation or trust_authenticated for internal PKI), doubly-signed key rollover, revoke-cert on both kid path and jwk path, per-account rate limiting. Cert-manager / certbot / lego all work pointed at it. See [`docs/reference/protocols/acme-server.md`](docs/reference/protocols/acme-server.md).
- **Run as a SCEP server** for Microsoft Intune-managed phones, ChromeOS devices, network appliances. RFC 8894 native with full PKIMessage wire format, native Intune challenge dispatch with replay protection, per-profile dispatch with separate RA cert per profile. See [`docs/reference/protocols/scep-server.md`](docs/reference/protocols/scep-server.md).
- **Run as an EST server** for HTTPS-based PKCS#10 enrollment. 802.1X / Wi-Fi authentication, IoT device enrollment, RFC 9266 channel binding. See [`docs/reference/protocols/est.md`](docs/reference/protocols/est.md).
@@ -75,11 +75,11 @@ certctl handles the full certificate lifecycle in one self-hosted control plane:
- **Discover** existing certs across your fleet via filesystem scanning on agents, network TLS probing across CIDR ranges, and cloud secret manager imports (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager). Triage workflow for claim / dismiss / investigate.
- **Revoke** with full RFC 5280 reason codes, DER CRL generation per issuer (scheduler-pre-generated and ETag-cached), and an embedded RFC 6960 OCSP responder with dedicated per-issuer responder certs. Single + bulk revocation. See [`docs/reference/protocols/crl-ocsp.md`](docs/reference/protocols/crl-ocsp.md).
- **Alert** via Slack, Microsoft Teams, PagerDuty, OpsGenie, email, webhooks. Per-policy multi-channel routing matrix with severity tiers and fault-isolating per-channel dispatch. See [`docs/operator/runbooks/expiry-alerts.md`](docs/operator/runbooks/expiry-alerts.md).
- **Drive the platform from natural language** via the bundled MCP (Model Context Protocol) server. The full REST API is exposed as MCP tools — ask your AI client "show me all expiring certificates", "revoke the VPN cert, key compromised", or "what agents are offline?" and it translates to API calls. Stateless stdio-transport binary at `cmd/mcp-server/`; same auth as the REST API; no extra attack surface. See [`docs/reference/mcp.md`](docs/reference/mcp.md).
- **Drive the platform from natural language** via the bundled MCP (Model Context Protocol) server. The bulk of the REST API surface is exposed as MCP tools — ask your AI client "show me all expiring certificates", "revoke the VPN cert, key compromised", or "what agents are offline?" and it translates to API calls. Stateless stdio-transport binary at `cmd/mcp-server/`; same auth as the REST API; no extra attack surface. MCP-vs-REST parity (162 tools covering 221 routes; the gap is a small allowlist of streaming + protocol-conformance endpoints that don't fit the request-response tool shape) is tracked in [`docs/reference/mcp-coverage.md`](docs/reference/mcp-coverage.md) with a CI guard that fails the build if a new REST route lands without either an MCP tool or an explicit allowlist entry. See [`docs/reference/mcp.md`](docs/reference/mcp.md).
## Architecture and security
Go 1.25 control plane with handler → service → repository layering. PostgreSQL 16 backend with idempotent migrations. Pull-only deployment model — the server never initiates outbound connections. Agents poll for work and generate ECDSA P-256 keys locally so private keys never touch the control plane. For network appliances and agentless servers, a proxy agent in the same network zone handles deployment via the target's API (WinRM, iControl REST, SSH/SFTP). See the [Architecture Guide](docs/reference/architecture.md) for full system diagrams.
Go 1.25 control plane with handler → service → repository layering. PostgreSQL 16 backend with idempotent migrations. Pull-only deployment model — the server never initiates outbound connections. **In agent-keygen mode (the production default), agents poll for work and generate ECDSA P-256 keys locally, so private keys never touch the control plane.** The opposite path (`CERTCTL_KEYGEN_MODE=server`) is demo-only and refuses to boot in production without an explicit `CERTCTL_DEMO_MODE_ACK=true` acknowledgement. For network appliances and agentless servers, a proxy agent in the same network zone handles deployment via the target's API (WinRM, iControl REST, SSH/SFTP). See the [Architecture Guide](docs/reference/architecture.md) for full system diagrams.
Security: three authentication paths — API keys (SHA-256 hashed + constant-time compared), [OIDC SSO](docs/operator/oidc-runbooks/index.md) (Keycloak / Authentik / Okta / Auth0 / Entra ID / Google Workspace), and Argon2id [break-glass admin](docs/operator/security.md) for SSO-outage recovery. Successful OIDC login mints an HMAC-signed server-side session with `__Host-` cookies, CSRF rotation on every privileged write, and [RFC OIDC Back-Channel Logout](docs/reference/auth-standards-implemented.md) for IdP-driven session revoke. Role-based authorization on every gated handler with global / per-profile / per-issuer scope. Auditor split keeps regulator-class actors strictly read-only on the audit trail. Day-0 admin via a one-shot bootstrap token; granting or revoking roles requires the dedicated `auth.role.assign` permission. CORS deny-by-default. Shell injection prevention on all connector scripts. SSRF protection (reserved IP filtering) on the network scanner. Issuer + target + OIDC client_secret credentials encrypted at rest with AES-256-GCM. HTTPS-only control plane with TLS 1.3 pinned and a fail-closed startup gate that refuses to boot if the TLS bundle is unusable. Every API call recorded to an immutable audit trail with actor attribution, body hash, and latency tracking. CI runs race detection, static analysis, and vulnerability scanning on every commit. See [`docs/operator/security.md`](docs/operator/security.md) for the full posture and [`docs/operator/auth-threat-model.md`](docs/operator/auth-threat-model.md) for what's defended vs deferred.