بلاگ/چک‌لیست امنیت Docker روی سرور مجازی و اختصاصی: سخت‌سازی کانتینر و میزبان
به‌روز شده

چک‌لیست امنیت Docker روی سرور مجازی و اختصاصی: سخت‌سازی کانتینر و میزبان

1405/08/150 بازدید
چک‌لیست امنیت Docker روی سرور مجازی و اختصاصی: سخت‌سازی کانتینر و میزبان

چک‌لیست امنیت Docker روی سرور مجازی و اختصاصی: سخت‌سازی کانتینر و میزبان

چک‌لیست امنیت داکر روی سرور مجازی و اختصاصی

نصب Docker روی یک سرور تنها چند دقیقه طول می‌کشد، اما تنظیمات پیش‌فرض آن برای یک محیط عمومی ساخته نشده‌اند. این مطلب یک چک‌لیست امنیت داکر می‌دهد که می‌توانید همین حالا روی سرور مجازی یا اختصاصی خودتان اجرا کنید، بدون اینکه نیاز به تغییر کل معماری باشد.

چرا تنظیمات پیش‌فرض Docker کافی نیستند؟

دیمون Docker با دسترسی root روی میزبان اجرا می‌شود و هر کسی که به سوکت آن دسترسی داشته باشد، عملا معادل root روی کل سرور است، نه فقط روی یک کانتینر. کانتینرها هم به‌طور پیش‌فرض با کاربر root داخل خودشان بالا می‌آیند و مجموعه کاملی از Capabilities لینوکس را در اختیار دارند. اگر یک برنامهٔ داخل کانتینر آسیب‌پذیری داشته باشد، همین دو مورد فاصله بین «نفوذ به یک کانتینر» و «نفوذ به کل سرور» را کوتاه می‌کنند.

خوب است بدانید: هیچ‌کدام از این موردها نیاز به ابزار اضافه یا سرویس پولی ندارند. همه با فلگ‌های خود Docker یا چند خط در docker-compose.yml قابل انجام‌اند.

مقایسهٔ یک کانتینر ناامن و قفل‌نشده در برابر یک کانتینر سخت‌سازی‌شده با قفل و شیلد امنیتی

۱. کاربر root را از داخل کانتینر بیرون بگذارید

کاربر پیش‌فرض اکثر ایمیج‌ها root است، حتی اگر فرآیند داخل کانتینر چیزی بیش از یک وب‌سرور ساده نباشد.

  • در Dockerfile یک کاربر غیر root بسازید و با دستور USER appuser قبل از CMD آن را فعال کنید.
  • اگر ایمیج را تغییر نمی‌دهید، هنگام اجرا از فلگ --user 1000:1000 استفاده کنید.
  • برای میزبان‌هایی که چند کانتینر از منابع نامعتبر اجرا می‌کنند، rootless Docker را در نظر بگیرید؛ در این حالت خود دیمون هم با یک کاربر عادی اجرا می‌شود، نه root.
docker run --user 1000:1000 --name app -d myimage:1.4

۲. Capabilities را به حداقل لازم محدود کنید

هر کانتینر به‌طور پیش‌فرض حدود چهارده Capability لینوکس دارد، در حالی که بیشتر برنامه‌ها به هیچ‌کدام نیاز ندارند. یک وب‌سرور ساده که فقط روی پورت ۸۰۸۰ گوش می‌دهد، نیازی به توانایی تغییر زمان سیستم یا دستکاری شبکه ندارد.

docker run --cap-drop ALL --cap-add NET_BIND_SERVICE --security-opt no-new-privileges -d myimage:1.4

خط بالا همه Capabilityها را خاموش می‌کند و فقط اجازهٔ باز کردن پورت زیر ۱۰۲۴ را برمی‌گرداند. فلگ no-new-privileges هم جلوی برنامه‌هایی را می‌گیرد که با SUID سعی می‌کنند بعدا دسترسی بیشتری بگیرند. برای سخت‌سازی یک قدم جلوتر، پروفایل AppArmor روی سطح میزبان هم فرآیند داخل کانتینر را محدودتر می‌کند.

۳. ایمیج امن انتخاب کنید و آن را اسکن کنید

منبع ایمیج همان‌قدر مهم است که تنظیمات اجرا.

  • از ایمیج‌های رسمی یا Alpine/Distroless شروع کنید؛ هرچه ابزار کمتری داخل ایمیج باشد، سطح حمله کوچک‌تر است.
  • تگ latest را در تولید فراموش کنید؛ یک تگ ثابت مثل 1.4.2 یعنی می‌دانید دقیقا چه نسخه‌ای روی سرور اجرا می‌شود و آپدیت یک تصمیم آگاهانه است، نه یک اتفاق هنگام ری‌استارت.
  • ایمیج را قبل از دیپلوی با docker scan یا Trivy اسکن کنید تا آسیب‌پذیری‌های شناخته‌شده در پکیج‌های داخل آن مشخص شود.
  • اگر رجیستری خودتان را می‌خواهید، راه‌اندازی Harbor اسکن امنیتی و RBAC را روی همان رجیستری انجام می‌دهد.
trivy image myimage:1.4.2

۴. فایروال و پروکسی داکر را درست بچینید

یکی از خطاهای رایج این است که پورت کانتینر مستقیم روی سرور باز می‌شود، بدون هیچ لایهٔ میانی.

  • هیچ‌وقت سوکت Docker را روی پورت شبکه (۲۳۷۵) باز نگذارید؛ اگر نیاز به دسترسی ریموت دارید، فقط از طریق TLS و گواهی کلاینت وصل شوید.
  • پورت کانتینرها را به‌جای 0.0.0.0 روی 127.0.0.1 باز کنید (مثلا -p 127.0.0.1:3000:3000) و دسترسی عمومی را به یک داکر پروکسی مثل Caddy یا nginx بسپرید. همین یک تنظیم، صفحهٔ ادمین یا دیتابیس داخل کانتینر را از دید اینترنت مخفی می‌کند.
  • فایروال سطح سرور (nftables یا ufw) را فقط روی پورت‌های ۲۲، ۸۰ و ۴۴۳ باز نگه دارید؛ Docker به‌طور پیش‌فرض قوانین iptables خودش را اضافه می‌کند، پس بعد از هر تغییر فایروال، قوانین را دوباره چک کنید.
  • اگر چند کانتینر باید با هم حرف بزنند، یک شبکهٔ سفارشی با docker network create بسازید؛ این‌طور فقط کانتینرهای همان شبکه به هم دسترسی دارند، نه همهٔ کانتینرهای سرور.

راه‌اندازی Caddy به‌عنوان ریورس پروکسی یک نمونهٔ آمادهٔ همین الگو است: HTTPS خودکار جلوی کانتینرها، بدون این‌که هیچ کانتینر مستقیم به اینترنت متصل باشد.

۵. محدودیت منابع و فایل‌سیستم فقط‌خواندنی

بدون محدودیت، یک کانتینر معیوب یا هک‌شده می‌تواند کل رم یا CPU سرور را مصرف کند و روی کانتینرهای دیگر اثر بگذارد.

services:
  app:
    image: myimage:1.4.2
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    read_only: true
    tmpfs:
      - /tmp
    pids_limit: 200

read_only: true اجازهٔ نوشتن روی فایل‌سیستم کانتینر را می‌بندد، به‌جز مسیرهایی که با tmpfs صریحا باز می‌کنید. این یعنی حتی اگر یک فرآیند مخرب در کانتینر اجرا شود، نمی‌تواند باینری یا اسکریپت جدیدی روی دیسک بنویسد. pids_limit هم جلوی fork bomb را می‌گیرد که می‌تواند کل سرور را از کار بیندازد.

۶. دسترسی به Docker socket را جدی بگیرید

مانت کردن /var/run/docker.sock داخل یک کانتینر دیگر، یکی از پرخطرترین کارهای رایج است؛ هر کسی که به آن کانتینر دسترسی پیدا کند، عملا کنترل کامل دیمون Docker و در نتیجه کل سرور را دارد.

حالتسطح دسترسیکاربرد واقعی
مانت کامل docker.sock در کانتینرمعادل root روی میزبانفقط ابزارهای مانیتورینگ قابل‌اعتماد مثل Portainer، آن هم پشت احراز هویت
عضویت کاربر در گروه dockerمعادل root بدون sudoباید محدود به ادمین‌های سرور باشد، نه هر کاربر
rootless Dockerبدون نیاز به root برای خود دیمونمناسب سرور اشتراکی بین چند تیم روی یک میزبان

