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

Shënim. përkth.: Ne paraqesim në vëmendjen tuaj përkthimin e një artikulli nga një inxhinier i lartë i sigurisë së aplikacioneve në kompaninë britanike ASOS.com. Me të, ai fillon një seri botimesh kushtuar përmirësimit të sigurisë në Kubernetes përmes përdorimit të seccomp. Nëse lexuesit e pëlqejnë hyrjen, ne do të ndjekim autorin dhe do të vazhdojmë me materialet e tij të ardhshme për këtë temë.

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

Ky artikull është i pari në një seri postimesh se si të krijoni profile seccomp në frymën e SecDevOps, pa iu drejtuar magjisë dhe magjisë. Në Pjesën 1, unë do të mbuloj bazat dhe detajet e brendshme të zbatimit të seccomp në Kubernetes.

Ekosistemi Kubernetes ofron një larmi mënyrash për të siguruar dhe izoluar kontejnerët. Artikulli ka të bëjë me modalitetin e sigurt të llogaritjes, i njohur gjithashtu si seccomp. Thelbi i tij është të filtroni thirrjet e sistemit të disponueshme për ekzekutim sipas kontejnerëve.

Pse është e rëndësishme? Një kontejner është thjesht një proces që funksionon në një makinë specifike. Dhe përdor kernelin ashtu si aplikacionet e tjera. Nëse kontejnerët mund të kryenin ndonjë thirrje sistemi, shumë shpejt malware do të përfitonte nga kjo për të anashkaluar izolimin e kontejnerëve dhe për të ndikuar në aplikacione të tjera: përgjimin e informacionit, ndryshimin e cilësimeve të sistemit, etj.

profilet seccomp përcaktojnë se cilat thirrje të sistemit duhet të lejohen ose çaktivizohen. Koha e ekzekutimit të kontejnerit i aktivizon ato kur fillon, në mënyrë që kerneli të monitorojë ekzekutimin e tyre. Përdorimi i profileve të tilla ju lejon të kufizoni vektorin e sulmit dhe të zvogëloni dëmin nëse ndonjë program brenda kontejnerit (d.m.th., varësitë tuaja ose varësitë e tyre) fillon të bëjë diçka që nuk lejohet të bëjë.

Arritja në bazat

Profili bazë seccomp përfshin tre elementë: 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 sistemi që nuk është specifikuar në seksion syscalls. Për t'i bërë gjërat më të lehta, le të përqendrohemi në dy vlerat kryesore që do të përdoren:

  • SCMP_ACT_ERRNO — bllokon ekzekutimin e një thirrjeje sistemi,
  • SCMP_ACT_ALLOW - lejon.

Në seksionin architectures arkitekturat e synuara janë të listuara. Kjo është e rëndësishme sepse vetë filtri, i aplikuar në nivel kernel, varet nga identifikuesit e thirrjeve të sistemit dhe jo nga emrat e tyre të specifikuar në profil. Koha e funksionimit të kontejnerit do t'i përputhet me identifikuesit përpara përdorimit. Ideja është që thirrjet e sistemit mund të kenë ID krejtësisht të ndryshme në varësi të arkitekturës së sistemit. Për shembull, thirrja e sistemit recvfrom (përdoret për të marrë informacion nga priza) ka ID = 64 në sistemet x64 dhe ID = 517 në x86. Këtu mund të gjeni një listë të të gjitha thirrjeve të sistemit për arkitekturat x86-x64.

Në seksionin syscalls liston të gjitha thirrjet e sistemit dhe specifikon se çfarë të bëhet me to. Për shembull, mund të krijoni një listë të bardhë duke vendosur defaultAction mbi SCMP_ACT_ERRNO, dhe telefonatat në seksion syscalls caktoj SCMP_ACT_ALLOW. Kështu, ju lejoni vetëm thirrjet e specifikuara në seksion syscalls, dhe ndaloni të gjithë të tjerët. Për listën e zezë duhet të ndryshoni vlerat defaultAction dhe veprimet në të kundërtën.

