mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:41:41 +00:00
feat(V2.2): bulk revocation — filter-based fleet-wide certificate revocation
Add POST /api/v1/certificates/bulk-revoke with filter criteria (profile_id, owner_id, agent_id, issuer_id, team_id, certificate_ids), partial-failure tolerance, and audit trail. Includes MCP tool, CLI command (certs bulk-revoke), server-side bulk modal in GUI replacing client-side sequential loop, OpenAPI spec, compliance mapping updates, and 21 new tests (12 service, 7 handler, 1 CLI, 1 frontend). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -467,6 +467,10 @@ The revocation is recorded in the `certificate_revocations` table (separate from
|
||||
|
||||
Short-lived certificates (those with profile TTL < 1 hour) return "good" from OCSP and are excluded from CRL — their rapid expiry is treated as sufficient revocation.
|
||||
|
||||
#### Bulk Revocation
|
||||
|
||||
For compliance events requiring fleet-wide revocation (key compromise, CA distrust, mass decommission), certctl supports bulk revocation by filter criteria. The `POST /api/v1/certificates/bulk-revoke` endpoint accepts filter parameters (profile_id, owner_id, agent_id, issuer_id) and creates individual revocation jobs for each matching certificate. Bulk revocation reuses the same 7-step single-cert flow for each certificate — no new issuer notification or audit mechanics. The operation is idempotent: revoking an already-revoked certificate is a no-op. Partial failures are tolerated — if one certificate fails to revoke (e.g., issuer unavailable), the operation continues for remaining certs and returns a summary. A single `bulk_revocation_initiated` audit event logs the operation with filter criteria, operator actor, and summary (total requested, succeeded, failed counts). Audit events for individual certificate revocations record the operator identity separately. The GUI bulk revoke button on the certificates list filters by visible selections and displays an affected-cert count modal before confirmation.
|
||||
|
||||
### 4. Automatic Renewal
|
||||
|
||||
The control plane runs a scheduler with seven background loops:
|
||||
@@ -846,6 +850,8 @@ The full API is documented in an OpenAPI 3.1 specification at `api/openapi.yaml`
|
||||
|
||||
Jobs support additional action endpoints: `POST /api/v1/jobs/{id}/cancel`, `POST /api/v1/jobs/{id}/approve`, `POST /api/v1/jobs/{id}/reject`.
|
||||
|
||||
**Bulk Operations:** `POST /api/v1/certificates/bulk-revoke` — Bulk revocation by filter criteria (profile_id, owner_id, agent_id, issuer_id). Creates individual revocation jobs for matching certificates, with partial-failure tolerance and a summary audit event.
|
||||
|
||||
**Enhanced Query Features (M20):** Certificate list endpoints support additional query capabilities beyond basic pagination:
|
||||
|
||||
- **Sorting**: `?sort=notAfter` (ascending) or `?sort=-createdAt` (descending). Whitelist: notAfter, expiresAt, createdAt, updatedAt, commonName, name, status, environment.
|
||||
@@ -1063,9 +1069,9 @@ Beyond one-time discovery, certctl continuously monitors TLS endpoints for certi
|
||||
|
||||
certctl is extensively tested across eight layers with CI-enforced coverage gates that act as regression floors. The goal is high-confidence regression prevention at the service and handler layers (where the most complex business logic lives), combined with integration tests that exercise the full request path from HTTP to database.
|
||||
|
||||
**Service layer unit tests** (`internal/service/*_test.go`) — Mock-based tests across all service files covering certificate CRUD, revocation (all RFC 5280 reason codes, OCSP/CRL generation), agent lifecycle, job state machine, policy evaluation, renewal/issuance flow (both keygen modes), notification deduplication, team/owner/agent group CRUD, issuer service CRUD with connection testing, and the issuer connector adapter. Mock repositories are simple structs with function fields — no heavy mocking frameworks.
|
||||
**Service layer unit tests** (`internal/service/*_test.go`) — Mock-based tests across all service files covering certificate CRUD, revocation (all RFC 5280 reason codes, OCSP/CRL generation, bulk revocation by filter with partial-failure tolerance), agent lifecycle, job state machine, policy evaluation, renewal/issuance flow (both keygen modes), notification deduplication, team/owner/agent group CRUD, issuer service CRUD with connection testing, and the issuer connector adapter. Mock repositories are simple structs with function fields — no heavy mocking frameworks.
|
||||
|
||||
**Handler layer tests** (`internal/api/handler/*_test.go`) — Every handler file has a corresponding test file using Go's `httptest` package: certificates (including revocation, DER CRL, OCSP), agents, jobs (including approve/reject), notifications, policies, profiles, issuers, targets, agent groups, teams, owners, discovery, network scan, verification, export, EST, digest, stats, and metrics. Tests cover the happy path, input validation, error propagation, method-not-allowed, and pagination.
|
||||
**Handler layer tests** (`internal/api/handler/*_test.go`) — Every handler file has a corresponding test file using Go's `httptest` package: certificates (including revocation, bulk revocation by profile/owner/agent/issuer, DER CRL, OCSP), agents, jobs (including approve/reject), notifications, policies, profiles, issuers, targets, agent groups, teams, owners, discovery, network scan, verification, export, EST, digest, stats, and metrics. Tests cover the happy path, input validation, error propagation, method-not-allowed, pagination, and bulk operation partial-failure scenarios.
|
||||
|
||||
**Integration tests** (`internal/integration/`) — Three test files exercising the full stack from HTTP request through router, handler, service, and repository layers. `lifecycle_test.go` covers the complete certificate lifecycle (team/owner creation through deployment and status reporting). `negative_test.go` covers error paths, endpoint validation, and revocation scenarios. `e2e_test.go` exercises cross-milestone features end-to-end (agent metadata, profiles, issuer registry, GUI operations, stats, revocation, notifications, enhanced query API).
|
||||
|
||||
|
||||
@@ -272,13 +272,16 @@ NIST SP 800-57 Part 3 covers revocation (Section 2.5) when keys are suspected co
|
||||
- OCSP responder queries revocation table in real-time
|
||||
- Short-lived certificate exemption: certs with TTL < 1 hour skip CRL/OCSP (expiry is sufficient revocation)
|
||||
|
||||
**Bulk Revocation for Large-Scale Compromise Response** (V2.2) — NIST SP 800-57 Part 3 emphasizes rapid revocation when keys are compromised. `POST /api/v1/certificates/bulk-revoke` revokes all certificates matching filter criteria (profile, owner, agent, issuer) in a single operation. This enables operators to execute fleet-wide revocation for key compromise events affecting multiple certificates. Each bulk revocation creates individual jobs reusing the existing revocation pipeline, ensuring every certificate is recorded in the audit trail with the incident reason.
|
||||
|
||||
**Revocation Audit Trail**
|
||||
All revocation events logged:
|
||||
- Event type: `certificate_revoked`
|
||||
- Event type: `certificate_revoked` or `bulk_revocation_initiated` (for fleet operations)
|
||||
- Actor: authenticated user or service
|
||||
- Reason code: RFC 5280 enum
|
||||
- Reason code: RFC 5280 enum (or incident justification for bulk operations)
|
||||
- Timestamp: RFC3339
|
||||
- Issuer notification status: success or error reason
|
||||
- Filter criteria: profile_id, owner_id, agent_id, issuer_id (for bulk revocation)
|
||||
|
||||
## Alignment Summary Table
|
||||
|
||||
@@ -301,9 +304,11 @@ All revocation events logged:
|
||||
- [x] RFC 5280 revocation support
|
||||
- [x] Immutable audit trail
|
||||
|
||||
### V2.2 (Planned: 2026)
|
||||
- Bulk revocation by profile/owner/agent/issuer (fleet-level revocation for incident response)
|
||||
|
||||
### V3 (Planned: 2026)
|
||||
- Role-based access control (limit revocation/approval to authorized operators)
|
||||
- Bulk revocation by profile/owner/agent (fleet-level revocation policy)
|
||||
|
||||
### V3 Pro (Planned)
|
||||
- HSM support for CA key storage and agent key storage (TPM 2.0, PKCS#11)
|
||||
|
||||
@@ -93,8 +93,10 @@ Your QSA will request evidence that your certificate and key management systems
|
||||
- **Certificate Status Tracking** — Four statuses: Active (deployed, not yet expired), Expiring (within threshold, awaiting renewal), Expired (past not-after date), Revoked (revoked via RFC 5280 revocation API). Dashboard charts show status distribution.
|
||||
|
||||
- **Revocation Infrastructure** (M15a, M15b):
|
||||
- Revocation API: `POST /api/v1/certificates/{id}/revoke` with RFC 5280 reason codes
|
||||
- CRL endpoint: `GET /api/v1/crl` (JSON format) or `GET /api/v1/crl/{issuer_id}` (DER X.509 CRL, 24h validity, signed by issuing CA)
|
||||
- OCSP responder: `GET /api/v1/ocsp/{issuer_id}/{serial}` (returns DER-encoded OCSP response: good/revoked/unknown)
|
||||
- Bulk revocation (V2.2): `POST /api/v1/certificates/bulk-revoke` with filter criteria (profile, owner, agent, issuer) for fleet-wide incident response
|
||||
- Short-lived cert exemption: certs with TTL < 1 hour skip CRL/OCSP (expiry is sufficient revocation)
|
||||
|
||||
- **Stats API** (M14) — Real-time visibility:
|
||||
@@ -331,6 +333,8 @@ This requirement covers key generation, storage, rotation, and destruction. Cert
|
||||
- OCSP: `GET /api/v1/ocsp/{issuer_id}/{serial}` (returns revoked status for clients validating certificate chain)
|
||||
- Clients checking certificate status via OCSP or CRL see revoked status within 24 hours.
|
||||
|
||||
- **Bulk Revocation for Incident Response** (V2.2) — `POST /api/v1/certificates/bulk-revoke` with filter criteria (profile, owner, agent, issuer) revokes all matching certificates in a single operation. PCI-DSS Req 4 requires rapid response to data transmission security incidents — bulk revocation enables operators to revoke an entire certificate set (e.g., all certs used by a compromised team or endpoint) in minutes rather than hours.
|
||||
|
||||
- **Private Key Destruction on Agent** — When certificate renewed or revoked:
|
||||
- Agent removes old private key file from `CERTCTL_KEY_DIR` when new certificate deployed.
|
||||
- Job status tracking confirms old key is no longer needed.
|
||||
|
||||
@@ -288,6 +288,7 @@ Each section includes:
|
||||
- Certificate owner (email)
|
||||
- Configured webhooks (if you have a SIEM that subscribes)
|
||||
- Slack/Teams channels (if notifiers are configured)
|
||||
- **Bulk Revocation for Fleet-Wide Incidents** (V2.2) — `POST /api/v1/certificates/bulk-revoke` with filter criteria (profile, owner, agent, issuer) revokes all matching certificates in a single operation. Essential for incident response: key compromise affecting multiple certs, CA distrust events, decommissioning a team's infrastructure. Each bulk revocation creates individual jobs reusing the existing revocation pipeline, ensuring audit trail and notifications for every certificate.
|
||||
- **Short-Lived Cert Exemption** — Certificates with TTL < 1 hour (configured in profile) skip CRL/OCSP publication. Expiry is the revocation mechanism for short-lived certs (e.g., Kubernetes pod certs, session tokens).
|
||||
- **Deployment Rollback** — If a revoked cert is still deployed (shouldn't happen, but race conditions exist), operators can manually redeploy a previous version via the GUI. Rollback is audited.
|
||||
|
||||
@@ -302,7 +303,6 @@ Each section includes:
|
||||
|
||||
**V3 Enhancement**:
|
||||
|
||||
- **Bulk Revocation** — Revoke all certs issued by a specific profile, owner, or agent in a single API call (useful for large-scale incidents like CA compromise)
|
||||
- **Revocation Automation** — Trigger revocation based on external events (e.g., employee termination, security breach alert from CT Log monitoring)
|
||||
|
||||
**Operator Responsibility**:
|
||||
|
||||
@@ -214,6 +214,8 @@ certctl implements revocation using three complementary mechanisms:
|
||||
|
||||
**Revocation API**: `POST /api/v1/certificates/{id}/revoke` marks a certificate as revoked in the inventory, records the revocation in a dedicated `certificate_revocations` table, notifies the issuing CA (best-effort — the revocation succeeds even if the CA is unreachable), creates an audit trail entry, and sends notifications. You can specify an RFC 5280 reason code (keyCompromise, superseded, cessationOfOperation, etc.) or let it default to "unspecified."
|
||||
|
||||
**Bulk Revocation** (Fleet-Level Incident Response): For large-scale incidents like CA compromise or team infrastructure decommissioning, `POST /api/v1/certificates/bulk-revoke` revokes all certificates matching filter criteria in a single operation. Filter by profile, owner, team, agent group, or issuer to target the affected certificate set. This is essential for incident response — instead of revoking certificates one-by-one, operators can revoke an entire fleet in minutes. Bulk revocation creates individual revocation jobs that reuse the existing revocation pipeline, ensuring every certificate is audited and notifications are sent.
|
||||
|
||||
**Certificate Revocation List (CRL)**: certctl serves both a JSON-formatted CRL at `GET /api/v1/crl` and DER-encoded X.509 CRLs per issuer at `GET /api/v1/crl/{issuer_id}`. The DER CRL is signed by the issuing CA's key and has 24-hour validity — clients can download it periodically to check revocation status offline.
|
||||
|
||||
**OCSP Responder**: For real-time revocation checking, certctl includes an embedded OCSP responder at `GET /api/v1/ocsp/{issuer_id}/{serial}`. It returns signed OCSP responses (good, revoked, or unknown) so clients can verify certificate status without downloading the full CRL.
|
||||
|
||||
+81
-1
@@ -182,6 +182,52 @@ Configurable per-policy thresholds stored as `alert_thresholds_days` JSONB (defa
|
||||
|
||||
Revocation is a 7-step process: validate eligibility → get serial → update status → record in `certificate_revocations` table → notify issuer (best-effort) → audit → send notification.
|
||||
|
||||
### Bulk Revocation
|
||||
|
||||
`POST /api/v1/certificates/bulk-revoke` revokes multiple certificates matching filter criteria in a single operation.
|
||||
|
||||
**Filter criteria** (at least one required):
|
||||
|
||||
- `profile_id` — revoke all certs issued with this profile
|
||||
- `owner_id` — revoke all certs owned by this owner
|
||||
- `agent_id` — revoke all certs deployed to this agent
|
||||
- `issuer_id` — revoke all certs from this issuer
|
||||
- `team_id` — revoke all certs owned by members of this team
|
||||
- `certificate_ids` — array of specific cert IDs to revoke
|
||||
|
||||
**Request body** example:
|
||||
|
||||
```json
|
||||
{
|
||||
"reason": "keyCompromise",
|
||||
"profile_id": "prof-staging",
|
||||
"team_id": "team-platform"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "job-bulk-rev-123",
|
||||
"criteria": {
|
||||
"reason": "keyCompromise",
|
||||
"profile_id": "prof-staging",
|
||||
"team_id": "team-platform"
|
||||
},
|
||||
"affected_count": 47,
|
||||
"status": "Pending"
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- Individual revocation jobs created for each matching cert (reuses existing revocation flow)
|
||||
- Progress tracked via job system (job status: Pending → Running → Completed)
|
||||
- Partial failures tolerated — if 47 certs match but 3 fail, the other 44 still revoke
|
||||
- Audit trail: single `bulk_revocation_initiated` event logs the criteria and actor
|
||||
- Optional `--reason` defaults to `unspecified` if omitted
|
||||
|
||||
### CRL Endpoints
|
||||
|
||||
- `GET /api/v1/crl` — JSON-formatted CRL (version, entries array, total count, timestamp)
|
||||
@@ -1110,7 +1156,7 @@ Same pattern as issuer configuration:
|
||||
| Page | Route | Description |
|
||||
|---|---|---|
|
||||
| Dashboard | `/` | Summary stats, 4 charts (status donut, expiration heatmap, renewal trends, issuance rate) |
|
||||
| Certificates | `/certificates` | List with bulk ops (renew, revoke, reassign owner), multi-select |
|
||||
| Certificates | `/certificates` | List with bulk ops (renew, revoke by filter criteria, reassign owner), multi-select. Bulk revoke via server-side filter API, not client-side sequential calls. |
|
||||
| Certificate Detail | `/certificates/:id` | Versions, deployment timeline, inline policy editor, export buttons |
|
||||
| Agents | `/agents` | List with OS/arch metadata |
|
||||
| Agent Detail | `/agents/:id` | System info, heartbeat status, capabilities, recent jobs |
|
||||
@@ -1163,6 +1209,7 @@ Latching state prevents refetch-driven dismissal. `localStorage` dismissal key:
|
||||
| `certs get ID` | Certificate details |
|
||||
| `certs renew ID` | Trigger renewal |
|
||||
| `certs revoke ID` | Revoke (with `--reason`) |
|
||||
| `certs bulk-revoke` | Bulk revoke by filter criteria (see below) |
|
||||
| `agents list` | List agents |
|
||||
| `agents get ID` | Agent details |
|
||||
| `jobs list` | List jobs |
|
||||
@@ -1180,6 +1227,39 @@ Latching state prevents refetch-driven dismissal. `localStorage` dismissal key:
|
||||
| `--api-key` | `CERTCTL_API_KEY` | (none) | API key |
|
||||
| `--format` | (none) | `table` | Output: `table` or `json` |
|
||||
|
||||
### Bulk Revocation Command
|
||||
|
||||
`certs bulk-revoke` revokes multiple certificates matching filter criteria.
|
||||
|
||||
**Usage:** `certs bulk-revoke [CERT_IDs...] [flags]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
| Flag | Description |
|
||||
|---|---|
|
||||
| `--reason` | RFC 5280 revocation reason (`keyCompromise`, `caCompromise`, `affiliationChanged`, `superseded`, `cessationOfOperation`, `certificateHold`, `privilegeWithdrawn`, `unspecified` — default). |
|
||||
| `--profile-id` | Revoke all certs with this profile ID |
|
||||
| `--owner-id` | Revoke all certs owned by this owner |
|
||||
| `--agent-id` | Revoke all certs deployed to this agent |
|
||||
| `--issuer-id` | Revoke all certs issued by this issuer |
|
||||
| `--team-id` | Revoke all certs owned by members of this team |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Revoke certs with specific IDs (positional args)
|
||||
certctl-cli certs bulk-revoke mc-api-prod mc-web-prod --reason keyCompromise
|
||||
|
||||
# Revoke by profile
|
||||
certctl-cli certs bulk-revoke --profile-id prof-staging --reason cessationOfOperation
|
||||
|
||||
# Revoke by team
|
||||
certctl-cli certs bulk-revoke --team-id team-platform --reason superseded
|
||||
|
||||
# Revoke by issuer (all certs from one CA)
|
||||
certctl-cli certs bulk-revoke --issuer-id iss-letsencrypt --reason caCompromise
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP Server
|
||||
|
||||
Reference in New Issue
Block a user