Seccomp во Kubernetes: 7 работи што треба да ги знаете од самиот почеток

Забелешка. превод.: Ви го претставуваме преводот на статија од виш инженер за безбедност на апликации во британската компанија ASOS.com. Со него, тој започнува серија публикации посветени на подобрување на безбедноста во Kubernetes преку употреба на seccomp. Доколку на читателите им се допадне воведот, ќе го следиме авторот и ќе продолжиме со неговите идни материјали на оваа тема.

Seccomp во Kubernetes: 7 работи што треба да ги знаете од самиот почеток

Оваа статија е прва во серијата објави за тоа како да креирате seccomp профили во духот на SecDevOps, без прибегнување кон магија и вештерство. Во Дел XNUMX, ќе ги опфатам основите и внатрешните детали за спроведување на seccomp во Kubernetes.

Екосистемот Kubernetes нуди широк спектар на начини за обезбедување и изолирање на контејнерите. Статијата е за безбеден режим на пресметување, познат и како секкомп. Нејзината суштина е да ги филтрира системските повици достапни за извршување по контејнери.

Зошто е важно? Контејнер е само процес кој работи на одредена машина. И го користи кернелот исто како и другите апликации. Ако контејнерите би можеле да извршуваат какви било системски повици, многу наскоро малициозен софтвер ќе го искористи ова за да ја заобиколи изолацијата на контејнерите и да влијае на другите апликации: пресретнување информации, промена на системските поставки итн.

Seccomp профилите дефинираат кои системски повици треба да бидат дозволени или оневозможени. Времето на извршување на контејнерот ги активира кога ќе започне, така што кернелот може да го следи нивното извршување. Користењето на такви профили ви овозможува да го ограничите векторот на напад и да ја намалите штетата доколку некоја програма во контејнерот (т.е. вашите зависности или нивните зависности) почне да прави нешто што не е дозволено да го прави.

Доаѓање до основите

Основниот профил на 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 целните архитектури се наведени. Ова е важно бидејќи самиот филтер, применет на ниво на јадрото, зависи од идентификаторите на системските повици, а не од нивните имиња наведени во профилот. Времето на работа на контејнерот ќе ги усогласи со идентификаторите пред употреба. Идејата е дека системските повици може да имаат сосема различни ID во зависност од архитектурата на системот. На пример, системски повик recvfrom (се користи за примање информации од штекерот) има ID = 64 на x64 системите и ID = 517 на x86. Тука можете да најдете листа на сите системски повици за архитектури x86-x64.

Во делот syscalls ги наведува сите системски повици и одредува што да прави со нив. На пример, можете да креирате бела листа со поставување defaultAction на SCMP_ACT_ERRNO, и повици во делот syscalls додели SCMP_ACT_ALLOW. Така, дозволувате само повици наведени во делот syscalls, и забрани ги сите други. За црната листа треба да ги промените вредностите defaultAction и дејствија на спротивното.

Сега треба да кажеме неколку зборови за нијанси кои не се толку очигледни. Имајте предвид дека препораките подолу претпоставуваат дека распоредувате линија деловни апликации на Kubernetes и сакате тие да работат со најмал можен износ на привилегии.

1. AllowPrivilegeEscalation=неточно

В 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 ќе се поврзе ќе стане ГА (овој настан се очекува во следното издание на Kubernetes - 1.18 - прибл. превод).

Малкумина знаат дека Кубернетес отсекогаш имал бубачкашто предизвика да се применат профилите на seccomp контејнер за пауза. Околината за траење делумно го компензира овој недостаток, но овој контејнер не исчезнува од подлогите, бидејќи се користи за конфигурирање на нивната инфраструктура.

Проблемот е што овој контејнер секогаш започнува со AllowPrivilegeEscalation=true, што доведува до проблемите изразени во став 1, и тоа не може да се промени.

Со користење на seccomp профили на ниво на контејнер, ја избегнувате оваа замка и можете да креирате профил кој е прилагоден на одреден контејнер. Ова ќе треба да се направи додека програмерите не го поправат баг и новата верзија (можеби 1.18?) не стане достапна за сите.

Совет # 2: Поставете seccomp профили на ниво на контејнер.

Во практична смисла, ова правило обично служи како универзален одговор на прашањето: „Зошто мојот seccomp профил работи со docker runно не работи по распоредувањето во кластерот Кубернетес?

3. Користете траење/стандардно само како последно средство

Kubernetes има две опции за вградени профили: runtime/default и docker/default. И двете се имплементирани од времето на траење на контејнерот, а не од Kubernetes. Затоа, тие може да се разликуваат во зависност од користената околина за траење и нејзината верзија.

Со други зборови, како резултат на промена на времето на работа, контејнерот може да има пристап до различен сет на системски повици, кои може или не ги користи. Повеќето работи користат Имплементација на Docker. Доколку сакате да го користите овој профил, проверете дали е соодветен за вас.

Профил docker/default е застарен од Kubernetes 1.11, затоа избегнувајте да го користите.

Според мое мислење, профил runtime/default совршено прилагоден за целта за која е создаден: заштита на корисниците од ризиците поврзани со извршување на команда docker run на нивните автомобили. Меѓутоа, кога станува збор за деловни апликации што работат на кластерите на Kubernetes, би се осмелил да тврдам дека таков профил е премногу отворен и програмерите треба да се фокусираат на креирање профили за нивните апликации (или типови апликации).

Совет # 3: Креирајте seccomp профили за одредени апликации. Ако тоа не е можно, креирајте профили за типови апликации, на пример, креирајте напреден профил кој ги вклучува сите веб API на апликацијата Golang. Користете само траење/стандардно како последно средство.

Во идните објави, ќе објаснам како да креирате seccomp профили инспирирани од SecDevOps, да ги автоматизирате и да ги тестирате во цевководи. Со други зборови, нема да имате изговор да не се надградувате на профили специфични за апликацијата.

4. Неограниченото НЕ е опција.

На првата безбедносна ревизија на Кубернетес испадна дека стандардно seccomp е оневозможен. Ова значи дека ако не поставите PodSecurityPolicy, што ќе го овозможи во кластерот, сите подови за кои не е дефиниран профилот seccomp ќе работат во seccomp=unconfined.

Работењето во овој режим значи дека се губи цел слој на изолација што го штити кластерот. Овој пристап не го препорачуваат експерти за безбедност.

Совет # 4: Ниту еден контејнер во кластерот не треба да работи seccomp=unconfined, особено во производствени средини.

5. „Режим за ревизија“

Оваа точка не е единствена за Kubernetes, но сепак спаѓа во категоријата „работи што треба да ги знаете пред да започнете“.

Како што се случува, создавањето на seccomp профили отсекогаш било предизвикувачко и во голема мера се потпира на обиди и грешки. Факт е дека корисниците едноставно немаат можност да ги тестираат во производствени средини без да ризикуваат да ја „испуштат“ апликацијата.

По објавувањето на кернелот Linux 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"
        }
    ]
}

(средно-мешан-секкомп.json)

Но запомнете дека треба да ги блокирате сите повици за кои знаете дека нема да се користат и кои потенцијално може да му наштетат на кластерот. Добра основа за составување листа е официјалното Докер документација. Детално објаснува кои системски повици се блокирани во стандардниот профил и зошто.

Сепак, постои еден улов. Иако SCMT_ACT_LOG поддржан од кернелот Линукс од крајот на 2017 година, тој влезе во екосистемот Кубернетес релативно неодамна. Затоа, за да го користите овој метод ќе ви треба кернелот на Linux 4.14 и верзијата runC не пониска v1.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 на неопределено време. Како резултат на тоа, ќе добиете висока употреба на процесорот во кластерот:

Seccomp во Kubernetes: 7 работи што треба да ги знаете од самиот почеток

Во такви случаи, комуналната компанија може да дојде до помош strace - ќе покаже што може да биде проблемот:

Seccomp во Kubernetes: 7 работи што треба да ги знаете од самиот почеток
sudo strace -c -p 9331

Проверете дали профилите ги содржат сите системски повици што и се потребни на апликацијата при извршување.

Совет # 7: Обрнете внимание на деталите и проверете дали сите потребни системски повици се на белата листа.

Ова го завршува првиот дел од серијата написи за користење на seccomp во Kubernetes во духот на SecDevOps. Во следните делови ќе зборуваме зошто е ова важно и како да се автоматизира процесот.

PS од преведувач

Прочитајте и на нашиот блог:

Извор: www.habr.com

Додадете коментар