Seccomp în Kubernetes: 7 lucruri pe care trebuie să le știi de la început.

Nota traducătorului.: Aducem în atenție traducerea articolului unui inginer senior în securitate aplicații din compania britanică ASOS.com. Acesta începe un ciclu de publicații dedicate creșterii securității în Kubernetes prin utilizarea seccomp. Dacă introducerea va plăcea cititorilor, vom urma autorul și vom continua cu materialele sale viitoare pe acest subiect.

Seccomp în Kubernetes: 7 lucruri pe care trebuie să le știi de la început.

Acest articol este primul dintr-o serie de publicații despre cum să creezi profile seccomp în spiritul SecDevOps, fără a recurge la magie și vrăjitorie. În prima parte voi vorbi despre bazele și detaliile interne ale implementării seccomp în Kubernetes.

Ecosistemul Kubernetes oferă o varietate suficientă de metode pentru asigurarea securității și izolării containerelor. Articolul se concentrează pe Secure Computing Mode, cunoscut și sub numele de seccomp. Esența sa constă în filtrarea apelurilor de sistem disponibile pentru execuție în containere.

De ce este important? Un container este doar un proces care rulează pe o anumită mașină. Acesta folosește nucleul la fel ca celelalte aplicații. Dacă containerele ar putea efectua orice apeluri de sistem, foarte repede ar fi folosite de programele malware pentru a ocoli izolarea containerului și a interfera cu alte aplicații: a intercepta informații, a modifica setările sistemului etc.

Profilele seccomp definesc ce apeluri de sistem ar trebui să fie permise sau interzise. Mediul de execuție al containerului le activează în momentul pornirii, astfel încât nucleul să poată monitoriza execuția acestora. Utilizarea unor astfel de profile permite limitarea vectorului de atac și reducerea daunelor în cazul în care vreun program din interiorul containerului (adică dependențele dvs. sau dependențele acestora) începe să facă ceea ce nu i este permis.

Să ne familiarizăm cu bazele

Profilul de bază seccomp include trei elemente: defaultAction, architectures (sau archMap) și 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 definează destinul implicit al oricărui apel de sistem nedefinit în secțiune syscalls. Pentru a simplifica sarcina, să ne concentrăm pe două valori principale care vor fi utilizate:

  • SCMP_ACT_ERRNO — blochează executarea apelului de sistem,
  • SCMP_ACT_ALLOW — permite.

În secțiune architectures sunt enumerate arhitecturile țintă. Acest lucru este important, deoarece filtrul aplicat la nivel de kernel depinde de identificatorii apelurilor de sistem și nu de denumirile acestora definite în profil. Înainte de aplicare, mediu de execuție a containerelor le va corela cu identificatorii. Ideea este că apelurile de sistem pot avea ID-uri complet diferite în funcție de arhitectura sistemului. De exemplu, apelul de sistem recvfrom (utilizat pentru a primi informații de la un socket) are ID = 64 în sisteme x64 și ID = 517 în x86. Aici puteți găsi o listă cu toate apelurile de sistem pentru arhitecturile x86-x64.

În secțiune syscalls sunt enumerate toate apelurile de sistem și se specifică ce trebuie făcut cu ele. De exemplu, se poate crea un whitelisting stabilind defaultAction pe SCMP_ACT_ERRNO, iar apelurilor din secțiune syscalls li se poate atribui SCMP_ACT_ALLOW. Astfel, permiteți doar apelurile definite în secțiune syscalls, și interziceți toate celelalte. Pentru un blacklist, ar trebui schimbate valorile defaultAction și acțiunile pentru a fi opuse.

Acum ar trebui să spun câteva cuvinte despre nuanțele care nu sunt atât de evidente. Rețineți că recomandările de mai jos derivă din faptul că dezvoltați o gamă de aplicații de afaceri în Kubernetes și este important pentru dumneavoastră să funcționeze cu cele mai mici privilegii.

1. AllowPrivilegeEscalation=false

În securityContext containerului are parametrul AllowPrivilegeEscalation. Dacă acesta este setat la false, containerele vor fi lansate cu bitul setat (on) no_new_priv . Ideea acestui parametru este evidentă din denumire: nu permite containerului să lanseze noi procese cu privilegii mai mari decât cele pe care le are.Un efect secundar al acestui parametru, setat pe

true true Containerului care execută o simplă

echo hi spune salut, vor fi necesare următoarele permisiuni:

{
    "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 loc de acestea:

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

Dar din nou, de ce este aceasta o problemă? Personal, aș evita să includ în lista albă următoarele apeluri de sistem (dacă nu există o necesitate reală): capset, set_tid_address, setgid, setgroups și setuid. Totuși, adevărata complexitate este că, permițând procesele pe care nu le controlați în totalitate, legați profilele de implementarea mediului de execuție al containerelor. Cu alte cuvinte, în viitor, este posibil să vă confruntați cu faptul că, după actualizarea mediu de execuție al containerului (de către dumneavoastră sau, mai probabil, de către furnizorul de servicii cloud), containerele nu vor mai porni subit.

Sfaturile nr. 1: Rulați containere cu AllowPrivilegeEscaltion=false. Acest lucru va reduce dimensiunea profilurilor seccomp și le va face mai puțin sensibile la modificările mediului de execuție al containerelor.

2. Stabilirea profilurilor seccomp la nivel de container

Profilul seccomp poate fi stabilit la nivel de pod:

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

… sau la nivel de container:

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

Rețineți că sintaxa de mai sus se va schimba când seccomp Kubernetes va deveni GA (această eveniment este așteptat în următoarea versiune Kubernetes — 1.18 — nota traducătorului).

Puțini știu că în Kubernetes a existat întotdeauna o eroare, din cauza căreia profilele seccomp erau aplicate la containerul pauseMediul de execuție compensează parțial această problemă, dar acest container rămâne în continuare în pod-uri, deoarece este folosit pentru configurarea infrastructurii acestora.

Problema este că acest container pornește întotdeauna cu AllowPrivilegeEscalation=true, ceea ce duce la problemele enunțate în punctul 1, iar acest lucru nu poate fi schimbat.

Aplicând profilele seccomp la nivel de container, evitați acest capcana și puteți crea un profil care să fie «adaptat» pentru un anumit container. Așa va trebui să faceți până când dezvoltatorii vor remedia bugul și noua versiune (poate, 1.18?) va fi disponibilă pentru toți cei interesați.

Sfaturi №2: Stabiliți profile seccomp la nivel de container.

În sens practic, această regulă servește de obicei ca răspuns universal la întrebarea: „De ce profilul meu seccomp funcționează cu docker run, dar nu funcționează după desfășurarea în clusterul Kubernetes?”.

3. Folosiți runtime/default doar în cazuri extreme

În Kubernetes există două opțiuni de profile încorporate: runtime/default și docker/default. Ambele sunt implementate de mediul de execuție a containerelor, nu de Kubernetes. Prin urmare, acestea pot varia în funcție de mediul de execuție utilizat și versiunea acestuia.

Cu alte cuvinte, datorită schimbării runtime-ului, containerul poate avea acces la un alt set de apeluri de sistem, pe care le poate folosi sau nu. Majoritatea mediilor de execuție folosesc implementarea Docker. Dacă doriți să folosiți acest profil, asigurați-vă că este potrivit pentru nevoile dumneavoastră.

Profilul docker/default este considerat învechit începând cu Kubernetes 1.11, așa că evitați-l.

În opinia mea, profilul runtime/default se potrivește perfect scopurilor pentru care a fost creat: protejarea utilizatorilor de riscuri legate de execuția comenzii docker run pe mașinile lor. Totuși, în ceea ce privește aplicațiile de afaceri care funcționează în clustere Kubernetes, aș avea curajul să afirm că un astfel de profil este prea deschis și dezvoltatorii ar trebui să se concentreze pe crearea de profile pentru aplicațiile lor (sau tipurile de aplicații).

Sfaturi №3: Creați profile seccomp pentru aplicații specifice. Dacă acest lucru nu este posibil, creați profile pentru tipuri de aplicații, de exemplu, creați un profil extins care să includă toate API-urile web ale aplicației pe Golang. Folosiți runtime/default doar ca ultimă soluție.

În viitoarele publicații, voi explica cum să creați profiluri seccomp în spiritul SecDevOps, să le automatizați și să le testați în pipeline-uri. Cu alte cuvinte, nu veți mai avea scuze pentru a nu trece la profiluri specifice aplicațiilor.

4. Unconfined — aceasta NU este o opțiune

Din primul audit de securitate Kubernetes s-a constat că, în mod implicit, seccomp este dezactivat. Aceasta înseamnă că, dacă nu specificați PodSecurityPolicy, care să-l activeze în cluster, toate pod-urile pentru care nu este definit un profil seccomp vor funcționa în modul seccomp=unconfined.

Funcționarea în acest mod înseamnă că se pierde un întreg strat de izolare, care asigură protecția cluster-ului. Această abordare nu este recomandată de specialiștii în securitate.

Sfatul nr. 4: Niciun container din cluster nu ar trebui să funcționeze în modul seccomp=unconfined, în special în medii de producție.

5. „Modul audit”

Această problemă nu este unică pentru Kubernetes, dar totuși se încadrează în categoria „ce ar trebui să știți înainte de a începe”.

Este o strategie bine cunoscută că crearea profilurilor seccomp a fost întotdeauna o activitate dificilă și s-a bazat în mare măsură pe metoda încercărilor și erorilor. Problema este că utilizatorii pur și simplu nu au oportunitatea de a le verifica în medii de producție, fără a risca „a cădea” aplicația.

După apariția nucleului Linux 4.14, a apărut posibilitatea de a rula părți ale profilului în modul audit, înregistrând în syslog informații despre toate apelurile de sistem, fără a le bloca. Acest mod poate fi activat cu ajutorul parametrului SCMT_ACT_LOG:

SCMP_ACT_LOG: seccomp nu va afecta funcționarea firului care face apelul de sistem, dacă acesta nu se încadrează sub nicio regulă din filtrul respectiv, totuși informația despre apelul de sistem va fi înregistrată în jurnal.

Iată o strategie tipică de utilizare a această posibilitate:

  1. Permiteți apelurile de sistem necesare.
  2. Blocați apelurile de sistem despre care se știe că nu vor fi utile.
  3. Informația despre toate celelalte apeluri să fie înregistrată în jurnal.

Un exemplu simplificat arată astfel:

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

Dar trebuie să rețineți că este necesar să blocați toate apelurile despre care se știe că nu vor fi folosite și care ar putea dăuna cluster-ului. O bază bună pentru întocmirea listei este documentația oficială Docker. Aceasta explică în detaliu ce apeluri de sistem sunt blocate în profilul implicit și de ce.

Cu toate acestea, există o capcană. Deși SCMT_ACT_LOG este suportat de nucleul Linux din 2017, a intrat în ecosistemul Kubernetes relativ recent. Prin urmare, pentru a utiliza această metodă, sunt necesare nucleul Linux 4.14 și runC versiunea 1.0.0-rc9 sau mai recentă. v1.0.0-rc9.

Sfaturi №5: Profilul modului de audit pentru testare în producție poate fi creat prin combinarea listelor negre și albe, iar toate excepțiile trebuie înregistrate în jurnal.

6. Utilizați listele albe

Crearea listelor albe necesită eforturi suplimentare, deoarece trebuie să identificați fiecare apel care ar putea fi necesar aplicației, totuși această abordare îmbunătățește semnificativ securitatea:

Se recomandă insistent utilizarea unui abordare bazată pe liste albe, deoarece este mai simplă și mai fiabilă. Lista neagră trebuie actualizată de fiecare dată când se adaugă un apel de sistem potențial periculos (sau un flag/opțiune periculoasă, dacă se află în lista neagră). În plus, adesea se poate schimba reprezentarea parametrului fără a-i schimba esența și, astfel, se poate ocoli restricțiile listei negre.

Pentru aplicațiile scrise în Go, am dezvoltat un instrument special care însoțește aplicația și colectează toate apelurile efectuate în timpul execuției. De exemplu, pentru următoarea aplicație:

package main

import "fmt"

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

… să rulăm gosystract așa:

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

… și vom obține următorul rezultat:

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

Deocamdat este doar un exemplu — detaliile despre instrumente vor urma mai departe.

Sfatul nr. 6: Permiteți doar apelurile de sistem de care aveți cu adevărat nevoie și blocați toate celelalte.

7. Așezați fundații corecte (sau pregătiți-vă pentru comportamente neașteptate)

Nucleul va monitoriza respectarea profilului indiferent de ceea ce ați configurat acolo. Chiar dacă nu este exact ceea ce ați dorit. De exemplu, dacă blocați accesul la apeluri precum exit sau exit_group, containerul nu va putea finaliza corect execuția și chiar o simplă comandă de tip spune salut va agăța acestape o perioadă nedeterminată. Ca urmare, veți avea o utilizare ridicată a CPU-ului în cluster:

Seccomp în Kubernetes: 7 lucruri pe care trebuie să le știi de la început.

În astfel de cazuri, utilitarul strace — vă va arăta în ce constă problema:

Seccomp în Kubernetes: 7 lucruri pe care trebuie să le știi de la început.
sudo strace -c -p 9331

Asigurați-vă că profilurile conțin toate apelurile de sistem necesare aplicației în timpul funcționării.

Sfatul nr. 7: Fiți atenți la detalii și verificați că toate apelurile de sistem necesare sunt incluse în lista albă.

Aceasta este prima parte a ciclului de articole despre utilizarea seccomp în Kubernetes în spiritul SecDevOps. În părțile următoare, vom vorbi despre importanța acestuia și cum să automatizăm procesul.

P.S. de la traducător

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster