Pulumi SSL/TLS
Pulumi IaC SSL TLS Certificate ACM Let's Encrypt Auto-renewal CloudFront ALB API Gateway DNS Validation Infrastructure as Code Python TypeScript
| Certificate | Provider | Cost | Auto-renew | เหมาะกับ |
|---|---|---|---|---|
| ACM | AWS | Free (AWS) | Auto | CloudFront ALB |
| Let's Encrypt | ISRG | Free | Certbot 90d | Any Server |
| DigiCert | DigiCert | $$$ | Manual/Auto | Enterprise EV |
| Cloudflare | Cloudflare | Free (CF) | Auto | Cloudflare Users |
| cert-manager | Kubernetes | Free | Auto | K8s Ingress |
ACM Certificate with Pulumi
# === Pulumi ACM Certificate (Python) ===
# pip install pulumi pulumi-aws
import pulumi
import pulumi_aws as aws
# ACM Certificate
certificate = aws.acm.Certificate("site-cert",
domain_name="example.com",
subject_alternative_names=[
"*.example.com",
"api.example.com",
"cdn.example.com",
],
validation_method="DNS",
tags={"Environment": "production", "ManagedBy": "pulumi"},
)
# Route 53 Zone
zone = aws.route53.get_zone(name="example.com")
# DNS Validation Records
validation_records = []
for i, dvo in enumerate(certificate.domain_validation_options):
record = aws.route53.Record(f"cert-validation-{i}",
zone_id=zone.zone_id,
name=dvo.resource_record_name,
type=dvo.resource_record_type,
records=[dvo.resource_record_value],
ttl=300,
)
validation_records.append(record)
# Wait for validation
cert_validation = aws.acm.CertificateValidation("cert-validation",
certificate_arn=certificate.arn,
validation_record_fqdns=[r.fqdn for r in validation_records],
)
# CloudFront Distribution with SSL
# cdn = aws.cloudfront.Distribution("cdn",
# origins=[aws.cloudfront.DistributionOriginArgs(
# domain_name="origin.example.com",
# origin_id="origin",
# custom_origin_config=aws.cloudfront.DistributionOriginCustomOriginConfigArgs(
# http_port=80,
# https_port=443,
# origin_protocol_policy="https-only",
# origin_ssl_protocols=["TLSv1.2"],
# ),
# )],
# default_cache_behavior=aws.cloudfront.DistributionDefaultCacheBehaviorArgs(
# target_origin_id="origin",
# viewer_protocol_policy="redirect-to-https",
# allowed_methods=["GET", "HEAD"],
# cached_methods=["GET", "HEAD"],
# forwarded_values=aws.cloudfront.DistributionDefaultCacheBehaviorForwardedValuesArgs(
# query_string=False,
# cookies=aws.cloudfront.DistributionDefaultCacheBehaviorForwardedValuesCookiesArgs(
# forward="none",
# ),
# ),
# ),
# viewer_certificate=aws.cloudfront.DistributionViewerCertificateArgs(
# acm_certificate_arn=cert_validation.certificate_arn,
# ssl_support_method="sni-only",
# minimum_protocol_version="TLSv1.2_2021",
# ),
# aliases=["example.com", "www.example.com"],
# enabled=True,
# )
pulumi.export("certificate_arn", certificate.arn)
pulumi.export("certificate_status", certificate.status)
from dataclasses import dataclass
@dataclass
class CertConfig:
domain: str
sans: list
validation: str
service: str
tls_version: str
configs = [
CertConfig("example.com", ["*.example.com"], "DNS", "CloudFront", "TLSv1.2"),
CertConfig("api.example.com", [], "DNS", "ALB", "TLSv1.2"),
CertConfig("admin.example.com", [], "DNS", "ALB", "TLSv1.3"),
]
print("=== Certificate Configurations ===")
for c in configs:
sans_str = ", ".join(c.sans) if c.sans else "None"
print(f" [{c.domain}] SANs: {sans_str}")
print(f" Validation: {c.validation} | Service: {c.service} | TLS: {c.tls_version}")
Let's Encrypt with Pulumi
# === Let's Encrypt + cert-manager on Kubernetes ===
# Pulumi Kubernetes — cert-manager setup
# import pulumi_kubernetes as k8s
#
# # Install cert-manager via Helm
# cert_manager = k8s.helm.v3.Release("cert-manager",
# chart="cert-manager",
# repository_opts=k8s.helm.v3.RepositoryOptsArgs(
# repo="https://charts.jetstack.io",
# ),
# namespace="cert-manager",
# create_namespace=True,
# values={
# "installCRDs": True,
# "global": {"leaderElection": {"namespace": "cert-manager"}},
# },
# )
#
# # ClusterIssuer for Let's Encrypt
# cluster_issuer = k8s.apiextensions.CustomResource("letsencrypt-prod",
# api_version="cert-manager.io/v1",
# kind="ClusterIssuer",
# metadata=k8s.meta.v1.ObjectMetaArgs(name="letsencrypt-prod"),
# spec={
# "acme": {
# "server": "https://acme-v02.api.letsencrypt.org/directory",
# "email": "admin@example.com",
# "privateKeySecretRef": {"name": "letsencrypt-prod-key"},
# "solvers": [
# {"http01": {"ingress": {"class": "nginx"}}},
# ],
# },
# },
# opts=pulumi.ResourceOptions(depends_on=[cert_manager]),
# )
#
# # Ingress with auto TLS
# ingress = k8s.networking.v1.Ingress("app-ingress",
# metadata=k8s.meta.v1.ObjectMetaArgs(
# annotations={
# "cert-manager.io/cluster-issuer": "letsencrypt-prod",
# "nginx.ingress.kubernetes.io/ssl-redirect": "true",
# },
# ),
# spec=k8s.networking.v1.IngressSpecArgs(
# tls=[k8s.networking.v1.IngressTLSArgs(
# hosts=["app.example.com"],
# secret_name="app-tls-cert",
# )],
# rules=[k8s.networking.v1.IngressRuleArgs(
# host="app.example.com",
# http=k8s.networking.v1.HTTPIngressRuleValueArgs(
# paths=[k8s.networking.v1.HTTPIngressPathArgs(
# path="/",
# path_type="Prefix",
# backend=k8s.networking.v1.IngressBackendArgs(
# service=k8s.networking.v1.IngressServiceBackendArgs(
# name="app-service",
# port=k8s.networking.v1.ServiceBackendPortArgs(number=80),
# ),
# ),
# )],
# ),
# )],
# ),
# )
# Certbot standalone
# certbot certonly --standalone -d example.com -d www.example.com \
# --email admin@example.com --agree-tos --non-interactive
#
# # Auto-renewal cron
# 0 0 1 * * certbot renew --quiet --post-hook "systemctl reload nginx"
@dataclass
class CertMethod:
method: str
provider: str
auto_renew: str
complexity: str
use_case: str
methods = [
CertMethod("ACM + Pulumi", "AWS", "Automatic", "ง่าย", "AWS CloudFront ALB"),
CertMethod("cert-manager + Pulumi", "Let's Encrypt", "Automatic", "ปานกลาง", "Kubernetes"),
CertMethod("Certbot Cron", "Let's Encrypt", "Cron Job", "ง่าย", "Standalone Server"),
CertMethod("Cloudflare SSL", "Cloudflare", "Automatic", "ง่าย", "Cloudflare Proxy"),
CertMethod("Pulumi + DigiCert", "DigiCert", "Manual/API", "ซับซ้อน", "Enterprise EV"),
]
print("\n=== Certificate Methods ===")
for m in methods:
print(f" [{m.method}]")
print(f" Provider: {m.provider} | Renew: {m.auto_renew}")
print(f" Complexity: {m.complexity} | Use: {m.use_case}")
Security Best Practices
# === TLS Security Configuration ===
# Nginx TLS Config
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# ssl_prefer_server_ciphers off;
# ssl_session_timeout 1d;
# ssl_session_cache shared:SSL:10m;
# ssl_session_tickets off;
# ssl_stapling on;
# ssl_stapling_verify on;
# add_header Strict-Transport-Security "max-age=63072000" always;
@dataclass
class TLSCheck:
check: str
expected: str
tool: str
severity: str
status: str
checks = [
TLSCheck("TLS Version", "1.2 / 1.3 only", "ssllabs.com", "Critical", "Pass"),
TLSCheck("Certificate Chain", "Valid complete chain", "openssl s_client", "Critical", "Pass"),
TLSCheck("HSTS Header", "max-age >= 31536000", "curl -I", "High", "Pass"),
TLSCheck("OCSP Stapling", "Enabled", "openssl s_client -status", "Medium", "Pass"),
TLSCheck("Cipher Suites", "AEAD only no CBC", "nmap --script ssl-enum-ciphers", "High", "Pass"),
TLSCheck("Certificate Expiry", "> 30 days", "openssl x509 -enddate", "Critical", "Pass"),
TLSCheck("CAA Record", "Present", "dig CAA example.com", "Medium", "Pass"),
TLSCheck("Key Size", "RSA 2048+ or ECDSA P-256+", "openssl x509 -text", "High", "Pass"),
]
print("TLS Security Checklist:")
for c in checks:
print(f" [{c.status}] [{c.severity}] {c.check}: {c.expected}")
print(f" Tool: {c.tool}")
# Certificate monitoring
monitoring = {
"Expiry Alert": "Alert 30 days before expiry",
"CT Log Monitor": "Monitor Certificate Transparency logs",
"SSL Labs Score": "Maintain A+ rating",
"Renewal Check": "Verify auto-renewal weekly",
"Revocation Check": "Monitor CRL OCSP status",
}
print(f"\n\nCertificate Monitoring:")
for k, v in monitoring.items():
print(f" [{k}]: {v}")
เคล็ดลับ
- ACM: ใช้ ACM สำหรับ AWS Services ฟรีและ Auto-renew
- Wildcard: ใช้ Wildcard Certificate *.example.com ลดจำนวน Cert
- TLS 1.3: ใช้ TLS 1.3 เป็น Default ปิด TLS 1.0 1.1
- HSTS: เปิด HSTS Header ป้องกัน Downgrade Attack
- Monitor: ตั้ง Alert แจ้งเตือน 30 วันก่อน Certificate หมดอายุ
Pulumi คืออะไร
IaC Platform ภาษาจริง Python TypeScript Go Cloud Resources AWS Azure GCP State Preview Secrets CI/CD Loop Condition Function
จัดการ SSL/TLS Certificate ด้วย Pulumi อย่างไร
ACM Certificate Domain SANs DNS Validation Route 53 Auto-renewal CloudFront ALB API Gateway Let's Encrypt cert-manager Kubernetes
ACM Certificate ต่างจาก Let's Encrypt อย่างไร
ACM ฟรี AWS Auto-renewal Managed CloudFront ALB Let's Encrypt ฟรีทุก Server Certbot 90 วัน Nginx Apache Non-AWS On-premise
ทำไมต้องใช้ IaC จัดการ Certificate
Reproducible Version Control Git Review Automation Multi-environment Drift Detection Rollback Documentation Code ทุก Environment เหมือนกัน
สรุป
Pulumi IaC SSL TLS Certificate ACM Let's Encrypt cert-manager Auto-renewal CloudFront ALB DNS Validation TLS 1.3 HSTS Security Production