اگر فقط به یک داشبورد مدیریتی نیاز دارید، Portainer همین کار را با یک پنل گرافیکی و کنترل دسترسی انجام می‌دهد؛ باز هم پشت یک رمز قوی و ترجیحا پشت همان ریورس پروکسی.

۷. میزبان را هم مثل کانتینر سخت‌سازی کنید

امنیت داکر فقط به تنظیمات کانتینر محدود نمی‌شود؛ خود سرور لینوکسی که Docker روی آن نصب است، پایهٔ همهٔ این لایه‌هاست.

  • دسترسی SSH با کلید، نه رمز؛ راهنمای کامل امن‌سازی SSH مراحل را قدم‌به‌قدم توضیح می‌دهد.
  • Docker Engine و کرنل را به‌روز نگه دارید؛ خیلی از آسیب‌پذیری‌های کانتینر از یک باگ کرنل قدیمی یا نسخهٔ پایین Docker شروع می‌شوند، نه از کد خود برنامه.
  • لاگ کانتینرها را به یک مسیر مرکزی بفرستید (مثلا با json-file با سقف حجم، یا یک درایور خارجی) تا رشد بی‌پایان لاگ دیسک سرور را پر نکند.
  • بین VPS و سرور اختصاصی، سطح ایزوله‌سازی پیش‌فرض فرق دارد:
موردسرور مجازی (VPS)سرور اختصاصی
ایزوله‌سازی از همسایه‌هاهایپروایزر منابع شما را از VPSهای دیگر جدا می‌کندکل سخت‌افزار فقط برای شماست
خطر یک آسیب‌پذیری کرنل میزبانمحدودتر، چون کرنل مشترک بین چند مشتری استریسک فقط متوجه کانتینرهای خودتان است
مناسب برایچند کانتینر با بار متوسط، شروع سریعده‌ها کانتینر یا بار سنگین روی منابع ثابت

این تفاوت در نحوهٔ مجازی‌سازی است، نه در این‌که کدام تنظیمات بالا را نیاز دارید؛ هر دو مدل، همهٔ موردهای این چک‌لیست را عینا لازم دارند.

جمع‌بندی

امنیت داکر یک تنظیم یک‌باره نیست، یک الگوی کار است: کاربر غیر root، Capabilities محدود، ایمیج تگ‌شده و اسکن‌شده، پورت‌ها پشت فایروال و پروکسی، و دسترسی به socket فقط برای افرادی که به آن نیاز دارند. شروع از همین چند مورد، بخش بزرگی از سطح حمله یک سرور Docker را می‌بندد.

اگر برای اجرای این تنظیمات به یک سرور تازه نیاز دارید، یک سرور مجازی آریانت برای شروع سریع با چند کانتینر کافی است؛ برای محیط‌هایی با تعداد کانتینر بالا یا نیاز به منابع ثابت، سرور اختصاصی ایزوله‌سازی کامل سخت‌افزاری می‌دهد.

سؤالات متداول

آیا rootless Docker را باید روی همهٔ سرورها فعال کنم؟ نه همیشه. برای یک سرور اختصاصی به یک تیم، دسترسی کنترل‌شده به گروه docker معمولا کافی است. روی سرورهایی که چند تیم یا پروژهٔ نامعتبر کانتینر اجرا می‌کنند، rootless Docker ارزش تنظیم اضافه را دارد.

فرق امنیت کانتینر با امنیت معمول سرور لینوکس چیست؟ لایهٔ سرور (فایروال، SSH، آپدیت کرنل) همچنان لازم است، اما کانتینر یک لایهٔ اضافه دارد: کاربر داخل ایمیج، Capabilities، و دسترسی به Docker socket. هرکدام از این‌ها باید جدا بررسی شوند.

آیا باز گذاشتن پورت دیمون Docker روی شبکه همیشه خطرناک است؟ در عمل بله، مگر پشت TLS و احراز هویت کلاینت باشد. راه امن‌تر این است که به جای باز کردن پورت ۲۳۷۵ روی شبکه، از SSH یا یک VPN برای مدیریت ریموت استفاده کنید.

برای اسکن امنیتی ایمیج‌ها از کجا شروع کنم؟ Trivy رایگان و ساده است و با یک دستور، لیست آسیب‌پذیری‌های شناخته‌شده در پکیج‌های داخل ایمیج را می‌دهد. آن را در مرحلهٔ build، قبل از push به رجیستری اجرا کنید تا ایمیج آسیب‌پذیر اصلا به سرور نرسد.