لو شهادة SSL خلصت يوم جمعة الساعة 3 الفجر، موقعك بيرجع للزوار برسالة NET::ERR_CERT_DATE_INVALID لحد الاتنين الصبح — و Google مش هيستنى، و SEO بيتأثر في 24 ساعة. الـ workflow اللي هنا بيمسك المشكلة دي قبل ما تحصل، بتكلفة صفر دولار.
المشكلة باختصار
كتير من الفرق بتعتمد على إن Let's Encrypt أو cert-manager "هيعمل renew لوحده". ده صحيح 95% من الوقت. المشكلة في الـ 5% الباقيين: DNS اتغير، rate limit من Let's Encrypt، webhook لـ cert-manager وقع، الشهادة اتجدّدت بس nginx مـعـملش reload. في حادثة Microsoft Teams يوم 3 فبراير 2020 شهادة انتهت وأوقفت الخدمة ساعات — وده فريق معاه موارد ضخمة. ركز: الأتمتة لوحدها مش كفاية، لازم verifier تاني بيتأكد إن الأتمتة شغالة فعلاً.
ليه auto-renew بتاع Let's Encrypt مش كفاية لوحده
Let's Encrypt بيبعت إيميل قبل انتهاء الشهادة بـ 20 و 10 و 1 يوم — بس على الإيميل المسجّل وقت إصدار الشهادة. لو المطوّر ده ساب الشركة من سنتين، الإيميل بيتوه. كمان ACME v2 بيفرض rate limit قدره 50 شهادة/أسبوع لكل registered domain، فلو الـ renew فشل مرتين بسبب DNS propagation، ممكن توصل للـ limit وتفضل أسبوع كامل بدون شهادة.
والأهم: بدءًا من 2026 الصناعة بتتجه لشهادات عمرها 47 يوم بدل 398 يوم. يعني كل شهر تقريبًا في renew. لو واحد منهم فشل ومفيش monitor خارجي، الموقع بيطير.
الحل: GitHub Actions يفحص كل 12 ساعة ويبعت Slack
المبدأ بسيط: GitHub Actions بيشغّل openssl s_client على كل دومين، يحسب كام يوم فاضل، ويبعت Slack webhook على ثلاث مراحل — 30 يوم، 15 يوم، 7 أيام (قاعدة 30-15-7 المتعارف عليها في الصناعة).
ليه GitHub Actions مش سيرفرك؟ لأن الـ monitor المفروض يكون خارج البنية اللي بيراقبها. لو السيرفر وقع، cron المحلي مش هيبعت تنبيه. GitHub Actions ده SLA تاني مختلف تمامًا.
الخطوات
- اعمل repo جديد (خاص أو عام مش فارق) اسمه
ssl-monitor. - ضيف Slack Incoming Webhook في Settings → Secrets → Actions باسم
SLACK_WEBHOOK. - احفظ الملف اللي تحت في
.github/workflows/ssl-check.yml. - حدّث مصفوفة
DOMAINSبدومينات موقعك. - شغّل
workflow_dispatchيدويًا مرة علشان تتأكد إن الـ Slack بيوصل.
الـ workflow كامل (انسخه وشغّله)
name: SSL & Domain Expiry Monitor
on:
schedule:
- cron: '0 */12 * * *' # كل 12 ساعة
workflow_dispatch:
jobs:
check-ssl:
runs-on: ubuntu-latest
steps:
- name: Install whois
run: sudo apt-get update && sudo apt-get install -y whois
- name: Check SSL and domain expiry
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
DOMAINS=("ahmedhaies.com" "lms.ahmedhaies.com" "api.ahmedhaies.com")
THRESHOLDS=(30 15 7)
notify() {
local msg="$1"
curl -sS -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\": \"$msg\"}"
}
for d in "${DOMAINS[@]}"; do
# 1) SSL expiry
EXP=$(echo | openssl s_client -servername "$d" -connect "$d:443" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
EXP_TS=$(date -d "$EXP" +%s)
NOW_TS=$(date +%s)
DAYS=$(( (EXP_TS - NOW_TS) / 86400 ))
echo "SSL $d -> $DAYS days"
for t in "${THRESHOLDS[@]}"; do
if [ "$DAYS" -eq "$t" ]; then
notify ":lock: SSL for *$d* expires in *$DAYS days* ($EXP)"
fi
done
# 2) Domain (whois) expiry
WEXP=$(whois "$d" | grep -iE 'Registry Expiry Date|Expiration Date' \
| head -1 | awk -F': ' '{print $2}')
if [ -n "$WEXP" ]; then
WEXP_TS=$(date -d "$WEXP" +%s)
WDAYS=$(( (WEXP_TS - NOW_TS) / 86400 ))
echo "Domain $d -> $WDAYS days"
if [ "$WDAYS" -lt 60 ]; then
notify ":earth_africa: Domain *$d* expires in *$WDAYS days*"
fi
fi
done
الـ workflow ده بيشتغل على الـ ubuntu-latest runner المجاني، كل تشغيلة بتاخد حوالي 15 ثانية. لو عندك 10 دومينات و بتتشغّل كل 12 ساعة، ده ≈ 600 ثانية شهريًا من الـ 2000 دقيقة المجانية. يعني فعلاً بصفر دولار.
مثال واقعي: فريق صغير معاه 12 دومين
فرضية: شركة SaaS فيها 12 دومين/subdomain، كلهم على Let's Encrypt مع cert-manager في Kubernetes. آخر 6 شهور حصل عندهم موقفين: مرة api-staging.x.com الشهادة فشلت renew بسبب DNS record اتشال بالغلط، ومرة status.x.com الشهادة اتجدّدت بس nginx-ingress مـعـمـلـش reload. المرتين الفريق اكتشف الموقف من Twitter.
بعد ما حطوا الـ workflow ده، بقى في تنبيه Slack يوم 30 قبل الانتهاء. الـ ROI الحقيقي مش في توفير الوقت — الـ ROI إنهم عرفوا يلحقوا قبل ما العميل يبلّغهم.
مفهوم سريع: ليه openssl s_client ده بالظبط
لو المفهوم جديد عليك، خليني أوضّح بمثال: تخيل إنك بتدق الباب على الموقع وتقول "ورّيني البطاقة الشخصية بتاعتك". الـ openssl s_client -connect host:443 بيفتح connection حقيقي TLS ويطلب الشهادة، و openssl x509 -enddate بيقرأ حقل "تاريخ الانتهاء" من الشهادة دي. ده أدق بكتير من إنك تسأل خدمة خارجية، لأنه بيحاكي نفس اللي المتصفح بيعمله. تقنيًا: الأمر ده بيعمل TLS handshake، بياخد الـ X.509 certificate chain، ويطبع الحقل notAfter.
trade-offs: GitHub Actions vs الأدوات التانية
- مقابل Uptime Kuma (self-hosted): Kuma أقوى في الـ UI ودعم أنواع فحوصات أكتر، بس محتاج سيرفر، RAM، ونسخ احتياطي. GitHub Actions: صفر صيانة، صفر سيرفر، بس UI محدود لـ logs الـ workflow.
- مقابل Better Uptime / BetterStack: بيدّيك dashboard احترافي وتنبيهات SMS، بس الـ free tier بيقف عند 10 monitors ومش بيغطي whois. بتكسب UI، بتخسر ~30$/شهر للـ tier الجاي.
- مقابل cert-manager alerts داخل k8s: cert-manager بيبعت Prometheus metrics، بس لو الـ Prometheus نفسه في نفس الكلاستر ووقع، مش هتعرف. الـ monitor الخارجي بيحل المشكلة دي.
- الـ trade-off الحقيقي هنا: بتكسب صفر تكلفة وصفر صيانة، بتخسر الـ rich alerting (SMS، call، PagerDuty integration). لو موقعك mission-critical ومحتاج SLA < 5 دقايق على الـ alert، كمّل معاه أداة مدفوعة كمان.
أخطاء شائعة لازم تتجنبها
- whois rate limit: registrars كتير بيحطّوا rate limit على whois. لو عندك > 20 دومين، اعمل الفحص ده مرة يوميًا بس مش كل 12 ساعة.
- SNI: لازم
-servernameموجود في أمر openssl، وإلا هتجيب default cert بتاع السيرفر مش اللي للـ domain المطلوب. - self-hosted runners: متشغّلش الـ workflow ده على runner جوا نفس البنية اللي بتراقبها. ده بيلغي قيمة "monitor خارجي".
- notify threshold == exactly N: الكود فوق بيبعت Slack لما الأيام تساوي 30 أو 15 أو 7. لو الـ workflow فات اليوم ده لأي سبب، مش هتعرف. بديل أأمن: استخدم
-leمع ملف state محفوظ في artifact علشان متكررش الـ alert.
متى لا تستخدم هذه الطريقة
الـ workflow ده مش مناسب في 3 حالات:
- عندك أكتر من 200 دومين — GitHub Actions minutes هتبدأ تكلّف، وبرضو بتوصل حدود whois. استخدم أداة متخصصة زي SSLMate Cert Spotter أو Prometheus blackbox exporter.
- محتاج SLA على الـ monitoring نفسه < 15 دقيقة — GitHub Actions فيه delays في الـ scheduled cron أحيانًا بتوصل 20–40 دقيقة وقت الزحمة.
- عندك Datadog أو New Relic أصلاً — فيهم SSL check جاهز، متعملش شغل مكرر.
الخطوة التالية
افتح repo جديد دلوقتي، الصق الـ workflow فوق، حط 3 دومينات بس في البداية، وشغّل workflow_dispatch يدوي. لو الـ Slack وصل في أول دقيقة، إنت خلاص مغطّى. لو ما وصلش، افحص قيمة SLACK_WEBHOOK في الـ secrets وجرّب تبعت curl من تيرمنالك مباشرة علشان تعزل السبب.