| 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
# 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: admin@myapp.com
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
# 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
#!/bin/bash
# certificate-monitor.sh - Monitor and alert on certificate expiration
set -euo pipefail
ALERT_DAYS=30
ALERT_EMAIL="admin@myapp.com"
# 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
# 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-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
# 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