This commit is contained in:
shankar0123
2026-05-05 18:18:29 +00:00
parent 7c5cc57d75
commit 75097909e9
71 changed files with 95 additions and 771 deletions
+3 -3
View File
@@ -1096,11 +1096,11 @@ Health checks live outside the API prefix: `GET /health` and `GET /ready`.
## MCP Server
certctl includes an MCP (Model Context Protocol) server as a separate binary (`cmd/mcp-server/`) that enables AI assistants to interact with the certificate platform. The MCP server uses the official MCP Go SDK (`modelcontextprotocol/go-sdk`) with stdio transport for integration with Claude, Cursor, and other MCP-compatible tools.
certctl includes an MCP (Model Context Protocol) server as a separate binary (`cmd/mcp-server/`) that enables AI assistants to interact with the certificate platform. The MCP server uses the official MCP Go SDK (`modelcontextprotocol/go-sdk`) with stdio transport for integration with any MCP-compatible AI client.
```mermaid
flowchart LR
AI["AI Assistant\n(Claude, Cursor)"] -->|"stdio"| MCP["MCP Server\ncmd/mcp-server/"]
AI["AI Assistant\n(any MCP client)"] -->|"stdio"| MCP["MCP Server\ncmd/mcp-server/"]
MCP -->|"HTTP + Bearer token"| API["certctl REST API\n:8443"]
subgraph "MCP Tools"
@@ -1318,7 +1318,7 @@ For detailed test procedures, smoke tests, and the release sign-off checklist, s
## Performance Characteristics
Closes the #8 acquisition-readiness blocker from the 2026-05-01 issuer coverage audit (see `cowork/issuer-coverage-audit-2026-05-01/RESULTS.md`). Pre-audit, certctl had no benchmarks or load tests for any API path, so any throughput claim was hand-waved; the harness in `deploy/test/loadtest/` substantiates the API-tier capacity numbers with reproducible methodology.
Closes the #8 acquisition-readiness blocker from the 2026-05-01 issuer coverage audit. Pre-audit, certctl had no benchmarks or load tests for any API path, so any throughput claim was hand-waved; the harness in `deploy/test/loadtest/` substantiates the API-tier capacity numbers with reproducible methodology.
The harness drives a k6 client at sustained 50 req/s × 2 scenarios × 5 minutes against a docker-compose stack of postgres + tls-init + certctl-server. Two scenarios run in parallel: `POST /api/v1/certificates` (issuance-acceptance hot path: auth + JSON decode + validation + service `CreateCertificate` + `managed_certificates` insert) and `GET /api/v1/certificates?per_page=50` (most-trafficked read endpoint). Hard regression-guard thresholds: p99 < 5 s for issuance-acceptance, p99 < 2 s for list, error rate < 1% globally. k6 exits non-zero on any threshold breach so a future PR that pushes p99 above the bar fails `make loadtest`. Run via `make loadtest` from the repo root or via `.github/workflows/loadtest.yml` (`workflow_dispatch` + weekly cron — never per-push).
+1 -1
View File
@@ -197,4 +197,4 @@ only when ALL of the following are true:
- This playbook's full procedure ran clean once on a real Windows host
Operator records the validation date + Windows Server version in
`cowork/<bundle>/iis-validation-receipts.md` for audit trail.
the project's per-bundle iis-validation receipts for audit trail.
+3 -3
View File
@@ -316,7 +316,7 @@ The cert version must exist in the local store: this means the cert was issued t
Reason codes follow RFC 5280 §5.3.1: nil reason maps to `unspecified` (0), and the connector accepts the canonical camelCase form (`keyCompromise`, `cACompromise`, `affiliationChanged`, `superseded`, `cessationOfOperation`, `certificateHold`, `removeFromCRL`, `privilegeWithdrawn`, `aACompromise`) plus underscore_lower and ALL_CAPS_UNDERSCORE variants. An unknown reason returns an error rather than silently demoting to `unspecified` — operators rely on the reason for audit reporting.
Audit reference: `cowork/issuer-coverage-audit-2026-05-01/RESULTS.md` Top-10 fix #7.
Audit reference: 2026-05-01 issuer coverage audit Top-10 fix #7.
Location: `internal/connector/issuer/acme/acme.go`, `internal/connector/issuer/acme/dns.go`
@@ -405,7 +405,7 @@ certctl's OpenSSL adapter `exec`s an operator-supplied script for every certific
**V3-Pro forward path:**
The hardened OpenSSL adapter (chroot/container by default, env-var allow-list at the adapter layer, signed-script-binary verification, audit-log-on-every-invocation, per-call concurrency bound shared with the API surface) is V3-Pro work. Tracking: `cowork/WORKSPACE-ROADMAP.md` (search "OpenSSL hardened mode").
The hardened OpenSSL adapter (chroot/container by default, env-var allow-list at the adapter layer, signed-script-binary verification, audit-log-on-every-invocation, per-call concurrency bound shared with the API surface) is V3-Pro work. Tracking: project roadmap, "OpenSSL hardened mode".
### Revocation Across Issuers
@@ -1711,7 +1711,7 @@ ORDER BY created_at DESC;
Each row corresponds to one fired alert. The `channel` metadata field tells you which notifier ran. Combined with the Prometheus `certctl_expiry_alerts_total{result="failure"}` counter, you have full forensic visibility on every dispatch attempt.
**V3-Pro forward path.** Per-owner / per-team channel routing (route the Production-CDN cert's alerts to its dedicated owner's PagerDuty service, the Internal-API cert's alerts to a different one), calendar-aware suppression (no T-30 informational alerts on weekends for non-on-call teams), and escalation chains (T-1 unanswered for 30m → escalate to manager) are tracked on `cowork/WORKSPACE-ROADMAP.md` under "Adapter hardening" → "Multi-channel expiry alerts: per-owner routing".
**V3-Pro forward path.** Per-owner / per-team channel routing (route the Production-CDN cert's alerts to its dedicated owner's PagerDuty service, the Internal-API cert's alerts to a different one), calendar-aware suppression (no T-30 informational alerts on weekends for non-on-call teams), and escalation chains (T-1 unanswered for 30m → escalate to manager) are tracked on the project roadmap under "Adapter hardening" → "Multi-channel expiry alerts: per-owner routing".
### Email (SMTP) Notifier
+1 -1
View File
@@ -147,7 +147,7 @@ The hardened OpenSSL adapter (chroot/container by default,
env-var allow-list at the adapter layer, signed-script-binary
verification, audit-log-on-every-invocation, per-call concurrency
bound shared with the API surface) is V3-Pro work. Tracking:
`cowork/WORKSPACE-ROADMAP.md` (search "OpenSSL hardened mode").
the project roadmap (search "OpenSSL hardened mode").
## Related docs
+5 -5
View File
@@ -23,7 +23,7 @@ a single shared primitive:
|---|---|---|
| **Atomic deploy with rollback** | F5 only (transactional API) | 12 of 13 connectors via `deploy.Apply` (K8s pending Bundle 2 — see [Section 1.5](#15-audit-closure-status-2026-05-02-deployment-target-audit)) |
| **Post-deploy TLS verification** | None | NGINX/Apache/HAProxy/Traefik/Caddy/Envoy/Postfix all do TLS handshake + SHA-256 fingerprint compare; fail → rollback |
| **Vendor-specific deployment recipes** | Light docs | (Bundle II — `cowork/deploy-hardening-ii-prompt.md`) |
| **Vendor-specific deployment recipes** | Light docs | (Bundle II — per the project's deploy-hardening II spec) |
This document describes the operator-visible surface. The Go-level
contract lives at `internal/deploy/doc.go`.
@@ -31,7 +31,7 @@ contract lives at `internal/deploy/doc.go`.
## 1.5. Audit closure status (2026-05-02 deployment-target audit)
The 2026-05-02 deployment-target coverage audit
(`cowork/deployment-target-audit-2026-05-02/RESULTS.md`) tightened the
(the 2026-05-02 deployment-target audit) tightened the
atomic + rollback contract on the connectors below. All bundles in the
table are committed to `master` as of this section's last edit; commit
hashes pin to the canonical landing commit for each piece of work.
@@ -54,7 +54,7 @@ hashes pin to the canonical landing commit for each piece of work.
real `k8s.io/client-go` implementation + `ResourceVersion` plumbing
+ post-deploy SHA-256 verify + kubelet sync poll is the remaining
V2 P0 blocker. Tracking prompt:
`cowork/deployment-target-audit-2026-05-02/k8s-real-client-prompt.md`.
the project's k8s-real-client spec.
Bundle 10 (per-connector loadtest harness, commit `6286cd4`) does not
modify the per-connector contract table; it's a CI / observability
@@ -134,7 +134,7 @@ Apply's algorithm:
| ssh | (Connect probe) | (SCP upload + remote chmod) | `tls.Dial` to remote TLS port | Pre-deploy SCP backup of remote files |
| wincertstore | (Get-ChildItem Cert:\) | (Import-PfxCertificate) | (admin probe) | Get-ChildItem snapshot for rollback |
| javakeystore | (`keytool -list`) | (`keytool -importkeystore`) | (admin probe) | keytool snapshot; rollback via `keytool -delete` + re-import |
| k8ssecret | (V2 blocker — see note below) | (V2 blocker — see note below) | (V2 blocker — see note below) | **V2 blocker — Bundle 2 of the 2026-05-02 deployment-target audit.** Production `realK8sClient` at `internal/connector/target/k8ssecret/k8ssecret.go:397-420` is a stub (every method returns `"real Kubernetes client not implemented — use NewWithClient for tests"`). The SHA-256 post-deploy verify and kubelet sync poll are designed but not yet implemented; production deploys to a real cluster fail with "not implemented" until Bundle 2 lands. Test mocks via `NewWithClient` work today. Tracking prompt: `cowork/deployment-target-audit-2026-05-02/k8s-real-client-prompt.md`. |
| k8ssecret | (V2 blocker — see note below) | (V2 blocker — see note below) | (V2 blocker — see note below) | **V2 blocker — Bundle 2 of the 2026-05-02 deployment-target audit.** Production `realK8sClient` at `internal/connector/target/k8ssecret/k8ssecret.go:397-420` is a stub (every method returns `"real Kubernetes client not implemented — use NewWithClient for tests"`). The SHA-256 post-deploy verify and kubelet sync poll are designed but not yet implemented; production deploys to a real cluster fail with "not implemented" until Bundle 2 lands. Test mocks via `NewWithClient` work today. Tracking prompt: the project's k8s-real-client spec. |
> **Postfix vs Dovecot mode**: see "Choosing Mode=postfix vs Mode=dovecot" in
> `docs/connectors.md` for the per-mode defaults (cert/key paths, validate +
@@ -302,7 +302,7 @@ Out of scope for the V2-free deploy-hardening I bundle:
deploy audit trail in a reviewer-friendly format.
- **Customer-paid validation matrices** — vendor-version certified
quirks (e.g. "tested on F5 v15.1 + v17.0 + v17.5"). See
`cowork/deploy-hardening-ii-prompt.md` for the per-vendor
the project's deploy-hardening II spec for the per-vendor
edge-case audit + integration test sidecars.
## 12. Per-connector quick reference
+7 -43
View File
@@ -2,9 +2,9 @@
> Last reviewed: 2026-05-05
certctl ships with an MCP (Model Context Protocol) server that lets AI assistants manage your certificate infrastructure through natural language. Ask Claude to "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?" and the MCP server translates that into API calls against your certctl instance.
certctl ships with an MCP (Model Context Protocol) server that lets AI assistants manage your certificate infrastructure through natural language. Ask your MCP-compatible AI client to "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?" and the MCP server translates that into API calls against your certctl instance.
This guide covers setup, configuration, and usage with Claude, Cursor, and other MCP-compatible tools.
This guide covers setup, configuration, and usage with any MCP-compatible AI client.
## What Is MCP?
@@ -18,7 +18,7 @@ You need:
1. A running certctl server (see [Quick Start](../getting-started/quickstart.md))
2. The MCP server binary — either built from source or from a Docker image
3. An MCP-compatible AI client (Claude Desktop, Cursor, VS Code with Copilot, etc.)
3. An MCP-compatible AI client
## Building the MCP Server
@@ -43,9 +43,9 @@ If your certctl server has auth enabled (the default), you must provide the API
Since v2.2 the certctl control plane is HTTPS-only. If the server cert is self-signed or chained to an internal CA, set `CERTCTL_SERVER_CA_BUNDLE_PATH` so the MCP server can verify the TLS handshake. Never set `CERTCTL_SERVER_TLS_INSECURE_SKIP_VERIFY=true` outside local development — it disables all certificate validation.
## Setting Up with Claude Desktop
## Configuring Your MCP Client
Add this to your Claude Desktop MCP configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
Most MCP clients accept a JSON config block of this shape. Consult your client's documentation for the exact config-file location.
```json
{
@@ -62,43 +62,7 @@ Add this to your Claude Desktop MCP configuration file (`~/Library/Application S
}
```
Restart Claude Desktop. You should see "certctl" appear in the MCP tools list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`).
## Setting Up with Cursor
In Cursor, go to Settings → MCP Servers and add:
```json
{
"certctl": {
"command": "/path/to/certctl-mcp",
"env": {
"CERTCTL_SERVER_URL": "https://localhost:8443",
"CERTCTL_SERVER_CA_BUNDLE_PATH": "/path/to/certctl/deploy/test/certs/ca.crt",
"CERTCTL_API_KEY": "your-api-key-here"
}
}
}
```
## Setting Up with Claude Code
Add certctl as an MCP server in your project's `.mcp.json`:
```json
{
"mcpServers": {
"certctl": {
"command": "/path/to/certctl-mcp",
"env": {
"CERTCTL_SERVER_URL": "https://localhost:8443",
"CERTCTL_SERVER_CA_BUNDLE_PATH": "/path/to/certctl/deploy/test/certs/ca.crt",
"CERTCTL_API_KEY": "your-api-key-here"
}
}
}
}
```
After saving, restart your MCP client. You should see "certctl" appear in its tool list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`).
## Available Tools
@@ -154,7 +118,7 @@ The AI calls `certctl_create_certificate` with the common name, team ID, and own
```mermaid
flowchart LR
AI["AI Assistant\n(Claude, Cursor)"]
AI["AI Assistant\n(any MCP client)"]
MCP["certctl MCP\ncmd/mcp-server/"]
SERVER["certctl Server\n:8443"]
@@ -137,7 +137,7 @@ multicast, IPv4-mapped-IPv6 to a reserved IPv4. See
CodeQL alert #23 flags `client.Do(req)` in the SCEP-probe call site
as `go/request-forgery` despite the dial-time guard; the analyzer
can't trace through a custom `Transport.DialContext`. Operator-
acknowledged false positive (CLAUDE.md task #10) — see the SCEP
acknowledged false positive (tracked internally) — see the SCEP
probe's same-shaped defense for the audit trail.
## DNS-01 cache poisoning posture
+1 -1
View File
@@ -609,7 +609,7 @@ Not yet automatic. Operators migrating: keep the old `managed_certificates`
rows; create new ones via the ACME flow; flip targets one by one. A
dedicated bulk-migration tool is on the roadmap (post-2.1.0). Track
via the master prompt's roadmap section in
`cowork/acme-server-endpoint-prompt.md`.
the project's acme-server-endpoint spec.
### What audit-log events fire on each ACME operation?
+1 -1
View File
@@ -116,5 +116,5 @@ enrollments per tick.
## Audit blocker reference
cowork/issuer-coverage-audit-2026-05-01/RESULTS.md, Top-10 fix #5
the 2026-05-01 issuer coverage audit, Top-10 fix #5
(Part 1.5 finding #4: "No polling backoff for async CAs").
+1 -1
View File
@@ -504,7 +504,7 @@ arbitrary).
EST signs certs using whatever issuer connector the profile binds.
The `internal/crypto/signer/` interface (post-2026-04-28) means a
future HSM/PKCS#11 driver bundle (parking-lot at
`cowork/hsm-pkcs11-driver-prompt.md`) plugs in transparently — the
planned) plugs in transparently — the
EST handler doesn't change. EST-issued certs benefit from HSM-backed
signing automatically once the HSM bundle ships and the operator
swaps the local issuer's `FileDriver` for a `PKCS11Driver`.