Seccomp në Kubernetes: 7 gjëra që duhet të dini që në fillim

Shën. përkth.: Ne prezantojmë përkthimin e artikullit nga inxhinieri më i lartë i sigurisë së aplikacioneve të kompanisë britanike ASOS.com. Me të fillon një cikël publikimesh që kanë për temë rritjen e sigurisë në Kubernetes falë përdorimit të seccomp. Nëse hyrja i pëlqen lexuesve, ne do ta ndjekim autorin dhe do të vazhdojmë me materialet e tij të ardhshme mbi këtë temë.

Seccomp në Kubernetes: 7 gjëra që duhet të dini që në fillim

Ky artikull është i pari në një seri publikimesh mbi mënyrën e krijimit të profileve seccomp në frymën e SecDevOps, pa u mbështetur në magji dhe zhgënjime. Në pjesën e parë do të flas për bazat dhe detajet e brendshme të implementimit të seccomp në Kubernetes.

Ekosistemi Kubernetes ofron një shumëllojshmëri të mjaftueshme mënyrash për të siguruar sigurinë dhe izolimin e kontejnerëve. Artikulli është dedikuar Modit të Sigurt të Llogaritjes, i njohur gjithashtu si seccomp. Thelbi i tij konsiston në filtrimin e thirrjeve sistemore, të cilat janë të disponueshme për t'u ekzekutuar nga kontejnerët.

Pse është kjo e rëndësishme? Një kontejner është thjesht një proces i caktuar, i aktivizuar në një makinë të caktuar. Ai përdor kernelin në përputhje me aplikacionet e tjera. Nëse kontejnerët do të mund të ekzekutonin çdo thirrje sistemore, shumë shpejt do të shfrytëzoheshin nga programe të dëmshme për të kaluar izolimin e kontejnerit dhe për të ndikuar në aplikacione të tjera: për të përvetësuar informacione, për të modifikuar parametrat e sistemit dhe kështu me radhë.

Profilet seccomp përcaktojnë cilat thirrje sistemore duhet të lejohen ose ndalohen. Mjedisi i ekzekutimit të kontejnerit aktivizon ato gjatë aktivizimit të tij, në mënyrë që kernel të mund të bëjë kontroll mbi ekzekutimin e tyre. Përdorimi i profileve të tilla lejon të kufizoni vektorët e sulmit dhe të zvogëloni dëmin në rast se ndonjë program brenda kontejnerit (dmth varësitë tuaja, ose varësitë e tyre) fillon të bëjë atë që nuk i lejohet.

Të kuptojmë bazat

Profili bazë i seccomp përfshin tre elemente: defaultAction, architectures (ose archMap) dhe 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 përcakton fatin e paracaktuar të çdo thirrjeje sistemore që nuk është e specifikuar në seksion syscalls. Për të thjeshtuar detyrën, le të përqendrohemi në dy kuptime kryesore që do të përdoren:

  • SCMP_ACT_ERRNO — ndalon ekzekutimin e thirrjes sĂ« sistemit,
  • SCMP_ACT_ALLOW — lejon.

Në seksionin architectures listohen arkitekturën e synuar. Kjo është e rëndësishme, pasi filtri vetë, që aplikohet në nivelin e bërthamës, varet nga identifikatorët e thirrjeve të sistemit, dhe jo nga emrat e tyre të shkruar në profil. Para se të aplikohet, mjedisi i ekzekutimit të kontejnerëve do t'i mapojë ato me identifikatorët. Qëllimi është se thirrjet e sistemit mund të kenë ID të ndryshme varësisht nga arkitektura e sistemit. Për shembull, thirrja e sistemit recvfrom (përdoret për të marrë informacion nga një socket) ka ID = 64 në sistemet x64 dhe ID = 517 në x86. Këtu mund të gjeni një listë të gjitha thirrjeve të sistemit për arkitat x86-x64.

Në seksionin syscalls listohen të gjitha thirrjet e sistemit dhe shpjegohet se çfarë duhet të bëni me to. Për shembull, mund të krijoni një listë të bardhë duke vendosur defaultAction në SCMP_ACT_ERRNO, dhe thirrjeve në seksionin syscalls t'u caktosh SCMP_ACT_ALLOW. Kështu, lejoni vetëm thirrjet e shkruara në seksionin syscalls, dhe ndaloni të gjitha të tjerat. Për blacklist, thjesht ndërroni vlerat defaultAction dhe veprimet në të kundërt.

Tani duhet të them disa fjalë për nuancat që nuk janë aq të qarta. Vini re se rekomandimet më poshtë lajmërojnë se po zhvilloni një rrjesht aplikacionesh biznesi në Kubernetes dhe ju importoni që ato të punojnë me privilegje të pakta.

1. AllowPrivilegeEscalation=false

Në securityContext në kontejner ka parametrin AllowPrivilegeEscalation. Nëse është vendosur në false, kontejnerët do të nisin me bitin e vendosur (në) no_new_priv. Qëllimi i këtij parametri është i qartë nga emri: ai nuk lejon kontejnerin të nisë procese të reja me privilegje më të mëdha se sa ka vetë.

Një efekt anësor i këtij parametri, i vendosur në true (vlera e paracaktuar), është se runtime i kontejnerit përdor profilin seccomp në fillim të procesit të nisjes. Kështu, të gjitha thirrjet e sistemit të nevojshme për të nisur proceset e brendshme të mjedisit të ekzekutimit (p.sh., vendosjen e identifikuesve të përdoruesit/grupit, hedhjen e disa capabilities), duhet të jenë të lejuara në profil.

Një kontejner që kryen një funksion të thjeshtë echo hi, do t'i nevojiten lejet e mëposhtme:

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


 në vend të këtyre:

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

Por përsëri, pse është kjo problem? Personalish, do të shmangesha nga lejimi i këtyre thirrjeve sistemore (nëse nuk ka një nevojë të vërtetë): capset, set_tid_address, setgid, setgroups dhe setuid. Megjithatë, vështirësia e vërtetë është se, duke lejuar procese që nuk i kontrolloni fare, lidheni profilet me implementimin e mjedisit të ekzekutimit të kontejnerëve. Në fjalë të tjera, një herë mund të përballeni me situatën kur pas azhurnimit të mjedisit të ekzekutimit të kontejnerit (nga ju ose, më shumë, nga ofruesi i shërbimeve të cloud) kontejnerët papritmas ndalin së funksionuari.

KĂ«shilla Nr. 1: Nisni kontejnerĂ«t me AllowPrivilegeEscaltion=false. Kjo do tĂ« zvogĂ«lojĂ« madhĂ«sinĂ« e profileve seccomp dhe do t’i bĂ«jĂ« ato mĂ« pak tĂ« ndjeshme ndaj ndryshimeve nĂ« mjedisin e ekzekutimit tĂ« kontejnerĂ«ve.

2. Caktimi i profileve seccomp në nivelin e kontejnerit

Profili seccomp mund të caktohet në nivelin e pod-it:

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


 ose në nivelin e kontejnerit:

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

Vini re se sintaksa e mësipërme do të ndryshojë kur Kubernetes seccomp të bëhet GA (kjo ngjarje pritet të ndodhë në versionin e ardhshëm të Kubernetes - 1.18 - shënim i përkthyesit).

Pak njerĂ«z e dinĂ« se nĂ« Kubernetes gjithmonĂ« ka pasur çështje, pĂ«r shkak tĂ« sĂ« cilĂ«s profilet seccomp janĂ« aplikuar nĂ« kontejnerin pause. Mjedisi i ekzekutimit pjesĂ«risht e kompenson kĂ«tĂ« mangĂ«si, megjithatĂ«, ky kontejner nuk zhduket nga pod’ët, pasi pĂ«rdoret pĂ«r tĂ« konfiguruar infrastrukturĂ«n e tyre.

Problemi është se ky kontejner gjithmonë aktivizohet me AllowPrivilegeEscalation=true, duke sjellë problemet e përmendura në pikën 1, dhe ndryshimi i kësaj është i pamundur.

Duke përdorur perfilin seccomp në nivelin e kontejnerit, ju shmangni këtë kurth dhe mund të krijoni një profil që është "i dizajnuar" për një kontejner specifik. Kështu do të bëni derisa zhvilluesit të rregullojnë defektin dhe versioni i ri (mund të jetë, 1.18?) të bëhet i disponueshëm për të gjithë që dëshirojnë.

Këshilla nr. 2: Caktoni profilorë seccomp në nivelin e kontejnerit.

NĂ« njĂ« kuptim praktik, ky rregull zakonisht shĂ«rben si njĂ« pĂ«rgjigje universale pĂ«r pyetjen: “Pse profili im seccomp funksionon me docker run, por nuk funksionon pas disfatĂ«s nĂ« klasterin Kubernetes?”.

3. Përdorni runtime/default vetëm në rast emergjence

Në Kubernetes ka dy mundësi të profilove të integruara: runtime/default dhe docker/default. Të dyja implementohen nga mjedisi i ekzekutimit të kontejnerëve, dhe jo nga Kubernetes. Prandaj, ato mund të ndryshojnë në varësi të mjedisit të ekzekutimit të përdorur dhe versionit të tij.

NĂ« tĂ« tjera fjalĂ«, si rezultat i ndryshimit tĂ« runtime, kontejneri mund tĂ« ketĂ« akses nĂ« njĂ« grup tjetĂ«r thirrjesh tĂ« sistemit, tĂ« cilat mund t’i pĂ«rdorĂ« ose jo. Shumica e mjediseve tĂ« ekzekutimit pĂ«rdorin implementimin e Docker. NĂ«se dĂ«shironi tĂ« aktivizoni kĂ«tĂ« profil, sigurohuni qĂ« ai Ă«shtĂ« i pĂ«rshtatur pĂ«r ju.

Profili docker/default konsiderohet i vjetruar që nga Kubernetes 1.11, prandaj shmangni përdorimin e tij.

Sipas mendimit tim, profili runtime/default i përshtatet shumë qëllimeve për të cilat u krijua: mbrojtjes së përdoruesve nga rreziqet e lidhura me ekzekutimin e komandës docker run në makinat e tyre. Megjithatë, nëse flasim për aplikacione biznesi që funksionojnë në klasteret Kubernetes, do të merrja guximin të them se ky profil është shumë i hapur dhe zhvilluesit duhet të fokusohen në krijimin e profileve për aplikacionet e tyre (ose llojet e aplikacioneve).

Këshilla nr. 3: Krijoni profile seccomp për aplikacione specifike. Nëse kjo nuk është e mundur, merruni me profile të llojeve të aplikacioneve, për shembull, krijoni një profil të zgjeruar që do të përfshijë të gjitha API-të e uebit të aplikacionit në Golang. Përdorni runtime/default vetëm si një mjet emergjence.

Në publikimet e ardhshme do të flas se si të krijoni profila seccomp në frymën e SecDevOps, si t'i automatizoni dhe t'i testoni ato në pipeline. Në fjalë të tjera, nuk do të keni justifikime për të mos kaluar në profile specifike për aplikacionet.

4. Unconfined—nuk Ă«shtĂ« opsion.

Nga auditin e parë të sigurisë Kubernetes. ka rezultuar se si parazgjedhje seccomp është i çaktivizuar.. Kjo do të thotë se nëse nuk e aktivizoni PodSecurityPolicy, që e aktivizon atë në klaster, të gjitha pod'ët për të cilat nuk është përcaktuar një profil seccomp do të funksionojnë në mënyrën seccomp=unconfined..

Puna në një mënyrë të tillë do të thotë se humbet një strat të tërë izolimi që siguron mbrojtjen e klasterit. Një qasje e tillë nuk rekomandohet nga specialistët e sigurisë.

Këshilla nr. 4: Asnjë kontejner në klaster nuk duhet të funksionojë në mënyrë seccomp=unconfined., sidomos në mjediset production.

5. "Mënyra e auditimit"

Ky moment nuk është unik për Kubernetes, por prapë bie në kategorinë "çfarë duhet të dini para fillimit".

Ka qenĂ« traditĂ« qĂ« krijimi i profileve seccomp tĂ« jetĂ« gjithmonĂ« njĂ« aktivitet i vĂ«shtirĂ« dhe nĂ« masĂ« tĂ« madhe tĂ« bazohet nĂ« metodĂ«n e provave dhe gabimeve. Çështja Ă«shtĂ« se pĂ«rdoruesit thjesht nuk kanĂ« mundĂ«sinĂ« ta testojnĂ« atĂ« nĂ« mjediset production, pa rrezikuar qĂ« tĂ« "bjerĂ«" aplikacioni.

Pas shfaqjes së kernelit Linux 4.14, u mundësua të ekzekutohen pjesë të profilit në mënyrë audituese, duke regjistruar në syslog informacionin për të gjitha thirrjet sistemike, por pa i bllokuar ato. Aktivizimi i kësaj mënyre bëhet duke përdorur parametrin SCMT_ACT_LOG.:

SCMP_ACT_LOG:seccomp nuk do të ndikojë në funksionimin e rrjedhës që bën thirrjen sistemike nëse ajo nuk përfshihet në ndonjë rregull nga filtrimi, megjithatë informacioni për thirrjen sistemike do të regjistrohet.

Ja një strategji tipike për përdorimin e kësaj mundësie:

  1. Lejoni thirrjet sistemike që janë të nevojshme.
  2. Bllokoni thirrjet sistemike për të cilat dihet se nuk do të nevojiten.
  3. Regjistroni informacionin për të gjitha thirrjet e tjera në jurnal.

Një shembull i thjeshtuar duket siç vijon:

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

Porositë se është e nevojshme të bllokoni të gjitha thirrjet që dihet se nuk do të përdoren, dhe që potencialisht mund të dëmtojnë klasterin. Një bazë e mirë për përgatitjen e listes është dokumentacioni zyrtar i Docker. Atij i shpjegohet në detaje se cilat thirrje sistemore janë bllokuar në profilin e paracaktuar dhe pse.

Megjithatë, ka një parakusht. Edhe pse SCMT_ACT_LOG. mbështetet nga bërthama Linux që nga fundi i vitit 2017, ai hyri në ekosistemin Kubernetes vetëm relativisht kohët e fundit. Prandaj, për të përdorur këtë metodë do t'ju nevojitet bërthama Linux 4.14 dhe runC versioni jo më të ulët se v1.0.0-rc9.

KĂ«shilla №5: Profili i modit tĂ« auditi pĂ«r testim nĂ« prodhim mund tĂ« krijohet duke kombinuar lista tĂ« bardha dhe tĂ« zeza, ndĂ«rsa tĂ« gjitha pĂ«rjashtimet regjistrohen nĂ« jurnal.

6. Përdorni lista të bardha

Krijimi i listave të bardha kërkon përpjekje shtesë, pasi duhet të identifikohet çdo thirrje që mund të nevojitet nga aplikacioni, megjithatë ky qasje rrit ndjeshëm sigurinë:

ËshtĂ« thelbĂ«sore tĂ« pĂ«rdoret njĂ« qasje e bazuar nĂ« lista tĂ« bardha, pasi ajo Ă«shtĂ« mĂ« e thjeshtĂ« dhe mĂ« e besueshme. Lista e zezĂ« do tĂ« duhet tĂ« pĂ«rditĂ«sohet çdo herĂ« qĂ« shtohet njĂ« thirrje sistemore potencialisht tĂ« rrezikshme (ose njĂ« fitallu/opcion tĂ« rrezikshĂ«m, nĂ«se ato ndodhen nĂ« listĂ«n e zezĂ«). PĂ«rveç kĂ«saj, shpesh mund tĂ« ndryshohet pĂ«rfaqĂ«simi i parametrave, pa ndryshuar thelbĂ«sinĂ« e tij dhe kĂ«shtu tĂ« anashkalohen kufizimet e listĂ«s sĂ« zezĂ«.

Për aplikacionet në gjuhën Go kam zhvilluar një mjet të veçantë që ndihmon aplikacionin dhe mbledh të gjitha thirrjet e kryera gjatë ekzekutimit. Për shembull, për aplikacionin e mëposhtëm:

package main

import "fmt"

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


 tĂ« lançojmĂ« gosystract e tillĂ«:

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


 dhe do të marrim rezultatin e mëposhtëm:

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

Der Ă«shtĂ« vetĂ« njĂ« shembull – detajet mbi mjetet do tĂ« vijnĂ« mĂ« vonĂ«.

KĂ«shilla №6: Lejoni vetĂ«m ato thirrje qĂ« ju nevojiten me tĂ« vĂ«rtetĂ« dhe bllokoni tĂ« gjitha tĂ« tjerat.

7. Vendosni bazat e duhura (ose përgatituni për sjellje të papritur)

Nuk do t'ju lejohet të menaxhoni profilin pavarësisht se çfarë keni shënuar atje. Edhe nëse nuk është saktësisht ajo që donit. Për shembull, nëse bllokoni aksesin në thirrje si exit ose "exit_group", konteneri nuk do të mund të dalë siç duhet dhe një komandë e thjeshtë si echo hi do ta ngrejë atëpër një kohë të pacaktuar. Si rezultat, do të keni një ngarkesë të lartë CPU në klaster:

Seccomp në Kubernetes: 7 gjëra që duhet të dini që në fillim

NĂ« kĂ«to raste, utilitarja strace mund tĂ« ndihmojĂ« – do t'ju tregojĂ« se ku mund tĂ« jetĂ« problemi:

Seccomp në Kubernetes: 7 gjëra që duhet të dini që në fillim
sudo strace -c -p 9331

Sigurohuni që profilet të përmbajnë të gjitha thirrjet sistemike të nevojshme për aplikacionin gjatë funksionimit.

KĂ«shilla №7: Kushtoni vĂ«mendje detajĂ«ve dhe kontrolloni se tĂ« gjitha thirrjet sistemike tĂ« nevojshme janĂ« tĂ« pĂ«rfshira nĂ« listĂ«n e bardhĂ«.

Kështu, pjesa e parë e ciklit të artikujve mbi përdorimin e seccomp në Kubernetes në frymën e SecDevOps po përfundon. Në pjesët e ardhshme, ne do të flasim për arsyet pse është e rëndësishme dhe si të automatizoni procesin.

P.S. nga përkthyesi

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster