Seccomp v Kubernetes: 7 věcí, které potřebujete vědět od samého začátku

Poznámka. přel.: Představujeme vám překlad článku vedoucího aplikačního bezpečnostního inženýra britské společnosti ASOS.com. Tím zahajuje sérii publikací věnovaných zlepšování zabezpečení v Kubernetes pomocí seccomp. Pokud se čtenářům úvod bude líbit, budeme autora sledovat a pokračovat v jeho budoucích materiálech na toto téma.

Seccomp v Kubernetes: 7 věcí, které potřebujete vědět od samého začátku

Tento článek je prvním ze série příspěvků o tom, jak vytvořit profily seccomp v duchu SecDevOps, aniž byste se uchýlili k magii a čarodějnictví. V části XNUMX se budu zabývat základy a interními detaily implementace seccompu v Kubernetes.

Ekosystém Kubernetes nabízí širokou škálu způsobů, jak zabezpečit a izolovat kontejnery. Tento článek je o režimu Secure Computing Mode, známém také jako seccomp. Jeho podstatou je filtrovat systémová volání dostupná pro provádění kontejnery.

Proč je to důležité? Kontejner je pouze proces běžící na konkrétním počítači. A používá jádro stejně jako jiné aplikace. Pokud by kontejnery mohly provádět jakákoli systémová volání, velmi brzy by toho malware využil k obejití izolace kontejneru a ovlivnil další aplikace: zachytil informace, změnil nastavení systému atd.

seccomp profily definují, která systémová volání by měla být povolena nebo zakázána. Runtime kontejneru je aktivuje při svém spuštění, aby jádro mohlo sledovat jejich provádění. Použití takových profilů vám umožňuje omezit vektor útoku a snížit poškození, pokud jakýkoli program uvnitř kontejneru (tj. vaše závislosti nebo jejich závislosti) začne dělat něco, co není povoleno.

Dostat se k základům

Základní profil seccomp obsahuje tři prvky: defaultAction, architectures (nebo 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 výchozí osud jakéhokoli systémového volání, které není uvedeno v této části syscalls. Abychom to usnadnili, zaměřme se na dvě hlavní hodnoty, které budou použity:

  • SCMP_ACT_ERRNO — blokuje provedení systémového volání,
  • SCMP_ACT_ALLOW - umožňuje.

V sekci architectures jsou uvedeny cílové architektury. To je důležité, protože samotný filtr, použitý na úrovni jádra, závisí na identifikátorech systémových volání a ne na jejich jménech uvedených v profilu. Runtime kontejneru je před použitím přiřadí k identifikátorům. Myšlenka je taková, že systémová volání mohou mít zcela odlišná ID v závislosti na architektuře systému. Například systémové volání recvfrom (používá se k příjmu informací ze soketu) má ID = 64 na systémech x64 a ID = 517 na x86. Zde můžete najít seznam všech systémových volání pro architektury x86-x64.

V sekci syscalls uvádí všechna systémová volání a určuje, co s nimi dělat. Můžete například vytvořit whitelist nastavením defaultAction na SCMP_ACT_ERRNO, a volá v sekci syscalls přiřadit SCMP_ACT_ALLOW. Povolíte tedy pouze volání specifikovaná v sekci syscallsa zakázat všechny ostatní. U černé listiny byste měli změnit hodnoty defaultAction a činy k opaku.

Nyní bychom měli říci pár slov o nuancích, které nejsou tak zřejmé. Vezměte prosím na vědomí, že níže uvedená doporučení předpokládají, že nasazujete řadu obchodních aplikací na Kubernetes a chcete, aby běžely s co nejmenším množstvím oprávnění.

1. AllowPrivilegeEscalation=false

В securityContext kontejner má parametr AllowPrivilegeEscalation. Pokud je nainstalován v false, kontejnery budou začínat (on) bit no_new_priv. Význam tohoto parametru je zřejmý z názvu: zabraňuje kontejneru spouštět nové procesy s více oprávněními, než má on sám.

Vedlejším efektem nastavení této možnosti true (výchozí) je, že běhový modul kontejneru aplikuje profil seccomp na samém začátku procesu spouštění. V profilu tedy musí být povolena všechna systémová volání potřebná ke spuštění interních runtime procesů (např. nastavení ID uživatelů/skupin, zrušení určitých schopností).

Do kontejneru, který dělá triviální věci echo hi, budou vyžadována následující oprávnění:

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

...místo 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 znovu, proč je to problém? Osobně bych se vyhnul přidávání následujících systémových volání na seznam povolených (pokud je skutečně nepotřebujete): capset, set_tid_address, setgid, setgroups и setuid. Skutečnou výzvou však je, že povolením procesů, nad kterými nemáte absolutně žádnou kontrolu, spojujete profily s implementací kontejneru runtime. Jinými slovy, jednoho dne můžete zjistit, že po aktualizaci běhového prostředí kontejneru (buď vámi, nebo spíše poskytovatelem cloudových služeb) kontejnery náhle přestanou běžet.

Rada číslo 1: Spusťte kontejnery s AllowPrivilegeEscaltion=false. Tím se zmenší velikost profilů seccomp a budou méně citlivé na změny v běhovém prostředí kontejneru.

2. Nastavení profilů seccomp na úrovni kontejneru

Profil seccomp lze nastavit na úrovni modulu:

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

...nebo na úrovni kontejneru:

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

Vezměte prosím na vědomí, že výše uvedená syntaxe se změní, když Kubernetes seccomp se stane GA (tato událost se očekává v příštím vydání Kubernetes - 1.18 - cca překlad).

Málokdo ví, že Kubernetes vždy měl chybacož způsobilo použití profilů seccomp pozastavit kontejner. Runtime prostředí tento nedostatek částečně kompenzuje, ale tento kontejner z podů nezmizí, protože slouží ke konfiguraci jejich infrastruktury.

Problém je v tom, že tento kontejner vždy začíná AllowPrivilegeEscalation=true, což vede k problémům uvedeným v odstavci 1, a to nelze změnit.

Použitím profilů seccomp na úrovni kontejneru se tomuto úskalí vyhnete a můžete vytvořit profil, který je přizpůsoben konkrétnímu kontejneru. To bude muset být provedeno, dokud vývojáři chybu neopraví a nová verze (možná 1.18?) bude dostupná všem.

Rada číslo 2: Nastavte profily seccomp na úrovni kontejneru.

V praktickém smyslu toto pravidlo obvykle slouží jako univerzální odpověď na otázku: „Proč můj profil seccomp pracuje s docker runale nefunguje po nasazení do clusteru Kubernetes?

3. Runtime/default používejte pouze jako poslední možnost

Kubernetes má dvě možnosti pro vestavěné profily: runtime/default и docker/default. Oba jsou implementovány modulem runtime kontejneru, nikoli Kubernetes. Mohou se proto lišit v závislosti na použitém runtime prostředí a jeho verzi.

Jinými slovy, v důsledku změny běhového prostředí může mít kontejner přístup k jiné sadě systémových volání, které může nebo nemusí používat. Většina runtime použití Implementace dockeru. Pokud chcete tento profil používat, ujistěte se, že je pro vás vhodný.

Profil docker/default byl od verze Kubernetes 1.11 zastaralý, proto jej nepoužívejte.

Podle mě profil runtime/default dokonale vyhovuje účelu, pro který byl vytvořen: ochrana uživatelů před riziky spojenými s prováděním příkazu docker run na jejich autech. Pokud však jde o podnikové aplikace běžící na clusterech Kubernetes, dovolil bych si tvrdit, že takový profil je příliš otevřený a vývojáři by se měli zaměřit na vytváření profilů pro své aplikace (nebo typy aplikací).

Rada číslo 3: Vytvořte profily seccomp pro konkrétní aplikace. Pokud to není možné, vytvořte profily pro typy aplikací, například vytvořte pokročilý profil, který zahrnuje všechna webová rozhraní API aplikace Golang. Runtime/default používejte pouze jako poslední možnost.

V budoucích příspěvcích se budu zabývat tím, jak vytvářet profily seccomp inspirované SecDevOps, automatizovat je a testovat v potrubí. Jinými slovy, nebudete mít žádnou omluvu, abyste neupgradovali na profily specifické pro aplikaci.

4. Unconfined NENÍ možnost.

Z první bezpečnostní audit Kubernetes ukázalo se, že standardně seccomp zakázán. To znamená, že pokud nenastavíte PodSecurityPolicy, který to povolí v clusteru, budou fungovat všechny pody, pro které není definován profil seccomp seccomp=unconfined.

Provoz v tomto režimu znamená, že se ztratí celá vrstva izolace, která chrání cluster. Bezpečnostní experti tento přístup nedoporučují.

Rada číslo 4: Žádný kontejner v clusteru by neměl být spuštěn seccomp=unconfined, zejména v produkčním prostředí.

5. "Režim auditu"

Tento bod není pro Kubernetes jedinečný, ale stále spadá do kategorie „věci, které byste měli vědět, než začnete“.

Jak už to tak bývá, vytváření profilů seccomp bylo vždy náročné a hodně se spoléhá na pokusy a omyly. Faktem je, že uživatelé jednoduše nemají možnost je otestovat v produkčním prostředí, aniž by riskovali „vysazení“ aplikace.

Po vydání linuxového jádra 4.14 bylo možné spouštět části profilu v režimu auditu, zaznamenávat informace o všech systémových voláních do syslog, ale bez jejich blokování. Tento režim můžete aktivovat pomocí parametru SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp neovlivní vlákno provádějící systémové volání, pokud neodpovídá žádnému pravidlu ve filtru, ale informace o systémovém volání budou zaznamenány.

Zde je typická strategie použití této funkce:

  1. Povolte potřebná systémová volání.
  2. Blokujte hovory ze systému, o kterých víte, že nebudou užitečné.
  3. Zaznamenejte informace o všech ostatních hovorech do protokolu.

Zjednodušený příklad vypadá 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)

Pamatujte však, že musíte zablokovat všechna volání, o kterých víte, že nebudou použita a která by mohla potenciálně poškodit cluster. Dobrým základem pro sestavení seznamu je úředník Docker dokumentace. Podrobně vysvětluje, která systémová volání jsou ve výchozím profilu blokována a proč.

Má to však jeden háček. Ačkoli SCMT_ACT_LOG podporovaný linuxovým jádrem od konce roku 2017, vstoupil do ekosystému Kubernetes teprve relativně nedávno. Proto, abyste mohli použít tuto metodu, budete potřebovat linuxové jádro 4.14 a verzi runC ne nižší v1.0.0-rc9.

Rada číslo 5: Profil režimu auditu pro testování v produkci lze vytvořit kombinací černé a bílé listiny a všechny výjimky lze zaprotokolovat.

6. Používejte bílé listiny

Whitelisting vyžaduje další úsilí, protože musíte identifikovat každé volání, které aplikace může potřebovat, ale tento přístup výrazně zlepšuje zabezpečení:

Důrazně se doporučuje používat přístup na seznam povolených, protože je jednodušší a spolehlivější. Černá listina bude muset být aktualizována vždy, když je přidáno potenciálně nebezpečné systémové volání (nebo nebezpečný příznak/volba, pokud je na černé listině). Kromě toho je často možné změnit zobrazení parametru, aniž by se změnila jeho podstata, a obejít tak omezení černé listiny.

Pro aplikace Go jsem vyvinul speciální nástroj, který aplikaci doprovází a shromažďuje všechna volání uskutečněná během provádění. Například pro následující aplikaci:

package main

import "fmt"

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

... pojďme spustit gosystract následovně:

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

...a dostaneme následující výsledek:

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

Prozatím je to jen příklad – další podrobnosti o nástrojích budou následovat.

Rada číslo 6: Povolte pouze ty hovory, které skutečně potřebujete, a zablokujte všechny ostatní.

7. Položte správné základy (nebo se připravte na neočekávané chování)

Jádro vynutí profil bez ohledu na to, co do něj napíšete. I když to není přesně to, co jste chtěli. Pokud například zablokujete přístup k hovorům jako exit nebo exit_group, kontejner se nebude moci správně vypnout a ani jednoduchý příkaz jako echo hi pověsit hoo na dobu neurčitou. V důsledku toho získáte vysoké využití CPU v clusteru:

Seccomp v Kubernetes: 7 věcí, které potřebujete vědět od samého začátku

V takových případech může pomocná společnost přijít na pomoc strace - ukáže, v čem může být problém:

Seccomp v Kubernetes: 7 věcí, které potřebujete vědět od samého začátku
sudo strace -c -p 9331

Ujistěte se, že profily obsahují všechna systémová volání, která aplikace potřebuje za běhu.

Rada číslo 7: Věnujte pozornost detailům a ujistěte se, že všechna nezbytná systémová volání jsou na seznamu povolených.

Tímto uzavíráme první část série článků o používání seccompu v Kubernetes v duchu SecDevOps. V následujících dílech si povíme, proč je to důležité a jak proces automatizovat.

PS od překladatele

Přečtěte si také na našem blogu:

Zdroj: www.habr.com

Přidat komentář