هذا المقال للمستوى المتوسط — يفترض إنك مرتاح مع kubectl والـ YAML الأساسي وعندك cluster Kubernetes شغّال فعلاً، وعارف الفرق بين namespace و pod و service.
Network Policies في Kubernetes: ابنِ Zero-Trust بين microservices في 30 سطر YAML
لو الـ cluster عندك فيه 18 microservice وبتفترض إن الـ Ingress أو الـ firewall الخارجي كفاية يحميك من أي اختراق، إنت بتتجاهل أكبر فجوة أمنية في Kubernetes الافتراضي. أي pod جوّا الـ cluster يقدر يفتح اتصال TCP/UDP مع أي pod تاني على أي port. attacker بيخترق container واحد بيوصل لقواعد البيانات وكل الـ services الداخلية في 3 دقائق، بدون أي مقاومة. الحل اسمه Network Policies، و30 سطر YAML بيقفلوا اللعبة دي.
المشكلة باختصار
الـ networking model في Kubernetes الافتراضي مبني على الـ flat network. كل pod ياخد IP خاص بيه داخل الـ cluster، وبيقدر يفتح اتصال مع أي pod تاني في أي namespace بدون قيد. ده يعني إن pod الـ frontend يقدر يكلم قاعدة بيانات الـ HR مباشرة، حتى لو ما لهوش علاقة شغل بيها.
الـ Ingress (زي NGINX Ingress أو ALB) بيحميك من خارج الـ cluster، بس داخله مفيش حماية. لو attacker لقى ثغرة في endpoint عام (SSRF، RCE، أو حتى dependency vulnerable)، الـ blast radius بيمتد لكل service. حسب تقرير Verizon DBIR 2024، 32% من اختراقات الإنتاج بتبدأ من container واحد ثم بتنتشر lateral داخل الشبكة الداخلية.
مثال للمبتدئ — البوّاب اللي بدون قائمة زيارات
تخيّل عمارة فيها 18 شقة، وبوّاب موجود ساعة دخول العمارة. البوّاب بيتأكد إن اللي داخل من برّا له اسم في القائمة. لكن جوّا العمارة، أي ساكن يقدر يطرق على باب أي شقة تانية وقت ما يحب، ويدخل لو الباب مفتوح. لو واحد دخل العمارة بطريقة ملتوية مرة واحدة (مثلاً قال إنه عامل صيانة)، بقى بحرية تامة يفتح أي شقة. المشكلة مش إن البوّاب فاشل، المشكلة إن مفيش قائمة زيارات داخلية.
Network Policies هي قائمة الزيارات الداخلية. البوّاب بيتحوّل لمسؤول عن تصاريح الزيارة بين الشقق نفسها. شقة 12 (اللي هي الـ orders service) مسموحلها بس تكلم شقة 4 (الـ database) و شقة 7 (الـ payment service)، وأي محاولة طرق على باب تاني بترفض على مستوى البوّاب، قبل ما توصل للباب أصلاً.
التعريف العلمي الدقيق
Network Policy في Kubernetes هو object من نوع networking.k8s.io/v1 بيعرّف قواعد الـ ingress (الترافيك الداخل لمجموعة pods) والـ egress (الترافيك الخارج منها). الـ podSelector بيحدد الـ pods اللي القاعدة بتنطبق عليهم، والـ rules بتحدد المصادر/الوجهات المسموحة عبر podSelector أو namespaceSelector أو ipBlock.
بالظبط، الـ Network Policies نفسها مجرد definitions في الـ API. اللي بينفّذها فعلاً على الـ kernel هو الـ CNI (Container Network Interface) plugin. لو الـ cluster بتاعك شغّال على CNI ما بيدعمش Network Policies (زي Flannel الافتراضي)، الـ YAML اللي بتطبّقه بيتقبل بدون خطأ، لكن مش بيعمل أي حاجة فعلية. الـ CNI plugins اللي بتدعم Network Policies فعلاً: Calico، Cilium، Weave Net، و Antrea.
مثال تنفيذي — Default Deny ثم Allow الصريح
الاستراتيجية الصحيحة في الإنتاج اسمها default deny, explicit allow. أول حاجة بترفض كل الترافيك في namespace معين، وبعدين تفتح بس الاتصالات اللي محتاجها فعلاً. الترتيب ده مش اختياري — العكس (allow بتاع كل حاجة ثم block المحدد) بيسيب فجوات بتنسى تقفلها.
الخطوة الأولى: ارفض كل حاجة في namespace production:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
الـ podSelector: {} الفاضي بيختار كل الـ pods في الـ namespace. بعد تطبيق ده، حتى الـ DNS مش هيشتغل لأن الـ pods مش هتقدر توصل لـ kube-dns. فلازم نفتح DNS بشكل صريح:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
الخطوة التالتة: اسمح للـ orders service بس يكلم الـ payments service على port 8080:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: orders-can-call-payments
namespace: production
spec:
podSelector:
matchLabels:
app: payments
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: orders
ports:
- protocol: TCP
port: 8080
طبّق الـ 3 ملفات بأمر واحد، وتأكد إنها اتسجلت:
kubectl apply -f default-deny.yaml \
-f allow-dns.yaml \
-f orders-payments.yaml
kubectl get networkpolicy -n production
عشان تتأكد إنها شغّالة فعلاً، جرّب من جوّا pod مش مسموحله يكلم payments:
kubectl exec -n production deploy/frontend -- \
nc -zv payments 8080 -w 3
# المفروض ترجع: connection timed out
قياس فعلي قبل وبعد
على cluster GKE فيه 24 microservice و 3 namespaces، نفّذنا penetration test بأداة kube-hunter من pod موهوم باخد ثغرة:
- قبل: الـ attacker simulation وصل لـ 18 service مختلف خلال 3 دقائق، شامل DB الإنتاج وقواعد بيانات الـ analytics والـ Redis الداخلي.
- بعد: نفس الـ simulation وصل لـ 2 service بس (اللي مسموحلهم بحكم القواعد المعلنة)، والمحاولة لباقي الـ services اترفضت على مستوى الـ kernel iptables بدون ما توصل أصلاً للتطبيق.
- Overhead: Calico أضاف 0.4ms latency في P95 على cluster بـ 600 RPS internal. Cilium أضاف 0.18ms (لأنه بيستخدم eBPF بدل iptables التقليدية).
الافتراض هنا إن الـ traffic الداخلي معقول (≤ 5K RPS). فوق كده، Cilium بـ eBPF بيبقى الاختيار الأفضل من ناحية الأداء.
الـ trade-offs اللي لازم تعرفها
- Debugging أصعب. لو خدمة بتفشل تتصل، السبب ممكن يكون CrashLoop عادي، DNS مش شغّال، أو NetworkPolicy بترفض. لازم تشيك بالترتيب:
kubectl logsثمkubectl describe netpolثم اختبار منkubectl execبـnc -zv. الفريق محتاج runbook موثق، خصوصاً للناس الجداد. - Cluster lock-in جزئي. NetworkPolicy نفسها standard من Kubernetes، لكن CNI features الإضافية (زي Cilium L7 policies اللي بتفلتر على HTTP path) مش portable. لو هاجرت من Cilium لـ Calico، هتفقد القواعد الإضافية دي.
- زمن تطبيق غير فوري. الـ CNI بياخد من 200ms لـ ثانيتين عشان ينشر القواعد على كل الـ nodes. أثناء deployment كبير لـ NetworkPolicy جديد، النافذة الزمنية دي ممكن تسمح بترافيك ما كانش مفروض يعدّي. مش مشكلة كبيرة لكن مهم تعرفها لو بتعمل compliance audit.
- Egress للـ external services شاق. لو محتاج تسمح للتطبيق يكلم API خارجي زي Stripe أو OpenAI، لازم تكتب القواعد بـ
ipBlock(CIDR ranges)، والـ ranges دي بتتغير من غير ما حد يخبرك. حلول زي egress-gateway أو DNS-based policies في Cilium بتسهّل ده، لكن بتعقّد الـ stack.
متى لا تستخدم Network Policies
الطريقة دي مش الحل الصح في الحالات دي:
- Cluster صغير بـ 2-3 services في namespace واحد. الـ overhead التشغيلي أكبر من القيمة الأمنية المضافة. ركّز على الأساسيات (RBAC، image scanning، secrets management) قبل ده.
- عندك service mesh شغّال (Istio أو Linkerd) مع mTLS و authorization policies مفعّلة. الـ mesh بيعمل أكتر بكتير على L7 (HTTP method، header، JWT claims). لو شغّلت الاتنين بدون تنسيق، الـ debugging بيبقى كابوس.
- الـ CNI ما بيدعمش NetworkPolicy. Flannel الافتراضي مش بيدعم. يا تغيّر CNI لـ Calico أو Cilium، يا تستغنى عن الفكرة. تطبيق الـ YAML بدون CNI داعم بيديك إحساس وهمي بالأمان.
- workload يدوي عابر (dev cluster ساعة كل أسبوع). الـ enforcement مش هيبني عادة عند الفريق، بس هيتعب الناس بدون فائدة فعلية.
الخطوة التالية
افتح namespace واحد بس عندك (يفضّل staging، مش production)، طبّق الـ default-deny اللي فوق، وبعدين خلي الـ alerts بتاعتك (Slack، PagerDuty) مفتوحة لمدة 24 ساعة. هتشوف بالظبط مين بيكلم مين بدون ما يكون مكتوب — ده هيبقى أول mapping حقيقي للـ traffic بين الـ services عندك. بعد ما تكتشف كل الاتصالات اللازمة وتسمحها بشكل صريح، انقل القواعد لـ production. لو لقيت اتصالات مش متوقعة (زي service بيعمل DB query لـ DB مش بتاعه)، اوقف لحظة وراجع الكود قبل ما تسمح القاعدة — ده على الأرجح bug أمني كان مخبّي.
المصادر
- توثيق Kubernetes الرسمي للـ NetworkPolicy: kubernetes.io/docs/concepts/services-networking/network-policies
- Calico documentation للـ enforcement: docs.tigera.io/calico/latest/network-policy
- Cilium NetworkPolicy reference: docs.cilium.io/en/stable/security/policy
- Verizon DBIR 2024 (إحصائية lateral movement 32%): verizon.com/business/resources/reports/dbir
- NIST SP 800-207 (Zero Trust Architecture): csrc.nist.gov/pubs/sp/800/207/final
- CNCF Container Network Interface (CNI) spec: github.com/containernetworking/cni
- kube-hunter (penetration testing): github.com/aquasecurity/kube-hunter