mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:51:30 +00:00
feat: M25 post-deployment TLS verification + M26 Traefik/Caddy targets
M25: After deploying a certificate, the agent probes the live TLS
endpoint and compares SHA-256 fingerprints to verify the correct cert
is being served. Best-effort — failures don't block deployments.
New endpoints: POST /jobs/{id}/verify, GET /jobs/{id}/verification.
Migration 000008 adds verification columns to jobs table.
M26: Traefik target connector (file provider, auto-reload) and Caddy
target connector (dual-mode: admin API hot-reload or file-based).
Both wired into agent dispatch.
Also: restructured README to highlight supported integrations (issuers,
targets, notifiers) earlier, moved API/CLI/MCP sections lower. Updated
all docs (features, connectors, architecture, testing guide, why-certctl)
and fixed integration tests for 18-param RegisterHandlers signature.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -510,6 +510,8 @@ flowchart TB
|
||||
TI --> NG["NGINX"]
|
||||
TI --> AP["Apache httpd"]
|
||||
TI --> HP["HAProxy"]
|
||||
TI --> TF["Traefik"]
|
||||
TI --> CD["Caddy"]
|
||||
TI --> F5["F5 BIG-IP (interface only)"]
|
||||
TI --> IIS["IIS (interface only)"]
|
||||
end
|
||||
@@ -579,7 +581,9 @@ type Connector interface {
|
||||
|
||||
The `DeploymentRequest` struct carries the full material needed by the target system: the signed certificate, the CA chain, the agent-generated private key, target-specific configuration, and arbitrary metadata. The key field is populated by the agent from its local key store (`CERTCTL_KEY_DIR`) — it never originates from the control plane.
|
||||
|
||||
Built-in targets: **NGINX** (writes cert/chain/key files, validates with `nginx -t`, reloads), **Apache httpd** (writes cert/chain/key files, validates with `apachectl configtest`, graceful reload), **HAProxy** (combined PEM file with cert+chain+key, validates config, reloads via systemctl/signal), **F5 BIG-IP** (interface only — proxy agent + iControl REST, implementation planned), **IIS** (interface only — dual-mode: agent-local PowerShell primary + proxy agent WinRM for agentless targets, implementation planned).
|
||||
Built-in targets: **NGINX** (writes cert/chain/key files, validates with `nginx -t`, reloads), **Apache httpd** (writes cert/chain/key files, validates with `apachectl configtest`, graceful reload), **HAProxy** (combined PEM file with cert+chain+key, validates config, reloads via systemctl/signal), **Traefik** (file provider — writes cert/key to watched directory, Traefik auto-reloads), **Caddy** (dual-mode: admin API hot-reload or file-based), **F5 BIG-IP** (interface only — proxy agent + iControl REST, implementation planned), **IIS** (interface only — dual-mode: agent-local PowerShell primary + proxy agent WinRM for agentless targets, implementation planned).
|
||||
|
||||
After deployment, agents can perform **post-deployment TLS verification**: the agent probes the live TLS endpoint using `crypto/tls.DialWithDialer` and compares the SHA-256 fingerprint of the served certificate against what was deployed. Results are reported via `POST /api/v1/jobs/{id}/verify` and stored on the job record. Verification is best-effort — failures don't block or rollback deployments.
|
||||
|
||||
Additional cloud, network, and Kubernetes target connectors are planned for future releases.
|
||||
|
||||
@@ -903,9 +907,9 @@ certctl uses a layered testing approach aligned with the handler → service →
|
||||
|
||||
**CLI tests** (`internal/cli/client_test.go`) — 14 tests covering all 10 CLI subcommands with httptest mock servers, PEM parsing for bulk import, auth header verification, and JSON/table output formatting.
|
||||
|
||||
**CI pipeline** (`.github/workflows/ci.yml`) — Two parallel jobs: Go (build, vet, test with coverage, coverage threshold enforcement) and Frontend (TypeScript type check, Vitest test suite, Vite production build). The Go job runs all tests with `-coverprofile`, then enforces coverage thresholds: service layer must be at least 30% (current: ~35%) and handler layer must be at least 50% (current: ~63%). These thresholds act as regression floors — they can only go up. The service layer threshold is deliberately lower because much of the service code depends on postgres repositories and external connectors that require real infrastructure to test meaningfully. Connector tests are included via `./internal/connector/issuer/...` and `./internal/connector/target/...` (covers Local CA, ACME, step-ca, NGINX, Apache, and HAProxy packages with unit tests for certificate signing logic, DNS solver, issuer validation, and deployment flows). The Frontend job runs `npx vitest run` between the TypeScript check and production build steps.
|
||||
**CI pipeline** (`.github/workflows/ci.yml`) — Two parallel jobs: Go (build, vet, test with coverage, coverage threshold enforcement) and Frontend (TypeScript type check, Vitest test suite, Vite production build). The Go job runs all tests with `-coverprofile`, then enforces coverage thresholds: service layer must be at least 30% (current: ~35%) and handler layer must be at least 50% (current: ~63%). These thresholds act as regression floors — they can only go up. The service layer threshold is deliberately lower because much of the service code depends on postgres repositories and external connectors that require real infrastructure to test meaningfully. Connector tests are included via `./internal/connector/issuer/...` and `./internal/connector/target/...` (covers Local CA, ACME, step-ca, NGINX, Apache, HAProxy, Traefik, and Caddy packages with unit tests for certificate signing logic, DNS solver, issuer validation, and deployment flows). The Frontend job runs `npx vitest run` between the TypeScript check and production build steps.
|
||||
|
||||
**Connector tests** (`internal/connector/`) — 57 test functions covering issuer, target, and notifier connectors. The Local CA connector has tests for self-signed and sub-CA modes (RSA, ECDSA, config validation, non-CA cert rejection). The ACME DNS solver has 10 tests for script-based DNS-01 and DNS-PERSIST-01 challenges (6 DNS-01 tests + 4 DNS-PERSIST-01 tests covering `PresentPersist` success, no-script error, script failure, and wildcard domain handling). The step-ca connector has tests with a mock HTTP server for issuance, renewal, revocation, and error paths. The OpenSSL/Custom CA connector has 14 tests covering config validation, issuance success/failure/timeout, renewal, revocation, and CRL generation. The NGINX target connector has 13 tests covering config validation, certificate deployment (file writing, permissions, validate/reload commands), and deployment validation. Apache httpd and HAProxy connectors each have 3 tests covering config validation, deployment, and validation flows. Notifier connector tests span 20 tests across Slack (5), Teams (4), PagerDuty (6), and OpsGenie (5) — verifying channel identity, payload formatting, HTTP error handling, connection failures, auth headers, and configuration defaults.
|
||||
**Connector tests** (`internal/connector/`) — 57 test functions covering issuer, target, and notifier connectors. The Local CA connector has tests for self-signed and sub-CA modes (RSA, ECDSA, config validation, non-CA cert rejection). The ACME DNS solver has 10 tests for script-based DNS-01 and DNS-PERSIST-01 challenges (6 DNS-01 tests + 4 DNS-PERSIST-01 tests covering `PresentPersist` success, no-script error, script failure, and wildcard domain handling). The step-ca connector has tests with a mock HTTP server for issuance, renewal, revocation, and error paths. The OpenSSL/Custom CA connector has 14 tests covering config validation, issuance success/failure/timeout, renewal, revocation, and CRL generation. The NGINX target connector has 13 tests covering config validation, certificate deployment (file writing, permissions, validate/reload commands), and deployment validation. Apache httpd and HAProxy connectors each have 3 tests covering config validation, deployment, and validation flows. Traefik and Caddy connectors have tests covering file-based deployment and (for Caddy) dual-mode API/file configuration. Notifier connector tests span 20 tests across Slack (5), Teams (4), PagerDuty (6), and OpsGenie (5) — verifying channel identity, payload formatting, HTTP error handling, connection failures, auth headers, and configuration defaults.
|
||||
|
||||
**What's not tested and why:** Postgres repository implementations (`internal/repository/postgres/`) require a real database and are tested only through integration tests, not unit tests. Target connectors for F5 BIG-IP and IIS are interface stubs (implementation planned for a future release). Scheduler loops are time-dependent and tested manually during development. The ACME connector requires a real ACME server (tested manually against Let's Encrypt staging). These are all candidates for future expansion as the test infrastructure matures.
|
||||
|
||||
|
||||
+43
-1
@@ -20,6 +20,8 @@ Connectors extend certctl to integrate with external systems for certificate iss
|
||||
- [Built-in: NGINX](#built-in-nginx)
|
||||
- [Built-in: Apache httpd](#built-in-apache-httpd)
|
||||
- [Built-in: HAProxy](#built-in-haproxy)
|
||||
- [Built-in: Traefik](#built-in-traefik)
|
||||
- [Built-in: Caddy](#built-in-caddy)
|
||||
- [F5 BIG-IP (Interface Only)](#f5-big-ip-interface-only)
|
||||
- [IIS (Interface Only, Dual-Mode)](#iis-interface-only-dual-mode)
|
||||
4. [Notifier Connector](#notifier-connector)
|
||||
@@ -50,7 +52,7 @@ Connectors extend certctl to integrate with external systems for certificate iss
|
||||
Three types of connectors:
|
||||
|
||||
1. **Issuer Connector** — Obtains certificates from CAs (Local CA with sub-CA support, ACME with HTTP-01 + DNS-01 + DNS-PERSIST-01, step-ca, OpenSSL/Custom CA implemented; additional CA integrations planned)
|
||||
2. **Target Connector** — Deploys certificates to infrastructure (NGINX, Apache httpd, HAProxy implemented; F5 via proxy agent, IIS dual-mode interface only; additional cloud and network targets planned)
|
||||
2. **Target Connector** — Deploys certificates to infrastructure (NGINX, Apache httpd, HAProxy, Traefik, Caddy implemented; F5 via proxy agent, IIS dual-mode interface only; additional cloud and network targets planned)
|
||||
3. **Notifier Connector** — Sends alerts about certificate events (Email, Webhooks, Slack, Microsoft Teams, PagerDuty, OpsGenie implemented)
|
||||
|
||||
All connectors accept JSON configuration at initialization, support config validation, and are registered in the service layer. Issuer connectors run on the control plane; target connectors run on agents. For network appliances where agents can't be installed, a **proxy agent** in the same network zone handles deployment — the server never initiates outbound connections.
|
||||
@@ -501,6 +503,46 @@ The combined PEM is built in this order: server certificate, intermediate/chain
|
||||
|
||||
Location: `internal/connector/target/haproxy/haproxy.go`
|
||||
|
||||
### Built-in: Traefik
|
||||
|
||||
The Traefik connector uses Traefik's file provider — it writes certificate and key files to a watched directory, and Traefik automatically picks up the changes without any explicit reload command. This is the simplest deployment model: write the files, and Traefik does the rest.
|
||||
|
||||
Configuration:
|
||||
```json
|
||||
{
|
||||
"cert_dir": "/etc/traefik/certs",
|
||||
"cert_file": "site.crt",
|
||||
"key_file": "site.key"
|
||||
}
|
||||
```
|
||||
|
||||
The `cert_dir` is the directory Traefik is configured to watch via its file provider (e.g., `providers.file.directory` in Traefik's static config). The connector writes `cert_file` and `key_file` into this directory with appropriate permissions. Traefik's file watcher detects the change and reloads the TLS configuration automatically.
|
||||
|
||||
Location: `internal/connector/target/traefik/traefik.go`
|
||||
|
||||
### Built-in: Caddy
|
||||
|
||||
The Caddy connector supports two deployment modes — choose based on your Caddy setup:
|
||||
|
||||
**API mode (recommended):** Posts the certificate directly to Caddy's admin API (`POST /load` or certificate-specific endpoints) for zero-downtime hot reload. Requires Caddy's admin API to be enabled and accessible from the agent.
|
||||
|
||||
**File mode (fallback):** Writes cert and key files to disk, relying on Caddy's built-in file watcher or a manual reload. Use this when the admin API isn't available or when Caddy is configured to read certificates from disk.
|
||||
|
||||
Configuration:
|
||||
```json
|
||||
{
|
||||
"mode": "api",
|
||||
"admin_api": "http://localhost:2019",
|
||||
"cert_dir": "/etc/caddy/certs",
|
||||
"cert_file": "site.crt",
|
||||
"key_file": "site.key"
|
||||
}
|
||||
```
|
||||
|
||||
When `mode` is `"api"`, the connector posts the certificate to the admin API endpoint. When `mode` is `"file"`, it writes files to `cert_dir` (same pattern as Traefik). The `admin_api` field is ignored in file mode.
|
||||
|
||||
Location: `internal/connector/target/caddy/caddy.go`
|
||||
|
||||
### F5 BIG-IP (Interface Only)
|
||||
|
||||
The F5 BIG-IP target connector interface is defined with the iControl REST flow mapped out, but the actual API calls are not yet implemented. F5 appliances can't run agents directly, so this connector uses the **proxy agent pattern**: a designated agent in the same network zone picks up F5 deployment jobs and calls the iControl REST API. The server assigns the work; the proxy agent executes it.
|
||||
|
||||
+47
-6
@@ -7,7 +7,7 @@ Complete reference of all features shipped in the V2 release (as of March 2026).
|
||||
## API Surface
|
||||
|
||||
### Overview
|
||||
- **95 endpoints** across 20 resource domains under `/api/v1/` + `/.well-known/est/`
|
||||
- **97 endpoints** across 21 resource domains under `/api/v1/` + `/.well-known/est/`
|
||||
- REST API with HTTP semantics (GET, POST, PUT, DELETE)
|
||||
- All endpoints require authentication by default (configurable)
|
||||
- OpenAPI 3.1 spec with full schema documentation
|
||||
@@ -94,6 +94,7 @@ curl -H "$AUTH" "$SERVER/api/v1/certificates?expires_before=2026-04-24T00:00:00Z
|
||||
| **Notifications** | 3 | List, get, mark as read |
|
||||
| **Stats** | 5 | Dashboard summary, certificates by status, expiration timeline, job trends, issuance rate |
|
||||
| **Metrics** | 2 | JSON metrics (gauges, counters, uptime), Prometheus exposition format |
|
||||
| **Verification** | 2 | Submit verification result, get verification status |
|
||||
| **EST (RFC 7030)** | 4 | CA certs (PKCS#7), simple enrollment, re-enrollment, CSR attributes |
|
||||
| **Health** | 4 | Health check, readiness check, auth info, auth check |
|
||||
|
||||
@@ -144,6 +145,32 @@ curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/certificates/mc-api-prod/deploy
|
||||
curl -H "$AUTH" "$SERVER/api/v1/certificates/mc-api-prod/deployments" | jq '.data[] | {id, name, type}'
|
||||
```
|
||||
|
||||
### Post-Deployment TLS Verification (M25)
|
||||
|
||||
After deploying a certificate, the agent connects back to the target's live TLS endpoint and verifies the served certificate matches what was deployed — using SHA-256 fingerprint comparison. This catches failures that deployment commands can't: wrong virtual host, stale cache, config that validates but doesn't apply.
|
||||
|
||||
```bash
|
||||
# Agent submits verification result after probing the live endpoint
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/jobs/j-deploy-123/verify -d '{
|
||||
"target_id": "tgt-nginx-prod",
|
||||
"expected_fingerprint": "sha256:a1b2c3...",
|
||||
"actual_fingerprint": "sha256:a1b2c3...",
|
||||
"verified": true
|
||||
}'
|
||||
|
||||
# Check verification status for a job
|
||||
curl -H "$AUTH" $SERVER/api/v1/jobs/j-deploy-123/verification | jq .
|
||||
```
|
||||
|
||||
| Feature | Details |
|
||||
|---------|---------|
|
||||
| **Verification Method** | `crypto/tls.DialWithDialer` with `InsecureSkipVerify=true` to handle self-signed and internal CA certs |
|
||||
| **Fingerprint Comparison** | SHA-256 of raw certificate DER bytes |
|
||||
| **Best-Effort** | Verification failures are recorded but don't block or rollback deployments |
|
||||
| **Job Fields** | `verification_status` (pending/success/failed/skipped), `verified_at`, `verification_fingerprint`, `verification_error` |
|
||||
| **Audit Trail** | `job_verification_success` and `job_verification_failed` events recorded |
|
||||
| **Configuration** | `CERTCTL_VERIFY_DEPLOYMENT` (enable/disable), `CERTCTL_VERIFY_TIMEOUT` (TLS dial timeout), `CERTCTL_VERIFY_DELAY` (wait after deploy before probing) |
|
||||
|
||||
---
|
||||
|
||||
## Revocation Infrastructure
|
||||
@@ -311,7 +338,7 @@ curl -H "$AUTH" "$SERVER/api/v1/policies/rp-standard/violations"
|
||||
|
||||
---
|
||||
|
||||
## Target Connectors (3 Implemented + 2 Stubs)
|
||||
## Target Connectors (5 Implemented + 2 Stubs)
|
||||
|
||||
### NGINX
|
||||
- **Deployment** — Separate cert, chain, and key files
|
||||
@@ -334,6 +361,19 @@ curl -H "$AUTH" "$SERVER/api/v1/policies/rp-standard/violations"
|
||||
- **Target Config** — Combined PEM path, optional reload command
|
||||
- **Status** — Fully implemented (M10)
|
||||
|
||||
### Traefik
|
||||
- **Deployment** — File provider: writes cert and key to Traefik's watched certificate directory
|
||||
- **Auto-Reload** — Traefik's file provider watches the directory for changes; no explicit reload needed
|
||||
- **Target Config** — Certificate directory, cert filename, key filename
|
||||
- **Status** — Fully implemented (M26)
|
||||
|
||||
### Caddy
|
||||
- **Dual-Mode Deployment** — Admin API (hot-reload via `POST /load`) or file-based (write cert+key, Caddy watches)
|
||||
- **API Mode** — Posts certificate to Caddy's admin API endpoint for zero-downtime reload
|
||||
- **File Mode** — Writes cert and key files to configured directory (fallback when admin API is unavailable)
|
||||
- **Target Config** — Admin API URL, certificate directory, cert filename, key filename, mode (api/file)
|
||||
- **Status** — Fully implemented (M26)
|
||||
|
||||
### F5 BIG-IP (Stub)
|
||||
- **Protocol** — iControl REST API via proxy agent
|
||||
- **Status** — Interface only in V2; implementation in V3 (paid)
|
||||
@@ -480,7 +520,7 @@ curl -H "$AUTH" "$SERVER/api/v1/agent-groups/ag-linux-dc1/members" | jq '.items[
|
||||
### Agent Capabilities
|
||||
Agents report to `/api/v1/agents/{id}/work` with supported target types and issuers.
|
||||
|
||||
- **Target Deployment** — NGINX, Apache httpd, HAProxy, F5 BIG-IP (proxy), IIS (proxy)
|
||||
- **Target Deployment** — NGINX, Apache httpd, HAProxy, Traefik, Caddy, F5 BIG-IP (proxy), IIS (proxy)
|
||||
- **Key Management** — ECDSA P-256 keygen, key storage at `CERTCTL_KEY_DIR` (default `/var/lib/certctl/keys`), 0600 file permissions
|
||||
- **CSR Submission** — `POST /api/v1/agents/{id}/csr` for AwaitingCSR jobs
|
||||
|
||||
@@ -798,7 +838,8 @@ curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/jobs/j-abc123/approve -d '{"reas
|
||||
5. **CSR received** → Server signs; Job transitioned to `Running`
|
||||
6. **Deployment scheduled** → New Deployment job created in `Pending`
|
||||
7. **Agent deploys** → Deployment job → `Running` → `Completed`
|
||||
8. **Status reported** → `POST /api/v1/agents/{id}/jobs/{job_id}/status`
|
||||
8. **Post-deployment verification** → Agent probes live TLS endpoint, compares SHA-256 fingerprint
|
||||
9. **Status reported** → `POST /api/v1/agents/{id}/jobs/{job_id}/status`
|
||||
|
||||
### Approval Flow (Interactive)
|
||||
1. **Renewal job created** in `AwaitingApproval` state (if policy requires)
|
||||
@@ -867,7 +908,7 @@ The web dashboard is the primary operational interface for certctl. Built with *
|
||||
- **Save/Cancel** — API mutations with optimistic updates via TanStack Query
|
||||
|
||||
#### Target Configuration Wizard
|
||||
- **Step 1: Select Type** — Radio or dropdown (NGINX, Apache, HAProxy, F5, IIS)
|
||||
- **Step 1: Select Type** — Radio or dropdown (NGINX, Apache, HAProxy, Traefik, Caddy, F5, IIS)
|
||||
- **Step 2: Configure** — Type-specific fields (cert path, chain path, key path, etc.)
|
||||
- **Step 3: Review** — Summary of config; confirm create
|
||||
- **Validation** — Real-time field validation; show errors; disable Create if invalid
|
||||
@@ -958,7 +999,7 @@ The web dashboard is the primary operational interface for certctl. Built with *
|
||||
|
||||
### OpenAPI 3.1 Specification
|
||||
- **File** — `api/openapi.yaml`
|
||||
- **Scope** — 97 operations (95 API + /health + /ready), all request/response schemas, enums, pagination
|
||||
- **Scope** — 99 operations (97 API + /health + /ready), all request/response schemas, enums, pagination
|
||||
- **Schemas** — Complete domain models with examples
|
||||
- **Enums** — Job types, states, policy rule types, notification types
|
||||
- **Pagination** — Standard envelope (data, total, page, per_page)
|
||||
|
||||
+175
-1
@@ -31,6 +31,8 @@ Comprehensive manual testing playbook. Every test has a concrete command, an exp
|
||||
- [Part 24: Documentation Verification](#part-24-documentation-verification)
|
||||
- [Part 25: Regression Tests](#part-25-regression-tests)
|
||||
- [Part 26: EST Server (RFC 7030)](#part-26-est-server-rfc-7030)
|
||||
- [Part 27: Post-Deployment TLS Verification](#part-27-post-deployment-tls-verification)
|
||||
- [Part 28: Traefik & Caddy Target Connectors](#part-28-traefik--caddy-target-connectors)
|
||||
- [Release Sign-Off](#release-sign-off)
|
||||
|
||||
---
|
||||
@@ -4195,9 +4197,179 @@ curl -s -H "Authorization: Bearer $API_KEY" \
|
||||
|
||||
---
|
||||
|
||||
## Part 27: Post-Deployment TLS Verification
|
||||
|
||||
### Why test this?
|
||||
|
||||
Post-deployment verification is the final confidence check: after a certificate is deployed to a target, the agent probes the live TLS endpoint and confirms the served certificate matches what was deployed. This catches silent failures where a reload command exits 0 but the certificate doesn't take effect.
|
||||
|
||||
### 27.1: Submit Verification Result (Success)
|
||||
|
||||
```bash
|
||||
# Create a deployment job first (or use an existing completed deployment job ID)
|
||||
JOB_ID="j-deploy-001"
|
||||
|
||||
# Submit a successful verification result
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/jobs/$JOB_ID/verify -d '{
|
||||
"target_id": "tgt-nginx-prod",
|
||||
"expected_fingerprint": "sha256:abc123def456",
|
||||
"actual_fingerprint": "sha256:abc123def456",
|
||||
"verified": true
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 200 OK with `{"job_id": "j-deploy-001", "verified": true, "verified_at": "..."}`.
|
||||
**PASS if** response contains `verified: true` and a valid `verified_at` timestamp.
|
||||
|
||||
### 27.2: Submit Verification Result (Failure — Fingerprint Mismatch)
|
||||
|
||||
```bash
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/jobs/$JOB_ID/verify -d '{
|
||||
"target_id": "tgt-nginx-prod",
|
||||
"expected_fingerprint": "sha256:abc123def456",
|
||||
"actual_fingerprint": "sha256:zzz999different",
|
||||
"verified": false,
|
||||
"error": "fingerprint mismatch"
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 200 OK with `verified: false`.
|
||||
**PASS if** verification failure recorded without error status code (verification is best-effort).
|
||||
|
||||
### 27.3: Get Verification Status
|
||||
|
||||
```bash
|
||||
curl -H "$AUTH" $SERVER/api/v1/jobs/$JOB_ID/verification | jq .
|
||||
```
|
||||
|
||||
**Expected:** Returns the verification result previously submitted.
|
||||
**PASS if** response includes `job_id`, `verified`, `verified_at`, and `actual_fingerprint`.
|
||||
|
||||
### 27.4: Missing Required Fields
|
||||
|
||||
```bash
|
||||
# Missing target_id
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/jobs/$JOB_ID/verify -d '{
|
||||
"expected_fingerprint": "sha256:abc",
|
||||
"actual_fingerprint": "sha256:abc",
|
||||
"verified": true
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 400 Bad Request with message about missing `target_id`.
|
||||
**PASS if** status code is 400.
|
||||
|
||||
### 27.5: Audit Trail
|
||||
|
||||
```bash
|
||||
curl -H "$AUTH" "$SERVER/api/v1/audit?action=job_verification_success" | jq '.data[0]'
|
||||
```
|
||||
|
||||
**Expected:** Audit event recorded with verification details (job_id, target_id, fingerprints).
|
||||
**PASS if** audit event exists with expected action and details.
|
||||
|
||||
### 27.6: Database Schema Verification
|
||||
|
||||
```bash
|
||||
docker compose exec postgres psql -U certctl -d certctl -c \
|
||||
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name='jobs' AND column_name LIKE 'verification%';"
|
||||
```
|
||||
|
||||
**Expected:** Four columns: `verification_status`, `verified_at`, `verification_fingerprint`, `verification_error`.
|
||||
**PASS if** all four columns exist with correct types.
|
||||
|
||||
---
|
||||
|
||||
## Part 28: Traefik & Caddy Target Connectors
|
||||
|
||||
### Why test this?
|
||||
|
||||
Traefik and Caddy are increasingly popular reverse proxies. Testing ensures cert deployment works with their specific file-watching and admin API patterns.
|
||||
|
||||
### 28.1: Traefik File Provider Deployment
|
||||
|
||||
**Setup:** Configure a target with type `Traefik` pointing to a test directory.
|
||||
|
||||
```bash
|
||||
# Create a Traefik target
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/targets -d '{
|
||||
"name": "Traefik Test",
|
||||
"type": "Traefik",
|
||||
"agent_id": "a-test-agent",
|
||||
"config": {
|
||||
"cert_dir": "/tmp/traefik-certs",
|
||||
"cert_file": "test.crt",
|
||||
"key_file": "test.key"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 201 Created with target details.
|
||||
**PASS if** target created with type `Traefik` and config fields preserved.
|
||||
|
||||
### 28.2: Caddy API Mode Deployment
|
||||
|
||||
```bash
|
||||
# Create a Caddy target in API mode
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/targets -d '{
|
||||
"name": "Caddy API Test",
|
||||
"type": "Caddy",
|
||||
"agent_id": "a-test-agent",
|
||||
"config": {
|
||||
"mode": "api",
|
||||
"admin_api": "http://localhost:2019",
|
||||
"cert_dir": "/etc/caddy/certs",
|
||||
"cert_file": "test.crt",
|
||||
"key_file": "test.key"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 201 Created.
|
||||
**PASS if** target created with mode `api` and `admin_api` URL preserved.
|
||||
|
||||
### 28.3: Caddy File Mode Deployment
|
||||
|
||||
```bash
|
||||
# Create a Caddy target in file mode
|
||||
curl -X POST -H "$AUTH" -H "$CT" $SERVER/api/v1/targets -d '{
|
||||
"name": "Caddy File Test",
|
||||
"type": "Caddy",
|
||||
"agent_id": "a-test-agent",
|
||||
"config": {
|
||||
"mode": "file",
|
||||
"cert_dir": "/etc/caddy/certs",
|
||||
"cert_file": "test.crt",
|
||||
"key_file": "test.key"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected:** 201 Created.
|
||||
**PASS if** target created with mode `file`.
|
||||
|
||||
### 28.4: Agent Connector Dispatch
|
||||
|
||||
Verify the agent binary recognizes Traefik and Caddy target types from the work endpoint response. This requires a running agent with deployment jobs assigned to Traefik/Caddy targets.
|
||||
|
||||
**Expected:** Agent logs show connector instantiation for the target type (e.g., "deploying to Traefik target" or "deploying to Caddy target").
|
||||
**PASS if** agent does not error with "unknown target type" for Traefik or Caddy.
|
||||
|
||||
### 28.5: Connector Unit Tests
|
||||
|
||||
```bash
|
||||
go test ./internal/connector/target/traefik/... -v
|
||||
go test ./internal/connector/target/caddy/... -v
|
||||
```
|
||||
|
||||
**Expected:** All tests pass.
|
||||
**PASS if** exit code 0 for both test suites.
|
||||
|
||||
---
|
||||
|
||||
## Release Sign-Off
|
||||
|
||||
All 26 parts must pass before tagging v2.0.1.
|
||||
All 28 parts must pass before tagging v2.0.7.
|
||||
|
||||
| Section | Pass? | Tester | Date | Notes |
|
||||
|---------|-------|--------|------|-------|
|
||||
@@ -4227,6 +4399,8 @@ All 26 parts must pass before tagging v2.0.1.
|
||||
| Part 24: Documentation Verification | ☐ | | | |
|
||||
| Part 25: Regression Tests | ☐ | | | |
|
||||
| Part 26: EST Server (RFC 7030) | ☐ | | | |
|
||||
| Part 27: Post-Deployment TLS Verification | ☐ | | | |
|
||||
| Part 28: Traefik & Caddy Target Connectors | ☐ | | | |
|
||||
|
||||
**Automated tests must also be green.** CI passing is necessary but not sufficient — this manual QA catches integration issues that isolated unit tests miss.
|
||||
|
||||
|
||||
+3
-3
@@ -39,11 +39,11 @@ certctl works with any certificate authority, not just ACME providers:
|
||||
|
||||
Every issuer connector implements the same interface. Switching CAs or running multiple CAs in parallel requires zero code changes — just configuration.
|
||||
|
||||
### 3. Post-Deployment Verification (coming in v2.0.6)
|
||||
### 3. Post-Deployment Verification
|
||||
|
||||
Every other tool in this space stops at "the deployment command succeeded." certctl is adding a step nobody else has: after deploying a certificate to a target, the agent connects back to the target's TLS endpoint and verifies the served certificate matches what was deployed, using SHA-256 fingerprint comparison.
|
||||
Every other tool in this space stops at "the deployment command succeeded." certctl goes further: after deploying a certificate to a target, the agent connects back to the target's TLS endpoint and verifies the served certificate matches what was deployed, using SHA-256 fingerprint comparison.
|
||||
|
||||
A reload command can exit 0 while the certificate doesn't take effect — wrong virtual host, stale cache, config that validates but doesn't apply. certctl will catch this.
|
||||
A reload command can exit 0 while the certificate doesn't take effect — wrong virtual host, stale cache, config that validates but doesn't apply. certctl catches this.
|
||||
|
||||
## How certctl Compares
|
||||
|
||||
|
||||
Reference in New Issue
Block a user