بلاگ/راه‌اندازی cert-manager برای صدور و تمدید خودکار گواهی SSL در Kubernetes خودمیزبان روی VPS و سرور اختصاصی
به‌روز شده

راه‌اندازی cert-manager برای صدور و تمدید خودکار گواهی SSL در Kubernetes خودمیزبان روی VPS و سرور اختصاصی

1405/07/250 بازدید
راه‌اندازی cert-manager برای صدور و تمدید خودکار گواهی SSL در Kubernetes خودمیزبان روی VPS و سرور اختصاصی

راه‌اندازی cert-manager برای صدور و تمدید خودکار گواهی SSL در Kubernetes خودمیزبان

اگر کلاستر Kubernetes را خودتان روی VPS یا سرور اختصاصی بالا آورده‌اید (با kubeadm یا K3s)، مثل سرویس‌های ابری نیستید که گواهی SSL را خودکار برایتان بسازند. این کار باید دستی مدیریت شود، مگر این‌که cert-manager را نصب کنید. در این مقاله نصب آن با Helm، ساخت ClusterIssuer برای Let's Encrypt و وصل کردنش به Ingress را قدم به قدم می‌بینیم.

نصب cert-manager روی Kubernetes خودمیزبان

cert-manager چیست و چه مشکلی را حل می‌کند

cert-manager یک کنترلر داخل کلاستر است که گواهی SSL را از یک مرجع صدور مثل Let's Encrypt می‌گیرد، آن را به صورت Secret در namespace موردنظر ذخیره می‌کند و قبل از انقضا خودش تمدیدش می‌کند. بدون آن، هر بار که گواهی رو به اتمام است باید certbot را دستی اجرا کنید و Secret را به‌روزرسانی کنید؛ کاری که در یک کلاستر با چند Ingress به سرعت فراموش می‌شود.

روی ابرهای عمومی این کار اغلب توسط Load Balancer مدیریت‌شده انجام می‌شود. در Kubernetes خودمیزبان این لایه وجود ندارد، پس cert-manager همان نقش را روی خود کلاستر بازی می‌کند.

پیش‌نیازها

قبل از شروع این چند مورد باید آماده باشد:

  • کلاستر در حال اجرا (با kubeadm یا با K3s)، با kubectl متصل به آن
  • Ingress controller نصب‌شده (nginx-ingress رایج‌ترین گزینه است)
  • یک دامنه یا ساب‌دامنه که رکورد A آن به IP عمومی سرور (یا LoadBalancer/NodePort) اشاره کند
  • Helm نصب روی ماشینی که از آن kubectl اجرا می‌کنید

اگر nginx-ingress هنوز نصب نیست، اول آن را راه بیندازید و با یک دامنه تست مطمئن شوید ترافیک HTTP به Ingress می‌رسد. cert-manager بدون Ingress سالم هیچ گواهی‌ای نمی‌تواند بگیرد، چون چالش HTTP-01 از همین مسیر رد می‌شود.

نصب cert-manager با Helm

ابتدا CRDهای مورد نیاز cert-manager را نصب کنید، بعد chart را با Helm بیاورید:

kubectl create namespace cert-manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.3/cert-manager.crds.yaml

helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --version v1.15.3

بعد از چند ثانیه سه پاد باید در وضعیت Running باشند:

kubectl get pods -n cert-manager

اگر یکی از پادها (معمولا cert-manager-webhook) مدت زیادی در Pending یا CrashLoopBackOff بماند، احتمالا منابع CPU/RAM نود کم است یا DNS داخلی کلاستر (CoreDNS) درست کار نمی‌کند. یک دستور سریع برای چک کردن مصرف منابع:

kubectl top pods -n cert-manager

ساخت ClusterIssuer برای Let's Encrypt

cert-manager خودش گواهی صادر نمی‌کند؛ به یک Issuer می‌گوید از کجا بگیرد. برای کلاستری که چند namespace دارد، ClusterIssuer بهتر از Issuer معمولی است چون در کل کلاستر قابل استفاده است، نه فقط یک namespace.

Let's Encrypt دو محیط دارد: staging برای تست (محدودیت نرخ خیلی سبک‌تر، ولی مرورگر به گواهی‌اش اعتماد نمی‌کند) و production برای گواهی واقعی. بهتر است اول روی staging تست کنید تا محدودیت روزانه production را هدر ندهید.

فایل cluster-issuer-staging.yaml:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: you@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

بعد از تست موفق، همان فایل را برای production کپی کنید و فقط server و نام‌ها را عوض کنید:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: you@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
kubectl apply -f cluster-issuer-staging.yaml
kubectl apply -f cluster-issuer-prod.yaml

برای چک کردن که Issuer سالم است:

kubectl describe clusterissuer letsencrypt-prod

اگر در بخش Status فیلد Ready: True را دیدید یعنی ارتباط با Let's Encrypt برقرار است.

