رفع مشکل

تمدید خودکار گواهی Let's Encrypt انجام نمی‌شود؛ عیب‌یابی قدم‌به‌قدم Certbot

تهیه: کلودیفای (نگارش با کمک هوش مصنوعی) · انتشار: · زمان مطالعه: ۷ دقیقه

Let'sEncryptCertbotرفع مشکلتمدید خودکار گواهی Let'sEncrypt انجام نمی‌شود؛عیب‌یابی قدم‌به‌قدم Certbotمجلهٔ کلودیفایclodify.net/blog
پاسخ کوتاه

اگر گواهی Let's Encrypt تمدید نمی‌شود، ابتدا با systemctl list-timers وجود تایمر certbot را بررسی کنید و با certbot renew --dry-run خطای واقعی را ببینید. شایع‌ترین علت‌ها: بسته بودن پورت ۸۰ در فایروال، ریدایرکت یا بلوکی در Nginx که مسیر acme-challenge را خراب می‌کند، CDN یا تغییر رکورد DNS که درخواست تأیید را به سرور دیگری می‌برد، و reload نشدن Nginx پس از تمدید. پس از رفع، تمدید را دستی اجرا و تاریخ گواهی را بررسی کنید.

چرا تمدید گواهی مهم است؟

گواهی‌های Let's Encrypt اعتبار کوتاهی دارند و طراحی آن‌ها بر این فرض است که تمدید کاملاً خودکار انجام شود. وقتی Certbot را نصب می‌کنید، یک تایمر systemd یا کرون‌جاب اضافه می‌شود که روزی دو بار بررسی می‌کند آیا گواهی‌ای به پایان اعتبارش نزدیک شده است یا نه، و در صورت نیاز آن را تمدید می‌کند.

مشکل اینجاست که این فرایند بی‌صدا شکست می‌خورد. تمدید ممکن است هفته‌ها پیاپی ناموفق باشد و تا روزی که گواهی منقضی شود و مرورگرها صفحهٔ هشدار نشان دهند، کسی متوجه نشود. خبر خوب این است که علت‌ها محدود و قابل‌تشخیص‌اند. اگر هنوز با مفهوم گواهی آشنا نیستید، مقالهٔ SSL و HTTPS چیست را بخوانید.

مسیر تأیید HTTP-01
یافتن آی‌پیHTTP روی ۸۰تحویل فایلLet's Encryptدرخواست فایل چالشDNSرکوردهای A و AAAAفایروالپورت ۸۰ باید باز باشدNginx/.well-known/acme-challengeیافتن آی‌پیHTTP روی ۸۰تحویل فایلLet's Encryptدرخواست فایل چالشDNSرکوردهای A و AAAAفایروالپورت ۸۰ باید باز باشدNginx/.well-known/acme-challenge
شکل ۱ هر حلقه از این زنجیره که خراب باشد، تمدید شکست می‌خورد؛ عیب‌یابی یعنی بررسی تک‌تک همین حلقه‌ها.

قدم اول: وضعیت گواهی و تایمر را ببینید

ابتدا ببینید چه گواهی‌هایی دارید و تا کی معتبرند:

sudo certbot certificates

خروجی برای هر گواهی نام دامنه‌ها، مسیر فایل‌ها و تاریخ انقضا را نشان می‌دهد. سپس مطمئن شوید تایمر تمدید فعال است:

systemctl list-timers | grep -i certbot

اگر Certbot را با snap نصب کرده باشید، تایمر با نام snap.certbot.renew.timer دیده می‌شود. اگر هیچ تایمری نیست، احتمالاً Certbot با روش غیرمعمولی نصب شده یا تایمر غیرفعال شده است. در این صورت یا آن را فعال کنید یا یک کرون‌جاب ساده اضافه کنید؛ نحوهٔ نوشتن کرون‌جاب در آموزش Cron آمده است.

قدم دوم: تمدید آزمایشی

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

sudo certbot renew --dry-run

اگر خروجی موفق بود، مشکل احتمالاً در تایمر یا reload نشدن وب‌سرور است. اگر خطا داد، پیام خطا تقریباً همیشه مستقیم به علت اشاره می‌کند. لاگ کامل هم در /var/log/letsencrypt/letsencrypt.log ذخیره می‌شود.

چالش HTTP-01 چطور کار می‌کند؟

