به گزارش از وبسایت cncf 😊

ابزارهای تأیید زنجیره تأمین کانتینر که امروزه به‌طور گسترده استفاده می‌شوند — مانند Kyverno، OPA Gatekeeper و Sigstore Policy Controller — معمولاً به‌عنوان admission webhooks در لایه Kubernetes API عمل می‌کنند. این وب‌هوک‌ها ایجاد کانتینرها را رهگیری می‌کنند، امضاها و گواهی‌ها را بررسی می‌کنند و در نهایت کانتینر را می‌پذیرند یا رد می‌کنند.

با این حال، عملکرد admission webhooks به پیکربندی دقیق و اتصال شبکه وابسته است. selectorهای فضای نام که به‌درستی تنظیم نشده‌اند می‌توانند اعتبارسنجی را نادیده بگیرند، قطع شبکه یا قطع وب‌هوک بین قفل خوشه‌ای یا دور زدن بی‌صدا انتخاب اجباری ایجاد می‌کند، و پادهای static یا دسترسی مستقیم به Kubelet API می‌توانند وب‌هوک‌ها را کاملاً دور بزنند.

اگر تأیید را به لایه‌ای پایین‌تر — یعنی زمان اجرای کانتینر — منتقل کنیم، جایی که هر کانتینر صرف‌نظر از نحوه برنامه‌ریزی باید از آن عبور کند، چه اتفاقی می‌افتد؟ پلاگین NRI زنجیره تأمین دقیقاً برای همین طراحی شده است: در سطح runtime و پیش از شروع هر کانتینر، اعتبارسنجی انجام می‌شود تا راه‌های دورزدن موجود در لایه API را ببندد.

این مسیرهای دورزدن زیر سرور API Kubernetes به روشنی مستندسازی شده‌اند. برای نمونه، پادهای static مستقیماً توسط kubelet مدیریت می‌شوند و حتی اگر admission webhook آنها را نپذیرد، کانتینر می‌تواند اجرا شود. یک پاد با namespace نامعتبر عملاً برای API نامرئی می‌شود و بنابراین از بررسی وب‌هوک‌ها عبور نمی‌کند؛ این وضعیت نشان می‌دهد که وب‌هوک‌ها به‌تنهایی کافی نیستند.

پلاگین NRI زنجیره تأمین از hook همزمان CreateContainer در زمان اجرا استفاده می‌کند. در هر فراخوان CreateContainer این پلاگین مرجع تصویر را استخراج می‌کند، حاشیه‌نویسی‌های runtime را می‌خواند، گواهی‌های تأیید زنجیره تأمین را از رجیستری OCI واکشی می‌کند، آنها را در برابر خط‌مشی فضای نام بررسی می‌کند و سپس تصمیم به مجاز یا رد کردن کانتینر می‌گیرد. این رفتار مکمل فایل containers-policy.json در CRI-O است که امضاهای تصویر را بررسی می‌کند اما محتوای گواهی (مانند منشأ یا وضعیت آسیب‌پذیری) را پوشش نمی‌دهد.

+-----------------------------------------------------------------+
| Kubernetes API                                                   |
| (با دور زدن غلاف های ثابت، دسترسی مستقیم به kubelet یا قطعی ها) |
+-----------------------------------------------------------------+
         v
+-----------------------------------------------------------------+
| زمان اجرا کانتینر                                                 |
| (CRI-O / containerd)                                             |
+-----------------------------------------------------------------+
         |
    Synchronous NRI Hook Call
         v
+-------------------------------------------------------------------+
| پلاگین NRI زنجیره تامین                                            |
| (قبل از شروع کانتینر گواهی‌های SLSA، VEX، و VSA را تأیید می‌کند)   |
+-------------------------------------------------------------------+

Admission webhooks can be skipped, but the NRI hook cannot be bypassed from the API layer because the runtime calls it synchronously for every container. این شامل کانتینرهایی است که از تصاویر از پیش کشیده یا ذخیره‌شده استفاده می‌کنند: اعتبارسنجی هنگام ایجاد کانتینر انجام می‌شود، نه هنگام pull، بنابراین حتی تصاویر کش‌شده ساعاتی پیش نیز قبل از اجرا بررسی می‌شوند. هر کانتینر روی هر گره از مسیر زمان اجرا عبور می‌کند؛ در نتیجه هیچ برچسب فراموش‌شده، نقطه شبکه‌ای برای ایجاد اختلال یا شکاف بین فضاهای نام وجود ندارد.

مدل تهدید این رویکرد فرض می‌کند که یک گره دست‌نخورده باقی مانده است. در صورتی که مهاجمی به سطح روت گره دسترسی یابد، می‌تواند فرآیند پلاگین را متوقف کند، فایل‌های خط‌مشی را جایگزین کند یا NRI را در پیکربندی زمان اجرا غیرفعال کند (مثلاً nri: { disable: true } در containerd یا –enable-nri=false در CRI-O). بنابراین تأیید زمان اجرا شکاف بین سرور API و کانتینر را می‌بندد، اما در برابر گره‌ای که کاملاً در کنترل مهاجم قرار گرفته باشد محافظت نمی‌کند.

چه چیزی تأیید می‌شود؟ این پلاگین سه نوع گواهی را بررسی می‌کند که هر یک به سؤال مشخصی درباره تصویر پاسخ می‌دهند:

SLSA (Software Supply-chain Levels for Artifact) — منشأ (این چگونه ساخته شده است؟): اثبات می‌کند که تصویر توسط سازنده مشخصی، از کد منبع مشخص، در محیط ساخت مشخصی تولید شده است. پلاگین امضای provenance را بررسی می‌کند، هویت سازنده را در برابر فهرست‌های مورد اعتماد تطبیق می‌دهد، مخزن منبع را بررسی می‌کند و نوع ساخت را با استفاده از SLSA Provenance v1 اعتبارسنجی می‌کند.

VEX (Vulnerability Exploitability eXchange) — آیا آسیب‌پذیری‌های شناخته‌شده قابل بهره‌برداری هستند؟: یک سند VEX نشان می‌دهد که آیا یک CVE شناخته‌شده تحت تأثیر است، تحت بررسی است یا از آن مصون است. هر وضعیتی که حاکی از «تأثیر» باشد، اجرای کانتینر را مسدود می‌کند؛ در صورت وجود چند سند VEX، محدودکننده‌ترین وضعیت اعمال می‌شود. پیاده‌سازی از مشخصات OpenVEX v0.2.0 پیروی می‌کند.

VSA (Verification Summary Attestation) — آیا یک مرجع قابل‌اعتماد قبلاً این تصویر را تأیید کرده است؟: VSA از سرویس‌های تأیید ارائه می‌شود که پیش از این SLSA و VEX را کامل بررسی کرده‌اند. اگر یک VSA معتبر و صادرکننده آن از فهرست تأییدکنندگان مورد اعتماد باشد، پلاگین می‌تواند سایر بررسی‌ها را کوتاه‌کرده و فوراً تصویر را بپذیرد؛ این مکانیزم امکان مقیاس‌دهی تأیید در خوشه‌های بزرگ را بدون تکرار کارهای سنگین رمزنگاری در هر گره فراهم می‌کند.

هر سه نوع گواهی از طریق فراخوان OCI Referrers API کشف می‌شوند (با بازگشت مبتنی بر برچسب cosign). امضاها به‌صورت رمزنگاری با استفاده از sigstore-go بررسی می‌شوند و از تأیید بدون کلید (Fulcio/OIDC) و تأیید مبتنی بر کلید پشتیبانی می‌کنند.

Verification Flow & Resilience: جریان از زمانی آغاز می‌شود که runtime قلاب CreateContainer را فرا می‌خواند. افزونه خلاصه (digest) تصویر را استخراج می‌کند، خط‌مشی فضای نام مربوطه را جستجو می‌کند، حافظه پنهان محلی را بررسی می‌کند و در صورت نبود کش، گواهی‌ها را از رجیستری واکشی می‌کند. تصمیمات طراحی کلیدی عبارت‌اند از:

– اولویت VSA: ابتدا بررسی می‌شود؛ یک VSA نامعتبر منجر به رد قطعی می‌شود.
– اجرای موازی: در صورت عدم وجود VSA مطمئن، بررسی‌های SLSA و VEX به‌صورت همزمان انجام می‌شوند.
– کش و پیش‌گرم‌سازی: نتایج بر اساس digest تصویر و فضای نام با TTL قابل تنظیم کش می‌شوند. هنگام راه‌اندازی، افزونه از callback همگام‌سازی NRI برای دریافت کانتینرهای در حال اجرا استفاده کرده و کش را در پس‌زمینه گرم می‌کند تا راه‌اندازی مجدد باعث سیل درخواست‌های رجیستری نشود.
– حذف درخواست‌های همزمان: وقتی چندین کانتینر به یک تصویر ارجاع می‌دهند، تنها یک بررسی اجرا می‌شود و بقیه نتایج را به‌اشتراک می‌گذارند.
– قطع‌کننده‌های مدار و سمافور: برای جلوگیری از خرابی‌های آبشاری در صورت از کار افتادن یک رجیستری و کنترل واکشی‌های موازی و خطاهای گذرا از عقب‌نشینی نمایی استفاده می‌شود.

پیکربندی به دو لایه تقسیم شده است تا تنظیمات عملیاتی (مانند اندازه کش و زمان‌بندی‌ها) از خط‌مشی‌های امنیتی جدا بماند؛ این کار به تیم‌های مختلف اجازه می‌دهد هر لایه را مستقل مدیریت کنند.

verification = "Enforce"
fetch_timeout = "30s"
fetch_failure_policy = "warn"
cache_ttl = "24h"
cache_failure_ttl = "5m"
policy_dir = "/etc/nri-supply-chain/policies"

{
  "trust": {
    "issuers": ["https://token.actions.githubusercontent.com"],
    "sanPatterns": ["https://github.com/saschagrunert/nri-supply-chain/**"],
    "sources": ["github.com::saschagrunert/*"],
    "missingPolicy": "deny"
  },
  "vex": {
    "missingPolicy": "deny"
  }
}

تنظیمات عملیاتی با تغییر زیرساخت تغییر می‌کنند و خط‌مشی‌های امنیتی با نیازهای انطباق تطبیق می‌یابند؛ جدا نگه داشتن این دو به‌معنای مدیریت انعطاف‌پذیرتر است. ⚙️

برای آزمایش محلی، باینری پلاگین از پرچم –verify-image پشتیبانی می‌کند تا بدون اتصال به NRI، یک تصویر را در برابر خط‌مشی بررسی کند؛ پرچم –validate فایل‌های پیکربندی و خط‌مشی را برای خطا بررسی می‌کند:

$ nri-supply-chain --config config.toml --verify-image ghcr.io/saschagrunert/nri-supply-chain:0.1.5

{
  "image": "ghcr.io/saschagrunert/nri-supply-chain",
  "digest": "sha256:1a8b39eeff74b8bb3e20c7f9fa773d4a9935241f7cc4e1217067c8186c2cee3c",
  "namespace": "default",
  "allowed": true,
  "checkResults": [
    {
      "type": "slsa",
      "pass": true,
      "status": "pass",
      "details": "SLSA provenance verified"
    },
    {
      "type": "vex",
      "pass": true,
      "status": "pass",
      "details": "VEX verification passed"
    }
  ]
}

مقدار allowed: true نشان می‌دهد که تصویر توسط یک سازنده قابل‌اعتماد از یک منبع مورد اعتماد ساخته شده و بررسی‌های VEX هیچ خطر بهره‌برداری شناخته‌شده‌ای را نشان نداده‌اند. اگر قصد دارید راستی‌آزمایی زمان اجرا را فعال کنید، پلاگین NRI می‌تواند به‌عنوان یک لایه دفاع در عمق عمل کند و مسیرهای دورزدن وابسته به لایه API را ببندد. 🔐