Complete M1, M1.1, M2: end-to-end lifecycle, agent deployment, ACME v2

- Wire issuer connector end-to-end with IssuerConnectorAdapter (dependency inversion)
- Renewal/issuance job processor: RSA key + CSR generation, Local CA signing, cert version storage
- Agent work API (GET /agents/{id}/work) and job status API (POST /agents/{id}/jobs/{job_id}/status)
- Agent-side deployment: WorkItem enrichment with target type/config, NGINX/F5/IIS connector invocation
- Full ACME v2 implementation: HTTP-01 challenge solving, account registration, order lifecycle
- Update all docs (README, architecture, connectors, demo-advanced, quickstart) for M1-M2
- Fix go vet warning in deployment.go (non-constant format string)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Shankar
2026-03-14 23:49:45 -04:00
parent 77a6ec7270
commit ab79dead13
16 changed files with 985 additions and 201 deletions
+37 -12
View File
@@ -215,13 +215,13 @@ Expected response:
The `202 Accepted` status code is deliberate. Certificate issuance can take seconds (Local CA) to minutes (ACME DNS challenges). The API doesn't block the caller — it creates a job and returns. The job processor loop (runs every 30 seconds) picks up pending jobs and executes them.
**What happens during a real renewal (production flow):**
**What happens during renewal (V1 flow with Local CA):**
```mermaid
sequenceDiagram
participant S as Scheduler
participant DB as PostgreSQL
participant SVC as CertificateService
participant SVC as RenewalService
participant ISS as IssuerConnector
participant A as Agent
@@ -233,23 +233,25 @@ sequenceDiagram
S->>DB: SELECT pending jobs
DB-->>S: [job-123: Renewal for mc-demo-api]
S->>A: Notify: generate CSR for demo-api.internal.example.com
A->>A: Generate RSA-2048 key pair locally
A->>A: Create CSR with CN + SANs
A->>SVC: POST /api/v1/agents/{id}/csr {csr_pem: "..."}
SVC->>ISS: IssueCertificate(CSR)
SVC->>SVC: Generate RSA-2048 key + CSR (server-side in V1)
SVC->>ISS: IssueCertificate(commonName, sans, csrPEM)
ISS-->>SVC: {cert_pem, chain_pem, serial, not_after}
SVC->>DB: INSERT certificate_version
SVC->>DB: INSERT certificate_version (PEM chain + fingerprint)
SVC->>DB: UPDATE managed_certificates SET status='Active'
SVC->>DB: INSERT audit_event (certificate_renewed)
SVC->>DB: CREATE deployment jobs for all targets
SVC-->>A: {certificate_pem, chain_pem}
A->>A: Store cert + chain locally (key never leaves)
Note over A: Agent polls GET /agents/{id}/work
A->>SVC: GET /api/v1/agents/{id}/work
SVC-->>A: [deployment job for mc-demo-api]
A->>SVC: GET /api/v1/agents/{id}/certificates/{certId}
SVC-->>A: {certificate PEM chain}
A->>A: Deploy to target system
A->>SVC: POST /api/v1/agents/{id}/jobs/{jobId}/status {Completed}
```
The critical security property: the private key is generated by the agent in step 3 and never transmitted. The CSR contains only the public key. The control plane forwards the CSR to the issuer and returns the signed certificate — it never has access to the private key material.
**V1 note:** In V1 with the Local CA, key generation happens server-side in `RenewalService.ProcessRenewalJob`. In V2+, agents will generate keys locally and submit CSRs, ensuring private keys never touch the control plane.
Check the jobs list:
@@ -322,6 +324,29 @@ Check for deployment jobs:
curl -s "$API/api/v1/jobs" | jq '.data[] | select(.certificate_id == "mc-demo-api")'
```
### Agent Work Polling & Status Reporting
In production, agents poll for work and report results. You can simulate this manually:
```bash
# Poll for pending deployment work (as an agent)
curl -s "$API/api/v1/agents/agent-nginx-prod/work" | jq .
```
This returns pending deployment jobs assigned to the agent. The agent would then fetch the certificate, deploy it, and report back:
```bash
# Report job completion (replace JOB_ID with an actual job ID from the work response)
curl -s -X POST "$API/api/v1/agents/agent-nginx-prod/jobs/JOB_ID/status" \
-H "Content-Type: application/json" \
-d '{
"status": "Completed",
"error": ""
}' | jq .
```
**How it works:** The `GET /api/v1/agents/{id}/work` endpoint returns all pending deployment jobs. The agent processes each one, then calls `POST /api/v1/agents/{id}/jobs/{job_id}/status` with either `"Completed"` or `"Failed"` (with an error message). The control plane updates the job record and logs an audit event.
---
## Part 6: View the Audit Trail