الامتثال والسياسات ككود

مفاهيم السياسة كرمز برمجي

18 دقيقة الدرس 3 من 27

مفاهيم السياسة كرمز برمجي

لكل مؤسسة قواعد: لا يعمل أي حاوية بصلاحيات الجذر (root)، يجب أن يمتلك كل دلو S3 تشفيرًا، يجب أن يُعلن كل Kubernetes Deployment عن حدود الموارد، يجب أن يحمل كل دور IAM وسمة توضيح الاستخدام. تقليديًا، كانت هذه القواعد تعيش في ملفات PDF والويكي والمعرفة الضمنية — تُطبَّق (أو لا تُطبَّق) خلال مراجعات ربع سنوية. السياسة كرمز برمجي (Policy as Code) هي ممارسة التعبير عن تلك القواعد برمز قابل للقراءة الآلية، محفوظ في نظام تحكم بالإصدارات، قابل للاختبار، ويعمل تلقائيًا في خط الإنتاج ذاته مع تطبيقك. والنتيجة: امتثال يعمل بسرعة CI/CD بدلًا من سرعة دورة المراجعة.

التحول الجوهري: تنقل السياسة كرمز التطبيق من قائمة تحقق يدوية بعد الحدث إلى بوابة آلية قبله. تُعبَّر القواعد بلغة رسمية، تُخزَّن في git، تُراجَع عبر pull request، وتُنفَّذ بواسطة بيئة تشغيل تستطيع الحظر، الكشف، أو المعالجة — بصورة تلقائية.

أوضاع التشغيل الثلاثة: المنع، الكشف، المعالجة

أي نظام لتطبيق السياسة يعمل في واحد أو أكثر من ثلاثة أوضاع. فهم المقايضات بينها ضروري قبل اختيار الأدوات أو كتابة قاعدة واحدة.

المنع (Admission Control)

يجلس محرك السياسة في المسار الحرج لعملية الكتابة — kubectl apply، أو تطبيق خطة Terraform، أو دمج pull request، أو تحديث CloudFormation stack — ويرفض الموارد غير المتوافقة قبل أن تنشأ. لا شيء يخالف السياسة يمكن إنشاؤه. هذا الوضع هو الأعلى قيمة: يلغي فئات كاملة من الحوادث بجعل المخالفة مستحيلة.

تكلفة المنع هي الكمون ونطاق التأثير. سياسة مُعدَّة بشكل خاطئ ترفض موارد صحيحة تتسبب في فشل النشر وتصعيد نداءات الطوارئ. في Google وMeta، تُنشر إضافات الاستقبال (admission webhooks) في وضع dry-run أولًا مع مقاييس لعدد عمليات الرفض المحتملة، قبل تفعيل التطبيق الفعلي. قاعدة منع واسعة وحيدة قد تحجب كل عمليات النشر عبر آلاف الفرق في cluster واحد.

الكشف (Continuous Audit)

يعمل محرك السياسة مقابل الحالة الراهنة لبيئتك — بصورة دورية أو عند الطلب — ويُبلِّغ عن المخالفات دون حجب أي شيء. تُعرض الموارد الموجودة التي تخالف السياسة على لوحة معلومات أو تُوجَّه إلى تنبيه. الكشف مناسب للبيئات القديمة التي لا يمكن فيها المنع بعد (استثناءات كثيرة لا تُعدَّد) وللسياسات ذات الطابع التطلعي لا المتطلب الصارم.

الكشف دون مسار معالجة هو تقارير مكلفة لا غير. كل نتيجة تبقى دون حل لأكثر من sprint واحد تُدهور نسبة الإشارة إلى الضوضاء في لوحة الامتثال حتى يكفّ المهندسون عن الاطلاع عليها. يجب أن يُغذّي الكشف دائمًا سير عمل محدد.

المعالجة (Auto-Correction)

عند اكتشاف مخالفة، يُعدِّل النظام الموردَ تلقائيًا ليعود إلى الامتثال — يُرفق وسمًا مفقودًا، يُفعِّل التشفير على دلو تخزين، يُعزل عقدة غير متوافقة. هذا الوضع الأقوى والأخطر. التعديل الآلي في الإنتاج يتطلب ثقة استثنائية بمنطق السياسة ومفتاح إيقاف: طريقة لتعطيل المعالجة حين تبدأ في خلق حلقة تغذية راجعة.

