6 phút đọc

Chứng chỉ HTTPS hết hạn lúc nửa đêm và cách để không bị bất ngờ

Let's Encrypt đã ngừng gửi email nhắc hết hạn từ tháng 6/2025, và thời hạn chứng chỉ đang rút dần về 45 ngày. Cách kiểm việc tự gia hạn, đặt cảnh báo, thêm bản ghi CAA và tránh chạm giới hạn cấp chứng chỉ.

Trình duyệt của khách hiện trang đỏ “Kết nối của bạn không phải là kết nối riêng tư”. App mobile báo lỗi mạng chung chung. Webhook của cổng thanh toán gửi tới mà thất bại liên tục. Tất cả bắt đầu lúc 0 giờ, khi chứng chỉ HTTPS hết hạn, và bạn biết chuyện lúc 7 giờ sáng qua tin nhắn của khách.

Nhiều người tin Let’s Encrypt sẽ gửi email nhắc trước. Điều đó không còn đúng: Let’s Encrypt đã ngừng dịch vụ email nhắc hết hạn từ ngày 4/6/2025, với lý do phần lớn người dùng đã tự động gia hạn, và việc giữ hàng triệu địa chỉ email gắn với lịch sử cấp chứng chỉ vừa tốn tiền vừa không tốt cho quyền riêng tư.

Linh vật robot ôm tấm thẻ chứng chỉ có ổ khóa, viền thẻ nứt đỏ, sau lưng là đồng hồ chỉ gần nửa đêm và vầng trăng

Vì sao “tự gia hạn” vẫn hỏng

Certbot, Caddy, Traefik, cert-manager đều tự gia hạn. Chứng chỉ vẫn hết hạn vì những lý do rất đời thường:

  • Cổng 80 bị chặn. Cách xác thực HTTP-01 cần Let’s Encrypt gọi được vào http://ten-mien/.well-known/acme-challenge/. Ai đó bật tường lửa chặn cổng 80 “cho an toàn”, hoặc thêm redirect sai, là lần gia hạn sau thất bại.
  • DNS đã chuyển. Tên miền trỏ sang Cloudflare hay máy chủ mới, nhưng máy chủ cũ vẫn là nơi chạy certbot.
  • Token API DNS hết hạn. Xác thực DNS-01 (bắt buộc cho chứng chỉ wildcard) dùng token của nhà cung cấp DNS. Token bị xoay hoặc thu hồi, việc gia hạn im lặng thất bại.
  • Gia hạn xong nhưng chưa nạp. Certbot ghi file mới nhưng Nginx vẫn giữ chứng chỉ cũ trong bộ nhớ cho tới khi reload.
  • Máy chủ phụ bị quên. Trang chính dùng Vercel tự lo, còn api., admin., mail server, máy chủ staging thì nằm trên VPS cài tay từ hai năm trước.

Thời hạn đang ngắn lại

Bây giờ chuyện này còn quan trọng hơn. Tháng 4/2025, CA/Browser Forum (nơi các hãng trình duyệt và tổ chức cấp chứng chỉ thống nhất luật chung) thông qua lịch rút thời hạn tối đa của chứng chỉ công khai từ 398 ngày xuống 47 ngày, bắt đầu từ tháng 3/2026 và xong vào tháng 3/2029.

Let’s Encrypt đi nhanh hơn. Theo thông báo của họ:

  • Từ 13/5/2026, profile tlsserver (tự chọn) cấp chứng chỉ 45 ngày.
  • Từ 10/2/2027, profile mặc định classic cấp chứng chỉ 64 ngày.
  • Từ 16/2/2028, profile mặc định cấp chứng chỉ 45 ngày.

Hệ quả thực tế: cron gia hạn cố định mỗi 60 ngày sẽ không kịp. Let’s Encrypt khuyên dùng ARI (ACME Renewal Information) để server của họ báo khi nào nên gia hạn, hoặc gia hạn khi chứng chỉ đã đi được khoảng hai phần ba thời hạn. Và gia hạn bằng tay thì không còn là lựa chọn.

Kiểm việc tự gia hạn

Với certbot:

sudo certbot certificates            # xem từng chứng chỉ, ngày hết hạn
sudo certbot renew --dry-run         # chạy thử gia hạn với môi trường staging
systemctl list-timers | grep certbot # có timer chạy định kỳ không

Thêm lệnh nạp lại web server sau mỗi lần gia hạn thành công:

# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
systemctl reload nginx

Caddy tự gia hạn và tự nạp, nhưng vẫn cần cổng 80 hoặc 443 mở ra ngoài. Traefik nhớ giữ file acme.json trên volume, nếu không mỗi lần container khởi động lại là xin chứng chỉ mới từ đầu.

