home / skills / aj-geddes / useful-ai-prompts / ssl-certificate-management

ssl-certificate-management skill

/skills/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-management

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
10.0 KB
---
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)

Overview

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.

How this skill works

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.

When to use it

  • Enable HTTPS/TLS for web services and APIs
  • Automate certificate renewal to avoid outages
  • Manage multi-domain or wildcard certificates at scale
  • Monitor certificates and send expiration alerts
  • Rotate certificates with zero downtime across services

Best practices

  • Automate renewal and deployment pipelines; avoid manual replacement
  • Use Lets Encrypt for public DV certs and ACM for AWS load balancers
  • Monitor expirations and alert well before expiry (30+ days)
  • Store private keys in a secure vault or cloud key management system
  • Use strong keys (2048+ RSA, 256+ ECDSA) and rotate regularly
  • Separate dev and production certificates; never commit keys to source control

Example use cases

  • Kubernetes ingress TLS with cert-manager and ACME HTTP-01/DNS-01 solvers for wildcard domains
  • AWS ALB with ACM certificates created via Terraform and validated by Route53 records
  • CronJob that scans Kubernetes secrets and ACM certificates, emailing admins when expiry is near
  • Automated renewal job that annotates cert-manager Certificates to trigger re-issuance without downtime
  • Nginx configuration enforcing certificate pinning for sensitive API endpoints

FAQ

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.