محیطکاربردمحدودیت نرخاعتماد مرورگر
stagingتست تنظیمات۳۰ هزار گواهی در هفتهندارد (هشدار امنیتی نشان می‌دهد)
productionگواهی واقعی سایت۵۰ گواهی در هفته برای هر دامنه ثبت‌شدهدارد

صدور گواهی از طریق Ingress

راحت‌ترین راه، اضافه کردن annotation به Ingress موجود است؛ cert-manager خودش یک منبع Certificate می‌سازد و درخواست را پیش می‌برد:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: my-app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-svc
                port:
                  number: 80

صدور گواهی SSL برای Ingress با cert-manager در Kubernetes خودمیزبان

بعد از apply کردن، cert-manager به صورت خودکار یک Certificate و یک CertificateRequest می‌سازد. وضعیتش را این‌طور می‌بینید:

kubectl get certificate -A
kubectl describe certificate my-app-tls

اگر در describe خط Status: Issued ظاهر شد، گواهی صادر شده و داخل Secret با نام my-app-tls نشسته. اگر چند دقیقه در حالت Pending ماند، سراغ رویدادها بروید:

kubectl get events --field-selector involvedObject.name=my-app-tls

رایج‌ترین دلیل گیر کردن، رکورد DNS است که هنوز به IP سرور نرسیده یا پورت ۸۰ از بیرون بسته است — چالش HTTP-01 دقیقا از همین پورت رد می‌شود.

تمدید خودکار چطور کار می‌کند

گواهی‌های Let's Encrypt ۹۰ روز اعتبار دارند. cert-manager به‌طور پیش‌فرض ۳۰ روز قبل از انقضا (یا طبق مقدار renewBefore که در Certificate تنظیم می‌کنید) خودش فرآیند تمدید را دوباره اجرا می‌کند، بدون این‌که Ingress یا Secret را دست بزنید. کافی است یک بار منابع را درست بسازید؛ از آن به بعد تنها کاری که باقی می‌ماند چک دوره‌ای سلامت است:

kubectl get certificate -A -o wide

ستون READY باید True باشد و ستون کنار آن تاریخ انقضای فعلی را نشان می‌دهد.

نکات برای VPS و سرور اختصاصی خودمیزبان

روی سرور ابری عمومی این مسائل معمولا توسط ارائه‌دهنده حل شده، اما روی VPS یا سرور اختصاصی خودتان مسئولشان هستید:

  • اگر چند زیردامنه یا wildcard (مثل *.example.com) لازم دارید، چالش HTTP-01 کافی نیست؛ باید solver را به DNS-01 عوض کنید و API ارائه‌دهنده DNS را به ClusterIssuer معرفی کنید.
  • پادهای cert-manager به‌طور مجموع حدود ۱۰۰ تا ۲۰۰ مگابایت RAM مصرف می‌کنند؛ روی VPS با منابع محدود این را در برنامه‌ریزی ظرفیت حساب کنید.
  • فایروال سرور باید پورت ۸۰ را برای چالش HTTP-01 باز نگه دارد، حتی اگر همه ترافیک واقعی از ۴۴۳ رد می‌شود.
  • Secretهای حاوی کلید خصوصی را backup بگیرید؛ اگر namespace cert-manager را اشتباهی پاک کنید، باید همه گواهی‌ها را از نو بگیرید.

اگر هنوز تصمیم نگرفته‌اید روی چه زیرساختی این کلاستر را بالا بیاورید، سرور مجازی آریانت منابع کافی برای یک کلاستر K3s کوچک یا یک نود kubeadm تست دارد، با پهنای باند و IP اختصاصی که چالش HTTP-01 را بدون پیچیدگی اضافه انجام می‌دهد.

سؤالات متداول

آیا cert-manager برای گواهی وایلدکارد هم کار می‌کند؟ بله، اما باید solver را از HTTP-01 به DNS-01 تغییر دهید و دسترسی API ارائه‌دهنده DNS دامنه را به ClusterIssuer بدهید.

روی K3s هم همین مراحل جواب می‌دهد؟ بله، نصب و تنظیمات یکسان است؛ تنها تفاوت این‌که K3s به‌صورت پیش‌فرض Traefik دارد، پس باید annotation و ingressClassName را متناسب با آن تنظیم کنید یا Traefik را با nginx-ingress جایگزین کنید.

اگر گواهی تمدید نشود چه اتفاقی می‌افتد؟ سایت با خطای گواهی منقضی به بازدیدکننده نشان داده می‌شود. به همین دلیل چک دوره‌ای kubectl get certificate -A یا یک آلارم مانیتورینگ روی تاریخ انقضا توصیه می‌شود.

استفاده از cert-manager و Let's Encrypt هزینه دارد؟ نه، هم cert-manager متن‌باز و رایگان است و هم Let's Encrypt گواهی را بدون هزینه صادر می‌کند؛ فقط باید در محدودیت نرخ هفتگی آن بمانید.