تبيع منصة SaaS متعددة المستأجرين Multi-Tenant لمؤسسة لها فريق أمن وفريق بريد وأنظمة خاصة بها، بنك أو مستشفى أو جامعة أو شركة كبيرة أو جهة حكومية، ويمر العقد بسلام، ثم يصلك من فريق الأمن عند العميل بريد فيه قائمة طلبات: نريد أن يعمل النظام على نطاقنا الفرعي xx.client.gov.sa بشهادتنا، ونريد أن تصل رسائل النظام من عنوان بريدنا، ونريد أن ترسل رموز التحقق عبر مزود الرسائل القصيرة SMS الذي نتعامل معه، ونريد أن تصل سجلات النظام إلى SIEM الخاص بنا، وفي آخر البريد جملة قصيرة: «أرسلوا لنا قائمة عناوين IP الخاصة بكم». وقد يأتي معها رد فريق البريد عندهم: «سوف نسمح لكم بالإرسال عبر بوابة البريد لدينا، فهي ترسل نيابة عنكم، ولن نغير سجلات SPF وDKIM وDMARC»، فتتساءل: كيف ينجح هذا دون أي تغيير؟ وما علاقة سجلات MX بالموضوع؟ وأي عنوان IP أعطيهم بالضبط؟ والعميل في أمثلة هذا المقال جهة حكومية نطاقها client.gov.sa، لأن الجهات الحكومية من أكثر العملاء تشدداً في هذه الطلبات، ولكن كل ما فيه ينطبق على أي مؤسسة تريد أن يعمل النظام على نطاقها وبشهادتها، وأن يصل بريده من عنوانها ورسائله القصيرة من حسابها، وأن تصل سجلاته إلى أنظمتها.
وقبل أن تبدأ، اقرأ مقال لماذا تفصل مدخل منصتك عن مخرجها؟، فهو يشرح لماذا تدخل الطلبات إلى المنصة من عنوان وتخرج منها من عنوان آخر، وهذا الفصل هو الأساس الذي تبنى عليه كل التكاملات التالية.
وهذا المقال من قسم دراسات الحالة، وهو يكمل مقال تعدد المستأجرين Multi-Tenancy الذي شرحنا فيه نماذج العزل وكيف يعرف الطلب مستأجره، ومقال التطبيق العملي مع Row-Level Security، ومثالنا فيه منصة تعمل عند أي مزود سحابي أو على سيرفرات تملكها، أمامها موزع حمل Load Balancer بعنوان عام للطلبات الداخلة، وخلفها بوابة NAT Gateway بعنوان عام تخرج منه كل الطلبات إلى الإنترنت، وبعض عملائها جهات تلتزم بالضوابط الأساسية للأمن السيبراني ECC. وعندما نحتاج إلى مثال من مزود معين نضعه في صندوق واضح، ونضع بجانبه ما يقابله عند المزودين الآخرين.
دليللماذا تفصل مدخل منصتك عن مخرجها؟ Ingress وEgressلماذا تضع سيرفراتك في شبكة خاصة خلف مدخل واحد ومخرج واحد بعنوانين ثابتين، وكم يكلفك ذلك، ومتى يكفيك سيرفر واحد.
دليلتعدد المستأجرين Multi-Tenancy في SaaS للمبتدئينكيف يخدم تطبيق SaaS واحد مئات العملاء دون أن يرى أحدهم بيانات غيره، وأي نموذج عزل يناسب تطبيقك.
دليلتطبيق SaaS متعدد المستأجرين عملياً مع Row-Level Securityنبني على PostgreSQL تطبيق SaaS يعزل كل مستأجر Tenant عن غيره بسياسات Row-Level Security، ونرى أين تتسرب البيانات رغم ذلك.
دليلشرح SPF وDKIM وDMARC للمبتدئين: كيف تثبت أن رسائلك منك فعلاًلماذا يستطيع أي شخص أن يرسل باسم نطاقك، وكيف يسد كل سجل من هذه السجلات الثلاثة ثغرة تركها الذي قبله.وسوف نناقش في هذا المقال ما يلي:
- الصورة الكاملة: أي تكامل يدخل إلى منصتك، وأيها يخرج منها، ومن يملك كل جزء.
- النطاق الفرعي للعميل وشهادته بأربع طرق، والسلسلة الكاملة للشهادة، وما يقبله موزع الحمل المدار عند مزودك، ومن يجدد الشهادة.
- البريد بثلاث طرق: من نطاقك، ومباشرة باسم نطاق العميل، وعبر بوابة بريد العميل أياً كان منتجها، ولماذا لا يحتاج الخيار الثالث أي تغيير في سجلاته، وما دور MX.
- الرسائل القصيرة SMS عبر مزود العميل، وتنظيمات هيئة الاتصالات والفضاء والتقنية في السعودية مثالاً، وتصميم محول Adapter لكل مزود.
- «قائمة عناوين IP»: العنوان الداخل والعنوان الخارج، ولماذا تحجزهما، وماذا يعني عنوان خروج مشترك بين المستأجرين.
- سجلات العميل في SIEM الخاص به: أربعة خيارات، والحقول، والتوقيت، والاحتفاظ، وضوابط الهيئة الوطنية للأمن السيبراني مثالاً.
- أسرار كل عميل، وقائمة تجهيز العميل، وجدول «ما نطلبه وما نعطيه»، وأسئلة فرق الأمن المعتادة عند العملاء.
وكل ما في هذا المقال من رسائل خطأ ومحادثات SMTP وترويسات رأيناه في التجربة العملية المرافقة، حيث بنينا المنصة والعميل ومزود بريد المستلم في Docker، وسوف نشير إليها عند كل خيار.
الصورة الكاملة: ما الذي يدخل وما الذي يخرج؟
قبل أن ندخل في التفاصيل، رتب الطلبات في ذهنك بحسب اتجاهها، لأن هذا الترتيب يجيب عن نصف الأسئلة. فالنطاق والشهادة يخصان الطلبات الداخلة: موظف العميل يكتب xx.client.gov.sa في المتصفح، فيصل الطلب إلى عنوان ال Load Balancer الخاص بك، وال Load Balancer يجب أن يقدم شهادة باسم هذا النطاق. أما البريد والرسائل القصيرة والسجلات فكلها طلبات تخرج من منصتك إلى أنظمة العميل أو إلى مزوديه، وتخرج كلها من عنوان ال NAT Gateway، وهذا هو العنوان الذي يضعه العميل في قوائم السماح Allowlists عنده، وقد رأينا في تجربة مقال المدخل والمخرج أن كل سيرفرات المنصة تظهر للطرف الآخر بهذا العنوان الواحد. والصورة التالية تبين التكاملات الأربعة واتجاه كل واحد منها:

والقاعدة التي سوف تتكرر في كل قسم هي أن منصتك مشتركة بين عملاء كثيرين، فكل تكامل تفتحه لعميل واحد يجب أن يبقى خاصاً به، فلا يرى سجلات غيره، ولا يرسل بريداً باسم غيره، ولا تثق بوابته بعميل آخر لأنه يشاركه العنوان نفسه. وقد شرحنا في نماذج العزل كيف تختار بين المنصة المشتركة Pool والنسخة المعزولة Silo، وهذا المقال يعمل على المنصة المشتركة، ويذكر متى يصبح الفصل الكامل هو الحل الأنسب.
وللتوضيح سوف نستخدم في الأمثلة عناوين التوثيق من RFC 5737: العنوان 203.0.113.10 لل Load Balancer، والعنوان 198.51.100.7 لل NAT Gateway، والنطاق saas.example لمنصتك، والنطاق client.gov.sa للعميل.
النطاق الفرعي للعميل وشهادته
الطلب الأول أن يعمل النظام على xx.client.gov.sa بدلاً من client.saas.example، وقد ذكرنا في قسم نطاق العميل الخاص Custom Domain أن العميل يضيف سجل CNAME يشير إلى منصتك، وأن التطبيق يحول اسم النطاق إلى رقم المستأجر. والسؤال الصعب هنا ليس سجل DNS وإنما الشهادة: من يصدرها؟ ومن يملك المفتاح الخاص Private Key؟ ومن يجددها قبل أن تنتهي؟ وهناك أربع طرق تبينها الصورة التالية، مرتبة من الأفضل إلى آخر خيار:

