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

Shën. përk.: Ne prezantojmë artikullin e inxhinierit kryesor për sigurinë aplikacioneve të kompanisë britanike ASOS.com. Ai fillon një cikël publikimesh që i përkushtohet rritjes së sigurisë në Kubernetes përmes përdorimit të seccomp. Nëse hyrja do t'i pëlqejë 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ë nga fillimi

Ky artikull është i pari në një seri publikimesh mbi mënyrën se si të krijoni profile seccomp në frymën e SecDevOps, pa përdorur magji ose shtrigëri. Në pjesën e parë do të flas për bazat dhe detajet brenda zbatimit të seccomp në Kubernetes.

Ekosistemi i Kubernetes ofron një shumëllojshmëri të mjaftueshme mënyrash për të siguruar sigurinë dhe izolimin e kontejnerëve. Artikulli është përkushtuar Režimit të Sigurt të Computimit, i njohur gjithashtu si seccomp. Qëllimi i tij është filtrimi i thirrjeve sistemike të accesueshme për t'u ekzekutuar nga kontejnerët.

Pse është e rëndësishme? Këndi është thjesht një proces i caktuar që ekzekutohet në një makinë të caktuar. Ai përdor bërthamën në të njëjtën mënyrë si aplikacione të tjera. Nëse kontejnerët mund të ekzekutojnë çdo thirrje sistemike, shumë shpejt do të shfrytëzoheshin nga programe të dëmshme për të anashkaluar izolimin e kontejnerit dhe për të ndikuar në aplikacione të tjera: për të kapur informacione, për të ndryshuar cilësimet e sistemit, etj.

Profilet seccomp përcaktojnë cilat thirrje sistemike duhet të lejohen ose ndalohen. Mjedisi i ekzekutimit të kontejnerit i aktivizon ato në kohën e nisjes, në mënyrë që bërthama të mund të monitorojë ekzekutimin e tyre. Përdorimi i profileve të tilla lejon të kufizohet vektori i sulmit dhe të reduktohet dëmi në rast se ndonjë program brenda kontejnerit (dmth. varësitë tuaja, ose varësitë e tyre) fillon të bëjë diçka që i është ndaluar.

Të kuptojmë bazat

Profili i bazës 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 sistemike që nuk është e specifikuar në seksionin syscalls. Për ta lehtësuar detyrën, le të fokusoheni në dy vlera kryesore që do të përdoren:

  • SCMP_ACT_ERRNO — ndalon ekzekutimin e thirrjes sistemike,
  • SCMP_ACT_ALLOW — lejon.

Në seksionin architectures përmenden arkitekturat e synuara. Kjo është e rëndësishme sepse filtri vetë, i aplikuar në nivelin e bërthamës, varet nga identifikuesit e thirrjeve sistemike, dhe jo nga emrat e tyre të shkruar në profil. Para zbatimit, mjedisi i ekzekutimit të kontejnerëve do t'i ndërlidh ata me identifikuesit. Kuptimi është që thirrjet sistemike mund të kenë identifikues të ndryshëm në varësi të arkitekturës së sistemit. Për shembull, thirrja sistemike 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 sistemike për arkitekturat x86-x64.

Në seksionin syscalls përmenden të gjitha thirrjet sistemike dhe tregohet se çfarë duhet të bëhet me to. Për shembull, mund të krijoni një listë të bardhë duke vendosur defaultActionSCMP_ACT_ERRNO, dhe thirrjeve në seksionin syscalls t'u japësh SCMP_ACT_ALLOW. Kështu, ju lejoni vetëm thirrjet që janë të shënuara në seksionin syscalls, dhe ndaloni të gjitha të tjerat. Për listën e zezë, duhet të ndërroni vlerat defaultAction dhe veprimet në të kundërt.

Tani duhet të them disa fjalë rreth nuancave që nuk janë aq të dukshme. Kini parasysh se rekomandimet e mëposhtme vijnë nga fakti që po zhvilloni një gamë të aplikacioneve të biznesit në Kubernetes dhe ju është e rëndësishme që ato të funksionojnë me privilegje sa më të ulëta.

1. AllowPrivilegeEscalation=false

securityContext kontejneri ka parametrin AllowPrivilegeEscalation. Nëse është vendosur në false, kontejnerët do të fillojnë me bitin e vendosur () no_new_priv . Kuptimi i këtij parametri është i qartë nga emri: ai nuk i lejon kontejnerit të fillojë procese të reja me privilegje më të larta se sa ka vetë.Efekti anësor i këtij parametri, kur është vendosur në

(vlera e parazgjedhur), është se runtime i kontejnerëve aplikon profilin seccomp qysh në fillim të procesit të nisjes. Kështu, të gjitha thirrjet sistemike të nevojshme për të filluar proceset e brendshme të mjedisit të ekzekutimit (p.sh., vendosja e identifikuesve të përdoruesit/grupit, heqja e disa capabilities) duhet të jenë të lejuara në profil. e vërtetë Një kontejner që ekzekuton thjesht