Tani duhet të themi disa fjalë për nuancat që nuk janë aq të dukshme. Ju lutemi vini re se rekomandimet e mëposhtme supozojnë se po vendosni një linjë aplikacionesh biznesi në Kubernetes dhe dëshironi që ato të funksionojnë me sasinë më të vogël të privilegjeve të mundshme.

1. AllowPrivilegeEscalation=false

В securityContext kontejneri ka një parametër AllowPrivilegeEscalation. Nëse është i instaluar në false, kontejnerët do të fillojnë me (on) pak no_new_priv. Kuptimi i këtij parametri është i qartë nga emri: ai e pengon kontejnerin të nisë procese të reja me më shumë privilegje sesa ka vetë.

Një efekt anësor i këtij opsioni është vendosur në true (parazgjedhja) është se koha e ekzekutimit të kontejnerit aplikon profilin seccomp në fillim të procesit të nisjes. Kështu, të gjitha thirrjet e sistemit të kërkuara për të ekzekutuar proceset e brendshme të kohës së ekzekutimit (p.sh. vendosja e ID-ve të përdoruesve/grupeve, heqja e disa aftësive) duhet të aktivizohen në profil.

Tek një enë që bën gjëra të parëndësishme echo hi, do të kërkohen 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ë ky një problem? Personalisht, do të shmangja listën e bardhë të thirrjeve të sistemit të mëposhtëm (përveç nëse ka nevojë reale për to): capset, set_tid_address, setgid, setgroups и setuid. Sidoqoftë, sfida e vërtetë është se duke lejuar procese mbi të cilat nuk keni absolutisht asnjë kontroll, po i lidhni profilet me zbatimin e kohës së funksionimit të kontejnerit. Me fjalë të tjera, një ditë mund të zbuloni se pas përditësimit të mjedisit të funksionimit të kontejnerit (qoftë nga ju ose, më shumë, nga ofruesi i shërbimit cloud), kontejnerët papritmas ndalojnë së funksionuari.

Këshilla # 1: Drejtoni 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 kohës së funksionimit të kontejnerit.

2. Vendosja e profileve seccomp në nivelin e kontejnerit

Profili seccomp mund të vendoset në nivelin e pod:

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

...ose në nivelin e kontejnerit:

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

Ju lutemi vini re se sintaksa e mësipërme do të ndryshojë kur Kubernetes seccomp do të bëhet GA (kjo ngjarje pritet në publikimin e ardhshëm të Kubernetes - 1.18 - përafërsisht përkth.).

Pak njerëz e dinë që Kubernetes ka pasur gjithmonë insektgjë që bëri që profilet seccomp të aplikoheshin në enë pauzë. Mjedisi i ekzekutimit kompenson pjesërisht këtë mangësi, por ky kontejner nuk zhduket nga pods, pasi përdoret për të konfiguruar infrastrukturën e tyre.

Problemi është se kjo enë gjithmonë fillon me AllowPrivilegeEscalation=true, duke çuar në problemet e shprehura në paragrafin 1, dhe kjo nuk mund të ndryshohet.

Duke përdorur profilet seccomp në nivelin e kontejnerit, ju shmangni këtë grackë dhe mund të krijoni një profil që është i përshtatur për një enë specifike. Kjo do të duhet të bëhet derisa zhvilluesit të rregullojnë gabimin dhe versioni i ri (ndoshta 1.18?) të bëhet i disponueshëm për të gjithë.

Këshilla # 2: Vendosni profilet seccomp në nivelin e kontejnerit.

Në një kuptim praktik, ky rregull zakonisht shërben si një përgjigje universale për pyetjen: "Pse funksionon profili im seccomp me docker runpor nuk funksionon pas vendosjes në një grupim Kubernetes?

3. Përdorni kohën e funksionimit/default vetëm si mjetin e fundit