نمط فشل معروف: وحدة تحكم المعالجة التي تُعدِّل موردًا تُشغّل حلقة تسوية في وحدة تحكم التطبيق التي تُعيد التعديل، وهو ما يُشغّل وحدة التحكم المعالجة من جديد — حلقة محكمة تستنزف وحدة المعالجة وتغرق خادم Kubernetes API. فرض تراجع أسي ودائرة قاطعة على أي وحدة تحكم معالجة أمر لا غنى عنه.

Prevent-Detect-Remediate Compliance Loop Prevent → Detect → Remediate: The Compliance Enforcement Loop Resource Request / Change PREVENT Admission Gate Rejected ✗ Live State Deployed Resources DETECT Continuous Audit Violation Finding / Alert REMEDIATE Auto-Correction deny allow periodic scan found trigger patch state
حلقة تطبيق الامتثال: المنع يحجب عمليات الكتابة قبل إنشاء الموارد؛ الكشف يراجع الحالة الحية باستمرار؛ المعالجة تُصحِّح المخالفات تلقائيًا وتُغذِّي الحالة الحية.

ترميز القواعد: ما الذي تمثله السياسة فعلًا

السياسة هي دالة منطقية تعمل على مستند مدخل. المدخل هو المورد قيد التقييم — مانيفست Kubernetes، خطة Terraform، قالب CloudFormation، تعريف دور IAM، قائمة طبقات صورة الحاوية. المخرج هو قرار: السماح أو الرفض، مع سبب مقروء للإنسان. كل شيء آخر — بيئة التشغيل، اللغة، نقطة التكامل — هو بنية تحتية لتنفيذ تلك الدالة في اللحظة المناسبة.

القاعدة المُرمَّزة بشكل جيد تتكون من أربعة مكونات:

  1. النطاق: ما أنواع الموارد والفضاءات (namespaces) التي تنطبق عليها هذه القاعدة؟
  2. الشرط: ما السمة أو مجموعة السمات التي تُشغِّل مخالفة؟
  3. الإجراء: منع، تحذير، أم معالجة؟
  4. الرسالة: شرح مقروء للإنسان يُخبر المهندس بالضبط ما يجب تصحيحه ولماذا.
اكتب السياسات كقواعد رفض، لا كقوائم سماح. قائمة السماح تتطلب منك تعداد كل تهيئة صحيحة — أمر مستحيل على النطاق الواسع. قاعدة الرفض تلتقط الشيء المحدد المحظور. الوضع الافتراضي هو مسموح؛ السياسات تُضيِّق بصورة انتقائية. هذا يعكس طريقة عمل مجموعات قواعد جدران الحماية الناضجة.

مثال ملموس: المنع + الكشف + المعالجة لقاعدة واحدة

لنأخذ القاعدة: "لا يجوز لأي Pod في Kubernetes التشغيل بـprivileged: true." إليك كيف يُنفِّذ كل وضع هذه القاعدة:

# ── وضع المنع: سياسة OPA Rego (تُقيِّمها Gatekeeper webhook) ── # الملف: policy/privileged-container.rego package k8scontainerprivileged violation[{"msg": msg}] { c := input.review.object.spec.containers[_] c.securityContext.privileged == true msg := sprintf("Container %v must not run as privileged", [c.name]) } violation[{"msg": msg}] { c := input.review.object.spec.initContainers[_] c.securityContext.privileged == true msg := sprintf("Init container %v must not run as privileged", [c.name]) }
# ── وضع الكشف: Kyverno ClusterPolicy في وضع المراجعة ── # الملف: policy/disallow-privileged-audit.yaml apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: disallow-privileged-containers annotations: policies.kyverno.io/description: Detect Pods with privileged containers. spec: validationFailureAction: Audit # للتقرير فقط؛ غيّر إلى Enforce للمنع rules: - name: check-privileged match: resources: kinds: [Pod] validate: message: "Privileged containers are not allowed." pattern: spec: containers: - securityContext: privileged: "false | null"
# ── وضع المعالجة: سياسة Kyverno للتعديل (تضبط privileged=false إن لم يكن موجودًا) ── # الملف: policy/default-nonprivileged-mutate.yaml apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: set-nonprivileged-default spec: rules: - name: set-privileged-false match: resources: kinds: [Pod] mutate: patchStrategicMerge: spec: containers: - (name): "*" securityContext: +(privileged): false # يُضبَط فقط إن كان المفتاح غائبًا # تحذير: التعديل لا يُغني عن التوعية. # إصلاح تهيئة خاطئة بصمت يُخفي السبب الجذري. # استخدم التعديل للإعدادات الافتراضية التي تمتلكها؛ استخدم المنع لحدود الأمان.