برای فهم خطاها باید بدانید Let's Encrypt چطور کنترل دامنه را تأیید می‌کند. در روش رایج HTTP-01، Certbot یک فایل موقت با نام تصادفی در مسیر /.well-known/acme-challenge/ قرار می‌دهد. سپس سرورهای Let's Encrypt از چند نقطهٔ مختلف اینترنت، آدرس آن فایل را روی دامنهٔ شما از طریق HTTP و پورت ۸۰ درخواست می‌کنند. اگر محتوای درست را دریافت کنند، کنترل دامنه تأیید می‌شود.

هر چیزی که بین سرورهای Let's Encrypt و آن فایل قرار بگیرد، می‌تواند تمدید را خراب کند: فایروال، رکورد DNS اشتباه، CDN، ریدایرکت یا پیکربندی Nginx.

root@server: ~
$ sudo certbot renew --dry-runProcessing /etc/letsencrypt/renewal/example.com.confSimulating renewal of an existing certificate for example.comCertbot failed to authenticate some domains  Type:   connection  Detail: Timeout during connect (likely firewall problem)$ 
شکل ۲ dry-run خطای واقعی را بدون دست زدن به گواهی نشان می‌دهد؛ اینجا پورت ۸۰ در فایروال بسته است.

علت ۱: پورت ۸۰ بسته است

برخی مدیران پس از فعال کردن HTTPS، پورت ۸۰ را در فایروال می‌بندند با این تصور که دیگر لازم نیست. اما چالش HTTP-01 همیشه از پورت ۸۰ شروع می‌شود. خطای معمول در این حالت شامل عبارت‌هایی مثل Timeout during connect است. پورت ۸۰ را باز نگه دارید و در Nginx آن را به HTTPS ریدایرکت کنید:

sudo ufw allow 80/tcp

ریدایرکت مشکلی ایجاد نمی‌کند، چون Let's Encrypt ریدایرکت به HTTPS را دنبال می‌کند. اگر فایروال ابری یا Security Group هم در پنل سرور دارید، آن را هم بررسی کنید. جزئیات فایروال در آموزش UFW آمده است.

علت ۲: رکورد DNS به سرور دیگری اشاره می‌کند

اگر سایت را به سرور جدیدی منتقل کرده‌اید، یا رکورد AAAA برای IPv6 دارید که به آدرس قدیمی یا نادرست اشاره می‌کند، درخواست تأیید به سرور اشتباه می‌رسد. Let's Encrypt در صورت وجود رکورد AAAA معمولاً اول از IPv6 استفاده می‌کند؛ پس یک رکورد AAAA فراموش‌شده که به جایی اشاره می‌کند که Nginx شما روی آن گوش نمی‌دهد، یکی از علت‌های پنهان و رایج است. رکوردها را با dig بررسی کنید:

dig +short A example.com
dig +short AAAA example.com

هر دو باید به همین سرور اشاره کنند یا رکورد AAAA نادرست حذف شود. راهنمای رکوردها در مقالهٔ رکوردهای DNS آمده است.

علت ۳: CDN جلوی سایت است

اگر دامنه پشت CDN با حالت پروکسی فعال باشد، درخواست Let's Encrypt ابتدا به CDN می‌رسد. اگر CDN درخواست HTTP را خودش به HTTPS ریدایرکت کند یا قوانین امنیتی‌اش آن را مسدود کند، تأیید شکست می‌خورد. چند راه‌حل وجود دارد:

  • مسیر /.well-known/acme-challenge/ را در CDN از کش و قوانین امنیتی مستثنا کنید.
  • از روش تأیید DNS-01 استفاده کنید که به مسیر HTTP وابسته نیست؛ برای این کار به افزونهٔ DNS مخصوص ارائه‌دهندهٔ DNS خود نیاز دارید.
  • از گواهی مبدأ که خود CDN صادر می‌کند استفاده کنید؛ توضیح حالت‌های مختلف در مقالهٔ حالت‌های SSL در CDN آمده است.

علت ۴: پیکربندی Nginx مسیر چالش را خراب می‌کند

اگر Certbot را با افزونهٔ Nginx اجرا کرده باشید، خودش هنگام تمدید پیکربندی موقتی اضافه می‌کند. اما برخی تنظیمات دستی می‌توانند این مسیر را خراب کنند: یک location کلی که همه‌چیز را به برنامهٔ دیگری پروکسی می‌کند، قاعده‌ای که دسترسی به فایل‌های نقطه‌دار را ممنوع می‌کند، یا ریدایرکت به دامنهٔ دیگر. برای اطمینان، یک بلوک صریح برای این مسیر در بلوک سرور پورت ۸۰ تعریف کنید:

location ^~ /.well-known/acme-challenge/ {
    root /var/www/html;
    default_type text/plain;
}

