مقدمه: چرا امنیت کانتینرهای داکر در سال ۲۰۲۶ حیاتی است؟
اگر با داکر کار میکنید، احتمالاً میدانید که کانتینرها ستون فقرات زیرساختهای مدرن شدهاند. بیش از ۸۰ درصد سازمانها الان در محیط تولید از کانتینر استفاده میکنند و این عدد هر سال بیشتر هم میشود. خب، طبیعیه که وقتی چیزی اینقدر گسترده بشه، مهاجمان هم سراغش میآیند.
گزارشهای امنیتی ۲۰۲۵ آمار نگرانکنندهای دارند: ۵۸ درصد ایمیجهای کانتینری هنوز با دسترسی root اجرا میشوند. حملات زنجیره تأمین بر ایمیجهای عمومی رشد ۳۴۰ درصدی داشته. و بدتر از همه، مهاجمان حالا با هوش مصنوعی میتوانند پیکربندیهای ناامن را در عرض چند ثانیه شناسایی کنند.
صادقانه بگویم، حملات زنجیره تأمین روی ایمیجهای داکر واقعاً ترسناک شدهاند. مهاجمان ایمیجهای پایه محبوب را در رجیستریهای عمومی آلوده میکنند و بدافزارهای استخراج رمزارز و دربهای پشتی را در هزاران محیط تولید پخش میکنند. یعنی شما فکر میکنید دارید یک ایمیج معتبر pull میکنید، ولی در واقع...
در این راهنما، کل چرخه حیات امنیت کانتینر را با هم بررسی میکنیم — از ساخت ایمیجهای امن و اجرای بدون root، تا محدودسازی با Seccomp و AppArmor، اسکن آسیبپذیری، یکپارچهسازی امنیت در CI/CD و نظارت در زمان اجرا. تمام نمونهها هم بر اساس Docker Engine 27.x (آخرین نسخه پایدار ۲۰۲۶) نوشته شدهاند.
ساخت ایمیجهای امن با Dockerfile
اولین و شاید مهمترین گام در امنسازی کانتینرها، ساخت ایمیجی است که از همان ابتدا با ذهنیت امنیتی طراحی شده باشد. یک ایمیج ناامن، هرچقدر هم تنظیمات زمان اجرا را سفتوسخت کنید، سطح حمله گستردهای ایجاد میکند.
استفاده از ایمیجهای پایه حداقلی
انتخاب ایمیج پایه تأثیر مستقیمی بر امنیت دارد. قاعده سادهاش این است: هرچه بستهها و ابزارهای کمتری باشد، سطح حمله کوچکتر است:
- Alpine Linux — حجمش تقریباً ۵ مگابایته و از
musl libcاستفاده میکند. برای بیشتر برنامهها گزینه خوبیست. - Distroless Images — ایمیجهای گوگل که فقط runtime برنامه را شامل میشوند. نه shell دارند، نه package manager، نه هیچ ابزار اضافیای. ایدهآل برای محیط تولید.
- Scratch — ایمیج کاملاً خالی برای باینریهای استاتیک. کوچکترین سطح حمله ممکن (عملاً صفر).
ساخت چندمرحلهای (Multi-stage Builds)
ساخت چندمرحلهای یکی از بهترین تکنیکهاییست که داکر در اختیارتان گذاشته. ایدهاش سادهست: در مرحله اول کد را کامپایل کنید و در مرحله دوم فقط باینری نهایی را به یک ایمیج حداقلی کپی کنید. اینطوری ابزارهای build هرگز وارد ایمیج نهایی نمیشوند.
این نمونه یک Dockerfile امن برای برنامه Go را نشان میدهد:
# مرحله ساخت
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# مرحله نهایی — ایمیج حداقلی
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
نکات کلیدی در نوشتن Dockerfile امن
- از COPY به جای ADD استفاده کنید — دستور
ADDقابلیتهای اضافی مانند استخراج خودکار فایلهای فشرده و دانلود از URL دارد که میتواند رفتارهای غیرمنتظره و ریسک امنیتی ایجاد کند.COPYقابل پیشبینیتر و امنتر است. - تگها و digest را پین کنید — هرگز، تأکید میکنم هرگز از تگ
latestاستفاده نکنید. همیشه نسخه دقیق و ترجیحاً digest ایمیج را مشخص کنید:
# نادرست — غیرقابل پیشبینی
FROM nginx:latest
# صحیح — نسخه مشخص
FROM nginx:1.27.3-alpine
# بهترین — با digest
FROM nginx:1.27.3-alpine@sha256:a1b2c3d4e5f6...
- فایل
.dockerignoreایجاد کنید — جلوی ارسال فایلهای حساس مانند.env،.gitو کلیدهای SSH به context ساخت را بگیرید. - لایهها را بهینه کنید — دستورات
RUNمرتبط را با&&ترکیب کنید و کش پکیجمنیجر را در همان لایه پاک کنید.
اجرای کانتینر بدون دسترسی root
این را جدی بگیرید: اجرای فرآیندها بهعنوان root داخل کانتینر یکی از رایجترین و خطرناکترین اشتباهات امنیتیست. اگر مهاجم بتواند از کانتینر فرار کند (container escape)، دسترسی root روی سیستم میزبان خواهد داشت. بهویژه با آسیبپذیریهایی مثل CVE-2024-21626 در runc، این موضوع اهمیت دوچندان پیدا میکند.
ایجاد کاربر اختصاصی در Dockerfile
همیشه یک کاربر غیر root با UID و GID مشخص بسازید. چرا عددی صریح؟ چون از تداخل با کاربران سیستم میزبان جلوگیری میکند:
FROM node:22-alpine
# ایجاد گروه و کاربر با UID/GID مشخص
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup -h /app -s /sbin/nologin
WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup . .
# تغییر به کاربر غیر root
USER appuser:appgroup
EXPOSE 3000
CMD ["node", "server.js"]
حالت بدون root داکر (Docker Rootless Mode)
Docker Engine 27.x از حالت rootless بهصورت کامل پشتیبانی میکند. توی این حالت، کل daemon داکر بدون دسترسی root اجرا میشود و این خیلی خبر خوبیست:
# نصب Docker در حالت rootless
dockerd-rootless-setuptool.sh install
# تنظیم متغیرهای محیطی
export PATH=/home/user/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock
# بررسی وضعیت
docker context use rootless
docker info | grep -i "root"
جلوگیری از ارتقای دسترسی
فلگ --security-opt=no-new-privileges یک لایه حفاظتی فوقالعاده مهم است. حتی اگر باینری setuid در کانتینر وجود داشته باشد، با این فلگ دیگر امکان ارتقای سطح دسترسی وجود ندارد:
docker run --security-opt=no-new-privileges:true \
--user 1001:1001 \
myapp:v1.0
محدودسازی با Seccomp و AppArmor
خب، بیایید سراغ لایههای عمیقتر امنیتی برویم. Seccomp و AppArmor دو مکانیزم امنیتی هسته لینوکس هستند که لایههای حفاظتی اضافی برای کانتینرها فراهم میکنند. استفاده همزمان از هر دو، رویکرد دفاع در عمق (Defense in Depth) را بهشکل محسوسی تقویت میکند.
پروفایل Seccomp پیشفرض داکر
داکر بهصورت پیشفرض یک پروفایل Seccomp اعمال میکند که حدود ۴۴ فراخوانی سیستمی خطرناک را مسدود میکند — چیزهایی مثل mount، reboot، kexec_load و ptrace. ولی این پروفایل عمومیست و برای امنیت واقعی باید پروفایل اختصاصی بنویسید.
ایجاد پروفایل Seccomp سفارشی
قدم اول: با strace ببینید برنامهتان واقعاً از کدام فراخوانیهای سیستمی استفاده میکند:
# شناسایی syscallهای مورد استفاده برنامه
strace -cf -p $(pgrep myapp) 2>&1 | tail -20
# یا اجرا با strace
strace -cf myapp 2>&1 | grep -v "^$"
بعد یک پروفایل JSON سفارشی بسازید. این پروفایل با رویکرد whitelist فقط syscallهای لازم را مجاز میکند (و بقیه را مسدود):
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_AARCH64"
],
"syscalls": [
{
"names": [
"read", "write", "open", "close",
"stat", "fstat", "lstat", "poll",
"mmap", "mprotect", "munmap", "brk",
"ioctl", "access", "pipe", "select",
"socket", "connect", "accept", "bind",
"listen", "sendto", "recvfrom",
"clone", "execve", "exit", "exit_group",
"wait4", "futex", "epoll_create1",
"epoll_ctl", "epoll_wait", "getrandom"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
# اجرا با پروفایل سفارشی
docker run --security-opt seccomp=./custom-seccomp.json myapp:v1.0
پروفایل AppArmor برای داکر
داکر بهصورت پیشفرض پروفایل docker-default را برای AppArmor اعمال میکند، ولی برای محدودسازی بیشتر بهتر است پروفایل اختصاصی بنویسید. مثال زیر یک پروفایل AppArmor سفارشی برای nginx است:
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/nameservice>
# دسترسی شبکه
network inet tcp,
network inet6 tcp,
# فایلهای nginx
/usr/sbin/nginx ix,
/etc/nginx/** r,
/var/log/nginx/** rw,
/var/cache/nginx/** rw,
/run/nginx.pid rw,
# فایلهای وب
/usr/share/nginx/html/** r,
# رد دسترسیهای خطرناک
deny /proc/*/mem rwklx,
deny /sys/** rwklx,
deny /etc/shadow r,
deny /etc/passwd w,
}
# بارگذاری و اعمال پروفایل
sudo apparmor_parser -r -W /etc/apparmor.d/docker-nginx
docker run --security-opt apparmor=docker-nginx nginx:1.27-alpine
ترکیب Seccomp و AppArmor در Docker Compose
بهترین نتیجه وقتی حاصل میشود که همه مکانیزمهای امنیتی را با هم ترکیب کنید. این مثال یک سرویس وب کاملاً امنشده در Docker Compose v2 را نشان میدهد — و من شخصاً پیشنهاد میکنم این الگو را بهعنوان نقطه شروع برای سرویسهایتان استفاده کنید:
services:
webapp:
image: myapp:v1.0@sha256:abc123...
read_only: true
user: "1001:1001"
security_opt:
- no-new-privileges:true
- seccomp:./seccomp-webapp.json
- apparmor:docker-webapp
tmpfs:
- /tmp:noexec,nosuid,size=64m
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
deploy:
resources:
limits:
memory: 256M
cpus: "0.5"
networks:
- frontend
ports:
- "8080:8080"
networks:
frontend:
driver: bridge
internal: false
مدیریت Capabilities در لینوکس
لینوکس قدیم دسترسیها را فقط به دو سطح تقسیم میکرد: root (که همهکاره بود) و غیر root (که محدود بود). Capabilities این مدل را شکستهاند و دسترسیهای root را به واحدهای کوچکتر تقسیم کردهاند. نکته مهم اینجاست: داکر بهصورت پیشفرض مجموعهای از capabilities را به کانتینرها میدهد که خیلیهایشان اصلاً لازم نیستند.
اصل حداقل دسترسی: ابتدا همه را بردار
بهترین رویکرد سادهست: اول همه capabilities را حذف کنید، بعد فقط آنچه واقعاً لازم دارید را اضافه کنید:
# حذف همه و اضافه فقط موارد لازم
docker run \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--cap-add=CHOWN \
--cap-add=SETUID \
--cap-add=SETGID \
--security-opt=no-new-privileges:true \
--user 1001:1001 \
myapp:v1.0
Capabilities پرخطر و توضیح آنها
بیایید مهمترینها را بشناسیم:
SYS_ADMIN— خطرناکترین capability. امکان mount، تغییر namespace و بسیاری عملیات مدیریتی را فراهم میکند. تقریباً هرگز نباید استفاده شود.NET_ADMIN— مدیریت کامل شبکه شامل routing، firewall و interface. فقط برای کانتینرهای شبکهای تخصصی مناسب است.SYS_PTRACE— امکان دیباگ فرآیندهای دیگر. مهاجم میتواند از آن برای خواندن حافظه فرآیندهای مجاور سوءاستفاده کند.NET_RAW— امکان ایجاد بستههای شبکه خام. میتواند برای حملات ARP spoofing استفاده شود.DAC_OVERRIDE— سطوح دسترسی فایلها را نادیده میگیرد. عملاً امکان خواندن و نوشتن تمام فایلها.NET_BIND_SERVICE— اجازه bind به پورتهای زیر ۱۰۲۴. معمولاً برای وبسرورها لازم است و نسبتاً بیخطر محسوب میشود.
# بررسی capabilities فعلی یک کانتینر
docker exec mycontainer cat /proc/1/status | grep -i cap
# یا با استفاده از capsh
docker exec mycontainer capsh --print
امنیت شبکه کانتینرها
یک چیزی که خیلیها نمیدانند: شبکه پیشفرض داکر (bridge) تمام کانتینرها را در یک شبکه مشترک قرار میدهد. یعنی هر کانتینری میتواند آزادانه با بقیه ارتباط برقرار کند. این از نظر امنیتی اصلاً خوب نیست.
شبکههای اختصاصی Bridge
به جای شبکه پیشفرض، شبکههای اختصاصی بسازید تا کانتینرها فقط با سرویسهایی که واقعاً نیاز دارند ارتباط داشته باشند:
# ایجاد شبکههای جداگانه
docker network create --driver bridge \
--subnet 172.20.0.0/24 \
--opt com.docker.network.bridge.enable_icc=false \
frontend-net
docker network create --driver bridge \
--subnet 172.21.0.0/24 \
--internal \
backend-net
# اتصال کانتینرها به شبکههای مناسب
docker run --network frontend-net --name webserver nginx:1.27-alpine
docker run --network backend-net --name database postgres:17-alpine
غیرفعالسازی ارتباط بین کانتینری (ICC)
فلگ enable_icc=false ارتباط مستقیم بین کانتینرها در یک شبکه را مسدود میکند. در این حالت، فقط ارتباطاتی که صریحاً تعریف شدهاند (از طریق --link یا سرویسهای Docker Compose) امکانپذیر خواهند بود.
محدودسازی پورتها
این یکی از آن اشتباهاتیست که زیاد میبینم: فقط پورتهای واقعاً لازم را باز کنید و حتماً به جای bind کردن روی همه interfaceها، آدرس IP مشخصی تعیین کنید:
# نادرست — در معرض تمام interfaceها
docker run -p 3306:3306 mysql:8
# صحیح — فقط localhost
docker run -p 127.0.0.1:3306:3306 mysql:8
فعالسازی TLS برای Docker Daemon
اگر Docker Daemon باید از راه دور قابل دسترسی باشد (که خب، گاهی اجتنابناپذیر است)، حتماً TLS را فعال کنید:
# تنظیم daemon.json
{
"tls": true,
"tlsverify": true,
"tlscacert": "/etc/docker/certs/ca.pem",
"tlscert": "/etc/docker/certs/server-cert.pem",
"tlskey": "/etc/docker/certs/server-key.pem",
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"]
}
اسکن آسیبپذیری با Trivy
Trivy از Aqua Security یکی از بهترین ابزارهای متنباز برای اسکن آسیبپذیریست. نسخه v0.59+ قابلیتهای خیلی خوبی داره — از اسکن ایمیجهای کانتینری و فایلسیستم گرفته تا مخازن git و تولید SBOM.
نصب و استفاده اولیه
# نصب Trivy در لینوکس
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.59.1
# اسکن ایمیج
trivy image myapp:v1.0
# فقط آسیبپذیریهای بحرانی و بالا
trivy image --severity CRITICAL,HIGH myapp:v1.0
# اسکن با خروجی JSON
trivy image --format json --output results.json myapp:v1.0
# تولید SBOM (Software Bill of Materials)
trivy image --format spdx-json --output sbom.json myapp:v1.0
تفسیر نتایج اسکن
خروجی Trivy آسیبپذیریها را بر اساس شدت دستهبندی میکند. هر ردیف شامل نام کتابخانه، شناسه CVE، سطح شدت، نسخه فعلی و نسخهای که مشکل در آن رفع شده نشان داده میشود:
myapp:v1.0 (alpine 3.20.3)
============================================
Total: 4 (CRITICAL: 1, HIGH: 2, MEDIUM: 1)
┌──────────────┬────────────────┬──────────┬─────────┬──────────────────────┐
│ Library │ Vulnerability │ Severity │ Version │ Fixed Version │
├──────────────┼────────────────┼──────────┼─────────┼──────────────────────┤
│ libcrypto3 │ CVE-2025-XXXX │ CRITICAL │ 3.3.1 │ 3.3.2 │
│ libssl3 │ CVE-2025-YYYY │ HIGH │ 3.3.1 │ 3.3.2 │
│ curl │ CVE-2025-ZZZZ │ HIGH │ 8.9.0 │ 8.9.1 │
│ zlib │ CVE-2025-WWWW │ MEDIUM │ 1.3.1 │ 1.3.2 │
└──────────────┴────────────────┴──────────┴─────────┴──────────────────────┘
آسیبپذیریهای CRITICAL و HIGH باید فوراً رفع شوند — یا با بهروزرسانی بستههای آسیبپذیر، یا با ارتقای ایمیج پایه. ستون Fixed Version دقیقاً نشان میدهد که به کدام نسخه باید آپدیت کنید.
یکپارچهسازی امنیت در CI/CD
اسکن امنیتی نباید کاری باشد که "یادمان بیفتد انجام بدهیم". باید بخش جداییناپذیر خط لوله CI/CD باشد. مفهومش سادهست: یک Security Gate (دروازه امنیتی) بگذارید تا اگر آسیبپذیری بحرانی پیدا شد، خط لوله متوقف شود و ایمیج ناامن هرگز به تولید نرسد.
نمونه GitHub Actions با اسکن Trivy
name: Docker Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: myapp:${{ github.sha }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/[email protected]
with:
image-ref: myapp:${{ github.sha }}
format: table
exit-code: 1
severity: CRITICAL,HIGH
ignore-unfixed: true
- name: Run Trivy SBOM generation
uses: aquasecurity/[email protected]
with:
image-ref: myapp:${{ github.sha }}
format: spdx-json
output: sbom.spdx.json
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
نکته کلیدی اینجا پارامتر exit-code: 1 است — اگر آسیبپذیری CRITICAL یا HIGH پیدا شود، کل pipeline متوقف میشود و ایمیج push نخواهد شد.
امضای ایمیج با Docker Content Trust
Docker Content Trust (DCT) با امضای دیجیتال، اصالت ایمیجها را تضمین میکند. وقتی DCT فعال باشد، فقط ایمیجهای امضاشده قابل pull و اجرا هستند:
# فعالسازی Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# Push ایمیج با امضا
docker push myregistry.io/myapp:v1.0
# بررسی امضای ایمیج
docker trust inspect --pretty myregistry.io/myapp:v1.0
نمونه GitLab CI برای اسکن امنیتی
اگر از GitLab استفاده میکنید، این پیکربندی نقطه شروع خوبیست:
stages:
- build
- security-scan
- deploy
build-image:
stage: build
image: docker:27
services:
- docker:27-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
trivy-scan:
stage: security-scan
image:
name: aquasec/trivy:0.59.1
entrypoint: [""]
script:
- trivy image --exit-code 1
--severity CRITICAL,HIGH
--ignore-unfixed
--format table
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
deploy-production:
stage: deploy
needs: ["trivy-scan"]
script:
- echo "Deploying verified image..."
only:
- main
نظارت و ثبت رویدادها در زمان اجرا
امنسازی فقط به مرحله ساخت و استقرار محدود نمیشود. بعد از deploy هم باید مراقب باشید. نظارت مستمر بر رفتار کانتینرها برای شناسایی فعالیتهای مشکوک و پاسخ سریع به حوادث، یک ضرورت است نه یک انتخاب.
استفاده از Docker Events API
Docker Events API رویدادهای چرخه حیات کانتینرها را ثبت میکند — ایجاد، شروع، توقف، حذف و خیلی چیزهای دیگر:
# مشاهده رویدادهای زنده
docker events --filter type=container
# فیلتر بر اساس نوع رویداد
docker events --filter type=container --filter event=start --filter event=die
# خروجی JSON برای پردازش ماشینی
docker events --format '{{json .}}' --since "2026-01-01"
شناسایی تهدید با Falco
Falco ابزار متنباز CNCF برای شناسایی تهدیدات در زمان اجرا است و واقعاً قدرتمنده. با eBPF فراخوانیهای سیستمی را رصد میکند و فعالیتهای مشکوک مثل اجرای shell در کانتینر، دسترسی به فایلهای حساس یا ارتباطات شبکهای غیرعادی را فوراً هشدار میدهد.
# نصب Falco با Helm
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--set falcosidekick.enabled=true \
--set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/..."
نمونهای از قواعد سفارشی Falco برای شناسایی فعالیتهای مشکوک:
- rule: Shell Spawned in Container
desc: Detect shell execution inside a running container
condition: >
spawned_process and
container and
proc.name in (bash, sh, zsh, ash, dash, ksh) and
not container.image.repository in (allowed_shell_images)
output: >
Shell spawned in container
(user=%user.name container=%container.name
image=%container.image.repository
shell=%proc.name parent=%proc.pname
cmdline=%proc.cmdline)
priority: WARNING
tags: [container, shell, mitre_execution]
- rule: Sensitive File Read in Container
desc: Detect reading of sensitive files inside containers
condition: >
open_read and
container and
fd.name pmatch (/etc/shadow, /etc/sudoers, /root/.ssh) and
not proc.name in (sshd, sudo)
output: >
Sensitive file opened for reading in container
(user=%user.name file=%fd.name
container=%container.name image=%container.image.repository)
priority: CRITICAL
tags: [container, filesystem, mitre_credential_access]
محدودسازی منابع
تعیین حدود مصرف منابع هم بخش مهمی از امنسازیست. بدون آن، یک کانتینر آلوده میتواند تمام منابع میزبان را ببلعد:
# محدودسازی حافظه، CPU و تعداد فرآیندها
docker run \
--memory=256m \
--memory-swap=256m \
--cpus=0.5 \
--pids-limit=100 \
--restart=on-failure:3 \
myapp:v1.0
پارامتر --pids-limit تعداد فرآیندهای داخل کانتینر را محدود میکند و عملاً جلوی حملات fork bomb را میگیرد. یک نکته ظریف: تنظیم --memory-swap برابر با --memory باعث غیرفعال شدن swap میشود، که این خودش از تبادل اطلاعات حساس با دیسک جلوگیری میکند.
چکلیست نهایی امنسازی داکر
خب، خلاصه تمام چیزهایی که گفتیم. این چکلیست را بهعنوان مرجع سریع نگه دارید و هر بار قبل از deploy بررسی کنید:
- از ایمیجهای پایه حداقلی استفاده کنید — Alpine، distroless یا scratch را به ایمیجهای کامل ترجیح دهید.
- ساخت چندمرحلهای پیادهسازی کنید — ابزارهای build را از ایمیج نهایی جدا نگه دارید.
- تگ و digest ایمیجها را پین کنید — هرگز از
latestدر محیط تولید استفاده نکنید. - کانتینرها را بدون root اجرا کنید — کاربر اختصاصی با UID/GID مشخص بسازید.
no-new-privilegesرا فعال کنید — از ارتقای سطح دسترسی جلوگیری کنید.- تمام capabilities را حذف کنید — فقط موارد واقعاً لازم را اضافه کنید (
--cap-drop=ALL). - پروفایل Seccomp سفارشی اعمال کنید — فراخوانیهای سیستمی را به حداقل محدود کنید.
- پروفایل AppArmor سفارشی بنویسید — دسترسی به فایلها و شبکه را کنترل کنید.
- فایلسیستم را فقط-خواندنی کنید — از
--read-onlyاستفاده و فقط مسیرهای لازم را باtmpfsقابل نوشتن کنید. - شبکههای اختصاصی بسازید — از شبکه پیشفرض bridge استفاده نکنید و ICC را غیرفعال کنید.
- پورتها را به localhost محدود کنید — فقط پورتهای لازم را روی
127.0.0.1باز کنید. - ایمیجها را قبل از استقرار اسکن کنید — Trivy را در CI/CD یکپارچه کنید.
- ایمیجها را امضا کنید — Docker Content Trust را فعال کنید.
- محدودیت منابع تعیین کنید — حافظه، CPU و تعداد فرآیندها را محدود کنید.
- نظارت زمان اجرا را فعال کنید — از Falco برای شناسایی فعالیتهای مشکوک استفاده کنید.
پرسشهای متداول
آیا داکر بهصورت پیشفرض امن است؟
کوتاه و صریح: خیر. داکر مکانیزمهای امنیتی پایهای مثل namespace isolation و پروفایل Seccomp پیشفرض دارد، ولی اینها برای محیط تولید کافی نیستند. بهصورت پیشفرض کانتینرها با root اجرا میشوند، capabilities غیرضروری مثل NET_RAW فعالاند، شبکه بین کانتینرها باز است و فایلسیستم قابل نوشتن است.
برای رسیدن به سطح امنیتی قابل قبول، باید اقداماتی مثل اجرای بدون root، حذف capabilities اضافی، پروفایلهای سفارشی Seccomp و AppArmor و محدودسازی شبکه را اعمال کنید.
تفاوت Seccomp و AppArmor چیست؟
Seccomp فراخوانیهای سیستمی (syscalls) را فیلتر میکند — یعنی تعیین میکند یک فرآیند مجاز به استفاده از کدام syscallها است. در سطح رابط بین برنامه و هسته عمل میکند.
AppArmor یک سیستم کنترل دسترسی اجباری (MAC) است که دسترسی به فایلها، شبکه و capabilities را بر اساس پروفایلهای تعریفشده محدود میکند.
ساده بگویم: Seccomp میگوید "چه کارهایی مجازی" و AppArmor میگوید "به چه چیزهایی دسترسی داری". این دو مکمل هماند و استفاده همزمان از هر دو قویاً توصیه میشود.
آیا اجرای کانتینر بدون root عملکرد را کاهش میدهد؟
خیر، حداقل نه بهشکل محسوس. اجرای فرآیند با کاربر غیر root داخل کانتینر هیچ تأثیر قابل اندازهگیریای بر عملکرد ندارد. حالت Docker Rootless Mode (که کل daemon را بدون root اجرا میکند) ممکن است سربار بسیار جزئی داشته باشد — کمتر از ۲ درصد — ولی در مقایسه با افزایش امنیت، کاملاً قابل چشمپوشیست. بسیاری از سازمانهای بزرگ هم از Docker rootless در محیط تولید با بار کاری بالا بدون مشکل استفاده میکنند.
چگونه از نشت اطلاعات محرمانه در ایمیجهای داکر جلوگیری کنیم؟
چند قاعده طلایی:
هرگز رمز عبور، کلید API یا گواهینامه را با دستور ENV یا COPY در Dockerfile قرار ندهید — این مقادیر در لایههای ایمیج ذخیره میشوند و قابل استخراجاند. از Docker BuildKit secrets با سینتکس --mount=type=secret استفاده کنید؛ اطلاعات حساس در ایمیج نهایی ذخیره نخواهند شد.
فایل .dockerignore را هم حتماً تنظیم کنید تا فایلهای حساس مثل .env، .git و کلیدهای SSH وارد context ساخت نشوند. برای زمان اجرا هم از Docker Secrets یا ابزارهایی مثل HashiCorp Vault استفاده کنید. و البته ابزارهایی مثل trufflehog یا gitleaks را هم به CI/CD اضافه کنید تا ایمیجها از نظر وجود secrets اسکن شوند.
بهترین ابزار اسکن آسیبپذیری کانتینر کدام است؟
Trivy محبوبترین انتخاب متنباز در ۲۰۲۶ است — سریعست، پایگاه داده جامعی دارد و بهراحتی با CI/CD یکپارچه میشود. گزینههای دیگر هم وجود دارند: Grype از Anchore (سبک و سریع)، Docker Scout (یکپارچه با Docker Desktop) و Snyk Container (تحلیل عمیقتر با پشتیبانی تجاری).
ولی راستش مهمتر از اینکه کدام ابزار را انتخاب کنید، این است که اسکن آسیبپذیری بهصورت خودکار و اجباری در خط لوله CI/CD اجرا شود. هر ابزاری بهتر از هیچ ابزاریست.