الوحدة 14 — الأمان والمستخدمون والنسخ الاحتياطيّة والتشغيل اليوميّ
اكتمل محرّك Veille، وفهرسته جاهزة، والاستعلامات تجري كما ينبغي. تريد إيناس الآن ضمانتَين قبل عرض أيّ شيء أمام عميل. الأولى ألّا تعدّل واجهة كريم برمجيًّا مقالًا بالخطأ، ولا تحذف بأمر خاطئ فهرسًا كاملًا. والثانية ألّا يمحو عطبٌ عتاديٌّ أو نسخة سيّئة من الحقيبة ستّة أشهر من التّجميعات المحفوظة في Kibana واللّوحات الّتي بنتها Léa. تُرسي هذه الوحدة الأدوار ومفاتيح API وsnapshots والرّوتين اليوميّ للمراقبة الّتي تجعل عنقودًا يصمد على المدى الطّويل، سواء أمام حادث برمجيّ أو خطأ إنسانيّ.
ما تُؤمّنه الحقيبة أصلًا
افتح docker-compose.yml في الحقيبة واقرأ كتلة elasticsearch: الأمان مُفعَّل افتراضيًّا في المختبر، وهذا ما تُغفله كثير من الشّروحات القديمة. أربعة أسطر تكفي لفهم حال العنقود.
- xpack.security.enabled=true
- xpack.security.http.ssl.enabled=false
- xpack.security.transport.ssl.enabled=false
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
يعني ذلك ثلاثة أمور. كلّ طلب مُصادَق عليه: من دون بيانات اعتماد، يردّ Elasticsearch بـ 401 security_exception. كلمة سرّ المستخدم الفائق هي المكتوبة في ملفّ .env (veille2026 افتراضيًّا)، تُضبَط عند أوّل تشغيل وتُحفَر في المُجلَّد es-data. HTTP يمرّ بلا تشفير: التبادلات بين veille-kibana وveille-python وveille-es غير مشفّرة، وهذا مقبول في شبكة Docker معزولة، لكنّه غير مقبول بمجرّد كشف المنفذ 9200 إلى الخارج.
تكمّل خدمة veille-setup الصّورة: تنتظر أن يصبح Elasticsearch healthy، ثمّ تستدعي واجهة /_security/user/kibana_system/_password لمواءمة كلمة السرّ الدّاخليّة للمستخدم النّظاميّ مع KIBANA_PASSWORD. لست إذن مضطرًّا يومًا إلى نسخ token يدويًّا بين الخدمتَين.
تُرجّح الحقيبة سهولة القراءة: يعمل curl أو ./lab.sh es بلا شهادة. في الإنتاج، ينبغي التبديل إلى HTTPS. توفّر Elastic أداةً هي elasticsearch-certutil (داخل الصّورة: bin/elasticsearch-certutil ca ثمّ cert) تُنشئ سلطة التّصديق وشهادات العقدة، ثمّ يُفعَّل xpack.security.http.ssl.enabled=true وتُحمَّل الملفّات إلى المستوعب. تبقى هذه الدّورة على HTTP؛ خطوات التّبديل موثَّقة على elastic.co وتُطبَّق في ساعة واحدة.
إن شاء دور ومستخدم للقراءة فقط لكريم
لا تحتاج واجهة كريم إلّا إلى قراءة الفهرس news: لا كتابة أبدًا، ولا حذف، ولا مساس بالـmapping. نبني له دورًا مخصّصًا، ثمّ مستخدمًا يحمل هذا الدّور. افتح Kibana Dev Tools.
POST _security/role/veille_lecture
{
"cluster": ["monitor"],
"indices": [
{
"names": ["news"],
"privileges": ["read", "view_index_metadata"]
}
]
}
يُجيز read استعمال _search و_count و_msearch و_mget؛ ويُتيح view_index_metadata استرجاع الـmapping والإعدادات، وهذا لا غنى عنه لعميل يريد معرفة اسم الحقل الزّمنيّ. أمّا monitor على مستوى العنقود فيسمح بـ _cluster/health: مسبار حياة التّطبيق لن يحتاج إلى إعادة المصادقة بـ elastic.
POST _security/user/api_karim
{
"password": "karim2026",
"roles": ["veille_lecture"],
"full_name": "API Veille (Karim)",
"email": "karim@veille.example"
}
يجيب Elasticsearch بـ {"created": true}. تحقّق فورًا بندائَين من الطّرفيّة، داخل المستوعب veille-es الّذي يتضمّن curl:
docker exec veille-es curl -s -u api_karim:karim2026 \
http://localhost:9200/news/_count
الخرج المتوقّع:
{"count":200853,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0}}
القراءة تمرّ. اختبر الآن رفض الكتابة:
docker exec veille-es curl -s -u api_karim:karim2026 \
-H 'Content-Type: application/json' \
-X PUT http://localhost:9200/news/_doc/999999 \
-d '{"headline":"tentative","date":"2026-09-09"}'
الخرج المتوقّع:
{"error":{"root_cause":[{"type":"security_exception","reason":"action [indices:data/write/index] is unauthorized for user [api_karim] with effective roles [veille_lecture] on indices [news]"}],"status":403}}
403 security_exception: الدّور يفعل تمامًا ما طُلب منه. هذا هو الانعكاس الأوّل للاختبار كلّما أنشأت مستخدمًا — تحقّق ممّا يمرّ وممّا يجب أن يُرفَض.
يُعيد GET _security/role/veille_lecture وGET _security/user/api_karim التّعريف JSON كاملًا. ويسرد GET _security/_query/user جميع المستخدمين مع ترقيم الصّفحات. في Kibana، تُقدّم Management → Stack Management → Security → Users / Roles الشّيء نفسه بالفأرة.
مفاتيح API للمصادقة الآليّة
تعمل كلمة السرّ، لكنّ كلّ خدمة تستعملها تحتاج إلى معرفتها كاملة، وإبطالها نظيفًا يستلزم إعادة النّشر الشّاملة. تحلّ مفاتيح API المشكلة: تحصل كلّ خدمة على مفتاحها الخاصّ، بدور مرتبط وتاريخ انتهاء صلاحيّة. يُبطَل مفتاح واحد دون المساس بالباقي.
POST _security/api_key
{
"name": "api-karim-lecture",
"expiration": "90d",
"role_descriptors": {
"veille_lecture": {
"cluster": ["monitor"],
"indices": [
{
"names": ["news"],
"privileges": ["read", "view_index_metadata"]
}
]
}
}
}
يجيب Elasticsearch بكائن فيه ثلاثة حقول مهمّة: id وapi_key و**encoded**. يحوي حقل encoded سلفًا التّسلسل id:api_key مُرمَّزًا بـ Base64، جاهزًا للتّمرير في ترويسة HTTP.
{
"id" : "V0cU5oQBz2ExampleId",
"name" : "api-karim-lecture",
"expiration" : 1770000000000,
"api_key" : "abcDEF...",
"encoded" : "VjBjVTVvUUJ6MkV4YW1wbGVJZDphYmNERUY..."
}
يستدعي التّطبيق Elasticsearch بلصق قيمة encoded بعد ApiKey :
docker exec veille-es curl -s \
-H "Authorization: ApiKey VjBjVTVvUUJ6MkV4YW1wbGVJZDphYmNERUY..." \
http://localhost:9200/news/_search?size=1
أمران مفيدان يوميًّا. يسرد GET _security/api_key?owner=true المفاتيح الّتي أنشأتها أنت مع تاريخ انتهائها. ويُبطل DELETE _security/api_key بجسم {"ids": ["V0cU5oQBz2ExampleId"]} مفتاحًا مسرَّبًا فورًا.
Kibana: نظرة على الفضاءات (Spaces)
تُقدّم Kibana فضاءات — بمثابة مجلّدات معزولة للكائنات المحفوظة: لوحات القيادة، وData Views، ومرئيّات Lens. الوصول عبر Management → Stack Management → Spaces. يمكن لفريق Veille إنشاء فضاء «Léa» لا يضمّ إلّا لوحات العملاء، وفضاء «Sami» مخصّصًا لآفاق التّشغيل. يستطيع دور Kibana حصر مستخدم في فضاء أو أكثر بمستوى وصول مختلف (read على فضاء العملاء، all على الفضاء الدّاخليّ). فائدة الفضاءات مزدوجة: أمنيّة لأنّها تخفي على العميل تفاصيل التّشغيل، وتنظيميّة لأنّ كلّ فريق يجد لوحاته ولا يتيه بين عشرات الملفّات المشتركة. يخرج التّحكّم الدّقيق بالصّلاحيّات (RBAC) في Kibana عن نطاق هذه الوحدة؛ يكفي أن تعرف أنّه موجود ومجّانيّ في رخصة basic.