UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

330 lines (266 loc) 8.99 kB
# Certificate Operations Runbook ## Purpose Manage the full PKI certificate lifecycle: issue new certificates, renew expiring certificates, revoke compromised certificates, and distribute trust anchors across the fleet. Incorrect certificate operations can cause service outages or security incidents — follow each procedure exactly. **Warning**: Revoking a certificate is irreversible. Issuing a wildcard certificate expands the attack surface. Always confirm the operation type and target before proceeding. ## System Topology | Field | Value | |-------|-------| | CA type | {internal CA / Let's Encrypt / commercial CA} | | CA host | {hostname of CA or ACME endpoint} | | CA root cert | {path — e.g., /etc/ssl/ca/root-ca.pem} | | CA intermediate cert | {path — e.g., /etc/ssl/ca/intermediate-ca.pem} | | Certificate store | {path — e.g., /etc/ssl/certs/} | | Private key store | {path — e.g., /etc/ssl/private/} | | ACME client | {certbot / acme.sh / step-ca} | | Trust distribution | {method — e.g., ansible, scp, update-ca-certificates} | | Monitoring | {cert-exporter endpoint or cron check} | ## Prerequisites - [ ] CA root and intermediate certificates are accessible - [ ] Private key storage directory exists with mode 700 - [ ] ACME client installed and configured (if using ACME) - [ ] DNS records exist for certificate SANs - [ ] Access to all target hosts for trust distribution ## Procedure: Issue New Certificate ### Step 1: Generate Private Key ```bash # Generate 4096-bit RSA key (or ECDSA P-256) openssl genrsa -out /etc/ssl/private/{domain}.key 4096 chmod 600 /etc/ssl/private/{domain}.key ``` **Expected output:** ``` Generating RSA private key, 4096 bit long modulus ... ``` ### Step 2: Create Certificate Signing Request ```bash # Generate CSR with SANs openssl req -new \ -key /etc/ssl/private/{domain}.key \ -out /tmp/{domain}.csr \ -subj "/CN={domain}/O={organization}/C={country}" \ -addext "subjectAltName=DNS:{domain},DNS:{san2},DNS:{san3}" ``` **Expected output:** (no output on success) ```bash # Verify CSR contents openssl req -in /tmp/{domain}.csr -noout -text | grep -A2 "Subject:" ``` **Expected output:** ``` Subject: CN = {domain}, O = {organization}, C = {country} ``` ### Step 3: Sign Certificate **Option A: Internal CA** ```bash openssl x509 -req \ -in /tmp/{domain}.csr \ -CA /etc/ssl/ca/intermediate-ca.pem \ -CAkey /etc/ssl/ca/intermediate-ca.key \ -CAcreateserial \ -out /etc/ssl/certs/{domain}.pem \ -days {validity-days} \ -sha256 \ -copy_extensions copyall ``` **Option B: ACME (Let's Encrypt)** ```bash certbot certonly \ --dns-{provider} \ -d {domain} \ -d {san2} \ --cert-path /etc/ssl/certs/{domain}.pem \ --key-path /etc/ssl/private/{domain}.key ``` ### Step 4: Build Certificate Chain ```bash # Concatenate cert + intermediate (leaf first) cat /etc/ssl/certs/{domain}.pem /etc/ssl/ca/intermediate-ca.pem > /etc/ssl/certs/{domain}-fullchain.pem ``` ### Step 5: Verify Certificate ```bash # Verify chain of trust openssl verify -CAfile /etc/ssl/ca/root-ca.pem -untrusted /etc/ssl/ca/intermediate-ca.pem /etc/ssl/certs/{domain}.pem ``` **Expected output:** ``` /etc/ssl/certs/{domain}.pem: OK ``` ```bash # Check expiration date openssl x509 -in /etc/ssl/certs/{domain}.pem -noout -enddate ``` **Expected output:** ``` notAfter={expiration date} ``` ```bash # Verify key matches certificate diff <(openssl x509 -in /etc/ssl/certs/{domain}.pem -noout -modulus) \ <(openssl rsa -in /etc/ssl/private/{domain}.key -noout -modulus) ``` **Expected output:** (no output = keys match) ## Procedure: Renew Expiring Certificate ### Step 1: Check Current Expiration ```bash # Check days until expiration openssl x509 -in /etc/ssl/certs/{domain}.pem -noout -enddate -checkend $((30*86400)) ``` **Expected output (expiring within 30 days):** ``` notAfter=... Certificate will expire ``` ### Step 2: Renew **ACME renewal:** ```bash certbot renew --cert-name {domain} --dry-run ``` **Expected output:** ``` Congratulations, all simulated renewals succeeded ``` ```bash # Execute actual renewal certbot renew --cert-name {domain} ``` **Manual renewal**: Follow the "Issue New Certificate" procedure above, reusing the existing key or generating a new one. ### Step 3: Reload Services ```bash # Reload services that use this certificate systemctl reload {nginx/haproxy/service} ``` ```bash # Verify service is using new certificate echo | openssl s_client -connect {hostname}:{port} -servername {domain} 2>/dev/null | openssl x509 -noout -enddate ``` **Expected output:** ``` notAfter={new expiration date} ``` ## Procedure: Revoke Certificate **This operation is irreversible. Confirm the certificate serial and reason before proceeding.** ### Step 1: Identify Certificate to Revoke ```bash # Get certificate serial number openssl x509 -in /etc/ssl/certs/{domain}.pem -noout -serial ``` **Expected output:** ``` serial={HEXSERIAL} ``` ### Step 2: Revoke **Internal CA:** ```bash openssl ca -revoke /etc/ssl/certs/{domain}.pem \ -config /etc/ssl/ca/openssl.cnf \ -crl_reason {keyCompromise/cessationOfOperation/superseded} ``` **ACME:** ```bash certbot revoke --cert-path /etc/ssl/certs/{domain}.pem --reason {reason} ``` ### Step 3: Regenerate CRL ```bash openssl ca -gencrl -out /etc/ssl/ca/crl.pem -config /etc/ssl/ca/openssl.cnf ``` ### Step 4: Distribute Updated CRL ```bash # Push CRL to all hosts that validate against this CA for host in {fleet-hosts}; do scp /etc/ssl/ca/crl.pem "$host":/etc/ssl/ca/crl.pem ssh "$host" "update-ca-certificates" done ``` ### Step 5: Replace Revoked Certificate Follow the "Issue New Certificate" procedure to generate a replacement. ## Procedure: Push Trust to Fleet ### Step 1: Distribute CA Certificates ```bash # Copy root CA to trust store on each host for host in {fleet-hosts}; do scp /etc/ssl/ca/root-ca.pem "$host":/usr/local/share/ca-certificates/internal-root-ca.crt scp /etc/ssl/ca/intermediate-ca.pem "$host":/usr/local/share/ca-certificates/internal-intermediate-ca.crt ssh "$host" "update-ca-certificates" done ``` **Expected output (per host):** ``` Updating certificates in /etc/ssl/certs... 2 added, 0 removed; done. ``` ### Step 2: Verify Trust ```bash # Verify a host trusts the internal CA for host in {fleet-hosts}; do echo -n "$host: " ssh "$host" "openssl verify -CApath /etc/ssl/certs /etc/ssl/certs/{domain}.pem" 2>&1 | tail -1 done ``` **Expected output:** ``` host-01: /etc/ssl/certs/{domain}.pem: OK host-02: /etc/ssl/certs/{domain}.pem: OK ``` ## Verification ```bash # 1. Certificate is valid and not expired openssl x509 -in /etc/ssl/certs/{domain}.pem -noout -dates ``` ```bash # 2. Chain validates openssl verify -CAfile /etc/ssl/ca/root-ca.pem -untrusted /etc/ssl/ca/intermediate-ca.pem /etc/ssl/certs/{domain}.pem ``` **Expected output:** ``` ...: OK ``` ```bash # 3. TLS handshake succeeds echo | openssl s_client -connect {hostname}:{port} -servername {domain} 2>/dev/null | head -5 ``` **Expected output:** ``` CONNECTED(00000003) depth=2 ... verify return:1 ``` ```bash # 4. Key permissions are restrictive stat -c '%a %U' /etc/ssl/private/{domain}.key ``` **Expected output:** ``` 600 root ``` ## Troubleshooting | Symptom | Likely Cause | Resolution | |---------|-------------|------------| | `verify error:unable to get local issuer certificate` | Incomplete chain | Ensure intermediate cert is included in fullchain | | `certificate has expired` | Renewal not run or not reloaded | Renew cert, then `systemctl reload {service}` | | `key values mismatch` | Key and cert from different CSRs | Re-issue cert using the correct key | | ACME challenge fails | DNS not propagated or port 80 blocked | Verify DNS, check firewall rules | | `certificate revoked` on clients | CRL check hitting revoked cert | Issue replacement cert, distribute to clients | ## What NOT to Fix - CA root key rotation — separate, high-ceremony procedure with its own runbook - Client certificate issues for external partners — coordinate through security team - Certificate transparency log submissions — handled automatically by public CAs ## Agent Rules - DO: Verify certificate chain after every issuance - DO: Check key-cert match before deploying - DO: Record serial numbers of all issued certificates - DO NOT: Generate private keys with fewer than 2048 bits (prefer 4096 RSA or P-256 ECDSA) - DO NOT: Issue wildcard certificates without explicit human approval - DO NOT: Revoke certificates without human confirmation - DO NOT: Store private keys in world-readable locations - ESCALATE IF: CA key may be compromised - ESCALATE IF: Revocation requested for a production certificate - ESCALATE IF: Certificate chain validation fails after issuance ## Audit Trail | Field | Value | |-------|-------| | Author | {author} | | Created | {date} | | Last tested | {date} | | Last modified | {date} | | CA fingerprint | {root CA SHA-256 fingerprint} |