HTTPS دیگر luxury نیست — مرورگرها HTTP را «ناامن» میزنند، APIها TLS میخواهند، و compliance بدون encryption قبول نمیشود. روی Kubernetes معمولاً Ingress + TLS Secret داریم — اما certificate از کجا میآید؟
راههای بد:
- certbot دستی روی یک node — expire میشود، کسی renew نمیکند
- copy/paste
.crt/.key— drift، audit ندارید - cloud LB certificate — vendor lock، per-service گران
- self-signed — مرورگر قرمز، mTLS داخلی OK ولی public نه
cert-manager پاسخ ecosystem است:
Cloud native certificate management — X.509 certificate management for Kubernetes and OpenShift workloads.
یعنی certificate مثل هر resource دیگر Kubernetes: declarative، automated، renewed قبل از expire.
پروژه توسط Jetstack ساخته و ۲۰۲۰ به CNCF donate شد. امروز ۵M+ download روزانه و استاندارد de facto TLS روی cluster.
مشکل certificate دستی در Kubernetes
مدل شکننده:
DevOps هر ۹۰ روز → SSH به jump box → certbot
→ kubectl create secret tls ...
→ فراموش → سایت down در جمعه شب| مشکل | پیامد |
|---|---|
| expire بدون renew | outage، SEO ضربه |
| Secret stale | Ingress از cert قدیمی serve میکند |
| wildcard با HTTP-01 | غیرممکن — نیاز DNS-01 |
| multi-cluster | cert جدا per cluster |
| audit | «چه کسی cert را عوض کرد؟» — نامشخص |
cert-manager lifecycle را automate میکند: request → challenge → Secret → renew → repeat.
cert-manager چیست؟
cert-manager یک Kubernetes controller است که:
- Certificate CRD را watch میکند
- از Issuer / ClusterIssuer certificate میگیرد
- کلید خصوصی + cert را در Secret ذخیره میکند
- قبل از expire renew میکند (پیشفرض ~۳۰ روز قبل)
- با Ingress، Gateway API، Service Mesh، Pod mount integrate میشود
Issuerهای پشتیبانیشده:
| نوع | مثال |
|---|---|
| ACME (public) | Let’s Encrypt |
| ACME (private) | Boulder، Smallstep |
| HashiCorp | Vault |
| Private PKI | CA داخلی، CyberArk |
| Self-signed | dev/test |
معماری و CRDها
نمای کلی
┌─────────────────────────────────────────────────────────┐
│ cert-manager │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ controller │ │ cainjector │ │ webhook │ │
│ └──────┬──────┘ └──────────────┘ └─────────────────┘ │
└─────────┼───────────────────────────────────────────────┘
│ reconcile
▼
Certificate CRD ──► CertificateRequest
│ │
▼ ▼
Issuer / Order (ACME)
ClusterIssuer │
│ ▼
│ Challenge (HTTP-01 / DNS-01)
▼
Kubernetes Secret (tls.crt + tls.key)
│
▼
Ingress / Gateway / Pod volumeCRDهای کلیدی
| CRD | scope | کاربرد |
|---|---|---|
| Issuer | namespace | CA برای یک namespace |
| ClusterIssuer | cluster | CA مشترک (Let’s Encrypt prod) |
| Certificate | namespace | «من Secret X با SAN Y میخواهم» |
| CertificateRequest | internal | request خام — معمولاً دستی نمیسازید |
| Order / Challenge | ACME | HTTP-01 یا DNS-01 flow |
ویژگیهای اصلی (از cert-manager.io)
| ویژگی | توضیح |
|---|---|
| Automated issuance & renewal | Ingress TLS بدون cron دستی |
| Public & private CA | Let’s Encrypt تا Vault |
| mTLS | Pod-to-pod با private PKI |
| Wildcard | DNS-01 challenge |
| Service mesh | add-on برای Istio SPIFFE |
| CSI driver | private key on-node، بدون Secret |
ACME challenges: HTTP-01 vs DNS-01
| HTTP-01 | DNS-01 | |
|---|---|---|
| نیاز | Ingress از اینترنت reachable | API DNS (Route53، Cloudflare، …) |
| Wildcard | ❌ | ✅ *.example.com |
| firewall strict | سخت | آسانتر |
| پیچیدگی | کم | IAM + DNS provider |
برای p30light.ir پشت Cloudflare معمولاً DNS-01 با Cloudflare solver یا HTTP-01 اگر origin expose است.
نصب cert-manager
Helm (توصیه production)
از Helm docs:
helm install cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.17.2 \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=trueتأیید:
kubectl get pods -n cert-manager
kubectl get crd | grep cert-managerkubectl (سریع)
kubectl apply -f \
https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yamlهمیشه CRDها را با نسخه cert-manager همخوان نگه دارید.
ClusterIssuer: Let’s Encrypt staging
همیشه اول staging — rate limit واقعی نمیخورید:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
class: nginxkubectl apply -f clusterissuer-staging.yaml
kubectl describe clusterissuer letsencrypt-staging
# Status: ReadyCertificate + Ingress (HTTP-01)
روش ۱: Certificate CRD صریح
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: production
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
dnsNames:
- app.example.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: production
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: myapp-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80cert-manager Certificate را watch میکند → Challenge → Secret پر میشود → Ingress TLS فعال.
روش ۲: Ingress annotation (nginx)
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
tls:
- hosts:
- app.example.com
secretName: app-example-com-tlsسادهتر برای app team — cert-manager Certificate را خودکار میسازد.
Production Issuer
پس از تست staging:
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-account-key
solvers:
- http01:
ingress:
class: nginxCertificate را به letsencrypt-prod switch کنید.
Wildcard با DNS-01 (Route53 مثال)
از Route53 docs:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-dns01-key
solvers:
- dns01:
route53:
region: eu-central-1
# با IRSA روی EKS — role از ServiceAccount
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example
namespace: production
spec:
secretName: wildcard-example-tls
issuerRef:
name: letsencrypt-dns01
kind: ClusterIssuer
dnsNames:
- "*.example.com"
- example.comPrivate PKI و mTLS داخلی
برای service-to-service (نه public CA):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-internal
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-api-mtls
namespace: backend
spec:
secretName: internal-api-tls
issuerRef:
name: selfsigned-internal
kind: ClusterIssuer
dnsNames:
- api.internal.svc.cluster.local
usages:
- server auth
- client authبرای production internal: Vault Issuer یا CA سازمانی.
چه زمانی cert-manager؟
✅ مناسب
- Kubernetes/OpenShift با Ingress/Gateway زیاد
- Let’s Encrypt automate
- Wildcard یا multi-SAN
- GitOps (Argo CD) — Certificate YAML در Git
- Renew خودکار — SLA uptime
- mTLS mesh یا internal API
❌ کمتر مناسب
| وضعیت | جایگزین |
|---|---|
| TLS فقط روی Cloudflare (proxy) | Cloudflare Universal SSL |
| یک static site خارج K8s | CDN cert |
| VM بدون K8s | certbot + systemd timer |
| cert از vendor hardware HSM | manual process |
مقایسه
| معیار | certbot دستی | cloud LB cert | cert-manager |
|---|---|---|---|
| K8s native | ❌ | جزئی | ✅ |
| GitOps | ❌ | ❌ | ✅ |
| Auto renew | cron دستی | vendor | ✅ controller |
| Wildcard LE | DNS script | ❌ | ✅ DNS-01 |
| Multi-namespace | سخت | per-LB | ClusterIssuer |
| Audit CRD | ❌ | ❌ | ✅ events |
cert-manager در stack P30Light
Internet
│
▼
Cloudflare / nginx ([nginx tuning](/blog/nginx-optimization-high-traffic/))
│
▼
Ingress + cert-manager Secret (TLS)
│
▼
Astro static / apps on K8s
│
├── [Argo CD](/blog/argo-project-kubernetes-gitops-cicd/) sync
└── [Kube-OVN](/blog/kube-ovn-kubernetes-cloud-native-networking/) networkCertificate manifest در همان GitOps repo — deploy و renew همراستا با app.
Troubleshooting
# وضعیت Certificate
kubectl describe certificate -n production myapp-tls
kubectl get certificaterequest,order,challenge -n production
# لاگ controller
kubectl logs -n cert-manager deploy/cert-manager -f
# رویدادهای رایج
kubectl get events -n production --field-selector reason=Failed| علامت | علت محتمل | fix |
|---|---|---|
| Certificate Pending | Challenge fail | describe Challenge |
| HTTP-01 timeout | Ingress unreachable | DNS/firewall |
| DNS-01 fail | IAM/API token | Route53/CF credentials |
| Secret empty | Issuer not Ready | describe ClusterIssuer |
| staging cert in prod | wrong issuerRef | switch to prod Issuer |
امنیت و best practices
- Separate staging/prod Issuer — هرگز staging را در prod فراموش نکنید (مرورگر warning)
- email ACME واقعی — expire notice از Let’s Encrypt
- RBAC — فقط platform team ClusterIssuer بسازد
- Rate limits LE — از staging برای test
- Backup نیست — cert قابل re-issue است؛ account key Secret را backup کنید
- Uninstall — CRDها Certificateها را پاک میکنند (Helm docs)
عملیات روزمره
kubectl get certificate -A
kubectl get clusterissuer
kubectl cert-manager check api --wait=2m # اگر plugin نصب است
kubectl describe certificate myapp-tls -n productionRenew معمولاً خودکار — monitor با Prometheus metrics cert-manager.
جمعبندی
| بدون cert-manager | با cert-manager |
|---|---|
| certbot + cron | Controller reconcile loop |
| expire = outage | Renew proactive |
| Ingress TLS دستی | Certificate CRD |
| wildcard سخت | DNS-01 یک Issuer |
| audit ضعیف | Kubernetes events + Git |
cert-manager برای هر cluster Kubernetes که HTTPS جدی میخواهد — public یا internal — almost mandatory است. یک بار ClusterIssuer درست setup کنید؛ بعد صدها Certificate از همان pattern.
قدم بعدی
- Helm install روی staging cluster
letsencrypt-stagingClusterIssuer + یک Certificate تست- Ingress با TLS — verify در مرورگر (staging warning OK)
- switch به
letsencrypt-prod - اگر wildcard لازم است — DNS-01 با provider DNS خود
منابع:
- cert-manager — Official Site
- Documentation
- Helm Installation
- ACME / Let’s Encrypt
- DNS-01 Route53
- AWS EKS Tutorial
- GitHub — cert-manager/cert-manager
منتشر شده در P30Light — بخش زیرساخت سرور و Cloud Native.