Kubernetes ka dy opsione për profilet e integruara: runtime/default и docker/default. Të dyja zbatohen nga koha e ekzekutimit të kontejnerit, jo nga Kubernetes. Prandaj, ato mund të ndryshojnë në varësi të mjedisit të funksionimit të përdorur dhe versionit të tij.

Me fjalë të tjera, si rezultat i ndryshimit të kohës së funksionimit, kontejneri mund të ketë akses në një grup të ndryshëm thirrjesh të sistemit, të cilat mund t'i përdorë ose jo. Shumica e kohëzgjatjeve përdorin Zbatimi i dokerit. Nëse dëshironi të përdorni këtë profil, ju lutemi sigurohuni që të jetë i përshtatshëm për ju.

Profili docker/default është zhvlerësuar që nga Kubernetes 1.11, ndaj shmangni përdorimin e tij.

Sipas mendimit tim, profili runtime/default i përshtatshëm për qëllimin për të cilin është krijuar: mbrojtja e përdoruesve nga rreziqet që lidhen me ekzekutimin e një komande docker run në makinat e tyre. Megjithatë, kur bëhet fjalë për aplikacionet e biznesit që ekzekutohen në grupet Kubernetes, do të guxoja të argumentoja se një profil i tillë është shumë i hapur dhe zhvilluesit duhet të fokusohen në krijimin e profileve për aplikacionet e tyre (ose llojet e aplikacioneve).

Këshilla # 3: Krijoni profile seccomp për aplikacione specifike. Nëse kjo nuk është e mundur, krijoni profile për llojet e aplikacioneve, për shembull, krijoni një profil të avancuar që përfshin të gjitha API-të e uebit të aplikacionit Golang. Përdorni vetëm kohën e funksionimit/parazgjedhjen si mjetin e fundit.

Në postimet e ardhshme, do të mbuloj se si të krijojmë profile seccomp të frymëzuara nga SecDevOps, t'i automatizojmë dhe t'i testojmë në tubacione. Me fjalë të tjera, nuk do të keni asnjë justifikim për të mos përmirësuar në profilet specifike të aplikacionit.

4. I pakufizuar NUK është një opsion.

Nga auditimi i parë i sigurisë Kubernetes doli që si parazgjedhje seccomp i çaktivizuar. Kjo do të thotë se nëse nuk vendosni PodSecurityPolicy, i cili do ta mundësojë atë në grup, të gjitha podet për të cilat nuk është përcaktuar profili seccomp do të funksionojnë në seccomp=unconfined.

Funksionimi në këtë mënyrë do të thotë që humbet një shtresë e tërë izolimi që mbron grupin. Kjo qasje nuk rekomandohet nga ekspertët e sigurisë.

Këshilla # 4: Asnjë kontejner në grup nuk duhet të futet brenda seccomp=unconfined, veçanërisht në mjediset e prodhimit.

5. "Modaliteti i auditimit"

Kjo pikë nuk është unike për Kubernetes, por gjithsesi bie në kategorinë "gjëra që duhet të dini përpara se të filloni".

Siç ndodh, krijimi i profileve seccomp ka qenë gjithmonë sfidues dhe mbështetet shumë në prova dhe gabime. Fakti është se përdoruesit thjesht nuk kanë mundësinë t'i testojnë në mjediset e prodhimit pa rrezikuar "heqjen" e aplikacionit.

Pas lëshimit të kernelit Linux 4.14, u bë e mundur ekzekutimi i pjesëve të një profili në modalitetin e auditimit, duke regjistruar informacione për të gjitha thirrjet e sistemit në syslog, por pa i bllokuar ato. Ju mund ta aktivizoni këtë modalitet duke përdorur parametrin SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp nuk do të ndikojë në lidhjen që bën thirrjen e sistemit nëse nuk përputhet me ndonjë rregull në filtër, por informacioni për thirrjen e sistemit do të regjistrohet.

Këtu është një strategji tipike për përdorimin e kësaj veçorie:

  1. Lejo thirrjet e sistemit që nevojiten.
  2. Blloko telefonatat nga sistemi që e dini se nuk do të jenë të dobishme.
  3. Regjistroni informacionin për të gjitha thirrjet e tjera në regjistër.

Një shembull i thjeshtuar duket si ky:

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

Por mbani mend se duhet të bllokoni të gjitha telefonatat që e dini se nuk do të përdoren dhe që mund të dëmtojnë grupin. Një bazë e mirë për përpilimin e një liste është zyrtari Dokumentacioni i dokerit. Ai shpjegon në detaje se cilat thirrje të sistemit janë të bllokuara në profilin e paracaktuar dhe pse.

Megjithatë, ka një kapje. Edhe pse SCMT_ACT_LOG i mbështetur nga kerneli 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 duhet një kernel Linux 4.14 dhe versioni runC jo më i ulët v1.0.0-rc9.

Këshilla # 5: Një profil i modalitetit të auditimit për testimin në prodhim mund të krijohet duke kombinuar listat bardh e zi dhe të gjitha përjashtimet mund të regjistrohen.

6. Përdorni listat e bardha

Lista e bardhë kërkon përpjekje shtesë sepse duhet të identifikoni çdo telefonatë që mund t'i nevojitet aplikacionit, por kjo qasje përmirëson shumë sigurinë:

Rekomandohet shumë të përdorni qasjen e listës së bardhë pasi është më e thjeshtë dhe më e besueshme. Lista e zezë do të duhet të përditësohet sa herë që shtohet një telefonatë potencialisht e rrezikshme e sistemit (ose një flamur/opsion i rrezikshëm nëse është në listën e zezë). Për më tepër, shpesh është e mundur të ndryshohet përfaqësimi i një parametri pa ndryshuar thelbin e tij dhe në këtë mënyrë të anashkalojë kufizimet e listës së zezë.

Për aplikacionet Go, kam zhvilluar një mjet të veçantë që shoqëron aplikacionin dhe mbledh të gjitha thirrjet e bëra gjatë ekzekutimit. Për shembull, për aplikacionin e mëposhtëm:

package main

import "fmt"

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

... le të nisim gosystract si më poshtë:

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

... dhe 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",

Për momentin, ky është vetëm një shembull - më shumë detaje rreth mjeteve do të pasojnë.

Këshilla # 6: Lejo vetëm ato telefonata që të duhen vërtet dhe blloko të gjitha të tjerat.

7. Vendosni themelet e duhura (ose përgatituni për sjellje të papritura)

Kerneli do të zbatojë profilin pavarësisht se çfarë shkruani në të. Edhe nëse nuk është pikërisht ajo që dëshironit. Për shembull, nëse bllokoni qasjen në thirrje si p.sh exit ose exit_group, kontejneri nuk do të jetë në gjendje të mbyllet siç duhet dhe madje edhe një komandë e thjeshtë si echo hi var atëo për një periudhë të pacaktuar. Si rezultat, do të merrni përdorim të lartë të CPU-së në grup:

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

Në raste të tilla, një shërbim mund të vijë në shpëtim strace - do të tregojë se cili 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 përmbajnë të gjitha thirrjet e sistemit që i nevojiten aplikacionit në kohën e ekzekutimit.

Këshilla # 7: Kushtojini vëmendje detajeve dhe sigurohuni që të gjitha thirrjet e nevojshme të sistemit të jenë në listën e bardhë.

Kjo përfundon pjesën e parë të një serie artikujsh mbi përdorimin e seccomp në Kubernetes në frymën e SecDevOps. Në pjesët në vijim do të flasim pse kjo është e rëndësishme dhe si të automatizohet procesi.

PS nga përkthyesi

Lexoni edhe në blogun tonë:

Burimi: www.habr.com

Shto një koment