Seccomp v Kubernetes: 7 vecí, ktoré potrebujete vedieť od úplného začiatku

Poznámka. preklad.: Predstavujeme vám preklad článku seniorného aplikačného bezpečnostného inžiniera britskej spoločnosti ASOS.com. S ním začína sériu publikácií venovaných zlepšovaniu bezpečnosti v Kubernetes pomocou seccomp. Ak sa čitateľom úvod páči, budeme autora sledovať a pokračovať v jeho budúcich materiáloch na túto tému.

Seccomp v Kubernetes: 7 vecí, ktoré potrebujete vedieť od úplného začiatku

Tento článok je prvým zo série príspevkov o tom, ako vytvoriť profily seccomp v duchu SecDevOps, bez použitia mágie a čarodejníctva. V časti XNUMX sa budem venovať základom a interným detailom implementácie seccomp v Kubernetes.

Ekosystém Kubernetes ponúka širokú škálu spôsobov zabezpečenia a izolácie kontajnerov. Tento článok je o režime Secure Computing, ktorý je tiež známy ako seccomp. Jeho podstatou je filtrovanie systémových volaní dostupných na vykonanie podľa kontajnerov.

Prečo je to dôležité? Kontajner je len proces bežiaci na konkrétnom stroji. A používa jadro rovnako ako iné aplikácie. Ak by kontajnery mohli vykonávať akékoľvek systémové volania, malvér by to veľmi skoro využil na obídenie izolácie kontajnera a ovplyvnil by ďalšie aplikácie: zachytával informácie, menil systémové nastavenia atď.

seccomp profily definujú, ktoré systémové volania by mali byť povolené alebo zakázané. Runtime kontajnera ich aktivuje pri spustení, aby jadro mohlo sledovať ich vykonávanie. Používanie takýchto profilov vám umožňuje obmedziť vektor útoku a znížiť poškodenie, ak akýkoľvek program vo vnútri kontajnera (to znamená vaše závislosti alebo ich závislosti) začne robiť niečo, čo nie je povolené.

Pochopenie základov

Základný profil seccomp obsahuje tri prvky: defaultAction, architectures (Alebo archMap) A 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 určuje predvolený osud akéhokoľvek systémového volania, ktorý nie je špecifikovaný v sekcii syscalls. Aby sme to uľahčili, zamerajme sa na dve hlavné hodnoty, ktoré sa použijú:

  • SCMP_ACT_ERRNO — blokuje vykonanie systémového volania,
  • SCMP_ACT_ALLOW - dovoľuje.

V sekcii architectures sú uvedené cieľové architektúry. Je to dôležité, pretože samotný filter, aplikovaný na úrovni jadra, závisí od identifikátorov systémových volaní a nie od ich mien špecifikovaných v profile. Spustenie kontajnera ich pred použitím priradí k identifikátorom. Ide o to, že systémové volania môžu mať úplne odlišné ID v závislosti od architektúry systému. Napríklad systémové volanie recvfrom (používa sa na príjem informácií zo zásuvky) má ID = 64 na systémoch x64 a ID = 517 na x86. Tu nájdete zoznam všetkých systémových volaní pre architektúry x86-x64.

V sekcii syscalls uvádza všetky systémové volania a určuje, čo sa s nimi má robiť. Môžete napríklad vytvoriť whitelist nastavením defaultAction na SCMP_ACT_ERRNO, a volá v sekcii syscalls priradiť SCMP_ACT_ALLOW. Povolíte teda iba hovory špecifikované v sekcii syscallsa zakázať všetky ostatné. Pre čiernu listinu by ste mali zmeniť hodnoty defaultAction a činy k opaku.

Teraz by sme mali povedať pár slov o nuansách, ktoré nie sú také zrejmé. Upozorňujeme, že nižšie uvedené odporúčania predpokladajú, že na Kubernetes nasadzujete rad podnikových aplikácií a chcete, aby fungovali s čo najmenším množstvom privilégií.

1. AllowPrivilegeEscalation=false

В securityContext kontajner má parameter AllowPrivilegeEscalation. Ak je nainštalovaný v false, kontajnery začnú s (on) trocha no_new_priv. Význam tohto parametra je zrejmý už z názvu: bráni kontajneru spúšťať nové procesy s väčšími oprávneniami, než má on sám.

Vedľajší efekt nastavenia tejto možnosti true (predvolené) je, že modul runtime kontajnera aplikuje profil seccomp na samom začiatku procesu spustenia. Preto musia byť v profile povolené všetky systémové volania potrebné na spustenie interných runtime procesov (napr. nastavenie ID používateľov/skupiny, zrušenie určitých schopností).

Do kontajnera, ktorý robí triviálne veci echo hi, budú potrebné nasledujúce povolenia:

{
    "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)

...namiesto týchto:

{
    "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)

Ale znova, prečo je to problém? Osobne by som sa vyhol pridávaniu nasledujúcich systémových volaní na bielu listinu (pokiaľ nie sú skutočne potrebné): capset, set_tid_address, setgid, setgroups и setuid. Skutočnou výzvou však je, že povolením procesov, nad ktorými nemáte absolútne žiadnu kontrolu, spájate profily s implementáciou kontajnera. Inými slovami, jedného dňa môžete zistiť, že po aktualizácii prostredia spustenia kontajnera (buď vami alebo, čo je pravdepodobnejšie, poskytovateľom cloudových služieb), kontajnery náhle prestanú fungovať.

Tip č. 1: Spustite kontajnery s AllowPrivilegeEscaltion=false. Tým sa zníži veľkosť profilov seccomp a budú menej citlivé na zmeny v prostredí spustenia kontajnera.

2. Nastavenie profilov seccomp na úrovni kontajnera

Profil seccomp je možné nastaviť na úrovni modulu:

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

...alebo na úrovni kontajnera:

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

Upozorňujeme, že vyššie uvedená syntax sa zmení, keď Kubernetes seccomp sa stane GA (táto udalosť sa očakáva v ďalšom vydaní Kubernetes - 1.18 - približne preklad).

Málokto vie, že Kubernetes vždy mal chybačo spôsobilo použitie profilov seccomp pozastaviť kontajner. Runtime prostredie tento nedostatok čiastočne kompenzuje, no tento kontajner z podov nezmizne, keďže slúži na konfiguráciu ich infraštruktúry.

Problém je, že tento kontajner vždy začína s AllowPrivilegeEscalation=true, čo vedie k problémom uvedeným v odseku 1, a to nemožno zmeniť.

Použitím profilov seccomp na úrovni kontajnera sa vyhnete tejto nástrahe a môžete vytvoriť profil, ktorý je prispôsobený konkrétnemu kontajneru. Toto bude musieť byť vykonané, kým vývojári neopravia chybu a nová verzia (možno 1.18?) nebude dostupná pre všetkých.

Tip č. 2: Nastavte profily seccomp na úrovni kontajnera.

V praktickom zmysle toto pravidlo zvyčajne slúži ako univerzálna odpoveď na otázku: „Prečo môj profil seccomp pracuje s docker runale po nasadení do klastra Kubernetes nefunguje?

3. Používajte runtime/default len ​​ako poslednú možnosť

В Kubernetes имеется два варианта встроенных профилей: runtime/default и docker/default. Obe implementuje modul runtime kontajnera, nie Kubernetes. Preto sa môžu líšiť v závislosti od použitého runtime prostredia a jeho verzie.

Inými slovami, v dôsledku zmeny runtime môže mať kontajner prístup k inej skupine systémových volaní, ktoré môže alebo nemusí použiť. Používa sa väčšina runtime Implementácia dockera. Ak chcete používať tento profil, uistite sa, že je pre vás vhodný.

profil docker/default Zastarané od Kubernetes 1.11, preto ho nepoužívajte.

Podľa mňa profil runtime/default dokonale vyhovuje účelu, na ktorý bol vytvorený: ochrana používateľov pred rizikami spojenými s vykonaním príkazu docker run na svojich autách. Pokiaľ však ide o biznis aplikácie bežiace na klastroch Kubernetes, dovolím si tvrdiť, že takýto profil je príliš otvorený a vývojári by sa mali zamerať na vytváranie profilov pre svoje aplikácie (alebo typy aplikácií).

Tip č. 3: Vytvorte profily seccomp pre konkrétne aplikácie. Ak to nie je možné, vytvorte profily pre typy aplikácií, napríklad vytvorte pokročilý profil, ktorý zahŕňa všetky webové rozhrania API aplikácie Golang. Runtime/default používajte len ako poslednú možnosť.

V budúcich príspevkoch sa budem zaoberať tým, ako vytvoriť profily seccomp inšpirované SecDevOps, automatizovať ich a testovať ich v potrubí. Inými slovami, nebudete mať žiadne ospravedlnenie, aby ste neupgradovali na profily špecifické pre aplikáciu.

4. Unconfined NIE JE možnosťou.

Z prvý bezpečnostný audit Kubernetes ukázalo sa, že štandardne seccomp vypnutý. To znamená, že ak nenastavíte PodSecurityPolicy, ktorý to umožní v klastri, budú fungovať všetky pody, pre ktoré nie je definovaný profil seccomp seccomp=unconfined.

Prevádzka v tomto režime znamená, že sa stratí celá vrstva izolácie, ktorá chráni klaster. Tento prístup odborníci na bezpečnosť neodporúčajú.

Tip č. 4: Žiadny kontajner v klastri by nemal byť spustený seccomp=unconfined, особенно в production-средах.

5. "Režim auditu"

Tento bod nie je jedinečný pre Kubernetes, ale stále spadá do kategórie „veci, ktoré by ste mali vedieť predtým, ako začnete“.

Ako sa stáva, vytváranie profilov seccomp bolo vždy náročné a vo veľkej miere sa spolieha na pokusy a omyly. Faktom je, že používatelia jednoducho nemajú možnosť otestovať ich v produkčnom prostredí bez toho, aby riskovali „vypadnutie“ aplikácie.

Po vydaní linuxového jadra 4.14 bolo možné spúšťať časti profilu v režime auditu, zaznamenávať informácie o všetkých systémových volaniach v syslog, ale bez ich blokovania. Tento režim môžete aktivovať pomocou parametra SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp neovplyvní vlákno, ktoré vykonáva systémové volanie, ak nezodpovedá žiadnemu pravidlu vo filtri, ale informácie o systémovom volaní budú zaznamenané.

Tu je typická stratégia používania tejto funkcie:

  1. Povoliť systémové volania, ktoré sú potrebné.
  2. Blokujte hovory zo systému, o ktorých viete, že nebudú užitočné.
  3. Zaznamenajte informácie o všetkých ostatných hovoroch do denníka.

Zjednodušený príklad vyzerá takto:

{
    "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)

Pamätajte však, že musíte zablokovať všetky hovory, o ktorých viete, že nebudú použité a ktoré by mohli potenciálne poškodiť klaster. Dobrým základom na zostavenie zoznamu je úradník Dokumentácia Docker. Podrobne vysvetľuje, ktoré systémové volania sú v predvolenom profile blokované a prečo.

Má to však jeden háčik. Hoci SCMT_ACT_LOG podporovaný linuxovým jadrom od konca roka 2017, vstúpil do ekosystému Kubernetes len relatívne nedávno. Preto na použitie tejto metódy budete potrebovať jadro Linuxu 4.14 a verziu runC nie nižšiu v1.0.0-rc9.

Tip č. 5: Profil režimu auditu na testovanie v produkcii možno vytvoriť kombináciou čiernej a bielej listiny a všetky výnimky je možné zaprotokolovať.

6. Používajte biele listiny

Whitelisting si vyžaduje ďalšie úsilie, pretože musíte identifikovať každý hovor, ktorý môže aplikácia potrebovať, ale tento prístup výrazne zlepšuje bezpečnosť:

Dôrazne sa odporúča použiť prístup na bielu listinu, pretože je jednoduchší a spoľahlivejší. Čierna listina sa bude musieť aktualizovať vždy, keď sa pridá potenciálne nebezpečné systémové volanie (alebo nebezpečný príznak/možnosť, ak je na čiernej listine). Okrem toho je často možné zmeniť zobrazenie parametra bez toho, aby sa zmenila jeho podstata, a tým obísť obmedzenia čiernej listiny.

Pre aplikácie Go som vyvinul špeciálny nástroj, ktorý aplikáciu sprevádza a zhromažďuje všetky hovory uskutočnené počas vykonávania. Napríklad pre nasledujúcu aplikáciu:

package main

import "fmt"

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

... začnime gosystract nasledovne:

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

...a dostaneme nasledujúci výsledok:

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

Zatiaľ je to len príklad – ďalšie podrobnosti o nástrojoch budú nasledovať.

Tip č. 6: Povoľte iba tie hovory, ktoré skutočne potrebujete, a zablokujte všetky ostatné.

7. Položte správne základy (alebo sa pripravte na neočakávané správanie)

Jadro vynúti profil bez ohľadu na to, čo doň napíšete. Aj keď to nie je presne to, čo ste chceli. Napríklad, ak zablokujete prístup k hovorom ako exit alebo exit_group, kontajner sa nebude vedieť správne vypnúť a ani jednoduchý príkaz ako echo hi zaveste hoo na dobu neurčitú. V dôsledku toho získate vysoké využitie procesora v klastri:

Seccomp v Kubernetes: 7 vecí, ktoré potrebujete vedieť od úplného začiatku

V takýchto prípadoch môže prísť na pomoc utilita strace - ukáže, v čom môže byť problém:

Seccomp v Kubernetes: 7 vecí, ktoré potrebujete vedieť od úplného začiatku
sudo strace -c -p 9331

Uistite sa, že profily obsahujú všetky systémové volania, ktoré aplikácia potrebuje za behu.

Tip č. 7: Venujte pozornosť detailom a uistite sa, že všetky potrebné systémové volania sú na bielej listine.

Týmto sa uzatvára prvá časť série článkov o používaní seccompu v Kubernetes v duchu SecDevOps. V nasledujúcich častiach si povieme, prečo je to dôležité a ako proces automatizovať.

PS od prekladateľa

Prečítajte si aj na našom blogu:

Zdroj: hab.com

Pridať komentár