SSL证书管理 ssl-certificate-management

自动化 SSL/TLS 证书管理,包括证书供应、续签、监控和安全分发,关键词:自动化续签、证书监控、零停机时间轮换、内部 PKI 管理。

云安全 0 次安装 0 次浏览 更新于 3/4/2026

SSL 证书管理

概览

实现自动化的 SSL/TLS 证书管理,涵盖基础设施的证书供应、续签、监控和安全分发到服务。

使用场景

  • HTTPS/TLS 启用
  • 证书续签自动化
  • 多域名证书管理
  • 通配符证书处理
  • 证书监控和警报
  • 零停机时间证书轮换
  • 内部 PKI 管理

实施示例

1. Let’s Encrypt 与 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 解算器用于标准域名
      - http01:
          ingress:
            class: nginx
        selector:
          dnsNames:
            - "myapp.com"
            - "www.myapp.com"

      # DNS-01 解算器用于通配符域名
      - 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 天
  renewBefore: 720h  # 30 天前到期

---
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 证书管理

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

# 创建 Route53 验证记录
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_acm_certificate_validation" "main" {
  certificate_arn           = aws_acm_certificate.main.arn
  timeouts {
    create = "5m"
  }

  depends_on = [aws_route53_record.cert_validation]
}

# 在 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. 证书监控和续签

#!/bin/bash
# certificate-monitor.sh - 监控和警报证书到期

set -euo pipefail

ALERT_DAYS=30
ALERT_EMAIL="admin@myapp.com"

# 检查 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
}

# 检查 AWS ACM 证书到期情况
check_acm_certificates() {
    echo "Checking AWS ACM certificate expiration..."

    aws acm list-certificates \
        --query 'CertificateSummaryList[*].CertificateArn' \
        --output text | tr '\t' '
' | \
    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_k8s_certificates
check_acm_certificates

echo "Certificate check complete"

4. 自动化证书续签

# certificate-renewal-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: certificate-renewal
  namespace: operations
spec:
  schedule: "0 2 * * 0"  # 每周日 2 点
  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..."

                  # 获取所有证书
                  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. 证书固定

# 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;

    # API 客户端的证书固定
    add_header Public-Key-Pins 'pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="; max-age=2592000; includeSubDomains' always;

    location / {
        proxy_pass http://backend;
    }
}

最佳实践

✅ 执行

  • 自动化证书续签
  • 公共证书使用 Let’s Encrypt
  • 监控证书到期
  • 策略性使用通配符证书
  • 实施证书固定
  • 定期轮换证书
  • 安全存储密钥
  • 使用强密钥大小 (2048+ RSA, 256+ ECDSA)

❌ 不执行

  • 手动证书管理
  • 生产中使用自签名证书
  • 共享私钥
  • 忽略到期警告
  • 使用弱密钥大小
  • 混合开发和生产证书
  • 提交证书到 git
  • 禁用证书验证

证书类型

  • 自签名: 仅限开发
  • 域名验证 (DV): 单一域名
  • 通配符: 所有子域名
  • 多 SAN: 多个域名
  • 扩展验证 (EV): 高信任

常用命令

# 生成 CSR
openssl req -new -key server.key -out server.csr

# 检查证书
openssl x509 -in cert.pem -text -noout

# 获取证书指纹
openssl x509 -in cert.pem -noout -fingerprint -sha256

# 续签证书
certbot renew --force-renewal

资源