اگر Certbot را با روش webroot اجرا می‌کنید، مسیر root باید با مسیری که به Certbot داده‌اید یکی باشد.

علت ۵: تمدید شده اما Nginx گواهی قدیمی را نشان می‌دهد

گاهی تمدید با موفقیت انجام شده اما مرورگر هنوز گواهی قدیمی را می‌بیند. علت این است که Nginx فایل‌های گواهی را فقط هنگام شروع یا reload می‌خواند. افزونهٔ Nginx در Certbot معمولاً این کار را خودکار انجام می‌دهد، اما در روش‌های webroot یا standalone باید یک hook اضافه کنید:

echo -e '#!/bin/sh\nsystemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

اسکریپت‌های این پوشه پس از هر تمدید موفق اجرا می‌شوند.

چک‌لیست رفع مشکل تمدید
  1. وضعیت گواهی و تایمر

    certbot certificates و systemctl list-timers

  2. تمدید آزمایشی

    certbot renew --dry-run و خواندن پیام خطا

  3. شبکه و DNS

    پورت ۸۰ باز، رکوردهای A و AAAA درست، رفتار CDN

  4. پیکربندی Nginx

    مسیر acme-challenge در دسترس و بدون ریدایرکت اشتباه

  5. reload و پایش

    deploy hook برای reload و مانیتور تاریخ انقضا

شکل ۳ بیشتر مشکلات تمدید در یکی از همین پنج مرحله پیدا می‌شوند.

علت ۶: محدودیت تعداد درخواست

اگر در مدت کوتاه چندین بار تلاش ناموفق برای گرفتن گواهی کرده باشید، Let's Encrypt موقتاً درخواست‌های بیشتر را رد می‌کند و پیام خطا به too many اشاره می‌کند. به همین دلیل همیشه ابتدا با --dry-run آزمایش کنید و فقط بعد از رفع مشکل، درخواست واقعی را اجرا کنید. اگر به این محدودیت خوردید، تنها راه صبر کردن تا پایان دورهٔ محدودیت است.

پس از رفع مشکل

وقتی dry-run موفق بود، تمدید واقعی را اجرا کنید و نتیجه را از بیرون بررسی کنید:

sudo certbot renew
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

برای اینکه دفعهٔ بعد غافلگیر نشوید، یک مانیتور خارجی که تاریخ انقضای گواهی را بررسی کند و پیش از انقضا هشدار دهد، بهترین بیمه است. ابزارهایی مثل Uptime Kuma این قابلیت را دارند. همچنین ایمیلی که هنگام ثبت در Certbot وارد کرده‌اید باید معتبر باشد؛ اگرچه نباید فقط به هشدارهای ایمیلی تکیه کنید.

با این بررسی‌ها، تمدید خودکار روی سرور مجازی شما دوباره بی‌دردسر کار می‌کند. مراحل نصب اولیه هم در آموزش Nginx و Let's Encrypt آمده است.

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

تمدید خودکار Certbot چند وقت یک بار اجرا می‌شود؟

تایمر Certbot معمولاً روزی دو بار اجرا می‌شود، اما فقط گواهی‌هایی را تمدید می‌کند که به پایان اعتبارشان نزدیک شده‌اند. اجرای مکرر آن هیچ ضرری ندارد.

آیا می‌توانم پورت ۸۰ را ببندم و فقط ۴۴۳ را باز بگذارم؟

با روش تأیید HTTP-01 خیر؛ این روش همیشه از پورت ۸۰ شروع می‌کند. پورت ۸۰ را باز بگذارید و به HTTPS ریدایرکت کنید، یا به روش DNS-01 بروید که به هیچ پورتی وابسته نیست.

dry-run موفق است ولی گواهی هنوز قدیمی است؛ چرا؟

احتمالاً تمدید واقعی انجام شده اما Nginx بارگذاری مجدد نشده است. یک deploy hook برای reload کردن Nginx اضافه کنید یا یک بار دستی systemctl reload nginx را اجرا کنید.

برای گواهی Wildcard چطور تمدید کنم؟

گواهی Wildcard فقط با روش DNS-01 صادر می‌شود. برای تمدید خودکار، باید از افزونهٔ DNS مخصوص ارائه‌دهندهٔ DNS خود با یک توکن API استفاده کنید؛ وگرنه هر بار باید رکورد TXT را دستی بسازید.

آماده‌اید؟ سرورتان را بسازید.

از ۱٬۲۰۰ تومان در ساعت · پرداخت ریالی

سرورتان را همین حالا بسازید.

ساعتی بپردازید. هر وقت خواستید حذف کنید.