Certificate lifecycle management including rotation, renewal, monitoring, automation with Let's Encrypt and ACME
Scope: Certificate rotation, renewal, monitoring, Let's Encrypt, ACME protocol, automation Lines: ~350 Last Updated: 2025-10-27 Format Version: 1.0 (Atomic)
Activate this skill when:
1. Generation → Create private key and CSR
2. Issuance → CA signs certificate
3. Deployment → Install on servers
4. Monitoring → Track expiration
5. Renewal → Get new certificate before expiry
6. Rotation → Replace old with new
7. Revocation → Invalidate if compromised (optional)
Let's Encrypt:
ACME Protocol:
1. Account creation
2. Domain validation (HTTP-01, DNS-01, TLS-ALPN-01)
3. Certificate request
4. Certificate issuance
5. Certificate renewal
Installation:
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# macOS
brew install certbot
HTTP-01 Challenge (webroot):
# Obtain certificate
sudo certbot certonly --webroot \
-w /var/www/html \
-d example.com \
-d www.example.com
# Certificates stored in:
# /etc/letsencrypt/live/example.com/fullchain.pem
# /etc/letsencrypt/live/example.com/privkey.pem
Nginx plugin (automatic):
# Obtain and install certificate
sudo certbot --nginx -d example.com -d www.example.com
# Certbot automatically:
# - Validates domain
# - Obtains certificate
# - Updates nginx config
# - Reloads nginx
DNS-01 Challenge (wildcards):
# For wildcard certificates
sudo certbot certonly --manual \
--preferred-challenges dns \
-d '*.example.com' \
-d example.com
# Add TXT record to DNS:
# _acme-challenge.example.com → [provided value]
Auto-renewal:
# Test renewal
sudo certbot renew --dry-run
# Setup cron (or systemd timer)
0 0,12 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
Alternative ACME client:
# Install
⚠️ **SECURITY**: Piping curl to shell is dangerous. For production:
curl -O https://get.acme.sh
sha256sum get.acme.sh
less get.acme.sh
bash get.acme.sh
For development/learning only:
curl https://get.acme.sh | sh
acme.sh --issue -d example.com -w /var/www/html
export CF_Token="your-cloudflare-api-token" # Placeholder - replace with actual API token from Cloudflare dashboard acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'
acme.sh --install-cert -d example.com \ --key-file /etc/ssl/private/example.com.key \ --fullchain-file /etc/ssl/certs/example.com.crt \ --reloadcmd "systemctl reload nginx"
acme.sh --cron
---
## Automation Patterns
### Pattern 1: Systemd Timer for Renewal
**Service file** (`/etc/systemd/system/certbot-renew.service`):
[Unit] Description=Certbot Renewal
[Service] Type=oneshot ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
**Timer file** (`/etc/systemd/system/certbot-renew.timer`):
[Unit] Description=Certbot Renewal Timer
[Timer] OnCalendar=daily RandomizedDelaySec=1h Persistent=true
[Install] WantedBy=timers.target
**Enable**:
sudo systemctl enable --now certbot-renew.timer sudo systemctl list-timers certbot-renew.timer
### Pattern 2: Kubernetes cert-manager
**Install cert-manager**:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
**ClusterIssuer** (Let's Encrypt):
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: admin@example.com privateKeySecretRef: name: letsencrypt-prod solvers:
ingress: class: nginx
**Certificate resource**:
apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: example-com-tls namespace: default spec: secretName: example-com-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer dnsNames:
**Ingress annotation** (automatic):
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls:
secretName: example-com-tls rules:
http: paths:
pathType: Prefix backend: service: name: example port: number: 80
### Pattern 3: Custom Automation Script
**Python renewal script**:
#!/usr/bin/env python3 import subprocess import ssl import socket from datetime import datetime, timedelta import smtplib from email.message import EmailMessage
def check_certificate_expiry(hostname, port=443): """Check days until certificate expires""" context = ssl.create_default_context() with socket.create_connection((hostname, port)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as ssock: cert = ssock.getpeercert() not_after = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z') days_remaining = (not_after - datetime.now()).days return days_remaining
def renew_certificate(domain): """Renew certificate using certbot""" result = subprocess.run( ['certbot', 'renew', '--cert-name', domain, '--quiet'], capture_output=True, text=True ) return result.returncode == 0
def send_alert(subject, body): """Send email alert""" msg = EmailMessage() msg['Subject'] = subject msg['From'] = 'certbot@example.com' msg['To'] = 'admin@example.com' msg.set_content(body)
with smtplib.SMTP('localhost') as s: s.send_message(msg)
def main(): domains = ['example.com', 'api.example.com']
for domain in domains: try: days = check_certificate_expiry(domain) print(f"{domain}: {days} days until expiry")
# Renew if < 30 days remaining if days < 30: print(f"Renewing {domain}...") if renew_certificate(domain): send_alert( f"Certificate renewed: {domain}", f"Successfully renewed certificate for {domain}" ) else: send_alert( f"Certificate renewal FAILED: {domain}", f"Failed to renew certificate for {domain}" )
# Alert if < 14 days and renewal failed elif days < 14: send_alert( f"Certificate expiring soon: {domain}", f"Certificate for {domain} expires in {days} days" )
except Exception as e: send_alert( f"Certificate check failed: {domain}", f"Error checking {domain}: {str(e)}" )
if __name__ == '__main__': main()
---
## Monitoring
### Check Expiration
**OpenSSL**:
echo | openssl s_client -connect example.com:443 2>/dev/null | \ openssl x509 -noout -dates
echo | openssl s_client -connect example.com:443 2>/dev/null | \ openssl x509 -noout -enddate | \ awk -F= '{print $2}' | xargs -I {} date -d {} +%s | \ awk -v now=$(date +%s) '{print int(($1-now)/86400)}'
**Prometheus Exporter**:
apiVersion: v1 kind: Service metadata: name: ssl-exporter spec: ports:
selector: app: ssl-exporter --- apiVersion: apps/v1 kind: Deployment metadata: name: ssl-exporter spec: template: spec: containers:
image: ribbybibby/ssl-exporter:latest args:
**Prometheus alert**:
groups:
rules:
expr: ssl_cert_not_after - time() < 86400 * 30 labels: severity: warning annotations: summary: "Certificate expiring in < 30 days" description: "Certificate for {{ $labels.instance }} expires in {{ $value | humanizeDuration }}"
### Monitoring Services
**SSL Labs API**:
import requests import time
def check_ssl_labs(domain): """Check SSL configuration with SSL Labs""" api_url = "https://api.ssllabs.com/api/v3/analyze"
# Start scan requests.get(api_url, params={'host': domain, 'startNew': 'on'})
# Poll for results while True: resp = requests.get(api_url, params={'host': domain}).json() if resp['status'] == 'READY': grade = resp['endpoints'][0]['grade'] return grade time.sleep(30)
grade = check_ssl_labs('example.com') print(f"SSL Labs grade: {grade}")
---
## Certificate Rotation
### Zero-Downtime Rotation
**Strategy**:
**Nginx example**:
certbot certonly --webroot -w /var/www/html -d example.com
nginx -t
systemctl reload nginx
echo | openssl s_client -connect example.com:443 2>/dev/null | \ openssl x509 -noout -dates
### Multi-Server Rotation
**Ansible playbook**:
hosts: webservers tasks:
copy: src: "{{ item }}" dest: "/etc/ssl/certs/" mode: 0644 loop:
copy: src: example.com.key dest: /etc/ssl/private/ mode: 0600
systemd: name: nginx state: reloaded
shell: | echo | openssl s_client -connect localhost:443 2>/dev/null | \ openssl x509 -noout -dates register: cert_check changed_when: false
debug: var: cert_check.stdout
---
## Best Practices
### 1. Renewal Window
if [ $days_remaining -lt 30 ]; then renew_certificate fi
if [ $days_remaining -lt 3 ]; then renew_certificate fi
### 2. Monitoring
### 3. Backup Certificates
cp /etc/ssl/certs/example.com.crt /backup/ certbot renew
certbot renew
---
## Troubleshooting
### Issue 1: Rate Limits
**Let's Encrypt limits**:
**Solution**: Use staging environment for testing
certbot --staging -d example.com
### Issue 2: Renewal Failure
**Check renewal status**:
certbot certificates
**Common issues**:
- Webroot path wrong
- DNS not propagated (DNS-01)
- Firewall blocking (HTTP-01)
- Rate limit exceeded
**Debug**:
certbot renew --dry-run --debug
---
## Related Skills
- `cryptography-pki-fundamentals` - Certificate basics
- `cryptography-tls-configuration` - TLS setup
- `infrastructure-automation` - Deployment automation
- `observability-metrics-monitoring` - Monitoring setup
---
## Level 3: Resources
**Location**: `~/.hanzo/skills/cryptography/certificate-management/resources/`
This skill includes comprehensive Level 3 resources for production certificate management implementations:
### REFERENCE.md (~3,100 lines)
Comprehensive technical reference covering:
- **Fundamentals**: Digital certificates, X.509 standard, trust chains, PKI concepts
- **Certificate Components**: Subject, SANs, extensions, key usage, certificate formats (PEM, DER, PKCS#12)
- **Certificate Lifecycle**: Generation, CSR creation, issuance, deployment, monitoring, renewal, rotation, revocation
- **CA Hierarchies**: Root/intermediate CAs, public vs private CAs, trust models
- **ACME Protocol**: Challenge types (HTTP-01, DNS-01, TLS-ALPN-01), ACME flow, client implementations
- **Let's Encrypt**: Rate limits, staging environment, trust chain, wildcard certificates
- **Certificate Automation**: Certbot usage, acme.sh, renewal hooks, systemd timers
- **Kubernetes cert-manager**: Installation, issuers, certificate resources, ingress annotations
- **Mutual TLS (mTLS)**: Server/client configuration, service mesh integration, zero-trust patterns
- **Certificate Revocation**: CRL, OCSP, OCSP stapling, must-staple extension
- **Certificate Transparency**: CT logs, monitoring, unauthorized certificate detection
- **Certificate Pinning**: Public key pinning, HPKP (deprecated), mobile implementations
- **Monitoring**: Prometheus exporters, expiration tracking, alerting strategies
- **Best Practices**: Lifetime management, key protection, automation, testing, backup/recovery
- **Common Issues**: Troubleshooting expired certs, chain issues, key mismatches, SAN errors
- **Security**: Private key protection, validation requirements, weak cryptography avoidance
- **Compliance**: PCI-DSS, HIPAA, SOC2, GDPR certificate requirements
- **Tools**: OpenSSL, certbot, acme.sh, step CLI, cfssl, cert-manager, SSL Labs, testssl.sh
### Scripts (3 production-ready tools)
**validate_certificates.py** (580+ lines) - Comprehensive certificate validation
- Validates certificates from remote hosts or local files
- Checks expiration dates with configurable thresholds
- Detects weak algorithms (MD5, SHA-1, weak RSA keys)
- Validates certificate chains and hostname matching
- SAN validation including wildcard matching
- OCSP/CRL revocation checking support
- Compliance validation (PCI-DSS, HIPAA, SOC2)
- Batch validation from hosts file
- JSON output for CI/CD integration
- Detailed reporting with severity levels
- Usage: `./validate_certificates.py --host example.com --compliance PCI-DSS --json`
**renew_certificates.py** (450+ lines) - Automated ACME certificate renewal
- ACME protocol automation (Let's Encrypt, ZeroSSL)
- HTTP-01 and DNS-01 challenge support
- Automatic renewal based on expiration thresholds
- Pre/post/deploy hook support
- Automatic backup before renewal
- Rollback on renewal failure
- Batch renewal of all expiring certificates
- Dry-run mode for testing
- Staging environment support
- Custom ACME provider support
- DNS provider integration (Cloudflare, Route53, etc.)
- Usage: `./renew_certificates.py --domain example.com --dns-provider cloudflare --json`
**monitor_cert_expiry.sh** (550+ lines) - Certificate expiration monitoring
- Monitors certificates from remote hosts or local files
- Configurable warning/critical thresholds
- Multiple output formats (text, JSON, Prometheus, CSV)
- Batch monitoring from hosts file
- Directory scanning for certificate files
- Email/webhook/Slack alerting
- Certificate inventory export
- OCSP revocation status checking
- Prometheus metrics format for monitoring integration
- Color-coded terminal output
- Exit codes for CI/CD integration
- Usage: `./monitor_cert_expiry.sh --hosts-file hosts.txt --format prometheus --alert-email admin@example.com`
### Examples (6 production-ready implementations)
**python/acme_automation.py** - ACME protocol automation from scratch
- Account registration and key management
- Certificate ordering with ACME protocol
- HTTP-01 challenge implementation
- DNS-01 challenge for wildcard certificates
- CSR generation with multiple SANs
- Complete ACME workflow demonstration
- Staging environment support
- Production-ready patterns
**python/mtls_server_client.py** - Mutual TLS implementation
- mTLS server with client certificate verification
- mTLS client with certificate authentication
- Certificate generation helper
- TLS version and cipher suite configuration
- Client certificate extraction and validation
- Zero-trust authentication pattern
- Service-to-service authentication example
**python/prometheus_cert_exporter.py** - Prometheus metrics exporter
- Exports certificate expiration metrics
- Multi-host monitoring support
- Prometheus-compatible metrics format
- Certificate metadata collection
- Expiration time tracking
- Validity status monitoring
- Error counting and duration tracking
- Health check endpoint
- Production-ready monitoring integration
**kubernetes/cert-manager-setup.yaml** - Comprehensive cert-manager configuration
- ClusterIssuers for Let's Encrypt (production and staging)
- DNS-01 solvers (Cloudflare, Route53)
- Certificate resources with SANs
- Wildcard certificate examples
- Ingress annotation automation
- Namespace-scoped issuers
- Private CA issuer configuration
- mTLS client certificates
- Certificate lifecycle management
- Renewal configuration
- Troubleshooting helpers
**bash/certbot-automation.sh** - Complete certbot automation suite
- HTTP-01 webroot validation
- Nginx plugin integration
- Wildcard certificates (manual and automated DNS)
- DNS provider integration (Cloudflare, Route53)
- Systemd timer setup for auto-renewal
- Pre/post/deploy hooks
- Certificate revocation
- Force renewal
- Staging environment testing
- Custom CSR usage
- Certificate monitoring
- Nginx installation automation
- 18+ production examples
**config/nginx-ssl-best-practices.conf** - Production Nginx SSL configuration
- Modern TLS 1.2/1.3 configuration
- Strong cipher suite selection
- OCSP stapling setup
- HSTS and security headers
- Let's Encrypt integration
- HTTP to HTTPS redirect
- Wildcard certificate support
- mTLS configuration
- Multiple domain examples
- SSL Labs A+ rating configuration
- Certificate renewal integration
- PHP-FPM and reverse proxy examples
### Quick Start
cd ~/.hanzo/skills/cryptography/certificate-management/resources/scripts ./validate_certificates.py --host example.com --check-revocation
./renew_certificates.py --domain example.com --webroot /var/www/html --post-hook "systemctl reload nginx"
./monitor_cert_expiry.sh --hosts-file hosts.txt --threshold-warning 30 --json
./monitor_cert_expiry.sh --hosts-file hosts.txt --format prometheus > /var/lib/prometheus/cert_metrics.prom
cd ../examples/python pip install cryptography acme josepy prometheus_client python acme_automation.py http01 python mtls_server_client.py setup && python mtls_server_client.py server python prometheus_cert_exporter.py --hosts example.com:443
cd ../ less REFERENCE.md
### Integration Notes
**CI/CD Integration**:
run: | ./scripts/validate_certificates.py \ --batch-file production-hosts.txt \ --compliance PCI-DSS \ --json \ --check-revocation
**Monitoring Setup**:
scrape_configs:
static_configs:
scrape_interval: 60s
**Alerting**:
expr: ssl_certificate_expiry_seconds < 86400 * 30 labels: severity: warning annotations: summary: "Certificate expiring in < 30 days"
**Kubernetes Deployment**:
kubectl apply -f examples/kubernetes/cert-manager-setup.yaml
kubectl get certificates -A kubectl describe certificate example-com-tls
---
**Last Updated**: 2025-10-27