نظرة عامة
تُعدّ ملفات Kubernetes المُهيَّأة بشكل خاطئ من أبرز أسباب الحوادث الأمنية في بيئات الإنتاج. فحاوية تعمل بصلاحيات root، أو وسم صورة غير مُثبَّت، أو حدّ موارد مفقود، أو شبكة مضيف مكشوفة — كلّ واحدة من هذه قد تفتح الباب أمام تصعيد الصلاحيات أو استنزاف الموارد أو الحركة الجانبية داخل عنقودك.
المشكلة أنّ هذه الأخطاء تبقى غير مرئية حتى وقت النشر — أو الأسوأ، حتى يستغلّها مهاجم. الحلّ هو نقل الأمان إلى اليسار واكتشاف انتهاكات السياسة قبل أن تصل الملفات إلى العنقود.
في هذا المختبر العملي، ستستخدم Conftest — إطار اختبار مبني على محرّك Open Policy Agent (OPA) — لكتابة سياسات Rego تتحقّق من صحة ملفات Kubernetes. ثم ستدمج تلك الفحوصات في GitHub Actions وGitLab CI بحيث يتم فحص كلّ طلب دمج تلقائيًا بحثًا عن الانتهاكات.
بنهاية هذا المختبر ستكون قد حصلت على:
- مكتبة من سياسات Rego القابلة لإعادة الاستخدام تغطّي وسوم الصور وسياقات الأمان وحدود الموارد والوصول على مستوى المضيف.
- اختبارات وحدة لتلك السياسات باستخدام
opa test. - خطوط أنابيب CI/CD عاملة تحظر الملفات غير الآمنة وتوفّر رسائل انتهاك واضحة وقابلة للتنفيذ.
المتطلبات الأساسية
قبل البدء، تأكّد من توفّر الأدوات والمعرفة التالية:
- تثبيت Conftest CLI — التثبيت عبر Homebrew:
brew install conftestبدلاً من ذلك، حمّل الملف التنفيذي من صفحة إصدارات Conftest.
- kubectl وعنقود اختبار (اختياري) — إذا أردت التحقّق من أنّ ملفاتك المُصلَحة تُنشر فعلاً، أنشئ عنقودًا محليًا باستخدام
minikube startأوkind create cluster. - مستودع اختبار — أنشئ مستودع Git جديدًا أو استخدم مستودعًا موجودًا. سنبني جميع الملفات من الصفر.
- معرفة أساسية بـ YAML وKubernetes — يجب أن تكون مرتاحًا في قراءة ملفات Deployment وService وPod.
إعداد البيئة
ابدأ بإنشاء هيكل المشروع ومجموعة من ملفات Kubernetes غير الآمنة عمدًا. ستكون هذه بمثابة ملفات اختبار ثابتة طوال كلّ تمرين.
هيكل المشروع
conftest-k8s-lab/
├── k8s/
│ ├── deployment-latest-tag.yaml
│ ├── deployment-run-as-root.yaml
│ ├── deployment-no-limits.yaml
│ ├── service-loadbalancer.yaml
│ └── pod-host-network.yaml
└── policy/
أنشئ المجلدات:
mkdir -p conftest-k8s-lab/k8s conftest-k8s-lab/policy
cd conftest-k8s-lab
الملف 1 — Deployment بوسم صورة غير مُثبَّت
أنشئ k8s/deployment-latest-tag.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-latest
spec:
replicas: 1
selector:
matchLabels:
app: web-latest
template:
metadata:
labels:
app: web-latest
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
يستخدم هذا الملف nginx:latest، مما يعني أنّ كلّ عملية سحب قد تُدخل ملفًا تنفيذيًا مختلفًا بصمت إلى عنقودك.
الملف 2 — Deployment يعمل بصلاحيات Root
أنشئ k8s/deployment-run-as-root.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-root
spec:
replicas: 1
selector:
matchLabels:
app: web-root
template:
metadata:
labels:
app: web-root
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
لم يتم تعيين securityContext، لذا تعمل الحاوية افتراضيًا بصلاحيات root — وهو مسار معروف لتصعيد الصلاحيات.
الملف 3 — Deployment بدون حدود موارد
أنشئ k8s/deployment-no-limits.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-no-limits
spec:
replicas: 1
selector:
matchLabels:
app: web-no-limits
template:
metadata:
labels:
app: web-no-limits
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
بدون حدود CPU والذاكرة، يمكن لحاوية واحدة سيّئة السلوك أن تُجوِّع العقدة بأكملها.
الملف 4 — Service من نوع LoadBalancer
أنشئ k8s/service-loadbalancer.yaml:
apiVersion: v1
kind: Service
metadata:
name: web-lb
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80
خدمة LoadBalancer بدون تعليقات توضيحية قد تكشف أعباء العمل للإنترنت العام في البيئات السحابية.
الملف 5 — Pod مع وصول إلى شبكة المضيف
أنشئ k8s/pod-host-network.yaml:
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
hostNetwork: true
containers:
- name: debug
image: busybox:1.36
command: ["sleep", "3600"]
hostNetwork: true يمنح الحاوية وصولاً كاملاً إلى مكدّس شبكة العقدة، متجاوزًا سياسات الشبكة بالكامل.
التمرين 1: كتابة أول سياسة Rego — منع وسوم Latest
ستمنع سياستك الأولى أي صورة حاوية تستخدم وسم :latest أو تحذف الوسم بالكامل (والذي يُحلّ أيضًا إلى latest).
الخطوة 1 — إنشاء السياسة
أنشئ policy/tags.rego:
package main
import future.keywords.in
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
image := container.image
not contains(image, ":")
msg := sprintf("Container '%s' uses image '%s' without a tag. Pin to a specific version.", [container.name, image])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
image := container.image
endswith(image, ":latest")
msg := sprintf("Container '%s' uses the ':latest' tag in image '%s'. Pin to a specific version.", [container.name, image])
}
الخطوة 2 — تشغيل Conftest على الملف غير الآمن
conftest test k8s/deployment-latest-tag.yaml
المخرجات المتوقعة:
FAIL - k8s/deployment-latest-tag.yaml - main - Container 'nginx' uses the ':latest' tag in image 'nginx:latest'. Pin to a specific version.
1 test, 0 passed, 0 warnings, 1 failure
الخطوة 3 — إصلاح الملف
عدّل k8s/deployment-latest-tag.yaml وغيّر سطر الصورة:
image: nginx:1.25.4
شغّل Conftest مجددًا:
conftest test k8s/deployment-latest-tag.yaml
المخرجات المتوقعة:
1 test, 1 passed, 0 warnings, 0 failures
فهم بنية Rego
كلّ ملف سياسة Rego يستخدمه Conftest يتبع نمطًا بسيطًا:
package main— يبحث Conftest عن حزمةmainافتراضيًا. يمكنك تجاوز ذلك باستخدام--namespace.deny[msg]— مجموعة قواعد. إذا تحقّقت جميع الشروط داخل جسم القاعدة، تُطلَق القاعدة وتُضيفmsgإلى مجموعة الانتهاكات.input— يمثّل مستند YAML الذي يتم اختباره. يحلّله Conftest إلى كائن JSON تلقائيًا.sprintf— يُنسّق رسالة خطأ مقروءة تظهر في سجلات CI.
التمرين 2: منع الحاويات من العمل بصلاحيات Root
الحاويات التي تعمل بصلاحيات root يمكنها تعديل نظام الملفات وتثبيت الحزم، وإذا اقترنت بثغرة في النواة، يمكنها الهروب إلى المضيف. تفرض هذه السياسة ضابطين: runAsNonRoot: true وallowPrivilegeEscalation: false.
الخطوة 1 — إنشاء السياسة
أنشئ policy/security_context.rego:
package main
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.securityContext.runAsNonRoot == true
msg := sprintf("Container '%s' must set securityContext.runAsNonRoot to true.", [container.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.securityContext.allowPrivilegeEscalation == false
msg := sprintf("Container '%s' must set securityContext.allowPrivilegeEscalation to false.", [container.name])
}
الخطوة 2 — الاختبار على الملف غير الآمن
conftest test k8s/deployment-run-as-root.yaml
المخرجات المتوقعة:
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.runAsNonRoot to true.
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.allowPrivilegeEscalation to false.
1 test, 0 passed, 0 warnings, 2 failures
الخطوة 3 — إصلاح الملف
حدّث k8s/deployment-run-as-root.yaml ليتضمّن سياق أمان لكلّ حاوية:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-root
spec:
replicas: 1
selector:
matchLabels:
app: web-root
template:
metadata:
labels:
app: web-root
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
شغّل Conftest مجددًا — تمرّ كلتا القاعدتين الآن بنجاح.
التمرين 3: فرض حدود الموارد
بدون حدود الموارد، يمكن لحاوية واحدة أن تستهلك كلّ وحدة المعالجة المركزية والذاكرة على العقدة، مما يتسبّب في إخفاقات متتالية عبر أعباء عمل غير مرتبطة. تتطلب العديد من أطر الامتثال (SOC 2، CIS Benchmarks) حدودًا صريحة.
الخطوة 1 — إنشاء السياسة
أنشئ policy/resources.rego:
package main
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%s' must define resources.limits.cpu.", [container.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%s' must define resources.limits.memory.", [container.name])
}
الخطوة 2 — الاختبار على الملف غير الآمن
conftest test k8s/deployment-no-limits.yaml
المخرجات المتوقعة:
FAIL - k8s/deployment-no-limits.yaml - main - Container 'nginx' must define resources.limits.cpu.
FAIL - k8s/deployment-no-limits.yaml - main - Container 'nginx' must define resources.limits.memory.
1 test, 0 passed, 0 warnings, 2 failures
الخطوة 3 — إصلاح الملف
أضف حدود الموارد إلى k8s/deployment-no-limits.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-no-limits
spec:
replicas: 1
selector:
matchLabels:
app: web-no-limits
template:
metadata:
labels:
app: web-no-limits
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
شغّل Conftest مجددًا — تمرّ فحوصات CPU والذاكرة بنجاح.
التمرين 4: منع الوصول المميّز للمضيف
الحاويات التي تطلب وصولاً على مستوى المضيف — hostNetwork أو hostPID أو hostIPC أو سياق أمان مميّز — تعمل فعليًا خارج صندوق الحماية. يمكن لحاوية مخترقة بأيّ من هذه العلامات رؤية كلّ حركة المرور على العقدة، أو الارتباط بعمليات أخرى، أو الهروب إلى المضيف بالكامل.
الخطوة 1 — إنشاء السياسة
أنشئ policy/host_access.rego:
package main
deny[msg] {
input.kind == "Pod"
input.spec.hostNetwork == true
msg := sprintf("Pod '%s' must not use hostNetwork: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
input.spec.hostPID == true
msg := sprintf("Pod '%s' must not use hostPID: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
input.spec.hostIPC == true
msg := sprintf("Pod '%s' must not use hostIPC: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Pod"
container := input.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container '%s' in Pod '%s' must not run in privileged mode.", [container.name, input.metadata.name])
}
deny[msg] {
input.kind == "Deployment"
input.spec.template.spec.hostNetwork == true
msg := sprintf("Deployment '%s' must not use hostNetwork: true.", [input.metadata.name])
}
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Container '%s' in Deployment '%s' must not run in privileged mode.", [container.name, input.metadata.name])
}
الخطوة 2 — الاختبار على الحاوية غير الآمنة
conftest test k8s/pod-host-network.yaml
المخرجات المتوقعة:
FAIL - k8s/pod-host-network.yaml - main - Pod 'debug-pod' must not use hostNetwork: true.
1 test, 0 passed, 0 warnings, 1 failure
الخطوة 3 — إصلاح الملف
احذف سطر hostNetwork: true من k8s/pod-host-network.yaml:
apiVersion: v1
kind: Pod
metadata:
name: debug-pod
spec:
containers:
- name: debug
image: busybox:1.36
command: ["sleep", "3600"]
شغّل Conftest — تمرّ الحاوية الآن بجميع فحوصات الوصول للمضيف.
التمرين 5: اختبار السياسات باستخدام opa test
السياسات هي شيفرة برمجية، والشيفرة تحتاج اختبارات. بدون اختبارات لا يمكنك التأكّد من أنّ السياسة تلتقط ما يجب أو أنّ إعادة هيكلة مستقبلية لن تُدخل نتيجة إيجابية كاذبة تحظر عمليات النشر المشروعة.
الخطوة 1 — إنشاء حالات الاختبار
أنشئ policy/tags_test.rego:
package main
test_latest_denied {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx:latest"
}
]
}
}
}
}
count(deny) > 0
}
test_no_tag_denied {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx"
}
]
}
}
}
}
count(deny) > 0
}
test_pinned_allowed {
input := {
"kind": "Deployment",
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "app",
"image": "nginx:1.25.4"
}
]
}
}
}
}
count(deny) == 0
}
الخطوة 2 — تشغيل الاختبارات
opa test policy/ -v
المخرجات المتوقعة:
policy/tags_test.rego:
data.main.test_latest_denied: PASS (1.234ms)
data.main.test_no_tag_denied: PASS (0.567ms)
data.main.test_pinned_allowed: PASS (0.432ms)
--------------------------------------------------------------------------------
PASS: 3/3
لماذا يُعدّ اختبار السياسات مهمًا
في سياق CI/CD، النتيجة السلبية الكاذبة تعني أنّ ملفًا غير آمن يتسلّل، بينما النتيجة الإيجابية الكاذبة تحظر نشرًا مشروعًا وتُفقد المطوّرين ثقتهم في خط الأنابيب. بكتابة حالات اختبار صريحة لكلّ من المدخلات المسموحة والمرفوضة، تحصل على مجموعة اختبارات انحدار تعمل في أجزاء من الثانية وتضمن أنّ سياساتك تتصرّف بشكل صحيح مع نمو مكتبة القواعد.
اجعل من عادتك إضافة ملف *_test.rego لكلّ ملف سياسة جديد. شغّل opa test policy/ -v كجزء من خط أنابيب CI إلى جانب conftest test.
التمرين 6: دمج Conftest في GitHub Actions
بعد كتابة السياسات واختبارها، الخطوة التالية هي ربطها بخط أنابيب CI لديك بحيث يتم التحقّق من كلّ طلب دمج تلقائيًا.
الخطوة 1 — إنشاء سير العمل
أنشئ .github/workflows/policy-check.yml:
name: Kubernetes Policy Check
on:
pull_request:
paths:
- "k8s/**"
- "policy/**"
push:
branches: [main]
paths:
- "k8s/**"
- "policy/**"
jobs:
conftest:
name: Validate K8s Manifests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Conftest
run: |
CONFTEST_VERSION="0.56.0"
wget -q "https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
tar xzf "conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
sudo mv conftest /usr/local/bin/
conftest --version
- name: Install OPA
run: |
OPA_VERSION="v0.68.0"
curl -L -o opa "https://openpolicyagent.org/downloads/${OPA_VERSION}/opa_linux_amd64_static"
chmod +x opa
sudo mv opa /usr/local/bin/
opa version
- name: Run policy unit tests
run: opa test policy/ -v
- name: Run Conftest against all manifests
run: |
echo "Scanning all Kubernetes manifests in k8s/ ..."
FAILED=0
for file in k8s/*.yaml; do
echo ""
echo "--- Testing: $file ---"
if ! conftest test "$file" --policy policy/; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo ""
echo "One or more manifests violated policy. Fix the issues above."
exit 1
fi
echo ""
echo "All manifests passed policy checks."
الخطوة 2 — ملاحظة طلب دمج فاشل
ادفع فرعًا يحتوي على الملفات غير الآمنة الأصلية. ستبدو مخرجات خط الأنابيب كالتالي:
--- Testing: k8s/deployment-latest-tag.yaml ---
FAIL - k8s/deployment-latest-tag.yaml - main - Container 'nginx' uses the ':latest' tag in image 'nginx:latest'. Pin to a specific version.
--- Testing: k8s/deployment-run-as-root.yaml ---
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.runAsNonRoot to true.
FAIL - k8s/deployment-run-as-root.yaml - main - Container 'nginx' must set securityContext.allowPrivilegeEscalation to false.
--- Testing: k8s/pod-host-network.yaml ---
FAIL - k8s/pod-host-network.yaml - main - Pod 'debug-pod' must not use hostNetwork: true.
One or more manifests violated policy. Fix the issues above.
Error: Process completed with exit code 1.
يتحوّل فحص حالة طلب الدمج إلى اللون الأحمر مع رسائل انتهاك واضحة تخبر المطوّر بالضبط بما يجب إصلاحه وأين.
الخطوة 3 — ملاحظة طلب دمج ناجح
أصلح جميع الملفات كما هو موضّح في التمارين السابقة، ادفع مجددًا، وسيمرّ خط الأنابيب:
--- Testing: k8s/deployment-latest-tag.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
--- Testing: k8s/deployment-run-as-root.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
--- Testing: k8s/deployment-no-limits.yaml ---
1 test, 1 passed, 0 warnings, 0 failures
All manifests passed policy checks.
التمرين 7: دمج Conftest في GitLab CI
إذا كان فريقك يستخدم GitLab، فالتكامل بنفس السهولة. أضف الوظيفة التالية إلى ملف .gitlab-ci.yml:
التكوين الكامل العامل
stages:
- validate
conftest-policy-check:
stage: validate
image: alpine:3.19
variables:
CONFTEST_VERSION: "0.56.0"
OPA_VERSION: "v0.68.0"
before_script:
- apk add --no-cache curl wget tar
- wget -q "https://github.com/open-policy-agent/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
- tar xzf "conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz"
- mv conftest /usr/local/bin/
- curl -L -o /usr/local/bin/opa "https://openpolicyagent.org/downloads/${OPA_VERSION}/opa_linux_amd64_static"
- chmod +x /usr/local/bin/opa
script:
- echo "Running policy unit tests..."
- opa test policy/ -v
- echo "Running Conftest against all manifests..."
- |
FAILED=0
for file in k8s/*.yaml; do
echo ""
echo "--- Testing: $file ---"
if ! conftest test "$file" --policy policy/; then
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo ""
echo "One or more manifests violated policy."
exit 1
fi
echo ""
echo "All manifests passed policy checks."
rules:
- changes:
- k8s/**/*
- policy/**/*
when: always
سلوك النجاح والفشل
يعكس السلوك سير عمل GitHub Actions بالضبط. عند وجود ملفات غير آمنة، تفشل الوظيفة مع رسائل الانتهاك. عندما تكون جميع الملفات متوافقة، تمرّ الوظيفة بملخّص نظيف. يضمن قسم rules أنّ الوظيفة تعمل فقط عند تغيير ملفات Kubernetes أو ملفات السياسة، مما يحافظ على وقت تشغيل خط الأنابيب في حدّه الأدنى.
متقدّم: التحذيرات مقابل الرفض
ليس كلّ انتهاك للسياسة يجب أن يحظر النشر. بعضها توصيات — أفضل ممارسات تريد إظهارها دون كسر خط الأنابيب. يدعم Conftest هذا التمييز من خلال قواعد warn.
كيف يعمل
deny[msg]— بوابة صارمة. إذا أُطلقت أيّ قاعدة deny، يخرجconftest testبكود غير صفري ويفشل خط الأنابيب.warn[msg]— استشاري. تُطبع الرسالة لكن يبقى كود الخروج صفرًا، فيمرّ خط الأنابيب.conftest test --fail-on-warn— يرفع اختياريًا جميع التحذيرات إلى إخفاقات. مفيد عندما تريد تشديد السياسة تدريجيًا: ابدأ بـwarn، وبمجرد أن تُصلح الفرق الانتهاكات الموجودة، انتقل إلىdenyأو فعّل--fail-on-warn.
إنشاء سياسة استشارية
أنشئ policy/recommendations.rego:
package main
warn[msg] {
input.kind == "Service"
input.spec.type == "LoadBalancer"
not input.metadata.annotations
msg := sprintf("Service '%s' is of type LoadBalancer with no annotations. Consider adding cloud-provider-specific annotations for internal load balancers.", [input.metadata.name])
}
warn[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.readinessProbe
msg := sprintf("Container '%s' has no readinessProbe. Add one so Kubernetes can route traffic only to healthy pods.", [container.name])
}
warn[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.livenessProbe
msg := sprintf("Container '%s' has no livenessProbe. Add one so Kubernetes can restart unhealthy pods.", [container.name])
}
اختبار السياسة الاستشارية
conftest test k8s/service-loadbalancer.yaml
المخرجات المتوقعة:
WARN - k8s/service-loadbalancer.yaml - main - Service 'web-lb' is of type LoadBalancer with no annotations. Consider adding cloud-provider-specific annotations for internal load balancers.
1 test, 1 passed, 1 warning, 0 failures
ملاحظة: كود الخروج هو 0 — خط الأنابيب لا يزال يمرّ. إذا أردت فرض التحذيرات:
conftest test k8s/service-loadbalancer.yaml --fail-on-warn
الآن كود الخروج هو 1 وسيفشل خط الأنابيب.
يتيح لك هذا النمط طرح سياسات جديدة تدريجيًا: قدّمها كتحذيرات، وامنح الفرق وقتًا للمعالجة، ثم ارفعها إلى رفض.
التنظيف
عند الانتهاء من المختبر، احذف موارد الاختبار:
# Remove the lab directory
rm -rf conftest-k8s-lab
# If you deployed any fixed manifests to a test cluster
kubectl delete -f k8s/ --ignore-not-found
# If you created a kind cluster for this lab
kind delete cluster --name conftest-lab
النقاط الرئيسية
- انقل الأمان إلى اليسار بقوة. اكتشاف ملف مُهيَّأ بشكل خاطئ في طلب دمج أرخص بأضعاف من اكتشافه بعد اختراق.
- Conftest + Rego نقطة دخول خفيفة لسياسة كشيفرة. لا تحتاج إلى خادم OPA كامل أو تثبيت Gatekeeper لبدء فرض السياسات — ملف تنفيذي واحد وبضعة ملفات Rego كافية.
- اختبر سياساتك كشيفرة تطبيق. استخدم
opa testمع حالات اختبار صريحة إيجابية وسلبية لمنع التراجعات في مكتبة القواعد. - استخدم التحذيرات للطرح التدريجي. ابدأ السياسات الجديدة كقواعد
warn، ونشرها مع الفريق، ثم ارفعها إلىdenyبمجرد حلّ الانتهاكات الموجودة. - رسائل الخطأ القابلة للتنفيذ حاسمة. استخدم
sprintfفي كلّ قاعدة لإخبار المطوّر بأيّ حاوية وأيّ حقل وماذا يفعل حيال ذلك. الرسائل العامة “تم انتهاك السياسة” تُفقد الثقة في بوابات CI. - احتفظ بالسياسات في نفس مستودع الملفات. وضع
policy/بجانبk8s/يعني أنّ تغييرات السياسة تمرّ بنفس عملية المراجعة كتغييرات البنية التحتية.
الخطوات التالية
الآن بعد أن أصبح لديك خط أنابيب Conftest عامل، واصل بناء ممارسة سياسة كشيفرة:
- سياسة كشيفرة لـ CI/CD: OPA وRego — تعمّق أكثر في لغة Rego، وتعلّم عن استيراد البيانات وإدارة الحزم وتسجيل القرارات لمسارات التدقيق.
- الأنماط الدفاعية والتخفيفات — استكشف المشهد الأوسع لتعزيز أمان خطوط أنابيب CI/CD، من إدارة الأسرار إلى توقيع القطع الأثرية والتطبيق أثناء التشغيل.