Đặt cảnh báo, đừng chờ email

Cảnh báo nên chạy từ bên ngoài máy chủ, vì máy chủ hỏng thì nó cũng không tự báo được. Vài cách miễn phí:

  • Dịch vụ theo dõi uptime như Uptime Kuma (tự cài), Better Stack, UptimeRobot thường có tùy chọn báo khi chứng chỉ sắp hết hạn.

  • Let’s Encrypt gợi ý Red Sift Certificates Lite, miễn phí cho tối đa 250 chứng chỉ.

  • Tự viết một job chạy hằng ngày ở chỗ khác (GitHub Actions có lịch chạy, một máy khác):

    for d in app-cua-ban.com api.app-cua-ban.com admin.app-cua-ban.com; do
      end=$(echo | openssl s_client -servername "$d" -connect "$d:443" 2>/dev/null \
            | openssl x509 -noout -enddate | cut -d= -f2)
      days=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
      [ "$days" -lt 14 ] && echo "CANH BAO: $d con $days ngay"
    done

    (Trên macOS thay date -d bằng gdate -d sau khi cài coreutils.)

Đặt ngưỡng cảnh báo khoảng 14 ngày với chứng chỉ 90 ngày. Khi chuyển sang 45 ngày thì hạ xuống 7 đến 10 ngày, vì gia hạn bình thường đã diễn ra khi còn khoảng 15 ngày.

Bản ghi CAA: chỉ cho đúng nơi được cấp

CAA là bản ghi DNS nói “chỉ những tổ chức này được cấp chứng chỉ cho tên miền của tôi”. Tổ chức cấp chứng chỉ phải kiểm bản ghi này trước khi cấp. Nó không chặn được mọi thứ, nhưng làm khó hơn chuyện ai đó lợi dụng một tổ chức cấp chứng chỉ khác để xin chứng chỉ cho tên miền của bạn.

app-cua-ban.com.  CAA 0 issue "letsencrypt.org"
app-cua-ban.com.  CAA 0 issuewild ";"
app-cua-ban.com.  CAA 0 iodef "mailto:security@app-cua-ban.com"

Dòng thứ hai cấm cấp chứng chỉ wildcard, bỏ đi nếu bạn có dùng. Nếu đặt tên miền sau Cloudflare proxy, Cloudflare tự cấp chứng chỉ ở phía họ qua một số tổ chức khác, nên thêm cả các tổ chức đó theo tài liệu của Cloudflare, nếu không chứng chỉ phía Cloudflare sẽ không cấp được.

Kiểm bằng: dig CAA app-cua-ban.com +short.

Đừng chạm giới hạn của Let’s Encrypt

Lúc hoảng, người ta hay xóa thư mục chứng chỉ rồi xin lại liên tục. Let’s Encrypt có giới hạn:

  • 50 chứng chỉ mới cho mỗi tên miền đăng ký trong 7 ngày.
  • 5 chứng chỉ trùng hệt danh sách tên miền trong 7 ngày, không xin nới được.
  • 5 lần xác thực thất bại cho mỗi tên miền, mỗi tài khoản trong một giờ.

Chạm giới hạn thứ hai là phải chờ, trang của bạn đỏ suốt thời gian đó. Vì vậy khi thử cấu hình, luôn dùng môi trường staging (--dry-run hoặc --test-cert với certbot), và lưu thư mục /etc/letsencrypt hay acme.json vào chỗ bền.

Kiểm tra app của bạn trong vài phút

  1. Liệt kê mọi tên miền con đang chạy HTTPS: tên miền chính, api, admin, staging, mail. Một cách nhanh là tra lịch sử chứng chỉ của tên miền trên crt.sh.
  2. Xem ngày hết hạn của từng cái: echo | openssl s_client -servername ten-mien -connect ten-mien:443 2>/dev/null | openssl x509 -noout -enddate.
  3. Trên mỗi VPS tự quản: chạy certbot renew --dry-run và xem có lỗi không.
  4. Kiểm có deploy hook nạp lại Nginx hoặc Apache chưa.
  5. Đặt một cảnh báo chạy từ bên ngoài, báo trước ít nhất 7 ngày.
  6. Thêm bản ghi CAA, rồi dig CAA để chắc nó đã có.

Bạn đã bao giờ bị chứng chỉ hết hạn mà không biết chưa, và lý do là gì? Vào cộng đồng Vibe Code & Bảo Mật kể cho mọi người cùng tránh nhé.

Nguồn

Chia sẻ lên Facebook

Bình luận

Đang tải bình luận…