ssl-certificate-management

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SSL Certificate Management

SSL/TLS证书管理

Overview

概述

Implement automated SSL/TLS certificate management across infrastructure, including provisioning, renewal, monitoring, and secure distribution to services.
在基础设施中实现自动化SSL/TLS证书管理,包括配置、续期、监控以及向服务安全分发证书。

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
  • 启用HTTPS/TLS
  • 证书续期自动化
  • 多域名证书管理
  • 通配符证书处理
  • 证书监控与告警
  • 零停机证书轮换
  • 内部PKI管理

Implementation Examples

实现示例

1. Let's Encrypt with Cert-Manager

1. 使用Let's Encrypt搭配Cert-Manager

yaml
undefined
yaml
undefined

cert-manager-setup.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: 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
undefined
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
undefined

2. AWS ACM Certificate Management

2. AWS ACM证书管理

yaml
undefined
yaml
undefined

acm-certificates.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 } }
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

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 }
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

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] }
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

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 } }
undefined
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 } }
undefined

3. Certificate Monitoring and Renewal

3. 证书监控与续期

bash
#!/bin/bash
bash
#!/bin/bash

certificate-monitor.sh - Monitor and alert on certificate expiration

certificate-monitor.sh - Monitor and alert on certificate expiration

set -euo pipefail
ALERT_DAYS=30 ALERT_EMAIL="admin@myapp.com"
set -euo pipefail
ALERT_DAYS=30 ALERT_EMAIL="admin@myapp.com"

Check certificate expiration in Kubernetes

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_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 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
}
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

Main execution

check_k8s_certificates check_acm_certificates
echo "Certificate check complete"
undefined
check_k8s_certificates check_acm_certificates
echo "Certificate check complete"
undefined

4. Automated Certificate Renewal

4. 自动化证书续期

yaml
undefined
yaml
undefined

certificate-renewal-cronjob.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
undefined
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
undefined

5. Certificate Pinning

5. 证书固定

nginx
undefined
nginx
undefined

nginx-certificate-pinning.conf

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;
}
}
undefined
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;
}
}
undefined

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)
  • 自动化证书续期
  • 对公开证书使用Let's Encrypt
  • 监控证书过期时间
  • 合理使用通配符证书
  • 实现证书固定
  • 定期轮换证书
  • 安全存储密钥
  • 使用高强度密钥(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
  • 手动管理证书
  • 在生产环境使用自签名证书
  • 共享私钥
  • 忽略过期警告
  • 使用弱密钥
  • 混合使用开发和生产环境证书
  • 将证书提交至Git
  • 禁用证书验证

Certificate Types

证书类型

  • Self-signed: Development only
  • Domain Validated (DV): Single domain
  • Wildcard: All subdomains
  • Multi-SAN: Multiple domains
  • Extended Validation (EV): High trust
  • 自签名证书:仅用于开发环境
  • 域名验证(DV)证书:单域名
  • 通配符证书:所有子域名
  • 多SAN证书:多域名
  • 扩展验证(EV)证书:高可信度

Common Commands

常用命令

bash
undefined
bash
undefined

Generate CSR

Generate CSR

openssl req -new -key server.key -out server.csr
openssl req -new -key server.key -out server.csr

Check certificate

Check certificate

openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.pem -text -noout

Get certificate fingerprint

Get certificate fingerprint

openssl x509 -in cert.pem -noout -fingerprint -sha256
openssl x509 -in cert.pem -noout -fingerprint -sha256

Renew certificate

Renew certificate

certbot renew --force-renewal
undefined
certbot renew --force-renewal
undefined

Resources

参考资源