echo hi , do të kërkojë këto leje:{ "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" } ] }

… në vend të këtyre:

(hi-pod-seccomp.json)

… вместо этих:

{
    "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 ndonjëherë, pse është kjo një problem? Personal, do të evitova përfshirjen e këtyre thirrjeve sistemike në listën e bardhë (nëse nuk ka një nevojë reale): capset, set_tid_address, setgid, setgroups dhe setuid. Sidoqoftë, vështirësia e vërtetë është se, duke lejuar procese që nuk i kontrolloni fare, lidhni profilet me implementimin e ambientit të ekzekutimit të kontejnerëve. Me fjalë të tjera, një herë mund të përballeni me faktin se, pas një përditësimi të ambientit të ekzekutimit të kontejnerit (nga ju apo, më e mundshme, nga ofruesi i shërbimeve në cloud), kontejnerët papritmas ndalojnë së funksionuari.

Këshilla Nr. 1: Çoni kontejnerët me AllowPrivilegeEscaltion=false. Kjo do të reduktojë madhësinë e profileve seccomp dhe do t'i bëjë ato më të pakapshme ndaj ndryshimeve në ambientin e ekzekutimit të kontejnerit.

2. Caktimi i profileve seccomp në nivel kontejneri

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/<container-name>: "localhost/profile.json"

Vini re se sintaksa e mësipërme do të ndryshojë kur Kubernetes seccomp të bëhet GA (ky event pritet të ndodhë në lëshimin e ardhshëm të Kubernetes – 1.18 – shënim i përkthyesit).

Pak kush e di se në Kubernetes gjithmonë ka ekzistuar defekti, për shkak të cilit profilet seccomp i janë aplikuar kontejnerit pause. Ambienti i ekzekutimit e kompenson pjesërisht këtë mangësi, megjithatë, ky kontejner nuk zhduket nga pod-et, pasi përdoret për të konfiguruar infrastrukturën e tyre.

Problemi është se ky kontejner gjithmonë fillon me AllowPrivilegeEscalation=true, duke çuar në problemet e përmendura në pikën 1, dhe nuk mund të ndryshohet.

Duke aplikuar profile seccomp në nivel kontejneri, ju shmangni këtë kurth dhe mund të krijoni një profil që do të jetë "i përshtatur" për kontejnerin specifik. Kështu do të jetë derisa zhvilluesit të rregullojnë gabimin dhe versioni i ri (mund të jetë 1.18?) të bëhet i disponueshëm për të gjithë.

Këshilla Nr. 2: Caktoni profile seccomp në nivel kontejneri.

Në kuptimin 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 vendosjes në klasterin Kubernetes?".

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

Në Kubernetes ka dy mundësi profile të integruara: runtime/default dhe docker/default. Të dyja zbatohen nga ambienti i ekzekutimit të kontejnerit, e jo nga Kubernetes. Prandaj ata mund të ndryshojnë në varësi të ambientit të ekzekutimit që përdoret dhe versionit të tij.

Me fjalë të tjera, si rezultat i ndryshimit të runtime, kontejneri mund të ketë akses në një grup tjetër thirrjesh sistemore, që mund t'i përdorë ose jo. Shumica e mjediseve të ekzekutimit përdorin implementimin e Docker. Nëse dëshironi ta aktivizoni këtë profil, sigurohuni që ai t'ju përshtatet.

Profili docker/default mësohet se është në gjendje të vjetër nga Kubernetes 1.11, prandaj shmangni përdorimin e tij.

Mendimi im është se profili runtime/default është përshtatur mirë për qëllimet për të cilat është krijuar: për të mbrojtur përdoruesit nga rreziqet që lidhen me ekzekutimin e komandave docker run në makinat e tyre. Megjithatë, sa i përket aplikacioneve biznesore që funksionojnë në klasterët Kubernetes, do të guxoja të thosha se ky profil është shumë i hapur dhe zhvilluesit duhet të përqendrohen në krijimin e profileve për aplikacionet e tyre (ose llojet e aplikacioneve).

Këshillë nr. 3: Krijoni profile seccomp për aplikacione specifike. Nëse kjo nuk është e mundur, përqendrohuni në profile për lloje aplikacionesh, për shembull, krijoni një profil të zgjeruar që do të përfshijë të gjithë API-të e aplikacionit në Golang. Përdorni vetëm si një masë ekstreme runtime/default.

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

4. Unconfined — nuk është një opsion

Nga në auditimin e parë të sigurisë Kubernetes u zbulua se në mënyrë default seccomp është i çaktivizuar. Kjo do të thotë se nëse nuk përcaktoni Politika e Sigurisë së Pod-ëve, i cili do ta aktivizojë atë në klaster, të gjithë podët, për të cilat nuk është përcaktuar profili seccomp, do të funksionojnë në modalitetin seccomp=unconfined.

Funksionimi në këtë modalitet do të thotë se humb një shtresë tërë izolimi, që siguron mbrojtjen e klasterit. Ky qasje nuk rekomandohet nga specialistët e sigurisë.

Këshillë nr. 4: Asnjë kontejner në klaster nuk duhet të funksionojë në modalitetin seccomp=unconfined, veçanërisht në mjedise prodhimi.

5. "Modaliteti i auditimit"

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

Ka pasur një traditë që krijimi i profileve seccomp ka qenë gjithmonë një detyrë e vështirë dhe në masë të madhe e ka bazuar metoden në metoda provash dhe gabimesh. Problemi është se përdoruesit thjesht nuk kanë mundësi ta testojnë ato në mjediset prodhuese, pa rrezikuar të "rrëzojnë" aplikacionin.

Me paraqitjen e kernelit Linux 4.14, u mundësua aktivizimi i pjesëve të profilit në modalitetin e auditimit, duke regjistruar në syslog informacionin rreth të gjitha thirrjeve sistemore, por pa i bllokuar ato. Aktivizimi i këtij modaliteti mund të bëhet përmes parametrave SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp nuk do të ndikojë në ekzekutimin e rrjedhës që po bën thirrjen sistemore, nëse nuk i përket ndonjë rregulli nga filtrat, megjithatë, informacioni mbi thirrjen sistemore do të regjistrohet.

Këtu është një strategji tipike për përdorimin e kësaj mundësie:

  1. Lejoni thirrjet sistemore që janë të nevojshme.
  2. Bllokoni thirrjet sistemore për të cilat është e njohur se nuk do të nevojiten.
  3. Informacionin për të gjitha thirrjet e tjera regjistroni në ditar.

Një shembull i thjeshtë duket si më poshtë:

{
    "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 thirrjet për të cilat dihet se nuk do të përdoren dhe që potencialisht mund të dëmtojnë klasterin. Një bazë e mirë për përgatitjen e listës është dokumentacioni zyrtar i Docker. Ai jep shpjegime të hollësishme për thirrjet sistemore që janë të bllokuara në profilin e parazgjedhur dhe pse.

Megjithatë, ka një kurth. Megjithëse SCMT_ACT_LOG shtë mbështetur nga bërthama Linux që nga fundi i vitit 2017, në ekosistemin Kubernetes ka hyrë vetëm që 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 Nr. 5: Profilin e modës së audituar për testimin në prodhim mund ta krijoni duke kombinuar lista të bardha dhe të zeza, ndërsa të gjitha përjashtimet i regjistroni në ditar.

6. Përdorni lista të bardha

Krijimi i listave të bardha kërkon përpjekje shtesë, pasi duhet identifikuar çdo thirrje që mund t'i nevojitet aplikacionit, megjithatë ky qasje ndihmon në rritjen e sigurisë:

Rekomandohet fuqimisht që të përdoret një qasje e bazuar në lista të bardha, pasi është më e thjeshtë dhe më e besueshme. Lista e zezë do të duhet të përditësohet çdo herë kur shtohet një thirrje sistemore potencialisht e rrezikshme (ose një flag/opcion i rrezikshëm, nëse ata janë në listën e zezë). Për më tepër, shpesh mund të ndryshohet paraqitja e parametrave, pa ndryshuar thelbin e tij dhe kështu të anashkalohet kufizimi i listës së zezë.

Për aplikacionet në gjuhën Go, kam zhvilluar një mjet të veçantë që shoqëron aplikacionin dhe përmbledh 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")
}

... do të nisnim gosystract ashtu:

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

... dhe do të merrnim rezultatin e mëposhtëm:

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

Derisa ky është vetëm një shembull - detajet rreth mjeteve do të vijojnë më poshtë.

Këshilla Nr. 6: Lejoni vetëm ato thirrje që ju nevojiten vërtet, dhe bllokoni të gjitha të tjerat.

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

Nukern do të monitorojë përmbushjen e profilit pavarësisht se çfarë keni shkruar aty. Edhe nëse nuk është krejtësisht ajo që dëshironit. Për shembull, nëse bllokoni aksesin në thirrje si exit ose exit_group, konteneri nuk do të jetë në gjendje të mbylle saktë dhe madje një komandë e thjeshtë si , do të kërkojë këto leje: do ta ngrijë 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ë nga fillimi

Në kësi rastesh, një utilitare mund të ndihmojë strace — do të tregojë se ku mund të jetë problemi:

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

Sigurohuni që profillet përmbajnë të gjitha thirrjet sistemike që i duhen aplikacionit gjatë funksionimit.

Këshilla №7: Jepni vëmendje detajeve dhe kontrolloni që të gjitha thirrjet sistemike të nevojshme të jenë të përfshira në listën e bardhë.

Me këtë, pjesa e parë e serisë së artikujve rreth përdorimit të seccomp në Kubernetes në frymën e SecDevOps mbyllet. Në pjesët e ardhshme do të flasim mbi rëndësinë e kësaj dhe si ta automatizojmë procesin.

P.S. nga përkthyesi

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster