Seccomp v Kubernetesu: 7 stvari, ki jih morate vedeti od samega začetka

Opomba. prevod: Predstavljamo vam prevod članka višjega inženirja za varnost aplikacij pri britanskem podjetju ASOS.com. Z njim začenja serijo publikacij, namenjenih izboljšanju varnosti v Kubernetesu z uporabo seccomp. Če bo bralcem všeč uvod, bomo sledili avtorju in nadaljevali z njegovimi prihodnjimi gradivi na to temo.

Seccomp v Kubernetesu: 7 stvari, ki jih morate vedeti od samega začetka

Ta članek je prvi v nizu objav o tem, kako ustvariti profile seccomp v duhu SecDevOps, ne da bi se zatekli k magiji in čarovništvu. V XNUMX. delu bom obravnaval osnove in notranje podrobnosti implementacije seccomp v Kubernetes.

Ekosistem Kubernetes ponuja široko paleto načinov za zavarovanje in izolacijo vsebnikov. Članek govori o varnem računalniškem načinu, znanem tudi kot seccomp. Njegovo bistvo je filtriranje sistemskih klicev, ki so na voljo za izvedbo, po vsebnikih.

Zakaj je pomembno? Vsebnik je samo proces, ki se izvaja na določenem stroju. In uporablja jedro tako kot druge aplikacije. Če bi vsebniki lahko izvajali kakršne koli sistemske klice, bi zelo kmalu zlonamerna programska oprema to izkoristila, da bi zaobšla izolacijo vsebnika in vplivala na druge aplikacije: prestregla informacije, spremenila sistemske nastavitve itd.

Profili seccomp določajo, kateri sistemski klici naj bodo dovoljeni ali onemogočeni. Izvajalno okolje vsebnika jih aktivira, ko se zažene, tako da lahko jedro spremlja njihovo izvajanje. Uporaba takšnih profilov vam omogoča, da omejite vektor napada in zmanjšate škodo, če kateri koli program v vsebniku (to je vaše odvisnosti ali njihove odvisnosti) začne delati nekaj, kar mu ni dovoljeno.

Priti do osnov

Osnovni profil seccomp vključuje tri elemente: defaultAction, architectures (Ali archMap) In 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 določa privzeto usodo katerega koli sistemskega klica, ki ni določen v razdelku syscalls. Da bi olajšali stvari, se osredotočimo na dve glavni vrednosti, ki bosta uporabljeni:

  • SCMP_ACT_ERRNO — blokira izvedbo sistemskega klica,
  • SCMP_ACT_ALLOW - dovoljuje.

V oddelku architectures ciljne arhitekture so navedene. To je pomembno, ker je sam filter, uporabljen na ravni jedra, odvisen od identifikatorjev sistemskih klicev in ne od njihovih imen, navedenih v profilu. Izvajalno okolje vsebnika jih bo pred uporabo primerjalo z identifikatorji. Ideja je, da imajo lahko sistemski klici povsem različne ID-je, odvisno od sistemske arhitekture. Na primer sistemski klic recvfrom (uporablja se za sprejemanje informacij iz vtičnice) ima ID = 64 na sistemih x64 in ID = 517 na x86. Tukaj lahko najdete seznam vseh sistemskih klicev za arhitekture x86-x64.

V razdelku syscalls prikaže seznam vseh sistemskih klicev in določi, kaj storiti z njimi. Na primer, lahko ustvarite beli seznam z nastavitvijo defaultAction o SCMP_ACT_ERRNO, in klice v razdelku syscalls dodeliti SCMP_ACT_ALLOW. Tako dovolite samo klice, ki so navedeni v razdelku syscalls, vse druge pa prepovedati. Za črni seznam bi morali spremeniti vrednosti defaultAction dejanja pa nasprotno.

Zdaj bi morali povedati nekaj besed o niansah, ki niso tako očitne. Upoštevajte, da spodnja priporočila predvidevajo, da uvajate vrsto poslovnih aplikacij v Kubernetes in želite, da se izvajajo z najmanjšo možno količino privilegijev.

1. AllowPrivilegeEscalation=false

В securityContext vsebnik ima parameter AllowPrivilegeEscalation. Če je nameščen v false, vsebniki se bodo začeli z (on) bit no_new_priv. Pomen tega parametra je očiten že iz imena: vsebniku preprečuje zagon novih procesov z več privilegiji, kot jih ima sam.

Stranski učinek te možnosti, ki je nastavljena na true (privzeto) je, da izvajalno okolje vsebnika uporabi profil seccomp na samem začetku zagonskega procesa. Tako morajo biti v profilu omogočeni vsi sistemski klici, potrebni za zagon notranjih izvajalnih procesov (npr. nastavitev ID-jev uporabnikov/skupin, opustitev določenih zmožnosti).

Vsebniku, ki dela trivialne stvari echo hi, bodo potrebna naslednja dovoljenja:

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

...namesto teh:

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

Ampak spet, zakaj je to problem? Osebno bi se izogibal dodajanju naslednjih sistemskih klicev na seznam dovoljenih (razen če zanje obstaja resnična potreba): capset, set_tid_address, setgid, setgroups и setuid. Vendar pa je resnični izziv, da z omogočanjem procesov, nad katerimi nimate prav nobenega nadzora, povezujete profile z izvajanjem izvajalnega okolja vsebnika. Z drugimi besedami, nekega dne boste morda ugotovili, da po posodobitvi izvajalnega okolja vsebnika (bodisi vi ali, bolj verjetno, s strani ponudnika storitev v oblaku), vsebniki nenadoma prenehajo delovati.

Nasvet št: Zaženite vsebnike z AllowPrivilegeEscaltion=false. To bo zmanjšalo velikost profilov seccomp in jih naredilo manj občutljive na spremembe v izvajalnem okolju vsebnika.

2. Nastavitev profilov seccomp na ravni vsebnika

Profil seccomp lahko nastavite na ravni pod:

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

... ali na ravni vsebnika:

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

Upoštevajte, da se bo zgornja sintaksa spremenila, ko bo Kubernetes seccomp bo postal GA (ta dogodek se pričakuje v naslednji izdaji Kubernetesa - 1.18 - pribl. prev.).

Malo ljudi ve, da je Kubernetes vedno imel hroščekzaradi česar so bili uporabljeni profili seccomp vsebnik za premor. Izvajalno okolje delno kompenzira to pomanjkljivost, vendar ta vsebnik ne izgine iz podov, saj se uporablja za konfiguracijo njihove infrastrukture.

Težava je v tem, da se ta vsebnik vedno začne z AllowPrivilegeEscalation=true, kar vodi do težav, navedenih v odstavku 1, in tega ni mogoče spremeniti.

Z uporabo profilov seccomp na ravni vsebnika se izognete tej pasti in lahko ustvarite profil, ki je prilagojen določenemu vsebniku. To bo treba storiti, dokler razvijalci ne odpravijo napake in nova različica (morda 1.18?) ne bo na voljo vsem.

Nasvet št: Nastavite profile seccomp na ravni vsebnika.

V praktičnem smislu to pravilo običajno služi kot univerzalni odgovor na vprašanje: »Zakaj moj profil seccomp deluje z docker runvendar ne deluje po uvedbi v gručo Kubernetes?

3. Uporabite runtime/default samo kot zadnjo možnost

Kubernetes ima dve možnosti za vgrajene profile: runtime/default и docker/default. Oboje implementira izvajalno okolje vsebnika, ne Kubernetes. Zato se lahko razlikujejo glede na uporabljeno okolje izvajanja in njegovo različico.

Z drugimi besedami, zaradi spreminjanja izvajalnega okolja ima lahko vsebnik dostop do drugačnega niza sistemskih klicev, ki jih lahko uporabi ali ne. Večina izvajalnih časov uporablja Izvedba Dockerja. Če želite uporabljati ta profil, se prepričajte, da je primeren za vas.

Profil docker/default je opuščen od Kubernetes 1.11, zato se ga izogibajte uporabi.

Po mojem mnenju profil runtime/default popolnoma primeren za namen, za katerega je bil ustvarjen: zaščita uporabnikov pred tveganji, povezanimi z izvajanjem ukaza docker run na svojih avtomobilih. Ko pa gre za poslovne aplikacije, ki tečejo v gručah Kubernetes, bi si upal trditi, da je tak profil preveč odprt in bi se morali razvijalci osredotočiti na ustvarjanje profilov za svoje aplikacije (ali tipe aplikacij).

Nasvet št: ustvarite profile seccomp za posebne aplikacije. Če to ni mogoče, ustvarite profile za vrste aplikacij, na primer ustvarite napredni profil, ki vključuje vse spletne API-je aplikacije Golang. Runtime/default uporabite samo kot zadnjo možnost.

V prihodnjih objavah bom obravnaval, kako ustvariti profile seccomp, ki jih navdihuje SecDevOps, jih avtomatizirati in preizkusiti v cevovodih. Z drugimi besedami, ne boste imeli izgovora, da ne nadgradite na profile, specifične za aplikacijo.

4. Neomejeno NI možnost.

Od prva varnostna revizija Kubernetes izkazalo se je, da privzeto seccomp onemogočen. To pomeni, da če ne nastavite PodSecurityPolicy, ki ga bo omogočil v gruči, bodo vsi podi, za katere profil seccomp ni definiran, delovali v seccomp=unconfined.

Delovanje v tem načinu pomeni, da je izgubljen celoten sloj izolacije, ki ščiti grozd. Varnostni strokovnjaki tega pristopa ne priporočajo.

Nasvet št: Noben vsebnik v gruči se ne sme izvajati seccomp=unconfined, zlasti v produkcijskih okoljih.

5. "Način revizije"

Ta točka ni edinstvena za Kubernetes, vendar še vedno spada v kategorijo »stvari, ki jih morate vedeti, preden začnete«.

Ustvarjanje profilov seccomp je bilo vedno zahtevno in je v veliki meri odvisno od poskusov in napak. Dejstvo je, da uporabniki preprosto nimajo možnosti, da bi jih preizkusili v produkcijskih okoljih, ne da bi tvegali, da bi aplikacija "padla".

Po izdaji jedra Linuxa 4.14 je postalo mogoče zagnati dele profila v revizijskem načinu, snemati informacije o vseh sistemskih klicih v syslog, vendar brez njihovega blokiranja. Ta način lahko aktivirate s parametrom SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp ne bo vplival na nit, ki izvaja sistemski klic, če se ne ujema z nobenim pravilom v filtru, vendar bodo informacije o sistemskem klicu zabeležene.

Tukaj je tipična strategija za uporabo te funkcije:

  1. Dovoli sistemske klice, ki so potrebni.
  2. Blokirajte klice iz sistema, za katere veste, da ne bodo koristni.
  3. Zabeležite podatke o vseh drugih klicih v dnevnik.

Poenostavljen primer izgleda takole:

{
    "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"
        }
    ]
}

(srednje mešani-seccomp.json)

Ne pozabite pa, da morate blokirati vse klice, za katere veste, da ne bodo uporabljeni in ki bi lahko škodovali gruči. Dobra osnova za sestavo seznama je uradna Dockerjeva dokumentacija. Podrobno pojasnjuje, kateri sistemski klici so blokirani v privzetem profilu in zakaj.

Vendar pa obstaja ena caka. čeprav SCMT_ACT_LOG ki ga podpira jedro Linuxa od konca leta 2017, je v ekosistem Kubernetes vstopil šele relativno nedavno. Zato boste za uporabo te metode potrebovali jedro Linuxa 4.14 in različico runC, ki ni nižja v1.0.0-rc9.

Nasvet št: Profil načina revizije za testiranje v proizvodnji je mogoče ustvariti s kombinacijo črnih in belih seznamov, vse izjeme pa je mogoče zabeležiti.

6. Uporabite bele sezname

Seznam dovoljenih zahteva dodaten napor, ker morate identificirati vsak klic, ki bi ga aplikacija morda potrebovala, vendar ta pristop močno izboljša varnost:

Zelo priporočljivo je, da uporabite pristop seznama dovoljenih, saj je preprostejši in zanesljivejši. Črni seznam bo treba posodobiti vsakič, ko je dodan potencialno nevaren sistemski klic (ali nevarna zastavica/možnost, če je na črnem seznamu). Poleg tega je pogosto mogoče spremeniti predstavitev parametra, ne da bi spremenili njegovo bistvo, in s tem zaobiti omejitve črnega seznama.

Za aplikacije Go sem razvil posebno orodje, ki spremlja aplikacijo in zbira vse klice med izvajanjem. Na primer za naslednjo aplikacijo:

package main

import "fmt"

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

... začnimo gosystract takole:

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

... in dobimo naslednji rezultat:

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

Za zdaj je to samo primer – več podrobnosti o orodjih bo sledilo.

Nasvet št: Dovolite samo klice, ki jih resnično potrebujete, in blokirajte vse ostale.

7. Postavite prave temelje (ali se pripravite na nepričakovano vedenje)

Jedro bo uveljavilo profil ne glede na to, kaj vpišete vanj. Tudi če ni točno tisto, kar ste si želeli. Na primer, če blokirate dostop do klicev, kot je exit ali exit_group, se vsebnik ne bo mogel pravilno zaustaviti in niti preprost ukaz, kot je echo hi obesiti gao za nedoločen čas. Posledično boste v gruči dobili visoko porabo procesorja:

Seccomp v Kubernetesu: 7 stvari, ki jih morate vedeti od samega začetka

V takih primerih lahko na pomoč priskoči komunalno podjetje strace - pokazalo bo, v čem bi lahko bila težava:

Seccomp v Kubernetesu: 7 stvari, ki jih morate vedeti od samega začetka
sudo strace -c -p 9331

Prepričajte se, da profili vsebujejo vse sistemske klice, ki jih aplikacija potrebuje med izvajanjem.

Nasvet št: Bodite pozorni na podrobnosti in se prepričajte, da so vsi potrebni sistemski klici na seznamu dovoljenih.

S tem se zaključuje prvi del serije člankov o uporabi seccomp v Kubernetesu v duhu SecDevOps. V naslednjih delih bomo govorili o tem, zakaj je to pomembno in kako avtomatizirati proces.

PS od prevajalca

Preberite tudi na našem blogu:

Vir: www.habr.com

Dodaj komentar