Seccomp در Kubernetes: 7 چیز که باید از همان ابتدا بدانید

توجه داشته باشید. ترجمه: ترجمه مقاله ای از مهندس ارشد امنیت اپلیکیشن در شرکت بریتانیایی ASOS.com را تقدیم حضورتان می کنیم. با آن، او مجموعه‌ای از انتشارات را آغاز می‌کند که به بهبود امنیت در Kubernetes از طریق استفاده از seccomp اختصاص دارد. اگر خوانندگان مقدمه را دوست داشته باشند، نویسنده را دنبال می کنیم و مطالب بعدی او را در این زمینه ادامه می دهیم.

Seccomp در Kubernetes: 7 چیز که باید از همان ابتدا بدانید

این مقاله اولین مورد از یک سری پست در مورد نحوه ایجاد پروفایل های seccomp با روحیه SecDevOps بدون توسل به جادو و جادو است. در قسمت XNUMX، اصول و جزئیات داخلی پیاده سازی seccomp در Kubernetes را پوشش خواهم داد.

اکوسیستم Kubernetes طیف گسترده ای از راه ها را برای ایمن سازی و جداسازی ظروف ارائه می دهد. این مقاله در مورد حالت محاسبات ایمن است که به آن نیز معروف است seccomp. ماهیت آن فیلتر کردن فراخوان‌های سیستمی موجود برای اجرا توسط کانتینرها است.

چرا مهم است؟ کانتینر فقط فرآیندی است که روی یک ماشین خاص اجرا می شود. و مانند سایر برنامه ها از هسته استفاده می کند. اگر کانتینرها می توانستند هر گونه تماس سیستمی را انجام دهند، خیلی زود بدافزار از این مزیت برای دور زدن انزوا کانتینر استفاده می کند و بر سایر برنامه ها تأثیر می گذارد: اطلاعات را رهگیری می کند، تنظیمات سیستم را تغییر می دهد و غیره.

پروفایل‌های seccomp تعیین می‌کنند که کدام تماس‌های سیستمی باید مجاز یا غیرفعال شوند. Container Runtime زمانی که شروع می شود آنها را فعال می کند تا هسته بتواند بر اجرای آنها نظارت داشته باشد. استفاده از چنین پروفایل هایی به شما امکان می دهد تا بردار حمله را محدود کنید و اگر هر برنامه ای در داخل کانتینر (یعنی وابستگی های شما یا وابستگی های آنها) شروع به انجام کاری کند که مجاز به انجام آن نیست، آسیب را کاهش دهید.

درک اصول

نمایه seccomp اصلی شامل سه عنصر است: defaultAction, architectures (یا archMap) و syscalls:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "sched_yield",
                "futex",
                "write",
                "mmap",
                "exit_group",
                "madvise",
                "rt_sigprocmask",
                "getpid",
                "gettid",
                "tgkill",
                "rt_sigaction",
                "read",
                "getpgrp"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

(medium-basic-seccomp.json)

defaultAction تعیین سرنوشت پیش فرض هر فراخوانی سیستمی که در بخش مشخص نشده است syscalls. برای آسان‌تر کردن کارها، بیایید روی دو مقدار اصلی که استفاده می‌شود تمرکز کنیم:

  • SCMP_ACT_ERRNO - اجرای یک تماس سیستمی را مسدود می کند،
  • SCMP_ACT_ALLOW - اجازه می دهد.

در بخش architectures معماری های هدف ذکر شده است. این مهم است زیرا خود فیلتر، که در سطح هسته اعمال می شود، به شناسه های فراخوانی سیستم بستگی دارد، نه به نام آنها که در نمایه مشخص شده است. زمان اجرای کانتینر آنها را قبل از استفاده با شناسه ها مطابقت می دهد. ایده این است که تماس های سیستمی بسته به معماری سیستم می توانند شناسه های کاملا متفاوتی داشته باشند. مثلا تماس سیستمی recvfrom (برای دریافت اطلاعات از سوکت استفاده می شود) دارای ID = 64 در سیستم های x64 و ID = 517 در x86 است. اینجا می‌توانید فهرستی از تمام فراخوان‌های سیستم برای معماری‌های x86-x64 را پیدا کنید.

در بخش syscalls همه تماس های سیستمی را لیست می کند و مشخص می کند که با آنها چه کاری انجام دهید. به عنوان مثال، می توانید با تنظیم یک لیست سفید ایجاد کنید defaultAction بر SCMP_ACT_ERRNO، و در بخش تماس می گیرد syscalls اختصاص دهد SCMP_ACT_ALLOW. بنابراین، شما فقط به تماس های مشخص شده در بخش اجازه می دهید syscalls، و بقیه را ممنوع کنید. برای لیست سیاه باید مقادیر را تغییر دهید defaultAction و اقدامات خلاف آن.

اکنون باید چند کلمه در مورد نکات ظریفی که چندان واضح نیستند بگوییم. لطفاً توجه داشته باشید که توصیه‌های زیر فرض می‌کنند که شما در حال استقرار یک خط از برنامه‌های تجاری در Kubernetes هستید و می‌خواهید آنها با کمترین امتیاز ممکن اجرا شوند.

1. AllowPrivilegeEscalation=false

В securityContext ظرف یک پارامتر دارد AllowPrivilegeEscalation. اگر در آن نصب شده باشد false، ظروف با (on) بیت no_new_priv. معنی این پارامتر از نام آن مشخص است: این کانتینر از راه اندازی فرآیندهای جدید با امتیازات بیشتر از خود جلوگیری می کند.

یکی از عوارض جانبی این گزینه تنظیم شده است true (پیش‌فرض) این است که زمان اجرا کانتینر، نمایه seccomp را در همان ابتدای فرآیند راه‌اندازی اعمال می‌کند. بنابراین، تمام تماس‌های سیستمی مورد نیاز برای اجرای فرآیندهای زمان اجرا داخلی (به عنوان مثال تنظیم شناسه‌های کاربر/گروه، حذف قابلیت‌های خاص) باید در نمایه فعال شوند.

به ظرفی که کارهای پیش پا افتاده انجام می دهد echo hi، مجوزهای زیر مورد نیاز خواهد بود:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "brk",
                "capget",
                "capset",
                "chdir",
                "close",
                "execve",
                "exit_group",
                "fstat",
                "fstatfs",
                "futex",
                "getdents64",
                "getppid",
                "lstat",
                "mprotect",
                "nanosleep",
                "newfstatat",
                "openat",
                "prctl",
                "read",
                "rt_sigaction",
                "statfs",
                "setgid",
                "setgroups",
                "setuid",
                "stat",
                "uname",
                "write"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

(hi-pod-seccomp.json)

به جای اینها:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "brk",
                "close",
                "execve",
                "exit_group",
                "futex",
                "mprotect",
                "nanosleep",
                "stat",
                "write"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

(hi-container-seccomp.json)

اما دوباره، چرا این یک مشکل است؟ من شخصاً از قرار دادن تماس‌های سیستمی زیر در لیست سفید خودداری می‌کنم (مگر اینکه واقعاً نیازی به آنها وجود داشته باشد): capset, set_tid_address, setgid, setgroups и setuid. با این حال، چالش واقعی این است که با اجازه دادن به فرآیندهایی که مطلقاً هیچ کنترلی روی آنها ندارید، پروفایل‌ها را به اجرای زمان اجرا کانتینر گره می‌زنید. به عبارت دیگر، یک روز ممکن است متوجه شوید که پس از به‌روزرسانی محیط اجرای کانتینر (چه توسط شما یا به احتمال زیاد توسط ارائه‌دهنده خدمات ابری)، کانتینرها به‌طور ناگهانی کار نمی‌کنند.

نکته شماره 1: کانتینرها را با AllowPrivilegeEscaltion=false. این کار باعث کاهش اندازه پروفایل‌های seccomp می‌شود و حساسیت آن‌ها را نسبت به تغییرات در محیط اجرای کانتینر کاهش می‌دهد.

2. تنظیم پروفایل های seccomp در سطح ظرف

نمایه seccomp را می توان در سطح pod تنظیم کرد:

annotations:
  seccomp.security.alpha.kubernetes.io/pod: "localhost/profile.json"

... یا در سطح ظرف:

annotations:
  container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json"

لطفاً توجه داشته باشید که وقتی Kubernetes seccomp شود، نحو فوق تغییر می کند GA خواهد شد (این رویداد در نسخه بعدی Kubernetes - 1.18 - تقریباً ترجمه انتظار می رود).

تعداد کمی از مردم می دانند که Kubernetes همیشه داشته است حشرهکه باعث شد پروفایل های seccomp روی آنها اعمال شود ظرف مکث. محیط زمان اجرا تا حدی این کمبود را جبران می کند، اما این کانتینر از پادها ناپدید نمی شود، زیرا برای پیکربندی زیرساخت آنها استفاده می شود.

مشکل این است که این ظرف همیشه با شروع می شود AllowPrivilegeEscalation=true، منجر به مشکلات بیان شده در بند 1 می شود و این قابل تغییر نیست.

با استفاده از پروفایل‌های seccomp در سطح کانتینر، از این دام جلوگیری می‌کنید و می‌توانید پروفایلی را ایجاد کنید که متناسب با یک ظرف خاص باشد. این کار باید انجام شود تا زمانی که توسعه دهندگان باگ را برطرف کنند و نسخه جدید (شاید 1.18؟) در دسترس همه قرار گیرد.

نکته شماره 2: پروفایل های seccomp را در سطح ظرف تنظیم کنید.

از نظر عملی، این قانون معمولاً به عنوان یک پاسخ جهانی به این سؤال عمل می کند: "چرا نمایه seccomp من با docker runاما بعد از استقرار در یک خوشه Kubernetes کار نمی کند؟

3. از زمان اجرا/پیش فرض فقط به عنوان آخرین راه حل استفاده کنید

Kubernetes دو گزینه برای پروفایل های داخلی دارد: runtime/default и docker/default. هر دو توسط زمان اجرا کانتینر اجرا می شوند، نه Kubernetes. بنابراین، بسته به محیط اجرا و نسخه آن ممکن است متفاوت باشند.

به عبارت دیگر، در نتیجه تغییر در زمان اجرا، کانتینر ممکن است به مجموعه دیگری از تماس های سیستمی دسترسی داشته باشد که ممکن است از آنها استفاده کند یا نکند. اکثر زمان های اجرا استفاده می کنند اجرای داکر. اگر می خواهید از این نمایه استفاده کنید، لطفاً مطمئن شوید که برای شما مناسب است.

نیمرخ docker/default از Kubernetes 1.11 منسوخ شده است، بنابراین از استفاده از آن خودداری کنید.

به نظر من پروفایل runtime/default کاملاً مناسب برای هدفی که برای آن ایجاد شده است: محافظت از کاربران در برابر خطرات مربوط به اجرای یک فرمان docker run روی ماشین هایشان با این حال، وقتی صحبت از برنامه های تجاری در حال اجرا بر روی خوشه های Kubernetes می شود، به جرات می توانم استدلال کنم که چنین نمایه ای بسیار باز است و توسعه دهندگان باید روی ایجاد پروفایل برای برنامه های خود (یا انواع برنامه ها) تمرکز کنند.

نکته شماره 3: پروفایل های seccomp را برای برنامه های خاص ایجاد کنید. اگر این امکان وجود ندارد، پروفایل هایی برای انواع برنامه ایجاد کنید، به عنوان مثال، یک نمایه پیشرفته ایجاد کنید که شامل تمام API های وب برنامه Golang باشد. تنها از زمان اجرا/پیش فرض به عنوان آخرین راه حل استفاده کنید.

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

4. Unconfined یک گزینه نیست.

از اولین ممیزی امنیتی Kubernetes معلوم شد که به طور پیش فرض seccom غیرفعال است. این بدان معناست که اگر تنظیم نکنید PodSecurityPolicy، که آن را در خوشه فعال می کند، همه پادهایی که پروفایل seccomp برای آنها تعریف نشده است در seccomp=unconfined.

عملکرد در این حالت به این معنی است که یک لایه کامل از عایق از بین می رود که از خوشه محافظت می کند. این روش توسط کارشناسان امنیتی توصیه نمی شود.

نکته شماره 4: هیچ ظرفی در خوشه نباید در حال اجرا باشد seccomp=unconfinedبه خصوص در محیط های تولید.

5. "حالت حسابرسی"

این نکته منحصر به Kubernetes نیست، اما همچنان در دسته "چیزهایی که قبل از شروع باید بدانید" قرار می گیرد.

همانطور که اتفاق می افتد، ایجاد پروفایل های seccomp همیشه چالش برانگیز بوده و به شدت به آزمون و خطا متکی است. واقعیت این است که کاربران به سادگی این فرصت را ندارند که آنها را در محیط های تولیدی بدون خطر "افتادن" برنامه آزمایش کنند.

پس از انتشار هسته لینوکس 4.14، امکان اجرای بخش هایی از نمایه در حالت ممیزی، ثبت اطلاعات مربوط به همه تماس های سیستم در syslog، اما بدون مسدود کردن آنها، فراهم شد. با استفاده از پارامتر می توانید این حالت را فعال کنید SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp در صورتی که با قوانین موجود در فیلتر مطابقت نداشته باشد موضوعی که تماس سیستمی را برقرار می کند تأثیری نخواهد داشت، اما اطلاعات مربوط به تماس سیستم ثبت می شود.

در اینجا یک استراتژی معمول برای استفاده از این ویژگی آمده است:

  1. تماس های سیستمی مورد نیاز را مجاز کنید.
  2. مسدود کردن تماس‌های سیستمی که می‌دانید مفید نیستند.
  3. اطلاعات مربوط به همه تماس های دیگر را در گزارش ثبت کنید.

یک مثال ساده شده به این صورت است:

{
    "defaultAction": "SCMP_ACT_LOG",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "sched_yield",
                "futex",
                "write",
                "mmap",
                "exit_group",
                "madvise",
                "rt_sigprocmask",
                "getpid",
                "gettid",
                "tgkill",
                "rt_sigaction",
                "read",
                "getpgrp"
            ],
            "action": "SCMP_ACT_ALLOW"
        },
        {
            "names": [
                "add_key",
                "keyctl",
                "ptrace"
            ],
            "action": "SCMP_ACT_ERRNO"
        }
    ]
}

(medium-mixed-seccomp.json)

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

با این حال، یک شکار وجود دارد. با اينكه SCMT_ACT_LOG از اواخر سال 2017 توسط هسته لینوکس پشتیبانی می‌شود و به تازگی وارد اکوسیستم Kubernetes شده است. بنابراین، برای استفاده از این روش به هسته لینوکس 4.14 و نسخه runC نیاز دارید نسخه 1.0.0-rc9.

نکته شماره 5: نمایه حالت ممیزی برای آزمایش در تولید را می توان با ترکیب لیست های سیاه و سفید ایجاد کرد و همه استثناها را می توان ثبت کرد.

6. از لیست های سفید استفاده کنید

قرار دادن لیست سفید به تلاش بیشتری نیاز دارد زیرا شما باید هر تماسی را که ممکن است برنامه به آن نیاز داشته باشد شناسایی کنید، اما این رویکرد امنیت را تا حد زیادی بهبود می بخشد:

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

برای برنامه های Go، یک ابزار ویژه ایجاد کردم که همراه با برنامه است و تمام تماس های برقرار شده در حین اجرا را جمع آوری می کند. به عنوان مثال، برای برنامه زیر:

package main

import "fmt"

func main() {
	fmt.Println("test")
}

... راه اندازی کنیم gosystract بنابراین:

go install https://github.com/pjbgf/gosystract
gosystract --template='{{- range . }}{{printf ""%s",n" .Name}}{{- end}}' application-path

... و به نتیجه زیر می رسیم:

"sched_yield",
"futex",
"write",
"mmap",
"exit_group",
"madvise",
"rt_sigprocmask",
"getpid",
"gettid",
"tgkill",
"rt_sigaction",
"read",
"getpgrp",
"arch_prctl",

در حال حاضر، این فقط یک مثال است - جزئیات بیشتر در مورد ابزارها در ادامه خواهد آمد.

نکته شماره 6: فقط تماس هایی را مجاز کنید که واقعاً به آنها نیاز دارید و سایر تماس ها را مسدود کنید.

7. پایه های درست را بگذارید (یا برای رفتار غیرمنتظره آماده شوید)

هسته نمایه را بدون توجه به آنچه در آن می نویسید اجرا می کند. حتی اگر دقیقاً آن چیزی که شما می خواستید نباشد. به عنوان مثال، اگر دسترسی به تماس هایی مانند exit یا exit_group، کانتینر نمی تواند به درستی خاموش شود و حتی یک دستور ساده مانند echo hi او را آویزان کنیدo برای مدت نامحدود در نتیجه، مصرف بالای CPU را در کلاستر دریافت خواهید کرد:

Seccomp در Kubernetes: 7 چیز که باید از همان ابتدا بدانید

در چنین مواردی، یک ابزار می تواند به کمک بیاید strace - نشان خواهد داد که مشکل ممکن است چیست:

Seccomp در Kubernetes: 7 چیز که باید از همان ابتدا بدانید
sudo strace -c -p 9331

اطمینان حاصل کنید که پروفایل ها حاوی تمام تماس های سیستمی هستند که برنامه در زمان اجرا به آن نیاز دارد.

نکته شماره 7: به جزئیات توجه کنید و مطمئن شوید که همه تماس های سیستمی لازم در لیست سفید قرار دارند.

این قسمت اول یک سری مقالات در مورد استفاده از seccomp در Kubernetes در روح SecDevOps را به پایان می رساند. در قسمت های بعدی در مورد اینکه چرا این مهم است و چگونه فرآیند را خودکار کنیم صحبت خواهیم کرد.

PS از مترجم

در وبلاگ ما نیز بخوانید:

منبع: www.habr.com

اضافه کردن نظر