أتمتة فحص شهادات TLS قبل ما الموقع يقع
هتطلع من المقال ده بسكربت Python يفحص شهادات TLS لكل الدومينات عندك، ويقولك بدري مين قرب ينتهي بدل ما تكتشف المشكلة من عميل.
مستوى القارئ: متوسط
المشكلة باختصار
شهادة TLS المنتهية بتكسر تجربة المستخدم فورًا. المتصفح هيعرض تحذير أمان، بعض الـ APIs هتفشل، والـ webhooks ممكن توقف بدون رسالة واضحة في تطبيقك.
الطريقة الشائعة الغلط إنك تعتمد على تذكير مزود الشهادة أو calendar event. الطريقة دي بتفشل لما يكون عندك 12 دومين، 30 subdomain، وفريقين بيجددوا من أماكن مختلفة. الأفضل تعمل فحص يومي مستقل.
المثال الواقعي
افترض إن عندك SaaS صغير بـ 50K زائر يوميًا، وعندك دومين رئيسي، API، لوحة تحكم، و3 webhooks للعملاء. فحص يدوي مرة كل شهر ممكن ياخد 75 دقيقة بين فتح المتصفح، قراءة التفاصيل، وتحديث sheet. أوتوميشن يومي بياخد تقريبًا 6 دقائق مراجعة في الشهر لأنك بتقرأ تقرير واحد عند وجود خطر.
الفكرة ببساطة
السكربت هيفتح اتصال TLS لكل host على port 443، يقرأ تاريخ انتهاء الشهادة، يحسب عدد الأيام المتبقية، ثم يطبع جدول بالدومينات القريبة من الانتهاء.
ركز هنا: إحنا مش بنعمل vulnerability scanner. إحنا بنعمل guardrail صغير يمنع عطل متكرر. المكسب إنك تعرف المشكلة قبل 21 يوم. الـ trade-off إن الفحص من جهاز واحد قد لا يرى اختلافات CDN أو edge certificate في كل المناطق.
السكربت التنفيذي
اعمل ملف domains.txt فيه دومين في كل سطر:
ahmedhaies.com
api.example.com
app.example.comبعدها شغل سكربت Python ده:
import socket
import ssl
from datetime import datetime, timezone
from email.message import EmailMessage
import smtplib
THRESHOLD_DAYS = 21
SMTP_HOST = "smtp.example.com"
SMTP_PORT = 587
SMTP_USER = "alerts@example.com"
SMTP_PASS = "change-me"
TO_EMAIL = "ops@example.com"
def days_until_expiry(host: str) -> int:
context = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=8) as sock:
with context.wrap_socket(sock, server_hostname=host) as tls:
cert = tls.getpeercert()
expires_at = datetime.strptime(cert["notAfter"], "%b %d %H:%M:%S %Y %Z")
expires_at = expires_at.replace(tzinfo=timezone.utc)
return (expires_at - datetime.now(timezone.utc)).days
def send_email(rows):
if not rows:
return
body = "\n".join(f"{host}: {days} days left" for host, days in rows)
msg = EmailMessage()
msg["Subject"] = "TLS certificates need attention"
msg["From"] = SMTP_USER
msg["To"] = TO_EMAIL
msg.set_content(body)
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as smtp:
smtp.starttls()
smtp.login(SMTP_USER, SMTP_PASS)
smtp.send_message(msg)
def main():
risky = []
for raw in open("domains.txt", encoding="utf-8"):
host = raw.strip()
if not host or host.startswith("#"):
continue
try:
days = days_until_expiry(host)
print(f"{host}: {days} days left")
if days <= THRESHOLD_DAYS:
risky.append((host, days))
except Exception as exc:
risky.append((host, f"check failed: {exc}"))
send_email(risky)
if __name__ == "__main__":
main()شغله محليًا:
python check_tls_expiry.pyلو عايزه يوميًا على Linux، استخدم cron:
0 8 * * * cd /opt/tls-checker && /usr/bin/python3 check_tls_expiry.pyليه 21 يوم مش 3 أيام؟
ثلاثة أيام رقم خطر. لو مزود DNS عنده تأخير، أو عندك approval داخلي، أو شهادة wildcard محتاجة تغيير في load balancer، هتدخل في ضغط غير ضروري. 21 يوم بتديك مساحة لتجديد الشهادة، اختبارها، ومراجعة أي service بيستخدم certificate pinning.
الافتراض إن عندك أقل من 100 host. لو عندك آلاف الدومينات، الأفضل تخزن النتائج في قاعدة بيانات وتعمل rate limiting للفحص بدل فتح اتصالات كثيرة مرة واحدة.
الـ trade-offs وما يجب الانتباه له
- بتكسب إنذار مبكر. بتخسر اعتماد بسيط على SMTP credentials لازم تتخزن بأمان.
- بتكسب بساطة. بتخسر رؤية متعددة المناطق؛ الفحص من سيرفر واحد لا يمثل كل CDN edge.
- بتكسب تكلفة شبه صفرية. بتخسر بعض خصائص أدوات monitoring الجاهزة مثل dashboards وincident routing.
أفضل طريقة للبداية: خليه يرسل بريد فقط عند وجود شهادة أقل من 21 يوم أو عند فشل الفحص. لو بعت تقرير يومي كامل، الناس هتتجاهله بعد أسبوعين.
متى لا تستخدم هذه الطريقة
لا تستخدمها كبديل لمنصة observability عندك لو عندك SLA رسمي أو مئات الخدمات. ولا تعتمد عليها وحدها لو شهاداتك تُدار من CDN عالمي فيه edge certificates مختلفة حسب المنطقة. في الحالات دي استخدم أداة monitoring متخصصة، وخلي السكربت ده check ثانوي بسيط.
المصادر
الخطوة التالية
اكتب أول 10 دومينات عندك في domains.txt وشغل السكربت يدويًا. لو طلع أي host أقل من 21 يوم، جدده قبل ما تضيف الأتمتة للـ cron.