الطريقة (أ): CNAME إلينا والتحقق عبر HTTP
في هذه الطريقة يضيف العميل سجلاً واحداً، xx.client.gov.sa CNAME saas.example، وتطلب منصتك شهادة من جهة إصدار تعمل بمعيار ACME مثل Let's Encrypt، وتثبت الجهة أنك تتحكم في النطاق بالتحقق عبر HTTP (HTTP-01 Challenge) الموصوف في RFC 8555، أي أنها تطلب ملفاً صغيراً من http://xx.client.gov.sa/.well-known/acme-challenge/…، وبما أن الاسم يشير إليك فأنت من يجيب. وتذكر وثائق Let's Encrypt أن هذه الطريقة «تسمح لمزودي الاستضافة بإصدار شهادات للنطاقات التي تشير إليهم بسجل CNAME»، وأنها لا تصلح للشهادات الشاملة Wildcard.
والميزة أن المفتاح الخاص يولد عندك ولا يغادر سيرفرك، وأن التجديد آلي بالكامل، فلا يتصل بك أحد في منتصف الليل لأن الشهادة انتهت. وفي المنصة التي يضيف فيها العملاء نطاقاتهم بأنفسهم تستخدم ميزة On-Demand TLS في Caddy، أو ما يقابلها في Traefik، مع نقطة السؤال ask التي شرحناها في مقال تعدد المستأجرين، وتنص وثائق Caddy على أن رد هذه النقطة برمز من فئة 2xx يسمح بالإصدار، وأي رمز آخر يلغيه ويفشل مصافحة TLS. وفي التجربة العملية طلبنا نطاق العميل فوافقت نقطة السؤال وصدرت الشهادة في أول طلب، ثم طلبنا نطاقاً لا يعرفه النظام فرفضته:
ask domain=portal.client-gov.test tenant=client-gov -> 200
ask domain=random-site.test tenant=- -> 403ولكن لهذه الطريقة ثلاثة شروط يجب أن تسأل عنها العميل قبل أن تعده بها:
- الحركة يجب أن تصل إليك أولاً: لا تصدر الشهادة قبل أن يغير العميل سجل DNS، لذلك تبقى دقائق بين تغيير السجل وصدور الشهادة يرى فيها المستخدم خطأ شهادة، إلا إذا استخدمت الطريقة (ب).
- سجل CAA عند العميل: الجهات الكبيرة كثيراً ما تضع سجل CAA يحدد جهات الإصدار المسموح لها، وقد شرحناه في مقال الشهادات، فإذا لم تكن جهتك فيه فسوف ترفض الجهة الإصدار، لذلك افحصه بالأمر
dig CAA client.gov.saقبل البدء. - سياسة العميل نفسه: بعض الجهات لا تقبل شهادة من جهة إصدار عامة على نطاقها، وتريد شهادتها هي، وهنا تنتقل إلى الطريقة (ج).
الطريقة (ب): تفويض التحقق عبر DNS
في التحقق عبر DNS (DNS-01 Challenge) تطلب جهة الإصدار سجل TXT باسم _acme-challenge.xx.client.gov.sa كما في RFC 8555، ولن يعطيك العميل صلاحية الكتابة في DNS الخاص به، وهذا منطقي. والحل أن يضيف العميل سجل CNAME واحداً مرة واحدة يحول هذا الاسم إلى نطاق تملكه أنت، وتذكر وثائق Let's Encrypt صراحة أنك تستطيع استخدام سجلات CNAME أو NS لتفويض الإجابة عن _acme-challenge إلى نطاق آخر:
; عند العميل، مرة واحدة
_acme-challenge.xx.client.gov.sa. CNAME xx-client-gov.acme.saas.example.
; عندك، يكتبه عميل ACME عند كل تجديد ثم يحذفه
xx-client-gov.acme.saas.example. TXT "kH3...التوكن...Q"والميزة هنا أن الشهادة تصدر قبل أن ينقل العميل الحركة إليك، فيكون التحويل دون أي لحظة خطأ، وأنها الطريقة الوحيدة مع Let's Encrypt إذا أراد العميل شهادة شاملة مثل *.xx.client.gov.sa. والعيب أن منصتك تحتاج نطاقاً فرعياً مخصصاً للتحقق وصلاحية تعديل عليه من عميل ACME، فاجعل هذا النطاق منفصلاً عن نطاقك الرئيسي، وأعط مفتاح API الخاص به صلاحية هذا النطاق وحده، والسبب أن من يسرق هذا المفتاح يستطيع أن يصدر شهادات لكل عملائك الذين فوضوك.
الطريقة (ج): نرسل طلب CSR وجهة العميل تصدر
عندما تريد الجهة شهادة من جهة الإصدار التي تتعامل معها، أو من جهة إصدار داخلية خاصة بها، فالطريقة الصحيحة أن تولد أنت المفتاح الخاص على سيرفرك، ثم ترسل إليها طلب توقيع الشهادة Certificate Signing Request، واختصاراً CSR، وهو ملف فيه المفتاح العام والاسم فقط، وقد شرحناه خطوة خطوة في تجربة مقال الشهادات. فتوقعه جهة الإصدار وتعيد إليك الشهادة، والمفتاح الخاص لم يغادر سيرفرك أبداً:
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out xx.key
chmod 600 xx.key
openssl req -new -key xx.key -subj "/C=SA/O=Client Gov/CN=xx.client.gov.sa" \
-addext "subjectAltName=DNS:xx.client.gov.sa" -out xx.csr
openssl req -in xx.csr -noout -verify -subjectولاحظ أن الملف xx.csr ليس سراً، فتستطيع أن ترسله بالبريد دون مشكلة، أما الملف xx.key فلا يغادر سيرفرك، وفي الإنتاج يولد داخل خدمة الشهادات أو الخزنة مباشرة كما سيأتي. والعيب الوحيد أن التجديد يدوي: عندما تقترب الشهادة من نهايتها تولد طلباً جديداً، وترسله، وتنتظر رد جهة العميل، ثم تركب الشهادة، وكل خطوة فيها شخص قد يكون في إجازة، لذلك ابدأ قبل النهاية بشهر على الأقل.
وعندما تعود الشهادة من جهة العميل، تأكد أن المفتاح العام فيها هو مفتاحك قبل أن تركبها، فيكفي أن تقارن البصمة:
openssl pkey -in xx.key -pubout | sha256sum
openssl x509 -in xx.pem -noout -pubkey | sha256sumفإذا تطابق السطران فالشهادة لمفتاحك، وإذا اختلفا فقد وقعت الجهة طلباً آخر، وهذا يحدث أكثر مما تتوقع عندما يرسل أكثر من طلب في الأسبوع نفسه.
الطريقة (د): العميل يرسل الشهادة والمفتاح الخاص
وهي أن يولد العميل المفتاح والشهادة عنده ثم يرسلهما إليك، غالباً في ملف PFX بصيغة PKCS#12 أو في ملفي PEM. وهذه الطريقة منتشرة لأنها الأسهل على فريق العميل، ولكنها أسوأ الطرق من ناحية الأمان، والسبب أن المفتاح الخاص أصبح عند طرفين، ونسخ منه في بريد أو في محادثة أو في مجلد مشترك، وإذا تسرب من أي مكان منها فمن يملكه يستطيع أن ينتحل نطاق العميل. فإذا لم يكن هناك خيار آخر فاشترط ما يلي:
- لا يرسل المفتاح بالبريد أبداً: يرفعه العميل مباشرة إلى خزنة الأسرار عندك برابط مؤقت لمرة واحدة، أو يرسل ملف PFX مشفراً بكلمة مرور قوية، وكلمة المرور تصل في قناة أخرى مختلفة.
- ملف واحد للعميل، ومفتاح واحد للنطاق: لا تقبل مفتاحاً يستخدمه العميل على سيرفرات أخرى عنده، أو شهادة شاملة لكل نطاقه
*.client.gov.sa، والسبب أنك عندها تحمل مفتاحاً يفتح خدمات لا علاقة لك بها. - تاريخ انتهاء مسجل وتنبيه: سجل تاريخ الانتهاء في نظام المراقبة لحظة التركيب، كما سيأتي في قسم التجديد.
- خطة تدوير: اتفق مسبقاً على ما يحدث عند التجديد أو عند الشك في التسرب، ومن يلغي الشهادة القديمة.
السلسلة الكاملة: شهادة النطاق ثم الوسيطة
في الطريقتين (ج) و(د) تصلك الشهادة من العميل، وهنا يحدث أشهر خطأ في هذا الموضوع كله: يرسل العميل شهادة النطاق Leaf Certificate وحدها، أو يرسلها مع الشهادات الوسيطة في ملف منفصل لا يركبه أحد. وقد شرحنا في قسم لماذا يجب أن يرسل السيرفر الشهادة الوسيطة أن الشهادة الرئيسية Root موجودة عند الكلاينت، وأن السيرفر يجب أن يرسل شهادته ومعها الشهادات الوسيطة التي تربطها بالرئيسية. والترتيب الصحيح في الملف هو شهادة النطاق أولاً، ثم الشهادة الوسيطة التي وقعتها، ثم الوسيطة التي فوقها إن وجدت، أما الشهادة الرئيسية فلا داعي لإرسالها لأن الكلاينت يملكها في مخزن الثقة Trust Store، وإذا لم يكن يملكها فلن تنفعه نسخة يرسلها السيرفر.
والمشكلة أن السلسلة الناقصة لا تظهر لمن يختبر من المتصفح، فالموظف يفتح الموقع في Chrome فيعمل، ثم يأتي البلاغ من تطبيق الجوال ومن الشركة التي ترسل Webhook الدفع ومن نظام آخر يتصل بواجهة API، والصورة التالية تبين السبب:

فالمتصفحات تحفظ الشهادات الوسيطة التي رأتها من قبل، وFirefox ينزل قائمة الشهادات الوسيطة المعروفة مسبقاً، وChromium فيه مكون يجلب الشهادة الناقصة من الرابط CA Issuers في الامتداد Authority Information Access واختصاراً AIA. أما البرامج الأخرى فلا تفعل هذا في العادة، وتقول وثائق Android في قسم الشهادة الوسيطة الناقصة إن متصفحات سطح المكتب تحفظ الشهادات الوسيطة الموثوقة فلا تظهر الخطأ، ولكن تطبيق Android «ليس متسامحاً بالقدر نفسه»، وإن الحل أن تضبط السيرفر ليرسل الشهادة الوسيطة في السلسلة. وفي التجربة العملية ركبنا شهادة النطاق وحدها، فكان المخرج كما يلي:
$ curl https://secure.client-gov.test/
curl: (60) SSL certificate problem: unable to get local issuer certificate
$ python -c 'requests.get("https://secure.client-gov.test/")'
SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1032)')ثم فعلنا ما يفعله المتصفح يدوياً: قرأنا رابط AIA من الشهادة، ونزلنا الشهادة الوسيطة منه، فنجح الفحص، وهذا بالضبط سبب الجملة «يعمل في Chrome». والحل في كل الحالات أن تركب ملف السلسلة الكامل، وأن تفحصه قبل أن تضعه في الإنتاج بأمرين:
# هل تكمل الشهادات الوسيطة التي أرسلها العميل الطريق إلى الشهادة الرئيسية؟
openssl verify -CAfile root.pem -untrusted chain.pem leaf.pem
# بعد التركيب: ماذا يرسل السيرفر فعلاً؟
openssl s_client -connect xx.client.gov.sa:443 -servername xx.client.gov.sa -showcerts </dev/nullفي الأمر الأول ضع في root.pem الشهادة الرئيسية لجهة العميل، وهي موجودة مسبقاً في مخزن النظام إذا كانت جهة عامة، فتستطيع أن تحذف الخيار -CAfile، والخيار -untrusted معناه أن هذه الشهادات تستخدم لبناء الطريق فقط ولا يوثق بها بنفسها، والنتيجة الصحيحة leaf.pem: OK. وفي الأمر الثاني انظر إلى الأسطر التي تبدأ بالأرقام: السطر 0 s: لشهادة النطاق، والسطر 1 s: للوسيطة، وفي آخر المخرج يجب أن ترى Verify return code: 0 (ok). وإذا وجدت سطراً واحداً وVerify return code: 21 (unable to verify the first certificate) فالسيرفر يرسل شهادة النطاق وحدها. وتستطيع أن تفحص النطاق العام كذلك بأداة SSL Labs، فهي تقرأ السلسلة كما يرسلها السيرفر وتنبه إلى النقص فيها.
ولذلك أضف إلى قائمة تجهيز العميل قاعدة واضحة: نطلب من العميل شهادة النطاق والشهادات الوسيطة بصيغة PEM، ونفحص السلسلة بالأمرين أعلاه قبل التركيب، ولا نركب شهادة لم يمر فحصها.
جهة الإصدار ونوع الشهادة DV أو OV
قد يطلب العميل أن تكون الشهادة من جهة إصدار معينة، أو من نوع التحقق من المنظمة Organization Validation واختصاراً OV، بدلاً من التحقق من النطاق Domain Validation واختصاراً DV التي تصدرها Let's Encrypt. وقد شرحنا الفرق في قسم أنواع الشهادات: شهادة DV تثبت أن من طلبها يتحكم في النطاق فقط، وشهادة OV تضيف اسم المنظمة بعد أن تتحقق منه جهة الإصدار، وكلاهما يشفر الاتصال بالقوة نفسها. وشهادة OV باسم العميل لا تصدرها جهة الإصدار لك أنت، لأنك لست العميل، وإنما تصدرها له، لذلك فطلب OV يعني عملياً الطريقة (ج): أنت ترسل CSR، والعميل يطلب الشهادة باسمه من الجهة التي يتعامل معها ويعيدها إليك.
وإذا كانت جهة الإصدار داخلية خاصة بالعميل، أي ليست موجودة في مخازن الثقة العامة، فالموقع لن يعمل إلا على الأجهزة التي أضاف العميل إليها شهادته الرئيسية، وهذا مقبول لنظام داخلي يستخدمه موظفوه فقط، ولكنه يكسر أي جهة خارجية تتصل بالنظام، مثل مزود الدفع الذي يرسل Webhook، أو تطبيق جوال للمواطنين، فاسأل عن هذا قبل أن توافق.
الشهادات على موزع الحمل المدار عند مزودك
إذا كانت منصتك تنهي TLS في موزع حمل مدار Managed Load Balancer عند مزودك السحابي، فاسأل وثائق مزودك ثلاثة أسئلة قبل أن تعد العميل بأي شيء، والسبب أن الإجابات تختلف من مزود لآخر: أين أضع الشهادة الوسيطة؟ وهل يستطيع المستمع Listener الواحد أن يقدم لكل نطاق شهادته بحسب SNI؟ ومن يجدد الشهادة المستوردة Imported التي جاءت من العميل؟ والجدول التالي يبين الإجابات عند ثلاثة مزودين كما وجدناها في وثائقهم في أكتوبر 2026، وبعده مثال OCI بالتفصيل في صندوق مستقل:
| السؤال | OCI Load Balancer | AWS Application Load Balancer | Azure Application Gateway |
|---|---|---|---|
| أين تذهب الشهادة الوسيطة؟ | في ملف واحد مع شهادة النطاق، في حقل الشهادة نفسه | في حقل سلسلة الشهادات Certificate Chain بصيغة PEM عند الاستيراد إلى ACM | داخل ملف PFX، فالوثائق تطلب رفع السلسلة كاملة على المستمع |
| شهادة لكل نطاق على المستمع نفسه | حزمة شهادة واحدة لكل مستمع، ولم نجد ذكراً لاختيارها بحسب SNI | قائمة شهادات يختار منها بحسب SNI | مستمعات Multi-site تختار الشهادة بحسب SNI |
| من يجدد الشهادة المستوردة؟ | أنت، فخدمة OCI Certificates لا تجددها | أنت، فوثائق ACM تقول إنه لا يجدد الشهادات المستوردة | أنت ترفع الإصدار الجديد إلى Key Vault، وال Gateway يلتقطه وحده خلال أربع ساعات |
والنتيجة العملية واحدة عند كل المزودين:
- عدد قليل من نطاقات العملاء بشهاداتك أنت: شهادة متعددة النطاقات SAN، أو قائمة شهادات على موزع الحمل المدار، تكفي.
- نطاقات كثيرة، أو شهادات يرسلها كل عميل: اجعل TLS ينتهي في طبقة Reverse Proxy تملكها، مثل Caddy أو Traefik أو HAProxy، فهي تختار الشهادة بحسب SNI لكل نطاق، وتصدر شهادات ACME عند الطلب، وتضع أمامها موزع حمل من الطبقة الرابعة Network Load Balancer يمرر الحركة دون أن يفك التشفير ويحافظ على عنوان المصدر، فيصل عنوان الزائر الحقيقي إلى ال Proxy كما شرحنا في مقال تطبيقك خلف ال Reverse Proxy.
publicCertificate وcaCertificate وprivateKey وpassphrase وcertificateName كما في مرجع CreateCertificateDetails. وقد يتساءل البعض: أين أضع الشهادة الوسيطة، في حقل الشهادة أم في حقل CA؟ والإجابة في صفحة إدارة الشهادات، فهي تطلب أن تضع كل الشهادات التي تكون سلسلة واحدة في ملف واحد بالترتيب الصحيح قبل الرفع، ومثالها في الأمر التالي.cat ssl_certificate.crt IntermediateCA.crt >> certbundle.pemوأياً كان مزودك، افحص النتيجة بعد التركيب بالأمر openssl s_client أعلاه، لأن ما يرسله موزع الحمل فعلاً هو ما يراه العميل.
من يجدد الشهادة، ومن يراقبها؟
اكتب في العقد أو في وثيقة التجهيز من يملك التجديد في كل طريقة، لأن الشهادة المنتهية تبدو للمستخدم مثل نظام معطل، ولا يهمه من نسي. ففي الطريقتين (أ) و(ب) أنت تجدد آلياً، وعليك أن تراقب فشل التجديد، كأن يحذف العميل سجل CNAME أو يضيف سجل CAA يمنع جهتك. وفي الطريقتين (ج) و(د) التجديد مشترك: أنت تولد الطلب، والعميل يصدر، وأنت تركب. وفي كل الحالات راقب تاريخ الانتهاء من الخارج كما يراه الزائر، وليس من الملف على القرص، والسبب أن الملف الجديد قد يكون موجوداً ولم يركب بعد. والأمر التالي يفشل إذا بقي على الانتهاء أقل من 30 يوماً، وتستطيع أن تضعه في أي نظام مراقبة:
echo | openssl s_client -connect xx.client.gov.sa:443 -servername xx.client.gov.sa 2>/dev/null \
| openssl x509 -noout -enddate -checkend 2592000وفي التجربة العملية ركبنا شهادة منتهية لنرى ما يراه الزائر، فكانت رسالة curl certificate has expired، ورسالة OpenSSL Verify return code: 10 (certificate has expired). وتذكر أن مدة الشهادات العامة تقصر كل سنة كما شرحنا في قسم مدة الشهادات، فالتجديد اليدوي السنوي اليوم سوف يصبح أكثر تكراراً، وهذا سبب إضافي لتفضيل الطريقتين الآليتين.
ما الذي ننصح به؟
ابدأ دائماً بالطريقة (ب) إذا قبلها العميل، لأنها آلية والمفتاح عندك والتحويل دون انقطاع، ثم (أ) إذا لم يقبل تفويض DNS. وإذا اشترط العميل شهادة من جهته فالطريقة (ج)، مع وثيقة تجديد واضحة وتنبيه قبل 30 يوماً. ولا تقبل (د) إلا إذا كانت سياسة العميل لا تسمح بغيرها، وعندها بالشروط التي ذكرناها، ومع كل طريقة يدوية افحص السلسلة الكاملة قبل التركيب.
البريد: ثلاث طرق لإرسال رسائل العميل
رسائل النظام مثل تأكيد الطلب وإعادة تعيين كلمة المرور هي البريد التفاعلي Transactional Email الذي شرحناه في دليله الخاص، والسؤال هنا: من أي عنوان تصل رسائل عميلك؟ وقبل الخيارات تذكر فكرة واحدة من مقال SPF وDKIM وDMARC: سيرفر المستلم يقرأ النطاق في الحقل From الذي يراه القارئ، ثم يسأل: هل نجح SPF لنطاق يطابقه؟ أو هل نجح توقيع DKIM بنطاق d= يطابقه؟ فإذا نجح أحدهما متطابقاً Aligned نجح DMARC، وهذا هو تعريف التطابق Identifier Alignment في RFC 9989 الذي حل محل RFC 7489. ولاحظ أن DMARC لا يسأل من كتب الرسالة، ولا من أي منصة خرجت أولاً، وإنما يسأل من سلمها لسيرفر المستلم ومن وقعها. والصورة التالية تبين الطرق الثلاث وما يراه سيرفر المستلم في كل منها:

الطريقة 1: نرسل من نطاقنا نحن
ترسل كل رسائل العملاء من عنوان مثل [email protected]، مع اسم عرض يحمل اسم العميل مثل "Client Gov via SaaS" <[email protected]>. وهذا أسهل الخيارات بكثير: سجلات SPF وDKIM وDMARC لنطاقك أنت، وسمعة الإرسال لك، والعميل لا يفعل شيئاً. وفي التجربة العملية وصلت الرسالة بهذه الترويسة عند سيرفر المستلم:
Authentication-Results: mx.inbox.test;
dkim=pass header.d=saasco.test header.s=s2026 header.b=x5ELhpNw;
dmarc=pass (policy=reject) header.from=saasco.test;
spf=pass (mx.inbox.test: domain of [email protected] designates 10.77.77.25 as permitted sender) [email protected]ولاحظ أن عنوان الظرف [email protected] مختلف عن From، وقد شرحنا الفرق بين عنوان الظرف وعنوان الرؤوس في قسم الظرف والرؤوس، وأن DMARC نجح لأن النطاق الفرعي bounces.saasco.test يتطابق مع saasco.test في وضع التطابق المرن Relaxed. والعيب أن المواطن أو الموظف يرى نطاق منصتك وليس نطاق الجهة، وكثير من العملاء، والجهات الحكومية والبنوك خصوصاً، لا يقبلون هذا لرسائل تحمل اسمهم، كما أن سمعة نطاقك يشترك فيها كل عملائك، فعميل واحد يرسل رسائل مزعجة يضر الجميع، وهذا يدخل في مشكلة الجار المزعج.
الطريقة 2: نرسل مباشرة باسم نطاق العميل
هنا يكون From هو [email protected]، ولكن الرسالة تخرج من سيرفراتك أنت، لذلك يجب أن يخبر العميل العالم أن سيرفراتك مسموح لها بالإرسال باسمه، وذلك بسجلين يضيفهما في DNS الخاص به:
; 1) SPF: يضيف include لنطاقك إلى سجله الموجود (سجل SPF واحد فقط للنطاق)
client.gov.sa. TXT "v=spf1 ip4:<their servers> include:_spf.saas.example -all"
; 2) DKIM: يفوض اسم مفتاح واحد إليك بسجل CNAME، والمفتاح الخاص عندك
saas._domainkey.client.gov.sa. CNAME client-gov._dk.saas.example.وهذا الأسلوب نفسه تستخدمه الخدمات الكبيرة، فقد رأينا في دليل Amazon SES كيف تعطيك ثلاثة سجلات CNAME لمفاتيح DKIM. والسبب في CNAME بدلاً من أن يرسل لك العميل مفتاحاً أن المفتاح الخاص يولد عندك ولا يغادرك، وتستطيع أن تدوره متى شئت بتغيير السجل في نطاقك دون أن يتدخل العميل. وفي التجربة العملية أرسلنا قبل أن يضيف العميل السجلين، فرفض سيرفر المستلم الرسالة أثناء المحادثة:
C: mail from:<[email protected]> size=923
S: 250 2.1.0 Ok
...
S: 554 5.7.1 Rejected: DMARC policy of the From domain (no aligned SPF or DKIM pass)ثم أضاف العميل السجلين، فوصلت الرسالة وفيها:
Authentication-Results: mx.inbox.test;
dkim=pass header.d=client-gov.test header.s=saas header.b=AZ6fbDcY;
dmarc=pass (policy=reject) header.from=client-gov.test;
spf=pass (mx.inbox.test: domain of [email protected] designates 10.77.77.25 as permitted sender) [email protected]ولاحظ في الترويسة أعلاه أن header.d=client-gov.test مع أن التوقيع خرج من سيرفرنا، وهذا معنى التفويض بسجل CNAME. وانتبه إلى خطأ شائع رأيناه في التجربة كذلك: أن ترسل باسم نطاق العميل ولكن توقع بنطاقك أنت d=saasco.test وتستخدم عنوان ظرف من نطاقك، فينجح SPF وينجح DKIM ومع ذلك يفشل DMARC ويرفض المستلم الرسالة، والسبب أن كليهما لنطاق لا يطابق From، وهذا ما سجله سيرفر المستلم:
DMARC_POLICY_REJECT(2.00){client-gov.test : SPF not aligned (relaxed), DKIM not aligned (relaxed);reject;}
R_SPF_ALLOW(-0.20){+ip4:10.77.77.25;}
R_DKIM_ALLOW(-0.10){saasco.test:s=s2026;}ولهذه الطريقة ثمن على العميل: أي خطأ في رسائلك يضر سمعة نطاقه هو، وسجل SPF عنده يقترب من حد الاستعلامات العشرة مع كل include جديد، ولذلك يرفضها كثير من فرق البريد في المؤسسات الكبيرة، ويقترحون الطريقة الثالثة.
الطريقة 3: نرسل عبر بوابة بريد العميل
وهذه هي الجملة التي بدأنا بها: «سوف نسمح لكم بالإرسال عبر بوابة البريد لدينا، ولن نغير SPF وDKIM وDMARC». والفكرة أن منصتك لا تسلم الرسالة لسيرفر المستلم مباشرة، وإنما تسلمها لبوابة البريد عند العميل Mail Gateway، أياً كان منتجها: FortiMail أو Cisco Secure Email أو موصل Connector في Microsoft Exchange وMicrosoft 365 أو Proofpoint أو Postfix داخلي يديره فريقه، والبوابة هي التي تسلمها للمستلم. والفكرة واحدة في كل هذه المنتجات، وما يختلف هو أسماء الإعدادات ونصوص رسائل الخطأ. والصورة التالية تبين الطريق كاملاً:

والآن نجيب عن السؤال: لماذا لا يحتاج العميل إلى تغيير أي سجل؟ والإجابة أن سيرفر المستلم لا يرى منصتك أبداً، فهو يستلم الرسالة من بوابة العميل، وعنوان البوابة موجود أصلاً في سجل SPF الخاص بالعميل لأنها ترسل بريد موظفيه كل يوم، والبوابة توقع الرسالة بمفتاح DKIM الخاص بالعميل كما توقع بقية بريده، والحقل From من نطاق العميل، فيتطابق SPF وDKIM مع From وينجح DMARC. وهذا ما وصل في التجربة العملية بعد أن أعدنا سجل SPF للعميل إلى حاله الأصلي دون أي include لنا:
Received: from relay.client-gov.test (lab-tenant-gw.lab-tenant-net [10.77.77.30])
by mx.inbox.test (Postfix) with ESMTP id 4358A1D994A
Authentication-Results: mx.inbox.test;
dkim=pass header.d=client-gov.test header.s=fm2026 header.b=DIoMXRMD;
dmarc=pass (policy=reject) header.from=client-gov.test;
spf=pass (mx.inbox.test: domain of [email protected] designates 10.77.77.30 as permitted sender) [email protected]
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=client-gov.test;
s=fm2026; ...
Received: from mail.saasco.test (lab-tenant-app.lab-tenant-net [10.77.77.25])
by relay.client-gov.test (Postfix) with ESMTPSA id 1D0271D95F1في المخرج أعلاه لاحظ التالي:
- السطر
Receivedالأسفل يقول إن الرسالة دخلت البوابة من منصتنا10.77.77.25، والكلمةESMTPSAمعناها أنها دخلت بتشفير وتسجيل دخول، والسطر الأعلى يقول إن المستلم استلمها من البوابة10.77.77.30. - SPF فحص عنوان البوابة وليس عنوان منصتنا، لذلك نجح دون أي include.
- التوقيع
d=client-gov.test s=fm2026أضافته البوابة بمفتاح العميل، ومنصتنا لم توقع شيئاً.
وما علاقة سجلات MX؟
سجل MX يجيب عن سؤال واحد كما شرحنا في قسم سجل MX: إلى أي سيرفر أسلم رسالة موجهة إلى هذا النطاق؟ أي أنه طريق البريد الداخل إلى العميل، فعندما يكتب أي شخص إلى [email protected] يبحث سيرفره عن MX لنطاق العميل. أما في الإرسال عبر البوابة فمنصتك لا تبحث عن MX للعميل أصلاً، وإنما تتصل باسم البوابة الذي أعطاك إياه فريق العميل، مثل relay.client.gov.sa، والبوابة بعد ذلك تبحث عن MX لنطاق المستلم، مثل gmail.com. والسبب في الخلط أن جهاز البوابة نفسه كثيراً ما يكون هو MX العميل وبوابته الخارجة في الوقت نفسه، فيبدو أنه الشيء نفسه، ولكن الاتجاه مختلف: MX للداخل، وال Relay للخارج، ولذلك فسجلات MX لا علاقة لها بإرسالك، ولا تغيرها هذه الطريقة.
ما الذي يحتاجه فريق العميل منك؟
- عنوان الخروج: عنوان NAT Gateway العام الثابت، وهو العنوان الذي يضعه في قاعدة الوصول Access Control Rule في البوابة أو في الموصل، كما سيأتي في قسم قائمة العناوين.
- طريقة التحقق: حساب SMTP AUTH باسم مستخدم وكلمة مرور، أو موصل يعتمد على العنوان وحده، والأفضل الاثنان معاً، والسبب أن العنوان المشترك وحده لا يثبت أن الرسالة من منصتك لهذا العميل تحديداً.
- التشفير: STARTTLS مع TLS 1.2 أو أحدث، والبوابة ترفض أي أمر قبل التشفير، وأنت تتحقق من شهادتها، فإذا كانت من جهة إصدار داخلية فاطلب الشهادة الرئيسية لها.
- العناوين المسموحة: عنوان الظرف وعنوان
Fromاللذان سوف ترسل بهما، مثل[email protected]، لأن البوابة الجيدة تربط الحساب بعناوين محددة. - حد المعدل: كم رسالة في الدقيقة تقبل البوابة من منصتك، لأن رسائل النظام تأتي على شكل دفعات، والبوابة ترد برمز مؤقت عند تجاوز الحد.
- الرسائل المرتدة Bounces: الرسالة التي يرفضها المستلم بعد أن قبلتها البوابة ترتد إلى عنوان الظرف عند العميل وليس إليك، فاتفق على عنوان ظرف يصلك تقريره، أو على أن يرسل لك فريق العميل تقريراً، وإلا فلن تعرف أبداً أن رسائل إعادة كلمة المرور لا تصل.
ولنأخذ FortiMail مثالاً على منتج واحد، فتقول وثائق التحكم في الوصول فيه إن الجهاز يرفض افتراضياً البريد الموجه إلى نطاق غير محمي من عميل غير مسجل الدخول، وإن السماح بالإرسال إلى نطاقات خارجية يحتاج قاعدة وصول واحدة على الأقل، ومن إجراءاتها Relay الذي يقبل الرسالة، وReject الذي يرفضها بالرد 550 Relaying denied، وفي المنتجات الأخرى تجد القاعدة نفسها باسم آخر، مثل الموصل Connector في Microsoft 365 أو القيود smtpd_relay_restrictions في Postfix.
ما الذي يفشل، وكيف تقرأ الخطأ؟
كل هذه الأخطاء رأيناها في التجربة العملية على بوابة Postfix تقوم مقام بوابة بريد العميل، فهي ردود نموذجية Typical وليست نصوص منتج معين، والنص يختلف من منتج لآخر، ولكن الرمز ومعناه من RFC 5321 ثابتان، فالرموز التي تبدأ بالرقم 4 مؤقتة يعاد بعدها المحاولة، والتي تبدأ بالرقم 5 دائمة:
| ما حدث | رد نموذجي من البوابة (من التجربة) | السبب والحل |
|---|---|---|
| إرسال من عنوان غير مسموح، مثل عنوان NAT جديد بعد إعادة بناء الشبكة | 554 5.7.1 <…[10.77.77.66]>: Client host rejected: Access denied | العنوان ليس في قائمة السماح، حتى مع كلمة مرور صحيحة، والحل عنوان محجوز لا يتغير، وإبلاغ العميل قبل أي تغيير. |
| إرسال قبل STARTTLS | 530 5.7.0 Must issue a STARTTLS command first | البوابة تفرض التشفير، ففعل STARTTLS في إعدادات المرسل. |
| شهادة البوابة من جهة إصدار لا تثق بها منصتك | certificate verify failed: unable to get local issuer certificate | أضف الشهادة الرئيسية لجهة العميل إلى مخزن الثقة عندك، ولا تعطل التحقق. |
| دون تسجيل دخول | 554 5.7.1 <[email protected]>: Relay access denied | البوابة لا تمرر البريد إلى نطاقات خارجية لعميل لم يسجل الدخول. |
| عنوان ظرف لا يملكه الحساب | 553 5.7.1 <[email protected]>: Sender address rejected: not owned by user [email protected] | البوابة تربط الحساب بعناوين محددة، فاستخدم العنوان المتفق عليه. |
| تجاوز الحد | 450 4.7.1 Error: too much mail from 10.77.77.25 | رمز مؤقت، فطابور الإرسال عندك يعيد المحاولة لاحقاً، واتفق على الحد المناسب لدفعاتك. |
وهناك خطأ لا تراه في المحادثة أبداً، وهو الأخطر: أن تقبل البوابة الرسالة بالرد 250، ولكن الحقل From فيها من نطاقك أنت [email protected] وليس من نطاق العميل. فالبوابة لا توقع لنطاق لا تملكه، وعنوانها ليس في SPF نطاقك، فيفشل DMARC عند المستلم، وترتد الرسالة إلى عنوان الظرف عند العميل، وهذا ما سجلته البوابة في التجربة:
status=bounced (host mx.inbox.test[10.77.77.40] said: 554 5.7.1 Rejected: DMARC policy of the From domain (no aligned SPF or DKIM pass) (in reply to end of DATA command))فالقاعدة في هذه الطريقة: From دائماً من نطاق العميل الذي تمر رسائله عبر بوابته.
أي طريقة تختار؟
| 1 من نطاقنا | 2 مباشرة باسم العميل | 3 عبر بوابة العميل | |
|---|---|---|---|
| ما يراه المستلم | نطاق المنصة | نطاق العميل | نطاق العميل |
| ما يضيفه العميل | لا شيء | include في SPF وCNAME لمفتاح DKIM | قاعدة سماح لعنواننا وحساب SMTP |
| من يوقع DKIM | نحن بنطاقنا | نحن بنطاق العميل | بوابة العميل |
| السمعة | مشتركة بين كل العملاء | نطاق العميل يتأثر بأخطائنا | نطاق العميل، وبوابته ترى كل رسالة |
| الرسائل المرتدة | تصلنا | تصلنا إذا كان الظرف لنا | تصل العميل ما لم نتفق |
| نقطة الضعف | العميل لا يقبل النطاق | حد الاستعلامات العشرة وثقة العميل | عنوان الخروج المشترك وتوفر بوابته |
والترتيب الذي ننصح به: الطريقة 1 للعملاء الذين لا يطلبون شيئاً، والطريقة 3 للعملاء الذين يملكون بوابة بريد ويريدون أن يروا كل ما يخرج باسمهم، مثل البنوك والجهات الحكومية، وهي الأكثر قبولاً عند فرق الأمن لأن الرسائل تمر على فحوصهم، والطريقة 2 عندما لا تكون عند العميل بوابة مناسبة أو كان بريده على خدمة سحابية ترفض الإرسال عبرها. وفي الطرق الثلاث يبقى عندك طابور إرسال مع إعادة المحاولة، فالبوابة قد تتعطل والرمز 4xx ليس فشلاً نهائياً. وسوف تجد المحادثة الكاملة لكل طريقة سطراً سطراً في قسم البريد في التجربة العملية.
الرسائل القصيرة SMS عبر مزود العميل
رموز التحقق لمرة واحدة One-Time Password، واختصاراً OTP، تحتاج مزود رسائل، والعميل الكبير كثيراً ما يملك عقداً مع مزود مرخص واسم مرسل Sender Name باسمه، فيريد أن تصل رسائل النظام باسمه ومن حسابه. وقواعد هذه الخدمة تختلف من بلد لآخر، فاعرف ما ينطبق على بلد عميلك قبل التصميم، ولنأخذ السعودية مثالاً، فهذا ما وجدناه في تنظيمات تقديم خدمة الرسائل القصيرة من هيئة الاتصالات والفضاء والتقنية، الإصدار الثالث في فبراير 2024، وقد ذكرنا بعضها في مقال بوابة الدخول:
- تباع الرسائل للمشترك مباشرة من حامل الترخيص، ولا يسمح بإعادة بيعها، ولا بأكثر من وسيط واحد مرخص بين مقدم الخدمة والمشترك، وهذا يعني أن منصتك إذا اشترت رسائل من مزود ثم «باعتها» لعملائها ضمن الاشتراك فقد تقع في إعادة البيع، فاسأل المزود والمستشار القانوني قبل هذا النموذج، والأوضح أن يكون للعميل حسابه الخاص عند المزود.
- السيرفر داخل المملكة والبيانات مخزنة داخلها، والدخول إلى نظام الإرسال بتحقق ثنائي.
- للجهات الحكومية والبنوك قواعد خاصة في البند 5.17: محتوى الرسالة يعده موظف ويعتمده آخر، والرسائل الآلية مثل رموز التحقق يعتمد قالبها فقط، ويحفظ عنوان IP الذي استخدم للدخول إلى النظام.
أما تسجيل أسماء المرسلين Sender Names، فلم نجد في هذه الوثيقة أي نص عنه، لا من يسجلها ولا باسم من، لذلك لا نستطيع أن نقول لك ما الذي تطلبه الهيئة فيه، وما يحدث عملياً أن المزود المرخص هو من يطلب منك وثائق تسجيل الاسم، فاطلب من مزود العميل الإجراء مكتوباً، واجعل اسم المرسل مسجلاً للعميل في حسابه هو، لأن الاسم يمثل الجهة وليس منصتك.
وبعد ذلك يأتي التصميم: منصتك تخدم عملاء كثيرين، لكل منهم مزوده وطريقة الاتصال به، فمزود يريد JSON ومفتاح API في ترويسة، ومزود قديم يريد اسم مستخدم وكلمة مرور في نموذج Form، فلا تكتب شرطاً لكل عميل في الكود. والحل نمط المحول Adapter: الكود يستدعي دالة واحدة send_otp(tenant, phone, text)، وإعدادات المستأجر تحدد أي محول يستخدم وأي سر يقرأ من الخزنة، كما تبين الصورة التالية:

ولاحظ في الصورة أن مزود العميل يسمح بعنوان NAT الخاص بك فقط، وهذا شائع عند المزودين المحليين، ومعناه أن مفتاح API المسروق لا يكفي وحده، ورأينا في التجربة الرد 403 {'error': 'source IP 10.77.77.66 is not on the allowlist'} من جهاز يملك المفتاح الصحيح ولكن من عنوان آخر، والرد 422 عندما حاولنا الإرسال باسم مرسل غير مسجل لحساب العميل. وأما أمان الرمز نفسه، أي مدته وعدد المحاولات وحدود الإرسال وهجمات ضخ الرسائل SMS Pumping، فقد شرحناه في قسم أمان رمز التحقق، وكله ينطبق هنا، مع إضافة واحدة: اجعل الحدود لكل مستأجر، لأن ضخ الرسائل على عميل واحد يستهلك رصيده هو، والعميل سوف يسألك من أين جاءت الفاتورة.
«أرسلوا لنا قائمة عناوين IP الخاصة بكم»
هذه الجملة تعني شيئين مختلفين بحسب من يسأل، ولذلك أعطها دائماً في جدول بعمودين: الاتجاه والغرض. وقد شرحنا فكرة الترجمة نفسها في مقال NAT، ولماذا يختلف عنوان الدخول عن عنوان الخروج في مقال المدخل والمخرج، والصورة التالية تبين العنوانين وما يعنيه أن يكون عنوان الخروج مشتركاً:

- العنوان الداخل Inbound: عنوان ال Load Balancer العام. يحتاجه العميل إذا أراد سجل A بدلاً من CNAME، ويحتاجه فريق الشبكة عنده إذا كان جدار الحماية يمنع موظفيه من الخروج إلا لعناوين محددة.
- العنوان الخارج Outbound: عنوان NAT Gateway العام. هذا ما تحتاجه بوابة البريد ومزود الرسائل وال SIEM وأي جدار عند العميل يقبل اتصالات منك، وهو في أغلب الأحيان المقصود بالسؤال.
وكلا العنوانين يجب أن يكون محجوزاً Reserved Public IP، أي عنواناً تملكه في حسابك وتربطه بالمورد، وليس عنواناً مؤقتاً Ephemeral يذهب مع المورد إذا حذفته. والسبب أنك إذا حذفت ال NAT Gateway وأنشأته من جديد في يوم ما، بسكربت Terraform مثلاً، فسوف يأتي بعنوان جديد، وتتوقف رسائل كل عملائك في اللحظة نفسها، ورأينا في التجربة كيف ترد البوابة على عنوان لا تعرفه بالرد 554 5.7.1 … Client host rejected: Access denied. واسم العنوان المحجوز يختلف من مزود لآخر: Reserved Public IP في OCI، وElastic IP في AWS، وStandard Public IP أو Public IP Prefix في Azure، وقد جمعناها في جدول المقابلات.
ثم السؤال الذي يجب أن تقوله للعميل بصراحة: عنوان الخروج مشترك بين كل المستأجرين. فإذا وضع العميل العنوان 198.51.100.7 في قائمة السماح عند بوابة بريده، فهو يسمح لكل ما يخرج من منصتك، بما في ذلك كود يعمل لعميل آخر، وهذا لا يعني أن عميلاً آخر يستطيع الإرسال عبر بوابته، لأن تسجيل الدخول والعنوان المسموح ما زالا يمنعانه، ولكن قائمة السماح وحدها لم تعد تفرق بين عملائك. ولذلك فالقاعدة: العنوان المشترك يكمله دائماً تحقق خاص بالعميل، مثل حساب SMTP أو مفتاح API أو شهادة كلاينت Client Certificate، ولا يكون هو التحقق الوحيد.
وعندما يرفض العميل العنوان المشترك، أو تشترط خدمته عنواناً غير مشترك كما رأينا في Microsoft 365، فالحل عنوان خروج خاص به، والطريقة واحدة عند كل المزودين: تعمل خدمة هذا العميل التي تتصل بأنظمته في شبكة فرعية Subnet خاصة بها، وتوجه هذه الشبكة الفرعية حركتها إلى NAT Gateway آخر بعنوان آخر. ففي Azure تقول وثائق NAT Gateway إن الشبكات الفرعية في الشبكة نفسها تستطيع أن تستخدم بوابات NAT مختلفة، وإنك لا تستطيع ربط أكثر من بوابة بالشبكة الفرعية الواحدة، وفي AWS يحدد جدول التوجيه Route Table لكل شبكة فرعية بوابتها. وهذا يعني عمالاً Workers خاصين بإرسال بريد هذا العميل ورسائله، وهي خطوة نحو النموذج الهجين Bridge، ولها تكلفة تشغيل تضعها في سعر الباقة.
سجلات العميل في SIEM الخاص به
المؤسسات التي لها فريق أمن تراقب أنظمتها في SIEM، وقد شرحنا كيف يعمل من الداخل في مقال محرك كشف SIEM، ومنصتك أصبحت أحد أنظمتها، فيطلب فريق الأمن عندها سجلات الدخول والعمليات الإدارية في Splunk أو غيره. وهناك أربعة خيارات تبينها الصورة التالية:

الخيار (أ): وكيلهم على سيرفراتنا
يطلب الفريق أحياناً أن تثبت Splunk Universal Forwarder على سيرفراتك، وهو كما تصفه وثائق Splunk برنامج يرسل البيانات من جهازك إلى مستقبل، وهو غالباً فهرس في منصة Splunk. وفي منصة مشتركة هذا الطلب لا ينصح به إطلاقاً، والسبب أن الوكيل على السيرفر المشترك يقرأ ملفات السجلات وسجلات النظام لكل المستأجرين، فتصبح سجلات عميل آخر في SIEM هذا العميل، وهذا تسرب بيانات بكل معنى الكلمة. ثم إن الوكيل يعمل غالباً بصلاحيات واسعة، ويتحدث ويعدل إعداده من سيرفر يملكه طرف خارجي، فأي اختراق لنظام إدارة الوكلاء عند العميل يصبح طريقاً إلى سيرفراتك، وهذا من مخاطر سلسلة الإمداد Supply Chain. فإذا اشترط العميل وكيله فمكانه نسخة مخصصة له كما في الخيار (د).
الخيار (ب): نرسل سجلاته وحدها
وهو الخيار الذي ننصح به في المنصة المشتركة: سجلاتك المنظمة Structured Logs تحمل حقل المستأجر في كل سطر كما شرحنا في مقال السجلات، وأداة جمع مثل Vector أو Fluent Bit أو OpenTelemetry Collector تقرأها، وتمرر سطور هذا المستأجر وحده، وترسلها إلى العميل بالطريقة التي يقبلها SIEM عنده: HTTP Event Collector في Splunk، وهو واجهة HTTPS على المنفذ 8088 افتراضياً بمسار /services/collector/event وترويسة Authorization: Splunk <token>، أو syslog فوق TLS، أو OTLP لمن يستخدم OpenTelemetry. والقاعدة الأهم في هذا الخيار أن يكون الشرط مغلقاً عند الفشل Fail Closed: السطر الذي لا يحمل حقل المستأجر لا يخرج أبداً، والسبب أن الخطأ في الاتجاه الآخر يرسل سجلات لا تعرف أصحابها إلى طرف خارجي. وفي التجربة العملية كتبنا ستة أسطر، ثلاثة لعميلنا واثنان لعميل آخر وسطر دون مستأجر، فوصلت الثلاثة وحدها إلى مستقبل HEC الخاص بالعميل.
الخيار (ج): يسحبونها من API
تعرض منصتك واجهة لسجل التدقيق Audit Log مقيدة بالمستأجر، مثل GET /api/audit-events?cursor=… بتوكن خاص بالعميل، ويسحب SIEM عنده منها كل دقيقة. والميزة أن العميل يتحكم في التوقيت والإعادة، وأن منصتك لا تحتاج اتصالاً خارجاً إليه، والعيب أنك تبني واجهة وتحافظ عليها، وتحتاج مؤشراً Cursor لا يضيع أحداثاً، وحداً للمعدل، وحفظاً للأحداث مدة كافية حتى إذا توقف السحب يوماً لا تضيع السجلات.
الخيار (د): نسخة مخصصة
نسخة كاملة من المنصة للعميل وحده، فيستطيع أن يضع وكيله عليها لأنه لن يرى إلا بياناته، وهذا هو النموذج المعزول Silo من مقال تعدد المستأجرين، وتكلفته في التشغيل والتحديث أعلى بكثير، فاجعله في باقة خاصة وليس استثناء يدار باليد.
الحقول والتوقيت والاحتفاظ
وقبل أن يبدأ الإرسال اتفق مع العميل على قائمة الأحداث والحقول، والحد الأدنى الذي تطلبه فرق الأمن عادة:
- أحداث الدخول Authentication: النجاح والفشل، وطريقة التحقق الثنائي، وعنوان IP الحقيقي للمستخدم كما يصل خلف ال Proxy، والقفل بعد المحاولات.
- العمليات الإدارية Admin Actions: إنشاء المستخدمين وحذفهم، وتغيير الأدوار والصلاحيات، وتغيير إعدادات المستأجر، ومن فعلها ومتى.
- تصدير البيانات Data Export: من صدر، وماذا، وكم سجلاً، لأن هذا أول ما يسأل عنه في أي تحقيق.
- حقول ثابتة في كل سطر: الوقت بتوقيت UTC مع المنطقة الزمنية، ونوع الحدث، والمستخدم، والمستأجر، ومعرف الطلب Request ID، والنتيجة.
والتوقيت ليس تفصيلاً صغيراً، فقد ذكرنا في مقال الضوابط أن الضابط 2-3 يطلب مزامنة التوقيت مركزياً مع مصدر دقيق وموثوق، وأن السجل بتوقيت خاطئ دليل ضعيف في أي تحقيق، فاجعل كل سيرفراتك تتزامن، وأرسل وقت الحدث نفسه وليس وقت الإرسال، وقد ظهر هذا في التجربة العملية: الحقل timestamp كان وقت الإرسال حتى ضبطناه ليأخذ وقت الحدث. وأما الاحتفاظ، ففي الضوابط الأساسية يطلب المكون الفرعي 2-12 تفعيل السجلات للأصول الحساسة والحسابات ذات الصلاحيات الهامة وأحداث الدخول عن بعد، والاحتفاظ بها مدة لا تقل عن 12 شهراً في الضابط 2-12-3-5. وإذا كان بين عملائك جهات حكومية في السعودية، فاقرأ كذلك ضوابط الأمن السيبراني للحوسبة السحابية CCC-2:2024، وفيها على مقدم الخدمة في الضابط 2-11-P-1-3 تفعيل وحماية كل سجلات الأحداث للأنشطة التي ينفذها على مستوى المستأجر لدعم التحليل الجنائي، وفي 2-11-P-1-4 حماية السجلات من التعديل والإفصاح والإتلاف والوصول غير المصرح به، وفي 2-3-P-1-2 ضمان فصل البيانات والبيئات والأنظمة بين المستأجرين لمنع اختلاط البيانات. وانتبه إلى أن الاختصار CST في وثيقة CCC معناه المستأجر Cloud Service Tenant، وليس هيئة الاتصالات.
أسرار كل عميل في خزنة
بعد هذه التكاملات أصبح عندك لكل عميل مجموعة أسرار: كلمة مرور SMTP لبوابته، ومفتاح API لمزود رسائله، وتوكن HEC الخاص بال SIEM، والمفتاح الخاص لشهادته في الطريقتين (ج) و(د). ولا مكان لأي منها في جدول إعدادات المستأجر في قاعدة البيانات أو في ملف .env، والسبب أن نسخة احتياطية واحدة من قاعدة البيانات أو سطر سجل واحد يكشف أسرار كل العملاء معاً. والطريقة الصحيحة خزنة أسرار Secrets Manager مثل OpenBao، أو خدمة مزودك مثل OCI Vault وAWS Secrets Manager وAzure Key Vault، وقد شرحنا إدارة المفاتيح وتدويرها في قسم إدارة المفاتيح، والأدوات في مقال إدارة الهوية والوصول، وفيما يلي ما يخص تعدد المستأجرين تحديداً:
- مسار لكل مستأجر: مثل
tenants/client-gov/smtp، وجدول الإعدادات يحمل المسار فقط، كما في ملف إعدادات المستأجر في التجربة العملية. - صلاحية لكل خدمة: خدمة الإرسال تقرأ أسرار البريد فقط، وخدمة السجلات تقرأ توكن HEC فقط، ولا خدمة تقرأ أسرار كل شيء.
- سجل تدقيق: كل قراءة لسر مسجلة بالخدمة والمستأجر والوقت، فإذا سألك العميل من وصل إلى كلمة مرور بوابته فعندك الإجابة.
- تدوير متفق عليه: موعد لتغيير كل سر مع العميل، وإجراء مكتوب عند الشك في التسرب، وقد شرحنا في مقال فحص الأسرار المسربة لماذا يبدأ الرد على أي تسرب بالإلغاء والإصدار من جديد.
قائمة تجهيز العميل
اجمع كل ما سبق في وثيقة واحدة ترسلها للعميل مع العقد، وفيما يلي الجدول الذي نقترحه:
| التكامل | ما نطلبه من العميل | ما نعطيه للعميل |
|---|---|---|
| النطاق والشهادة | سجل CNAME للنطاق، وسجل CNAME لتفويض _acme-challenge، أو شهادة النطاق مع الشهادات الوسيطة بصيغة PEM رداً على طلب CSR، وفحص سجل CAA | اسم الهدف للسجل، وطلب CSR، وعنوان ال Load Balancer إن طلبه، وتاريخ انتهاء الشهادة ومن يجددها |
| البريد عبر بوابته | اسم البوابة ومنفذها، وحساب SMTP، والعناوين المسموحة، والحد، وشهادة جهة الإصدار إن كانت داخلية، وعنوان لتقارير الارتداد | عنوان NAT المحجوز، وتأكيد STARTTLS مع TLS 1.2 أو أحدث، وحجم الإرسال المتوقع في الساعة |
| البريد مباشرة باسمه | include في SPF، وCNAME لمفتاح DKIM، وسياسة DMARC الحالية | القيم الدقيقة للسجلين، وجدول تدوير المفتاح |
| SMS | حساب عند مزود مرخص، واسم المرسل المسجل له، ومفتاح API عبر الخزنة | عنوان NAT المحجوز لقائمة السماح عند المزود، وقوالب الرسائل للاعتماد |
| السجلات | نقطة HEC أو syslog مع شهادتها، والتوكن، وقائمة الأحداث المطلوبة | وصف الحقول، ومثال سطر، والتوقيت UTC، ومدة الاحتفاظ عندنا |
| الأسرار | قناة آمنة لتسليم الأسرار، وجهة اتصال للتدوير والحوادث | مسار الخزنة، ومن يصل إليه، وسجل التدقيق عند الطلب |
ثم اعمل بالترتيب التالي، وعلم كل خطوة عند اكتمالها:
- احجز عنوان ال Load Balancer وعنوان ال NAT قبل أول عميل، ولا تعط عنواناً مؤقتاً لأحد.
- افحص سجل CAA للعميل، واتفق على طريقة الشهادة ومن يجدد، وسجل تاريخ الانتهاء في المراقبة.
- عند استلام شهادة من العميل: افحص أن المفتاح يطابقها، وأن السلسلة كاملة بالأمر
openssl verify، ثم بعد التركيب بالأمرopenssl s_client -showcerts. - اتفق على طريقة البريد، واطلب رسالة اختبار إلى صندوق خارجي، واقرأ الترويسة
Authentication-Resultsفيه قبل الإطلاق. - جرب رمز التحقق إلى رقم حقيقي، وجرب الطلب نفسه من عنوان آخر لتتأكد أن قائمة السماح تعمل.
- أرسل حدثاً تجريبياً إلى SIEM العميل، واطلب منه أن يؤكد وصوله وصحة وقته، وتأكد أن سطراً لمستأجر آخر لا يصل.
- ضع كل سر في مساره في الخزنة، واحذف أي نسخة وصلت في بريد أو محادثة.
أسئلة تطرحها فرق الأمن عند العملاء
هل بياناتنا وسجلاتنا منفصلة عن بقية العملاء؟ أجب بما تطبقه فعلاً: العزل في قاعدة البيانات بسياسات الصفوف كما في مقال التطبيق العملي، والسجلات مفلترة بالمستأجر قبل أن تخرج، والأسرار في مسارات منفصلة، ولا تقل «معزولة تماماً» عن منصة مشتركة.
هل عنوان IP الذي نسمح له خاص بنا؟ لا، إلا إذا كانت عندكم باقة بعنوان خروج خاص، والعنوان المشترك لا يكفي وحده للتحقق، لذلك نستخدم معه حساباً أو مفتاحاً خاصاً بكم.
أين يحفظ المفتاح الخاص لشهادتنا، ومن يصل إليه؟ في الطرق (أ) و(ب) و(ج) المفتاح يولد عندنا ولا يغادر الخزنة أو ال Load Balancer، ومن يصل إليه مسجل في سجل التدقيق، وفي الطريقة (د) أنتم تملكون نسخة منه كذلك.
هل يتغير عنوانكم؟ العنوانان محجوزان ولا يتغيران مع إعادة بناء البنية، وأي تغيير مخطط نبلغكم به قبل موعده بمدة متفق عليها، ونضيف العنوان الجديد قبل أن نحذف القديم.
هل تستطيعون تثبيت وكيلنا على سيرفراتكم؟ ليس على المنصة المشتركة، لأن الوكيل سوف يرى بيانات غيركم، وما نقدمه إرسال سجلاتكم وحدها، أو واجهة تسحبون منها، أو نسخة مخصصة لكم.
كم مدة الاحتفاظ بالسجلات عندكم؟ اذكر الرقم الحقيقي، واذكر أن الضابط 2-12-3-5 في الضوابط الأساسية يطلب 12 شهراً على الأقل، وأن السجلات التي تصل إلى SIEM الخاص بهم يحتفظون بها هم بحسب سياستهم.
هل يخرج بريدنا عبر خدمة خارج بلدنا؟ في الطريقة 3 يخرج من بوابتكم أنتم، وفي الطريقتين 1 و2 اذكر مكان سيرفرات الإرسال بدقة، لأن هذا سؤال سوف يتكرر في أي مراجعة.
التجربة العملية
كل ما في هذا المقال بنيناه في Docker على جهاز واحد: Caddy مع On-Demand TLS ونقطة سؤال وجهة إصدار ACME محلية، وطلب CSR تصدره جهة إصدار «العميل»، والشهادة دون سلسلة ثم معها، والبريد بالطرق الثلاث مع سيرفر مستلم يفحص SPF وDKIM وDMARC، وبوابة بريد تقبل عنواننا فقط، ومزود رسائل بقائمة سماح، وسجلات مفلترة بالمستأجر إلى مستقبل HEC:
الخلاصة
- رتب طلبات العميل بحسب الاتجاه: النطاق والشهادة للداخل عبر عنوان ال Load Balancer، والبريد والرسائل والسجلات للخارج عبر عنوان ال NAT Gateway، وهذا الفصل بين مدخل المنصة ومخرجها هو أساس كل التكاملات.
- للشهادة أربع طرق، والأفضل أن يبقى المفتاح عندك ويتجدد آلياً بتفويض DNS أو بسجل CNAME، وإذا أرسل العميل الشهادة فاطلب شهادة النطاق والوسيطة بصيغة PEM وافحص السلسلة قبل التركيب، لأن المتصفح يخفي السلسلة الناقصة والتطبيقات لا تخفيها.
- موزع الحمل المدار يختلف من مزود لآخر في مكان الشهادة الوسيطة وعدد الشهادات على المستمع وتجديد الشهادة المستوردة، ففي OCI مثلاً حزمة شهادة واحدة لكل مستمع، لذلك تنهي TLS لنطاقات العملاء الكثيرة في Proxy تملكه خلف Network Load Balancer، والشهادة المستوردة لا يجددها أي مزود نيابة عنك.
- الإرسال عبر بوابة بريد العميل، أياً كان منتجها، لا يحتاج أي تغيير في SPF وDKIM وDMARC لأن البوابة موجودة في SPF وتوقع بمفتاحه، بشرط أن يكون
Fromمن نطاقه، وسجل MX طريق البريد الداخل إليه ولا علاقة له بإرسالك. - قواعد الرسائل القصيرة تختلف من بلد لآخر، وتنظيماتها في السعودية مثلاً تمنع إعادة بيع الرسائل وتشترط مزوداً مرخصاً، ولم نجد فيها نصاً عن تسجيل أسماء المرسلين، والمحول لكل مزود يبقي الكود نظيفاً.
- احجز العنوانين، وقل للعميل إن عنوان الخروج مشترك، وأكمله دائماً بتحقق خاص به، أو أعطه عنواناً خاصاً من شبكة فرعية بال NAT الخاص بها.
- لا وكيل للعميل على سيرفر مشترك، وإنما سجلاته وحدها بشرط مغلق عند الفشل، بتوقيت صحيح ومدة احتفاظ لا تقل عن 12 شهراً، وأسراره في خزنة بمسار وصلاحية وسجل تدقيق.
سجل التحديثات
- أكتوبر 2026: كتابة المقال، والتحقق من وثائق OCI وFortinet وMicrosoft وSplunk، ومن تنظيمات الرسائل القصيرة وضوابط CCC-2:2024، وكل المخرجات من التجربة العملية المرافقة.
- أكتوبر 2026: جعل المقال عاماً لأي مؤسسة تريد نطاقها وشهادتها وبريدها ورسائلها وسجلاتها، والجهات الحكومية مثال واحد، وتغيير العنوان والرابط، وإضافة رابط مقال المدخل والمخرج في البداية، ونقل تفاصيل OCI إلى صناديق أمثلة بجانب ما يقابلها في AWS وAzure بعد التحقق من وثائقهما، ووصف بوابة البريد دون منتج معين.