دورة حياة السياسة: الانتقال إلى اليسار عبر SDLC

أكثر تطبيقات السياسة فاعليةً تعمل في مراحل متعددة في آنٍ واحد، تلتقط المخالفات مبكرًا قدر الإمكان. كلما اكتُشفت المخالفة في مرحلة أبعد إلى اليمين (الإنتاج مقارنةً بمحرر الكود المحلي)، زادت تكلفة الإصلاح بمراتب.

  • IDE / pre-commit: تعمل conftest وkube-linter وtfsec محليًا وعند كل commit. تغذية راجعة فورية، لا بنية تحتية مطلوبة. تلتقط غالبية الأخطاء الشائعة قبل دخولها PR.
  • خط أنابيب CI: تعمل الأدوات ذاتها كفحوصات إلزامية في GitHub Actions وGitLab CI وJenkins. يفشل خط الأنابيب ولا يمكن دمج PR. هذه البوابة التطبيقية الأساسية لـ IaC.
  • Admission webhook (وقت التشغيل): تحجب Gatekeeper وKyverno استدعاءات kubectl apply غير المتوافقة حتى لو تجاوز CI (تشغيل يدوي لـkubectl من SRE، تثبيت Helm chart مباشرة). دفاع متعدد الطبقات.
  • المراجعة المستمرة: حلقة تسوية تقارن الحالة الحية لكل مورد بكل سياسة وتُرسل النتائج إلى SIEM أو لوحة امتثال. تلتقط الانجراف في التهيئة والتغييرات اليدوية والمخالفات السابقة لتاريخ السياسة.
نمط مضاد "المراجعة إلى الأبد": تنشر منظمات كثيرة السياسات في وضع Audit تجنبًا للإزعاج، ثم لا ترقيها أبدًا إلى Enforce. تتراكم نتائج المراجعة في لوحة لا أحد مسؤول عنها. عامل كل سياسة في وضع المراجعة على أن لها موعد ترقية — 30 أو 60 يومًا — بعده يجب إما تطبيقها أو تأجيلها رسميًا مع استثناء موثق. الدَّيْن في السياسات يتراكم تمامًا كالدَّيْن التقني.

السياسة كرمز تتطلب اختبار السياسة

سياسة لم تُختبر قط ليست سوى توثيق منظَّم. يجب أن يرافق كل ملف سياسة مجموعة اختبارات تغطي مسار السماح (موارد صحيحة يجب أن تجتاز) ومسار الرفض (موارد غير صحيحة يجب رفضها). تشحن OPA الأمر opa test؛ وتملك Kyverno الأمر kyverno test. شغِّل هذه الاختبارات في CI ضمن خط أنابيب مستودع السياسات — كسر اختبار سياسة يعني فشل البناء تمامًا ككسر اختبار وحدة في التطبيق.

# ── اختبار وحدة OPA لسياسة الحاويات المميزة ── # الملف: policy/privileged-container_test.rego package k8scontainerprivileged # يجب أن تنجح (حاوية غير مميزة — لا مخالفة متوقعة) test_nonprivileged_allowed { count(violation) == 0 with input as { "review": {"object": {"spec": {"containers": [ {"name": "app", "securityContext": {"privileged": false}} ]}}} } } # يجب أن تُرفض (حاوية مميزة — يجب رفع مخالفة) test_privileged_denied { count(violation) == 1 with input as { "review": {"object": {"spec": {"containers": [ {"name": "app", "securityContext": {"privileged": true}} ]}}} } } # للتشغيل: opa test ./policy/ -v