DOCKER

امن‌سازی Docker: ۱۰ اقدام ضروری برای کانتینرها و میزبان

کاربر غیر root، حذف capabilityها، فایل‌سیستم فقط‌خواندنی، اسکن ایمیج، مدیریت secret و محافظت از Docker socket؛ فهرست عملی امن‌سازی Docker.

کانتینر ماشین مجازی نیست؛ هسته‌ی میزبان را با بقیه‌ی کانتینرها به اشتراک می‌گذارد و ایزوله‌سازی آن به پیکربندی بستگی دارد. تنظیمات پیش‌فرض Docker برای سهولت کار طراحی شده‌اند، نه برای سخت‌ترین سناریوی امنیتی. این راهنما ده اقدام عملی را که بیشترین اثر را دارند، به ترتیب اولویت فهرست می‌کند؛ بیشتر آن‌ها با چند خط در Dockerfile یا Compose قابل‌اجرا هستند.

۱. دسترسی به Docker socket را به کانتینر ندهید

فایل /var/run/docker.sock رابط کنترل دیمن است و هر فرایندی که به آن دسترسی داشته باشد می‌تواند کانتینر دلخواه (حتی با دسترسی کامل به میزبان) بسازد. mount کردن آن در کانتینرهایی مثل ابزارهای مانیتورینگ یا CI، معادل دادن دسترسی root میزبان است.

# خطرناک مگر اینکه واقعاً لازم باشد
volumes:
  - /var/run/docker.sock:/var/run/docker.sock

اگر ناچارید، از یک socket proxy که فقط فراخوانی‌های لازم (مثلاً فقط‌خواندنی) را مجاز می‌کند استفاده کنید. گروه docker را هم مثل root در نظر بگیرید: کاربری که عضو آن است عملاً root میزبان است. API دیمن را هم هرگز روی tcp://0.0.0.0:2375 بدون TLS باز نکنید.

۲. با کاربر غیر root اجرا کنید

در Dockerfile یک کاربر بسازید و USER را تنظیم کنید، یا در Compose کاربر را مشخص کنید:

user: "10001:10001"

برای دسترسی به پورت‌های زیر ۱۰۲۴ نیازی به root نیست؛ می‌توانید پورت بالاتری را در کانتینر استفاده و آن را با ports روی پورت دلخواه میزبان منتشر کنید.

۳. capabilityها را حذف و ارتقای دسترسی را مسدود کنید

Docker به‌طور پیش‌فرض چند capability لینوکس به کانتینر می‌دهد که اغلب برنامه‌ها لازم ندارند. همه را حذف کنید و فقط لازم‌ها را برگردانید:

cap_drop:
  - ALL
cap_add:
  - NET_BIND_SERVICE      # فقط اگر برنامه باید پورت زیر ۱۰۲۴ را باز کند
security_opt:
  - no-new-privileges:true

گزینه‌ی no-new-privileges جلوی افزایش دسترسی فرایندها (مثلاً با باینری‌های setuid) را می‌گیرد. پروفایل‌های پیش‌فرض seccomp و AppArmor را هم غیرفعال نکنید (seccomp=unconfined)؛ این پروفایل‌ها ده‌ها فراخوان خطرناک سیستمی را مسدود می‌کنند.

۴. فایل‌سیستم را فقط‌خواندنی کنید

read_only: true
tmpfs:
  - /tmp
  - /run
volumes:
  - appdata:/var/lib/app      # فقط مسیرهایی که باید نوشته شوند

اگر مهاجم نتواند فایلی در کانتینر بنویسد، نصب ابزار و ماندگارکردن بدافزار سخت‌تر می‌شود. برنامه را یک‌بار با این تنظیم اجرا کنید؛ پیام‌های خطای «read-only file system» دقیقاً مسیرهایی را نشان می‌دهند که باید tmpfs یا volume شوند.

۵. ایمیج کوچک، ثابت‌شده و قابل‌اعتماد

هر بسته‌ی اضافه یعنی احتمال یک آسیب‌پذیری بیشتر. با multi-stage و ایمیج‌های slim یا distroless سطح حمله را کم کنید، تگ دقیق (یا digest) بنویسید و ایمیج پایه را فقط از منابع معتبر بگیرید. جزئیات در این مقاله نیامده؛ راهنمای نوشتن Dockerfile بهینه و امن آن را کامل توضیح می‌دهد.

IMGDocker Image Size Calculatorلایه‌ها و حجم ایمیج را ببینید و Dockerfile را برای مواردی مثل اجرا با root بررسی کنید.

۶. ایمیج‌ها را اسکن کنید

آسیب‌پذیری‌های شناخته‌شده در بسته‌های سیستمی و وابستگی‌های برنامه روزانه کشف می‌شوند؛ ایمیجی که ماه پیش پاک بوده ممکن است امروز نباشد. اسکن را در CI و به‌صورت دوره‌ای برای ایمیج‌های در حال اجرا انجام دهید:

trivy image --severity HIGH,CRITICAL --exit-code 1 registry.example.com/app:1.8.2

گزینه‌ی --exit-code 1 باعث می‌شود پایپ‌لاین با آسیب‌پذیری‌های شدید متوقف شود. هر آسیب‌پذیری لزوماً قابل‌بهره‌برداری نیست؛ هدف، دیدن فهرست و اولویت‌بندی آن است، نه صفرکردن بی‌قید‌وشرط آن.

۷. رمزها را در ایمیج و متغیر محیطی نگذارید

