fix(docs): correct migration guides — 17 issues found via repo audit

Fixes factual errors, broken links, wrong ports, inaccurate GUI
descriptions, and misleading config formats across all three migration
guides (certbot, acme.sh, cert-manager).

Key fixes:
- Correct server port from 8080/3000 to 8443 across all guides
- Fix HTTPS→HTTP for Docker Compose (not TLS-terminated)
- Fix heartbeat interval: 60 seconds, not 5 minutes
- Fix "50 servers" → "10 servers" (50 certs across 10 servers)
- Replace JSON config blocks with env var format (actual config method)
- Fix policy creation flow to match actual GUI (name/type/severity/config)
- Fix issuer wizard description to match actual 2-step flow
- Fix Vault PKI "coming in v2.1" → "planned" (ships post-2.1.0)
- Fix 5 broken links (cert-manager.md, quickstart anchors, architecture anchor)
- Remove claim of auto-generated suggestions in discovery flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-03-30 01:34:22 -04:00
parent a8fc177118
commit 2655493ac8
3 changed files with 73 additions and 56 deletions
+14 -13
View File
@@ -27,7 +27,7 @@ Result:
Deploy certctl control plane once (Docker Compose, Kubernetes Helm chart, or self-hosted). Deploy agents on your VMs, bare metal, and network appliances. One dashboard shows:
- **All cert-manager certs** via discovery scanning (agents find cert-manager-issued certs copied to target machines, or scan the cluster directly)
- **All certctl-managed certs** issued by shared issuers (ACME, step-ca, Vault PKI (coming in v2.1), private CA)
- **All certctl-managed certs** issued by shared issuers (ACME, step-ca, Vault PKI (planned), private CA)
- **Unified renewal and deployment** across both worlds
- **Single pane of glass** with expiration timeline, renewal status, deployment verification, audit trail
@@ -39,8 +39,7 @@ Deploy certctl control plane once (Docker Compose, Kubernetes Helm chart, or sel
```bash
cd /opt/certctl
docker compose up -d
# Dashboard: http://localhost:3000
# API: http://localhost:8080
# Dashboard & API: http://localhost:8443
```
**Option B: Kubernetes** (recommended for prod)
@@ -60,7 +59,7 @@ chmod +x /usr/local/bin/certctl-agent
# Config
sudo tee /etc/certctl/agent.env > /dev/null <<EOF
CERTCTL_SERVER_URL=https://certctl-control-plane:8080
CERTCTL_SERVER_URL=http://certctl-control-plane:8443
CERTCTL_API_KEY=your-api-key
CERTCTL_DISCOVERY_DIRS=/etc/nginx/certs,/etc/ssl,/etc/letsencrypt/live
CERTCTL_KEY_DIR=/var/lib/certctl/keys
@@ -83,18 +82,20 @@ Agents scan configured directories and report back all existing certs. In the da
Set up the same issuer certctl uses for non-Kubernetes certs:
- **ACME** (Let's Encrypt, for public certs)
- **step-ca** (Smallstep, for internal certs)
- **Vault PKI** (coming in v2.1) (HashiCorp Vault, for enterprise PKI)
- **Vault PKI** (planned) (HashiCorp Vault, for enterprise PKI)
- **Private CA** (your own internal root CA)
No new CA infrastructure needed. If cert-manager already uses your CA, certctl points to the same one.
### 5. Create Policies for Non-Kubernetes Certs
Go to **Policies****New Policy**:
- Issuer: shared (ACME, step-ca, Vault (coming in v2.1), private CA)
- Profile: serverAuth for NGINX/Apache/HAProxy, clientAuth for mTLS, emailProtection for S/MIME
- Renewal Threshold: 30 days (default, adjust per SLA)
- Scope: agent groups (VMs, bare metal, appliances)
Go to **Policies****+ New Policy** to create enforcement rules:
- **Name:** e.g., "VM Certificate Policy"
- **Type:** `expiration_window` or `key_algorithm` (enforce renewal thresholds or crypto requirements)
- **Severity:** `high`
- **Config:** set your enforcement parameters
Certificates are linked to issuers and profiles when created or claimed from discovery. Policies add guardrails — enforcing key algorithm requirements, expiration windows, and other compliance rules across your fleet.
### 6. View Unified Inventory
@@ -114,7 +115,7 @@ Go to **Policies** → **New Policy**:
If cert-manager and certctl both use the same CA:
- **ACME**: cert-manager uses ClusterIssuer + certctl uses ACME connector → same Let's Encrypt account, transparent coexistence
- **step-ca**: cert-manager uses external issuer CRD + certctl uses step-ca connector → same provisioner, shared certificate inventory
- **Vault PKI** (coming in v2.1): cert-manager uses external issuer CRD + certctl uses Vault connector → same mount, same audit trail
- **Vault PKI** (planned): cert-manager uses external issuer CRD + certctl uses Vault connector → same mount, same audit trail
No conflict. They just issue certs through the same CA. certctl's discovery scanning finds cert-manager-issued certs and shows them alongside certctl-managed ones.
@@ -138,6 +139,6 @@ For now: cert-manager handles Kubernetes, certctl handles everything else. They
## Next Steps
1. Review [Quick Start](./quickstart.md) for a 5-minute demo
2. Explore [Agents and Targets](./architecture.md#agents-and-targets) for deployment architecture
3. Read about [Discovery Scanning](./quickstart.md#discovery) to auto-find certs
2. Explore [Architecture](./architecture.md#agents) for deployment architecture
3. Read about [Discovery Scanning](./quickstart.md#certificate-discovery) to auto-find certs
4. Check [Helm Chart](../deploy/helm/certctl/) for production Kubernetes deployment
+30 -24
View File
@@ -99,18 +99,23 @@ Environment="CERTCTL_DISCOVERY_DIRS=/etc/acme.sh"
In the **Discovery** page:
1. Review the "Unmanaged" certificates found by the agent
2. Click **Claim** on each acme.sh certificate
3. Map to the certificate ID (certctl auto-generates suggestions)
3. Enter the managed certificate ID to link it (e.g., `mc-api-prod`)
Once claimed, the certificate appears in the main **Certificates** page with ownership, renewal history, and deployment status.
### 5. Create an ACME Issuer
In **Issuers****Configure New Issuer:**
In **Issuers****+ New Issuer:**
- **Type:** ACME v2
- **Directory URL:** `https://acme-v02.api.letsencrypt.org/directory` (production) or staging for testing
- **Email:** Same email as your acme.sh account (required for ACME ToS)
- **Challenge Type:** DNS-01 (to match acme.sh's DNS validation)
1. Select **ACME** from the issuer type grid
2. Fill in the type-specific fields: name, directory URL (`https://acme-v02.api.letsencrypt.org/directory`), and config
Or configure via environment variables:
```bash
export CERTCTL_ACME_DIRECTORY_URL=https://acme-v02.api.letsencrypt.org/directory
export CERTCTL_ACME_EMAIL=your-email@example.com # same as your acme.sh account
export CERTCTL_ACME_CHALLENGE_TYPE=dns-01
```
### 6. Adapt Your DNS Provider Scripts
@@ -182,26 +187,28 @@ curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_record
-H "X-Auth-Key: ${CF_KEY}"
```
Configure in the ACME issuer:
Configure the ACME issuer via environment variables:
```json
{
"challenge_type": "dns-01",
"dns_present_script": "/etc/certctl/dns/cloudflare-present.sh",
"dns_cleanup_script": "/etc/certctl/dns/cloudflare-cleanup.sh",
"dns_propagation_wait": 30
}
```bash
export CERTCTL_ACME_DIRECTORY_URL=https://acme-v02.api.letsencrypt.org/directory
export CERTCTL_ACME_EMAIL=your-email@example.com
export CERTCTL_ACME_CHALLENGE_TYPE=dns-01
export CERTCTL_ACME_DNS_PRESENT_SCRIPT=/etc/certctl/dns/cloudflare-present.sh
export CERTCTL_ACME_DNS_CLEANUP_SCRIPT=/etc/certctl/dns/cloudflare-cleanup.sh
```
Or create the issuer through the dashboard: **Issuers****+ New Issuer** → select **ACME** → fill in the config fields.
### 7. Create Renewal Policies
In **Policies:**
In **Policies****+ New Policy:**
- **Certificate Profile:** Select the issuer and challenge type from step 5
- **Renewal Threshold:** 30 days before expiry (or match your acme.sh cron settings)
- **Agent Group:** Select which agents should renew certificates
- **Name:** e.g., "ACME DNS-01 Policy"
- **Type:** `expiration_window` (enforces renewal thresholds)
- **Severity:** `high`
- **Config:** set your renewal window (default: 30 days before expiry)
Set one policy per domain or domain pattern.
Renewal scheduling is driven by the certificate's assigned profile and issuer. Policies add enforcement guardrails on top.
### 8. Phase Out acme.sh Cron
@@ -252,11 +259,10 @@ Benefits:
To enable:
```json
{
"challenge_type": "dns-persist-01",
"dns_persist_issuer_domain": "acme-v02.api.letsencrypt.org"
}
```bash
export CERTCTL_ACME_CHALLENGE_TYPE=dns-persist-01
export CERTCTL_ACME_DNS_PERSIST_ISSUER_DOMAIN=letsencrypt.org
export CERTCTL_ACME_DNS_PRESENT_SCRIPT=/etc/certctl/dns/cloudflare-present.sh
```
certctl automatically falls back to DNS-01 if the CA doesn't support dns-persist-01 yet.
+29 -19
View File
@@ -22,7 +22,7 @@ Option A: Docker Compose (quickest for evaluation)
```bash
cd /opt/certctl
docker compose up -d
# Dashboard & API: https://localhost:8443
# Dashboard & API: http://localhost:8443
# Default API key in logs (grep CERTCTL_API_KEY docker logs certctl-server)
```
@@ -45,7 +45,7 @@ chmod +x /usr/local/bin/certctl-agent
# Create config
sudo mkdir -p /etc/certctl /var/lib/certctl/keys
sudo tee /etc/certctl/agent.env > /dev/null <<EOF
CERTCTL_SERVER_URL=https://certctl-control-plane.example.com:8080
CERTCTL_SERVER_URL=http://certctl-control-plane.example.com:8443
CERTCTL_API_KEY=your-api-key-here
CERTCTL_DISCOVERY_DIRS=/etc/letsencrypt/live
CERTCTL_KEY_DIR=/var/lib/certctl/keys
@@ -71,24 +71,34 @@ The control plane now knows about all 50 certs and where they live.
### 4. Configure ACME Issuer
Go to **Issuers****Add Issuer**:
- Type: ACME
- Directory URL: `https://acme-v02.api.letsencrypt.org/directory` (production)
- Email: your Let's Encrypt account email
- Challenge Type: `http-01` (if you have HTTP access) or `dns-01` (for wildcard/internal certs)
- For DNS-01, provide your DNS provider's script hook (Cloudflare, Route53, Azure DNS, etc.)
Go to **Issuers****+ New Issuer**:
1. Select **ACME** from the issuer type grid
2. Fill in the type-specific fields: name, directory URL (`https://acme-v02.api.letsencrypt.org/directory`), and any required config
Test the connection. certctl uses the same Let's Encrypt account; no new credentials needed.
Alternatively, configure via environment variables before starting the server:
```bash
export CERTCTL_ACME_DIRECTORY_URL=https://acme-v02.api.letsencrypt.org/directory
export CERTCTL_ACME_EMAIL=your-email@example.com
export CERTCTL_ACME_CHALLENGE_TYPE=http-01 # or dns-01 for wildcard certs
```
For DNS-01, also set:
```bash
export CERTCTL_ACME_DNS_PRESENT_SCRIPT=/etc/certctl/dns/present.sh
export CERTCTL_ACME_DNS_CLEANUP_SCRIPT=/etc/certctl/dns/cleanup.sh
```
certctl uses the same Let's Encrypt account; no new credentials needed.
### 5. Create Renewal Policies
Go to **Policies****New Policy**:
- Profile: ACME (or create a new one with `serverAuth` EKU)
- Issuer: the ACME issuer you just created
- Renewal Threshold: 30 days before expiry (default, adjust as needed)
- Scope: select agent groups or individual agents managing your servers
Go to **Policies****+ New Policy** to create enforcement rules:
- Name: e.g., "ACME Renewal Policy"
- Type: `expiration_window` (to enforce renewal thresholds)
- Severity: `high`
- Config: set your renewal threshold (default: 30 days before expiry)
Assign this policy to your discovered certs.
Renewal scheduling is driven by the certificate's assigned profile and issuer. Policies add enforcement guardrails (key algorithm requirements, expiration windows, etc.).
### 6. Disable Certbot Cron, One Server at a Time
@@ -133,11 +143,11 @@ docker compose up -d
# Other options: CERTCTL_TEAMS_WEBHOOK_URL, CERTCTL_PAGERDUTY_ROUTING_KEY, CERTCTL_OPSGENIE_API_KEY
```
Now you get 30/14/7-day warnings before any cert expires, across all 50 servers, in one place.
Now you get 30/14/7-day warnings before any cert expires, across all 10 servers, in one place.
## What Changes
- **Renewal**: Agent polls certctl for work instead of Certbot cron triggering locally. Faster failure detection (agent heartbeat every 5 minutes vs. cron running once a day).
- **Renewal**: Agent polls certctl for work instead of Certbot cron triggering locally. Faster failure detection (agent heartbeat every 60 seconds vs. cron running once a day).
- **Deployment**: certctl verifies post-deployment by probing the live TLS endpoint and comparing SHA-256 fingerprints. Catches reload failures silently.
- **Audit Trail**: Every renewal, deployment, and alert is logged immutably. Answer "who renewed cert X when and why" within seconds.
- **Alerting**: Threshold-based alerts to Slack/email/webhook 30/14/7 days before expiry, not when cert expires.
@@ -157,5 +167,5 @@ certctl will stop renewing that cert when the policy is disabled. Certbot resume
## Next Steps
- Review the [Concepts Guide](./concepts.md) for terminology (profiles, policies, agents, jobs)
- Explore [Network Discovery](./quickstart.md#network-discovery) to find certificates you didn't know about
- Set up [Kubernetes cert-manager integration](./cert-manager.md) if you manage in-cluster certs too
- Explore [Network Discovery](./quickstart.md#network-discovery-agentless) to find certificates you didn't know about
- Set up [Kubernetes cert-manager integration](./certctl-for-cert-manager-users.md) if you manage in-cluster certs too