home / skills / aj-geddes / useful-ai-prompts / ssl-certificate-management
This skill automates SSL/TLS certificate provisioning, renewal, and monitoring across environments using Let's Encrypt, ACM, or Vault.
npx playbooks add skill aj-geddes/useful-ai-prompts --skill ssl-certificate-managementReview the files below or copy the command above to add this skill to your agents.
---
name: ssl-certificate-management
description: Manage SSL/TLS certificates with automated provisioning, renewal, and monitoring using Let's Encrypt, ACM, or Vault.
---
# SSL Certificate Management
## Overview
Implement automated SSL/TLS certificate management across infrastructure, including provisioning, renewal, monitoring, and secure distribution to services.
## When to Use
- HTTPS/TLS enablement
- Certificate renewal automation
- Multi-domain certificate management
- Wildcard certificate handling
- Certificate monitoring and alerts
- Zero-downtime certificate rotation
- Internal PKI management
## Implementation Examples
### 1. **Let's Encrypt with Cert-Manager**
```yaml
# cert-manager-setup.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
# HTTP-01 solver for standard domains
- http01:
ingress:
class: nginx
selector:
dnsNames:
- "myapp.com"
- "www.myapp.com"
# DNS-01 solver for wildcard domains
- dns01:
route53:
region: us-east-1
hostedZoneID: Z1234567890ABC
accessKeyID: AKIAIOSFODNN7EXAMPLE
secretAccessKeySecretRef:
name: route53-credentials
key: secret-access-key
selector:
dnsNames:
- "*.myapp.com"
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: production
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: myapp.com
dnsNames:
- myapp.com
- www.myapp.com
- api.myapp.com
- "*.myapp.com"
duration: 2160h # 90 days
renewBefore: 720h # 30 days before expiry
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: production
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.com
- www.myapp.com
secretName: myapp-tls-secret
rules:
- host: myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
```
### 2. **AWS ACM Certificate Management**
```yaml
# acm-certificates.yaml
resource "aws_acm_certificate" "main" {
domain_name = "myapp.com"
validation_method = "DNS"
subject_alternative_names = [
"www.myapp.com",
"api.myapp.com",
"*.myapp.com"
]
tags = {
Name = "myapp-certificate"
}
lifecycle {
create_before_destroy = true
}
}
# Create Route53 validation records
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = aws_route53_zone.main.zone_id
}
# Validate certificate
resource "aws_acm_certificate_validation" "main" {
certificate_arn = aws_acm_certificate.main.arn
timeouts {
create = "5m"
}
depends_on = [aws_route53_record.cert_validation]
}
# Use in ALB
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.main.arn
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
certificate_arn = aws_acm_certificate_validation.main.certificate_arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.main.arn
}
}
```
### 3. **Certificate Monitoring and Renewal**
```bash
#!/bin/bash
# certificate-monitor.sh - Monitor and alert on certificate expiration
set -euo pipefail
ALERT_DAYS=30
ALERT_EMAIL="[email protected]"
# Check certificate expiration in Kubernetes
check_k8s_certificates() {
echo "Checking Kubernetes certificate expiration..."
kubectl get secrets -A -o json | jq -r '.items[] | select(.type=="kubernetes.io/tls") | "\(.metadata.name) \(.metadata.namespace)"' | \
while read secret namespace; do
cert=$(kubectl get secret "$secret" -n "$namespace" -o jsonpath='{.data.tls\.crt}' | base64 -d)
expiry=$(echo "$cert" | openssl x509 -noout -enddate | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_until=$((($expiry_epoch - $now_epoch) / 86400))
if [ $days_until -lt $ALERT_DAYS ]; then
echo "WARNING: Certificate $secret in namespace $namespace expires in $days_until days"
echo "Certificate $secret expires on $expiry" | \
mail -s "Certificate Expiration Alert" "$ALERT_EMAIL"
fi
done
}
# Check AWS ACM certificates
check_acm_certificates() {
echo "Checking AWS ACM certificate expiration..."
aws acm list-certificates \
--query 'CertificateSummaryList[*].CertificateArn' \
--output text | tr '\t' '\n' | \
while read arn; do
expiry=$(aws acm describe-certificate --certificate-arn "$arn" \
--query 'Certificate.NotAfter' --output text)
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_until=$((($expiry_epoch - $now_epoch) / 86400))
if [ $days_until -lt $ALERT_DAYS ]; then
domain=$(aws acm describe-certificate --certificate-arn "$arn" \
--query 'Certificate.DomainName' --output text)
echo "WARNING: Certificate for $domain expires in $days_until days"
echo "ACM Certificate $domain expires on $expiry" | \
mail -s "Certificate Expiration Alert" "$ALERT_EMAIL"
fi
done
}
# Main execution
check_k8s_certificates
check_acm_certificates
echo "Certificate check complete"
```
### 4. **Automated Certificate Renewal**
```yaml
# certificate-renewal-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: certificate-renewal
namespace: operations
spec:
schedule: "0 2 * * 0" # Weekly at 2 AM Sunday
jobTemplate:
spec:
template:
spec:
serviceAccountName: cert-renewal-sa
containers:
- name: renewer
image: alpine:latest
command:
- sh
- -c
- |
apk add --no-cache kubectl curl jq openssl
echo "Checking certificate renewal status..."
# Get all certificates
kubectl get certificates -A -o json | jq -r '.items[] | "\(.metadata.name) \(.metadata.namespace)"' | \
while read cert namespace; do
status=$(kubectl get certificate "$cert" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
if [ "$status" != "True" ]; then
echo "Renewing certificate: $cert in namespace $namespace"
kubectl annotate certificate "$cert" -n "$namespace" \
cert-manager.io/issue-temporary-certificate="true" \
--overwrite || true
fi
done
echo "Certificate renewal check complete"
restartPolicy: OnFailure
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: cert-renewal-sa
namespace: operations
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cert-renewal
rules:
- apiGroups: ["cert-manager.io"]
resources: ["certificates"]
verbs: ["get", "list", "patch", "annotate"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cert-renewal
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cert-renewal
subjects:
- kind: ServiceAccount
name: cert-renewal-sa
namespace: operations
```
### 5. **Certificate Pinning**
```nginx
# nginx-certificate-pinning.conf
server {
listen 443 ssl http2;
server_name api.myapp.com;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Certificate pinning for API clients
add_header Public-Key-Pins 'pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; max-age=2592000; includeSubDomains' always;
location / {
proxy_pass http://backend;
}
}
```
## Best Practices
### ✅ DO
- Automate certificate renewal
- Use Let's Encrypt for public certs
- Monitor certificate expiration
- Use wildcard certs strategically
- Implement certificate pinning
- Rotate certificates regularly
- Store keys securely
- Use strong key sizes (2048+ RSA, 256+ ECDSA)
### ❌ DON'T
- Manual certificate management
- Self-signed certs in production
- Share private keys
- Ignore expiration warnings
- Use weak key sizes
- Mix dev and prod certs
- Commit certs to git
- Disable certificate validation
## Certificate Types
- **Self-signed**: Development only
- **Domain Validated (DV)**: Single domain
- **Wildcard**: All subdomains
- **Multi-SAN**: Multiple domains
- **Extended Validation (EV)**: High trust
## Common Commands
```bash
# Generate CSR
openssl req -new -key server.key -out server.csr
# Check certificate
openssl x509 -in cert.pem -text -noout
# Get certificate fingerprint
openssl x509 -in cert.pem -noout -fingerprint -sha256
# Renew certificate
certbot renew --force-renewal
```
## Resources
- [Let's Encrypt Documentation](https://letsencrypt.org/docs/)
- [Cert-Manager Documentation](https://cert-manager.io/docs/)
- [AWS ACM Documentation](https://docs.aws.amazon.com/acm/)
- [OWASP Certificate Pinning](https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning)
This skill automates SSL/TLS certificate lifecycle across environments, including provisioning, renewal, monitoring, and secure distribution using Let's Encrypt, AWS ACM, or HashiCorp Vault. It provides practical recipes and scripts for zero-downtime rotation, multi-domain and wildcard handling, and expiration alerting. The goal is reliable HTTPS enablement and reduced manual overhead.
The skill supplies configuration examples, automation cronjobs, and monitoring scripts that inspect certificate stores (Kubernetes secrets, AWS ACM) and perform actions (renew, annotate, create DNS validation records). It leverages ACME with cert-manager for Lets Encrypt, Terraform and Route53 for ACM issuance, and shell tooling to detect expirations and trigger alerts. Secure key handling and role-based access controls are recommended for distribution.
How do I choose between Lets Encrypt and AWS ACM?
Use Lets Encrypt for public web certificates outside cloud-managed load balancers; use ACM when you need seamless integration with AWS load balancers and managed distribution. Consider automation and validation method (HTTP-01 vs DNS-01).
What alert threshold should I use for expiration warnings?
A good default is 30 days. For high-risk services use 60 days. Alerts should provide domain, namespace/service, and exact expiry date so remediation can be prioritized.