مقدار ENV و ARG در تاریخچه‌ی ایمیج می‌ماند و متغیرهای محیطی با docker inspect دیده می‌شوند. رمزها را با secret mount زمان build، یا در زمان اجرا با Docker secrets/فایل mount‌شده بدهید و در Git نگه ندارید:

services:
  app:
    secrets: [db_password]
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password

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

هنگام مقایسه یا بررسی یکپارچگی فایل‌های دانلودشده (مثلاً باینری‌هایی که در Dockerfile دانلود می‌کنید)، checksum را بررسی کنید.

#Hash Generatorهش SHA-256 یک فایل را حساب کنید و با مقدار منتشرشده‌ی منبع مقایسه کنید.

۸. منابع کانتینر را محدود کنید

یک کانتینر بدون سقف می‌تواند تمام حافظه، CPU یا تعداد فرایندهای میزبان را مصرف و بقیه‌ی سرویس‌ها را از کار بیندازد (چه به‌دلیل باگ، چه حمله‌ی انکار سرویس):

deploy:
  resources:
    limits:
      cpus: "1.0"
      memory: 512M
pids_limit: 256

۹. سطح شبکه را کم کنید

  • فقط پورت‌هایی را منتشر کنید که از بیرون لازم است و در صورت امکان آن‌ها را روی 127.0.0.1 ببندید؛ پورت دیتابیس‌ها را هرگز روی همه‌ی اینترفیس‌ها باز نکنید.
  • سرویس‌های داخلی را در شبکه‌ی internal: true بگذارید تا دسترسی به بیرون نداشته باشند.
  • از network_mode: host فقط با دلیل مشخص استفاده کنید؛ در آن حالت ایزوله‌سازی شبکه وجود ندارد.

توضیح کامل این موضوع‌ها در راهنمای شبکه و Volume در Docker آمده است.

۱۰. میزبان را هم امن کنید

کانتینرها هسته‌ی میزبان را به اشتراک می‌گذارند؛ پس امنیت میزبان بخشی از امنیت کانتینرهاست:

  • هسته و Docker را به‌روز نگه دارید، و برای سرورهای مشترک Docker را در حالت rootless اجرا کنید (dockerd-rootless-setuptool.sh install).
  • ورود با SSH فقط با کلید، بدون ورود root و پشت فایروال.
  • برای اجرای مجدد کانتینرها در زمان به‌روزرسانی دیمن و کنترل لاگ‌ها، در /etc/docker/daemon.json تنظیمات زیر را در نظر بگیرید:
{
  "live-restore": true,
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

اگر این بسته‌ها را روی سرور ایران نصب می‌کنید، راهنمای نصب Docker از مخزن داخلی مراحل کامل را دارد.

چک‌لیست سریع

اقدامتنظیم
بدون rootuser: "10001:10001"
حذف capabilityهاcap_drop: [ALL]
جلوگیری از ارتقای دسترسیno-new-privileges:true
فایل‌سیستم فقط‌خواندنیread_only: true
سقف منابعlimits + pids_limit
اسکن ایمیجtrivy image …
بدون docker.sock در کانتینر—
پورت دیتابیس منتشر نشود127.0.0.1:…

برای بررسی خودکار بخشی از این موارد در فایل Compose خود، از اعتبارسنج زیر کمک بگیرید.

YMLDocker Compose Validatorساختار فایل Compose و کلیدهای ناشناخته یا مقدارهای نامعتبر را بررسی کنید.

جمع‌بندی

امنیت Docker لایه‌ای است: هیچ‌کدام از این اقدام‌ها به‌تنهایی کافی نیستند، ولی ترکیب کاربر غیر root، حذف capabilityها، فایل‌سیستم فقط‌خواندنی، اسکن ایمیج و محدودکردن شبکه، اثر یک نفوذ را به‌شدت کاهش می‌دهد. گام بعدی، بازبینی فایل Compose بر اساس این فهرست است.

پرسش‌های متداول

اجرای کانتینر با --privileged چه خطری دارد؟
این گزینه تقریباً همه‌ی محدودیت‌های ایزوله‌سازی را برمی‌دارد و به کانتینر دسترسی به دستگاه‌های میزبان می‌دهد. نفوذ به چنین کانتینری عملاً به معنای نفوذ به میزبان است. فقط در موارد خاص و با دلیل مستند از آن استفاده کنید.
Rootless Docker چیست و آیا ارزش دارد؟
در حالت rootless، خود دیمن Docker هم بدون دسترسی root اجرا می‌شود؛ پس حتی فرار از کانتینر، مهاجم را root نمی‌کند. محدودیت‌هایی (مثل پورت‌های زیر ۱۰۲۴ و بعضی درایورهای ذخیره‌سازی) دارد، ولی برای سرورهای مشترک و CI گزینه‌ی خوبی است.
اسکن ایمیج را با چه ابزاری انجام دهم؟
ابزارهای متن‌باز مثل Trivy و Grype ایمیج و وابستگی‌ها را با پایگاه آسیب‌پذیری‌ها مقایسه می‌کنند. اسکن را در CI قرار دهید و برای آسیب‌پذیری‌های با شدت بالا build را رد کنید.
آیا امن‌سازی کانتینر جای امن‌سازی میزبان را می‌گیرد؟
نه. کانتینرها هسته‌ی میزبان را به اشتراک می‌گذارند، پس به‌روزرسانی هسته، محدودکردن SSH و فایروال همچنان لازم است. این دو لایه‌ی مکمل‌اند.