الأمان والأداء

أفضل ممارسات أمان Docker

20 دقيقة الدرس 24 من 35

أفضل ممارسات أمان Docker

الحاويات ليست حدودًا أمنية بذاتها. بدون التكوين الصحيح، حاويات Docker يمكن أن تعرّض تطبيقك لثغرات خطيرة.

عزل الحاويات

الحاويات تشارك نواة المضيف، لذا استغلال النواة يمكن أن يكسر العزل:

<!-- الحاوية الأساسية تعمل كجذر بشكل افتراضي (غير آمن) -->
docker run ubuntu:22.04 whoami
# الناتج: root

<!-- استخدم فضاءات أسماء المستخدم للعزل -->
# /etc/docker/daemon.json
{
"userns-remap": "default"
}

<!-- أعد تشغيل Docker لتطبيق -->
sudo systemctl restart docker

<!-- أسقط القدرات غير الضرورية -->
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx

<!-- استخدم ملفات seccomp -->
docker run --security-opt seccomp=/path/to/seccomp-profile.json myapp

<!-- فعّل AppArmor أو SELinux -->
docker run --security-opt apparmor=docker-default myapp
تحذير: لا تشغل أبدًا الحاويات مع علامة --privileged في الإنتاج. هذا يعطل جميع ميزات الأمان ويمنح الحاوية وصولًا كاملًا لنظام المضيف.

صور أساسية صغيرة

استخدم صور أساسية صغيرة لتقليل سطح الهجوم:

<!-- سيء: صورة نظام تشغيل كامل (1.13 GB) -->
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nodejs npm
COPY . /app
CMD ["node", "server.js"]

<!-- أفضل: صورة Node الرسمية (910 MB) -->
FROM node:18
COPY . /app
WORKDIR /app
RUN npm install
CMD ["node", "server.js"]

<!-- الأفضل: صورة مبنية على Alpine (172 MB) -->
FROM node:18-alpine
COPY . /app
WORKDIR /app
RUN npm install --production
CMD ["node", "server.js"]

<!-- الأمثل: صورة Distroless (50 MB) -->
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

FROM gcr.io/distroless/nodejs18-debian11
COPY --from=builder /app /app
WORKDIR /app
CMD ["server.js"]
حجم الصورة مهم: صور أصغر = حزم أقل = ثغرات أقل = نشر أسرع. صور Alpine و distroless غالبًا لديها 90٪ أقل من CVEs من صور نظام التشغيل الكامل.

مستخدمون بدون جذر

شغّل الحاويات دائمًا كمستخدمين بدون جذر:

<!-- أنشئ واستخدم مستخدم بدون جذر في Dockerfile -->
FROM node:18-alpine

# أنشئ مستخدم التطبيق
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001

# اضبط الملكية
COPY --chown=nodejs:nodejs . /app
WORKDIR /app
RUN npm install --production

# انتقل لمستخدم بدون جذر
USER nodejs

EXPOSE 3000
CMD ["node", "server.js"]

<!-- تحقق من المستخدم في وقت التشغيل -->
docker run myapp whoami
# الناتج: nodejs (ليس root!)

<!-- فرض بدون جذر في Kubernetes -->
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
containers:
- name: app
image: myapp:latest

إدارة أسرار Docker

لا تكتب الأسرار بشكل ثابت في الصور أبدًا. استخدم أسرار Docker أو متغيرات البيئة:

<!-- سيء: أسرار في Dockerfile -->
FROM node:18
ENV DATABASE_PASSWORD="MySecretPass123"
ENV API_KEY="sk_live_abc123"

<!-- جيد: استخدم أسرار Docker (Swarm/Kubernetes) -->
# أنشئ سرًا
echo "MySecretPass123" | docker secret create db_password -

# استخدم في الخدمة
docker service create \
--name myapp \
--secret db_password \
myapp:latest

# الوصول في التطبيق
const fs = require('fs');
const dbPassword = fs.readFileSync('/run/secrets/db_password', 'utf8');

<!-- بديل: متغيرات البيئة -->
docker run -e DATABASE_PASSWORD="$DB_PASS" myapp

<!-- أسرار Kubernetes -->
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
db-password: TXlTZWNyZXRQYXNzMTIz # مشفر base64
---
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: db-password
إدارة الأسرار: أسرار Docker تُخزن مشفرة في Swarm. في Kubernetes، استخدم sealed-secrets أو مديري أسرار خارجيين (AWS Secrets Manager، HashiCorp Vault) للإنتاج.

فحص الصور

افحص الصور للثغرات قبل النشر:

<!-- Trivy - ماسح ثغرات شامل -->
# تثبيت Trivy
brew install aquasecurity/trivy/trivy

# فحص الصورة
trivy image nginx:latest

# فحص مع تصفية الشدة
trivy image --severity HIGH,CRITICAL myapp:latest

# فشل بناء CI عند الثغرات الحرجة
trivy image --exit-code 1 --severity CRITICAL myapp:latest

<!-- Docker Scout (مدمج في Docker Desktop) -->
docker scout cves myapp:latest
docker scout recommendations myapp:latest

<!-- Grype - ماسح ثغرات سريع -->
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
./grype myapp:latest

<!-- فحص في بناء Dockerfile -->
FROM node:18-alpine AS scanner
RUN apk add --no-cache curl
RUN curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
COPY --from=build /app /scan
RUN trivy fs --severity HIGH,CRITICAL --exit-code 1 /scan

أمان وقت التشغيل

راقب واحمِ الحاويات في وقت التشغيل:

<!-- نظام ملفات جذر للقراءة فقط -->
docker run --read-only --tmpfs /tmp myapp

<!-- في Dockerfile -->
FROM node:18-alpine
COPY . /app
WORKDIR /app
RUN npm install
USER nodejs
CMD ["node", "server.js"]

<!-- في docker-compose.yml -->
version: '3.8'
services:
app:
image: myapp:latest
read_only: true
tmpfs:
- /tmp
- /app/logs
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE

<!-- Kubernetes SecurityContext -->
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
حرج: أنظمة الملفات للقراءة فقط تمنع المهاجمين من تعديل الملفات الثنائية أو تثبيت برامج ضارة. استخدم دائمًا tmpfs للدلائل التي تحتاج وصول الكتابة.

أمان Docker Compose

أمّن التطبيقات متعددة الحاويات:

<!-- docker-compose.yml مع أفضل ممارسات الأمان -->
version: '3.8'

services:
web:
image: myapp:latest
build:
context: .
dockerfile: Dockerfile
user: "1001:1001"
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
environment:
- DB_HOST=postgres
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3

postgres:
image: postgres:15-alpine
user: postgres
read_only: true
tmpfs:
- /tmp
- /var/run/postgresql
volumes:
- postgres_data:/var/lib/postgresql/data:rw
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
networks:
- backend

secrets:
db_password:
file: ./secrets/db_password.txt

networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # لا وصول خارجي

volumes:
postgres_data:
driver: local

فحص الأمان في CI/CD

أتمتة فحص الصور في خط الأنابيب:

<!-- سير عمل GitHub Actions -->
# .github/workflows/docker-security.yml
name: فحص أمان Docker
on: [push, pull_request]

jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: بناء الصورة
run: docker build -t myapp:${{ github.sha }} .

- name: تشغيل ماسح Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'

- name: رفع نتائج Trivy
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'

- name: تشغيل Hadolint (فاحص Dockerfile)
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
failure-threshold: warning

- name: فحص CVE لـ Docker Scout
uses: docker/scout-action@v1
with:
command: cves
image: myapp:${{ github.sha }}
only-severities: critical,high
exit-code: true
دفاع متعدد الطبقات: استخدم Hadolint للكشف عن أخطاء تكوين Dockerfile، Trivy/Scout لفحص الثغرات، وأدوات أمان وقت التشغيل مثل Falco للمراقبة الحية.

قائمة أمان Docker

<!-- قائمة أمان Docker الكاملة -->

✓ أمان الصورة:
- استخدم صور أساسية صغيرة (Alpine, distroless)
- افحص الصور مع Trivy/Grype
- وقّع الصور مع Docker Content Trust
- استخدم وسوم محددة، ليس :latest
- بناءات متعددة المراحل لاستبعاد أدوات البناء

✓ أمان وقت التشغيل:
- شغّل كمستخدم بدون جذر
- نظام ملفات جذر للقراءة فقط
- أسقط جميع القدرات، أضف المطلوبة فقط
- استخدم ملفات seccomp/AppArmor
- فعّل فضاءات أسماء المستخدم

✓ إدارة الأسرار:
- لا تكتب الأسرار بشكل ثابت في الصور أبدًا
- استخدم أسرار Docker أو مديري أسرار
- افحص الأسرار المكشوفة مع git-secrets
- دوّر الأسرار بانتظام

✓ أمان الشبكة:
- استخدم شبكات جسر مخصصة
- اعزل خدمات الخلفية (internal: true)
- نفّذ سياسات الشبكة
- استخدم TLS للاتصال بين الخدمات

✓ المراقبة:
- فعّل تسجيل تدقيق Docker
- راقب استخدام الموارد (المعالج، الذاكرة)
- استخدم أدوات أمان وقت التشغيل (Falco, Sysdig)
- عمليات تدقيق أمان منتظمة
تمرين: أمّن Dockerfile موجود: (1) انتقل لصورة أساسية Alpine أو distroless، (2) أضف مستخدم بدون جذر، (3) فعّل نظام ملفات للقراءة فقط، (4) افحص مع Trivy وأصلح الثغرات HIGH/CRITICAL. قارن أحجام الصور وعدد الثغرات قبل/بعد.