Vanilla Kubernetes
Prethodne lekcije razdvojile su identitet workload-a od njegovih dozvola: ServiceAccount određuje ko šalje API zahtev, a RBAC šta taj identitet sme da uradi. Međutim, ni precizan RBAC ne sprečava proces u kontejneru da bude pokrenut kao korisnik root, zatraži opasne Linux capabilities, koristi host mrežu, montira putanju čvora ili pokuša eskalaciju privilegija. Ova lekcija uvodi sledeću granicu odbrane: deklarativna podešavanja procesa u SecurityContext objektu i admission kontrolu koja neprihvatljiv Pod odbija pre nego što stigne do kubelet-a.
Bezbedan workload nije rezultat jednog polja. Slika mora biti izgrađena za rad bez root privilegija, manifest mora eksplicitno smanjiti ovlašćenja, runtime i kernel moraju podržavati izabrane mehanizme, a API server mora dosledno sprovoditi organizacionu politiku. Kubernetes Pod Security Standards (PSS) definišu tri verzionisana profila, dok ugrađeni Pod Security Admission (PSA) primenjuje te profile pri prijemu zahteva. Ovi mehanizmi smanjuju rizik, ali ne zamenjuju bezbednu sliku, RBAC, NetworkPolicy, zaštitu tajni, skeniranje ranjivosti, runtime detekciju ni audit.
SecurityContext kao ugovor o izvršavanju procesa
securityContext opisuje privilegije i kontrole pristupa sa kojima se proces izvršava. Polje postoji na nivou poda kao PodSecurityContext i na nivou pojedinačnog kontejnera kao SecurityContext. Podešavanja poda, kao što su runAsUser, runAsGroup, fsGroup, supplementalGroups, SELinux opcije i pod-level seccomp profil, mogu postaviti zajedničku osnovu. Kontejnerski nivo sadrži i kontrole kao što su privileged, allowPrivilegeEscalation, capabilities i readOnlyRootFilesystem; kada isto podržano podešavanje postoji na oba nivoa, eksplicitna vrednost kontejnera ima prednost za taj kontejner.
Init i ephemeral kontejneri su deo iste granice poverenja. Administrator koji proveri samo spec.containers može prevideti privilegovani init kontejner koji menja volume pre pokretanja aplikacije ili ephemeral kontejner dodat tokom dijagnostike. PSS zato relevantne kontrole primenjuje na regularne, init i ephemeral kontejnere. Sidecar implementiran kao restartabilni init kontejner takođe se proverava kao init kontejner.
| Podešavanje | Namena i posledica | Važna granica |
|---|---|---|
runAsNonRoot: true |
Kubelet odbija pokretanje kada utvrdi da bi proces radio kao UID 0. | Ako slika koristi simboličkog korisnika čiji UID runtime ne može potvrditi, pokretanje može biti odbijeno; numerički ne-root UID daje proverljiviji ugovor. |
runAsUser i runAsGroup |
Postavljaju numerički UID i primarni GID procesa. | Nasumičan ili fiksan UID mora imati pristup aplikacionim fajlovima i volume-u; pogrešne dozvole izazivaju Permission denied. |
allowPrivilegeEscalation: false |
Postavlja Linux no_new_privs i sprečava sticanje većih privilegija preko setuid/setgid binarnih fajlova. |
Efektivno je uvek true za privilegovani kontejner ili kontejner sa CAP_SYS_ADMIN; ne znači da proces nema postojeće capabilities. |
privileged: true |
Uklanja veliki deo izolacije i daje kontejneru privilegije približne procesu na hostu. | Ne koristi se za poslovne aplikacije; opravdani infrastrukturni izuzetak zahteva odvojenu zonu poverenja i dodatne kontrole. |
capabilities |
Razlaže tradicionalne root privilegije na manje Linux jedinice; preporučeno je drop: ["ALL"] pa dodavanje samo dokazane potrebe. |
Capability je svojstvo Linux kernela. Dodela NET_ADMIN, SYS_ADMIN, SYS_PTRACE ili sličnih moćnih prava zahteva posebnu analizu. |
seccompProfile |
Filtrira sistemske pozive; RuntimeDefault koristi podrazumevani profil container runtime-a, a Localhost profil sa čvora. |
RuntimeDefault se može razlikovati između runtime-a i verzija; Localhost profil mora postojati na svakom mogućem čvoru. |
readOnlyRootFilesystem: true |
Sprečava upis u sloj korenskog filesystem-a kontejnera i otežava trajno menjanje binarnih fajlova. | Aplikacija mora dobiti namenski writable volume za privremene fajlove, keš ili PID; PSS Restricted ne zahteva ovo polje. |
fsGroup i fsGroupChangePolicy |
Omogućavaju grupni pristup podržanim volume-ima; politika OnRootMismatch može izbeći nepotrebnu rekurzivnu promenu vlasništva. |
Promena vlasništva velikog volume-a može usporiti start; ponašanje zavisi od volume tipa i CSI drajvera. |
Linux capabilities, seccomp, AppArmor i SELinux
Ovi mehanizmi rešavaju različite probleme i najbolje rade slojevito. Capabilities određuju koje privilegovane kernel operacije proces može da izvrši. Seccomp ograničava skup sistemskih poziva. AppArmor i SELinux predstavljaju Linux Security Module kontrole koje prema profilu ili oznakama ograničavaju pristup fajlovima, procesima i drugim objektima. Nijedan sloj samostalno ne predstavlja potpun sandbox.
PSS Restricted za Linux zahteva eksplicitni seccomp profil RuntimeDefault ili Localhost, zabranjuje Unconfined, zahteva uklanjanje capability-ja ALL i dozvoljava vraćanje samo NET_BIND_SERVICE. Aktuelni PSS prihvata odgovarajuće AppArmor vrednosti, ali stvarno sprovođenje zavisi od podrške čvora. Od Kubernetes 1.30 AppArmor se zadaje poljem securityContext.appArmorProfile; stara anotacija container.apparmor.security.beta.kubernetes.io/* je deprecated. Za seccomp se koriste strukturisana polja; istorijske alpha seccomp anotacije nisu funkcionalne u podržanim izdanjima.
SELinux oznake se ne izmišljaju u aplikacionom timu. Dozvoljene vrednosti i kompatibilnost zavise od OS-a, runtime-a i politike na čvorovima. PSS ograničava tipove koji se mogu navesti, ali admission prihvatanje ne dokazuje da je konkretan profil učitan niti da će aplikacija imati potreban pristup. Posle admission validacije mora se proveriti stvarno pokretanje procesa i kernel/runtime poruke na čvoru.
Bezbedna osnova manifesta
Sledeći fragment predstavlja produkcionu početnu tačku za tipičnu Linux aplikaciju bez potrebe za Kubernetes API-jem. Ne može se slepo dodati svakoj slici: aplikacija mora podržavati ne-root UID, upis samo u deklarisani emptyDir i rad bez dodatnih capabilities.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hardened-web
namespace: secure-apps
spec:
replicas: 2
selector:
matchLabels:
app: hardened-web
template:
metadata:
labels:
app: hardened-web
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: registry.example.invalid/platform/hardened-web@sha256:REPLACE_ME
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 500m
memory: 256Mi
volumes:
- name: tmp
emptyDir:
sizeLimit: 128Mi
Digest je namerno ostavljen kao obavezna zamena, jer izmišljeni digest ne bi bio izvršiv primer. readOnlyRootFilesystem ne čini emptyDir read-only; proces i dalje može pisati u eksplicitno montiranu putanju. sizeLimit pomaže u kontroli lokalne ephemeral storage potrošnje, ali raspoloživost zavisi i od ukupnog prostora čvora i odgovarajućih resource ograničenja. Readiness/liveness probe, mrežna politika i Service dodaju se prema aplikaciji i ne pripadaju samom PSS profilu.
Pod Security Standards: Privileged, Baseline i Restricted
PSS nisu Kubernetes API objekti niti proizvoljne politike koje administrator uređuje. To su upstream definisani, kumulativni i verzionisani profili. Privileged je odsustvo ograničenja i namenjen je pouzdanim infrastrukturnim workload-ima. Baseline sprečava poznate eskalacije uz visoku kompatibilnost. Restricted primenjuje aktuelne hardening prakse uz cenu veće verovatnoće da starija ili loše pripremljena aplikacija neće proći.
| Oblast | Baseline | Dodatni zahtev Restricted profila |
|---|---|---|
| Privilegovani i host pristup | Zabranjuje privileged kontejnere, HostProcess, host PID/IPC, hostNetwork i nedozvoljene hostPort vrednosti. | Nasleđuje Baseline. |
| Volume tipovi | Zabranjuje hostPath i druge poznato rizične obrasce kroz svoja pravila. |
Dozvoljava samo eksplicitni skup: ConfigMap, CSI, downwardAPI, emptyDir, ephemeral, PVC, projected i Secret. |
| Identitet procesa | Ne zahteva obavezno non-root izvršavanje. | Zahteva runAsNonRoot: true i ne dozvoljava eksplicitni UID 0. |
| Eskalacija privilegija | Zabranjuje poznate opasne kombinacije, ali ne zahteva svako Restricted polje. | Za Linux zahteva allowPrivilegeEscalation: false. |
| Capabilities | Zabranjuje dodavanje definisanog skupa opasnih capabilities. | Zahteva drop: ["ALL"]; sme se dodati samo NET_BIND_SERVICE. |
| Seccomp | Ne dozvoljava eksplicitni Unconfined, ali profil može biti izostavljen. |
Za Linux zahteva RuntimeDefault ili Localhost. |
| Sysctl | Dozvoljava samo verzijski definisan skup safe sysctl vrednosti. | Nasleđuje Baseline. |
Tabela je operativni pregled, ne zamena za zvaničnu verzionisanu specifikaciju. Dopušteni sysctl parametri, SELinux tipovi i druge pojedinosti menjali su se kroz minor verzije. Zato produkciona promena mora dokumentovati i nivo i verziju profila. Vrednost latest automatski usvaja buduću specifikaciju posle nadogradnje control plane-a, što je korisno za kontinuirani audit, ali može neočekivano promeniti enforce odluke.
Šta PSS namerno ne rešava
PSS je široko primenljiva osnovna zaštita, ne kompletna organizaciona politika. Restricted profil, na primer, ne zahteva readOnlyRootFilesystem: true, ne bira dozvoljene image registre ili digest-e, ne ograničava ServiceAccount, ne zahteva resource requests/limits, ne proverava potpis slike, ne definiše egress i ne sprečava aplikacionu ranjivost. PSS takođe ne može izraziti pravilo „samo ovaj konkretni CSI drajver” ili „samo UID iz opsega dodeljenog tenant-u”.
Za dodatna pravila koriste se ValidatingAdmissionPolicy zasnovan na CEL-u ili pažljivo projektovani validating/mutating webhook-ovi. Mutacija može postaviti bezbedne podrazumevane vrednosti, ali ne sme skrivati činjenicu da aplikacioni manifest nije usaglašen. Svaki webhook uvodi zavisnost u API putanju, TLS sertifikate, dostupnost i failure policy; pogrešna politika može blokirati sav deployment ili, uz failurePolicy: Ignore, propustiti kontrolu upravo tokom kvara.
Admission tok i mesto Pod Security kontrole
Posle autentikacije i autorizacije API zahtev prolazi kroz mutating admission, zatim validaciju objekta i validating admission pre upisa u etcd. Admission ne autorizuje korisnika: RBAC može dozvoliti kreiranje poda, a PodSecurity ga ipak odbiti jer je manifest rizičan. Obrnuto, usaglašen SecurityContext ne daje pravo korisniku da kreira workload.
Ugrađeni PodSecurity je validating admission kontroler, stabilan od Kubernetes 1.25 i u aktuelnom Kubernetes 1.36 uključen u podrazumevani skup admission pluginova. Nasledio je uklonjeni PodSecurityPolicy (PSP), ali nije njegov API-kompatibilan naslednik: ne bira između proizvoljnih policy objekata, ne mutira pod da bi popunio security defaults i ne vezuje politiku za korisnika preko RBAC-a. Migracija sa PSP-a zato zahteva analizu stvarnih workload-a, dodavanje eksplicitnih polja i odabir namespace strategije, a ne samo promenu naziva plugina.
Deployment, StatefulSet, DaemonSet ili Job obično samo čuvaju Pod template; njihov kontroler kasnije kreira stvarni Pod. PSA primenjuje enforce na zahtev za Pod, ne na samu kreaciju workload kontrolera. Režimi warn i audit proveravaju Pod template podržanih workload resursa kako bi korisnik dobio raniji signal, ali konačna enforce odluka nastaje kada kontroler pokuša da kreira Pod. Zbog toga je moguće da Deployment objekat bude kreiran, a da nema dostupne replike jer su njegovi Pod zahtevi odbijeni.
Namespace oznake: enforce, audit i warn
PSA politiku najčešće dobija iz standardnih labela namespace-a. Svaki režim ima labelu nivoa i opcionu labelu verzije. Režimi mogu koristiti različite profile: razuman migracioni obrazac je enforce=baseline, dok warn i audit proveravaju restricted. Tako se poznate eskalacije odmah blokiraju, a tim dobija dokaz o preprekama za stroži profil.
apiVersion: v1
kind: Namespace
metadata:
name: secure-apps
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.36
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.36
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.36
enforce odbija Pod koji krši profil. warn vraća upozorenje klijentu, ali dozvoljava zahtev. audit dodaje anotaciju audit događaju, pod uslovom da je Kubernetes audit konfigurisan tako da događaj bude zabeležen. Warn nije trajni inventar, a audit bez odgovarajuće audit politike i centralnog prikupljanja nije operativan dokaz.
Namespace labela je bezbednosna kontrola. Pravo menjanja Namespace objekata ili ovih labela praktično omogućava ublažavanje politike, pa mora biti ograničeno RBAC-om i praćeno auditom. Labela se ne kopira automatski na druge namespace-e, a promena globalnog default-a ne mora biti vidljiva u njihovim metapodacima. Inventar zato mora razlikovati eksplicitno označene namespace-e od onih koji nasleđuju cluster-wide default.
Verzionisanje politike i bezbedna nadogradnja
PSS verzija je vezana za Kubernetes minor izdanje, na primer v1.36. Zaključavanje verzije čini ponašanje predvidivim tokom control plane nadogradnje. latest prati najnoviju politiku koju API server poznaje. Dobra produkciona praksa je da enforce ostane zaključan na testiranu verziju, dok audit/warn privremeno koriste ciljnu noviju verziju. Posle sanacije svih nalaza i testa u preprodukciji pomeraju se enforce labela i dokumentovani kriterijum prihvatanja.
Verzija politike nije isto što i verzija manifesta ili kubelet-a. U klasteru sa skew-om čvorova potrebno je potvrditi da runtime i OS podržavaju tražena polja. Posebno treba proveriti mešovite Linux/Windows klastere: od PSS v1.25 kontrole specifične za Linux, uključujući privilege escalation, seccomp i capabilities, ne primenjuju se kada pod eksplicitno navede spec.os.name: windows. Ako to polje izostane, kubelet pre v1.24 ga ne popunjava i politika može manifest tretirati kao Linux. Node selector i spec.os.name moraju biti međusobno usklađeni.
Cluster-wide konfiguracija i izuzeci
Samoupravni vanilla klaster može proslediti AdmissionConfiguration API serveru parametrom --admission-control-config-file i definisati cluster-wide podrazumevane režime. Konfiguracioni API pod-security.admission.config.k8s.io/v1 zahteva Kubernetes 1.25 ili noviji; 1.23–1.24 koristili su v1beta1, a 1.22 v1alpha1. U upravljanom klasteru pristup parametrima API servera može biti nedostupan i koristi se podržani mehanizam provajdera.
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: baseline
enforce-version: v1.36
audit: restricted
audit-version: v1.36
warn: restricted
warn-version: v1.36
exemptions:
usernames: []
runtimeClasses: []
namespaces:
- kube-system
Primer je arhitektonski obrazac, ne univerzalna preporuka da se kube-system trajno izuzme. Izuzeci mogu biti korisni za kontrolisane infrastrukturne komponente, ali su široki: zahtev koji ispuni uslov izuzetka preskače sva tri režima, pa nema ni enforce, ni warn, ni audit evaluacije PSA. Izuzeci se mogu definisati po autentifikovanom korisničkom imenu, RuntimeClass nazivu ili namespace-u i moraju biti eksplicitni.
Izuzimanje krajnjeg korisnika često ne daje očekivani rezultat, jer Deployment-ov Pod kreira controller ServiceAccount, ne korisnik koji je kreirao Deployment. Izuzimanje controller ServiceAccount-a je još opasnije: posredno izuzima Pod template svakog korisnika koji sme da kreira odgovarajući workload. Prednost imaju namenski infrastrukturni namespace, ograničen RBAC, posebni čvorovi, audit i najmanji mogući izuzetak sa vlasnikom i rokom ukidanja.
Projektovanje politike po zonama poverenja
Jedna politika za ceo klaster često ili blokira platformu ili ostavlja aplikacije nedovoljno zaštićene. Produkcioni model razdvaja namespace-e prema funkciji i poverenju. Poslovne aplikacije treba projektovati za Restricted. Nasleđene aplikacije mogu privremeno imati Baseline enforce i Restricted warn/audit uz plan migracije. Infrastrukturni DaemonSet-i kojima su dokazano potrebni hostNetwork, hostPath ili dodatne capabilities smeštaju se u namenski, strogo administriran namespace i na odvojene čvorove kada je moguće.
| Zona | Predložena PSA osnova | Dodatne kontrole |
|---|---|---|
| Poslovne aplikacije | Restricted enforce, audit i warn na zaključanoj verziji | Non-root slike, read-only root FS, NetworkPolicy, digest/potpis slike, minimalni ServiceAccount |
| Prelazna zona | Baseline enforce; Restricted audit/warn | Inventar prekršaja, vlasnik, rok sanacije i zabrana novih izuzetaka bez odobrenja |
| Platformski add-on-i | Posebno procenjen profil ili najmanji izuzetak | Ograničen RBAC, namenski namespace/čvorovi, provenance slike, runtime monitoring i detaljan audit |
| Eksperimentalni lab | Najmanje Baseline enforce | Bez produkcionih tajni i mrežnog puta ka kritičnim sistemima; vremenski ograničen namespace |
Validacija pre promene stanja
Server-side dry-run prolazi kroz autentikaciju, autorizaciju, defaulting i admission, ali ne upisuje objekat. Zato je vredniji od samog lokalnog parsiranja YAML-a. Ipak, dry-run ne pokreće scheduler, image pull, CSI montažu ni proces; uspeh znači samo da je zahtev prihvatljiv API serveru u tom trenutku.
kubectl apply --dry-run=server -f workload.yaml
kubectl label --dry-run=server --overwrite namespace secure-apps \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=v1.36
Kada se enforce labela dodaje ili menja, PSA proverava postojeće podove i vraća zbirna upozorenja, ali ne briše već pokrenute podove. Nova politika utiče na buduće kreacije; sledeći rollout, rescheduling ili zamena poda može biti odbijena. Zato namespace ne treba proglasiti usaglašenim samo zato što labela postoji. Potrebno je testirati sve Pod template-e, CronJob/Job scenarije, init kontejnere i operativne ephemeral kontejnere.
Integrisani praktikum: od detekcije do Restricted enforce politike
Poslovni i tehnički kontekst. Tim uvodi namespace secure-apps za internu web aplikaciju. Bezbednosni zahtev je da se poznato privilegovani workload odmah odbije, dok usaglašena aplikacija radi kao ne-root proces bez dodatnih capabilities, bez automatski montiranog API tokena i sa read-only korenskim filesystem-om. Vežba prvo uvodi Baseline enforce i Restricted warn/audit, zatim otklanja nalaze i podiže enforce na Restricted.
Topologija i preduslovi. Potreban je izolovan laboratorijski vanilla Kubernetes klaster 1.25 ili noviji sa omogućenim PodSecurity admission kontrolerom, Linux worker čvorom, funkcionalnim DNS-om i mogućnošću preuzimanja slike nginxinc/nginx-unprivileged:1.27-alpine. Za potpuno ponovljivu produkcionu primenu slika se preslikava u interni registry, skenira i referencira digest-om. Primer zaključava PSS na v1.36; na starijem API serveru mora se koristiti verzija koju taj server poznaje.
Prava i rizik. Izvršilac mora moći da kreira i briše namespace i laboratorijske Deployment/Pod objekte i da čita događaje. Promena namespace labela menja bezbednosnu politiku i u produkciji zahteva odobrenje. Cleanup koji briše namespace je destruktivan i izvršava se samo u izolovanoj laboratoriji.
-
Proveriti kontekst, verziju i admission ponašanje. Komande su read-only osim server-side dry-run zahteva, koji ne upisuje stanje.
Ako namespace već postoji, vežba se zaustavlja dok se ne potvrdi vlasnik. Na samoupravnom kubeadm control plane-u ovlašćeni administrator može read-only proveriti static Pod manifest, ali ne treba menjati ga u ovoj vežbi:kubectl config current-context kubectl version kubectl auth whoami kubectl get --raw='/version' kubectl get namespace secure-apps
Odsustvo eksplicitnogsudo grep -n -- '--enable-admission-plugins\|--disable-admission-plugins\|--admission-control-config-file' \ /etc/kubernetes/manifests/kube-apiserver.yamlPodSecurityargumenta ne dokazuje da je plugin isključen, jer je podrazumevano omogućen u podržanim izdanjima. Funkcionalni test u narednim koracima daje pouzdaniji dokaz. -
Kreirati namespace u postepenom režimu. Ova komanda menja stanje. Baseline se odmah sprovodi, dok Restricted za sada samo upozorava i ostavlja audit anotaciju.
Očekivani rezultat: namespace sadrži svih šest labela. Ako server prijavi dakubectl create namespace secure-apps kubectl label namespace secure-apps --overwrite \ pod-security.kubernetes.io/enforce=baseline \ pod-security.kubernetes.io/enforce-version=v1.36 \ pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/audit-version=v1.36 \ pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/warn-version=v1.36 kubectl get namespace secure-apps --show-labelsv1.36nije validna policy verzija, ne koristi selatestnapamet; utvrđuje se verzija control plane-a i manifest prilagođava podržanoj, dokumentovanoj verziji. -
Dokazati da Baseline odbija privilegovani Pod. Manifest je namerno nebezbedan i koristi se samo kao server-side dry-run; nijedan Pod se ne upisuje.
Očekivani rezultat: API server odbija zahtev porukom koja navodi kršenje Baseline kontrole za privileged container. Ako se YAML ipak vrati bez odbijanja, proveravaju se namespace, labele i da li jemkdir -p pod-security-lab cd pod-security-lab kubectl -n secure-apps run privileged-probe \ --image=busybox:1.36.1 \ --restart=Never \ --overrides='{"spec":{"containers":[{"name":"privileged-probe","image":"busybox:1.36.1","command":["sleep","3600"],"securityContext":{"privileged":true}}]}}' \ --dry-run=server -o yamlPodSecurityadmission aktivan. Ne uklanja se labela da bi test „prošao”. -
Kreirati aplikacioni manifest koji je Baseline-kompatibilan, ali namerno nepotpun za Restricted.
Očekivani rezultat: zahtev je dozvoljen pod Baseline enforce režimom, alicat > 01-baseline-app.yaml <<'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: web namespace: secure-apps labels: app: web spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web image: nginxinc/nginx-unprivileged:1.27-alpine ports: - name: http containerPort: 8080 resources: requests: cpu: 25m memory: 32Mi limits: cpu: 250m memory: 128Mi EOF kubectl apply --dry-run=server -f 01-baseline-app.yaml kubectl apply -f 01-baseline-app.yamlkubectlprikazuje Restricted upozorenja, tipično za nedostajućeallowPrivilegeEscalation: false,runAsNonRoot: true,capabilities.drop: ["ALL"]i seccomp profil. Tačan tekst zavisi od policy verzije. Upozorenje se čuva kao nalaz, ne tumači kao neuspeh API-ja. -
Prikupiti početne dokaze.
Podovi treba da budu Ready. Audit zapis, ako je audit omogućen, treba da sadrži PSA audit anotacije za Restricted prekršaje. Lokacija zavisi odkubectl -n secure-apps rollout status deployment/web --timeout=120s kubectl -n secure-apps get deployment,replicaset,pod -o wide kubectl -n secure-apps get events --sort-by='.metadata.creationTimestamp' kubectl -n secure-apps get deployment web -o yaml--audit-log-pathili centralnog sistema upravljanog klastera. Warn izlaz terminala nije zamena za audit zapis. -
Izraditi Restricted-kompatibilan i dodatno ojačan manifest. Ova verzija dodaje sva ključna Restricted polja, isključuje nepotrebni ServiceAccount token i postavlja read-only root filesystem sa namenskim writable putanjama koje nginx slika koristi.
Kriterijum: dry-run prolazi bez Restricted upozorenja. Ako slika u korišćenom izdanju zahteva druge writable putanje ili UID, to se potvrđuje dokumentacijom i testom slike; ne vraća se root niti uklanja zaštita bez analize.cat > 02-restricted-app.yaml <<'EOF' apiVersion: apps/v1 kind: Deployment metadata: name: web namespace: secure-apps labels: app: web spec: replicas: 2 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: automountServiceAccountToken: false securityContext: runAsNonRoot: true runAsUser: 101 runAsGroup: 101 seccompProfile: type: RuntimeDefault containers: - name: web image: nginxinc/nginx-unprivileged:1.27-alpine ports: - name: http containerPort: 8080 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL readinessProbe: httpGet: path: / port: http initialDelaySeconds: 2 periodSeconds: 5 volumeMounts: - name: cache mountPath: /var/cache/nginx - name: pid mountPath: /var/run - name: tmp mountPath: /tmp resources: requests: cpu: 25m memory: 32Mi limits: cpu: 250m memory: 128Mi volumes: - name: cache emptyDir: sizeLimit: 64Mi - name: pid emptyDir: sizeLimit: 8Mi - name: tmp emptyDir: sizeLimit: 32Mi EOF kubectl apply --dry-run=server -f 02-restricted-app.yaml -
Primeniti hardening kroz kontrolisani rollout.
Ako rollout ne uspe, prvo se čuvaju događaji i logovi. Bezbedan rollback jekubectl apply -f 02-restricted-app.yaml kubectl -n secure-apps rollout status deployment/web --timeout=180s kubectl -n secure-apps get pods -l app=web kubectl -n secure-apps logs deployment/web --tail=50kubectl -n secure-apps rollout undo deployment/web, jer je prethodna verzija i dalje Baseline-kompatibilna; zatim se u preprodukciji ispravlja image/permissions problem. Rollback je privremeno zaobilazno rešenje, ne trajno odustajanje od Restricted cilja. -
Dokazati efektivni identitet i filesystem kontrole.
Očekivani rezultat: UID nije 0, upis uPOD_NAME=$(kubectl -n secure-apps get pod -l app=web \ -o jsonpath='{.items[0].metadata.name}') kubectl -n secure-apps exec "$POD_NAME" -- id kubectl -n secure-apps exec "$POD_NAME" -- sh -c \ 'test "$(id -u)" != "0" && touch /tmp/allowed && ! touch /should-fail' kubectl -n secure-apps get pod "$POD_NAME" \ -o jsonpath='{.spec.securityContext.seccompProfile.type}{"\n"}{.spec.containers[0].securityContext}{"\n"}'/tmpuspeva, upis u root filesystem ne uspeva, a manifest pokazujeRuntimeDefault, uklonjene capabilities i onemogućenu eskalaciju. Komanda ne dokazuje sadržaj kernel capability seta; za dublju proveru koristi se odobren alat ili čitanje/proc/1/statusuz pravilno tumačenje heksadecimalnih maski. -
Podignuti enforce na Restricted tek nakon dry-run provere.
Validacija: labela pokazuje Restricted v1.36, postojeće dve replike ostaju Ready, a ponovno izvršavanje privileged dry-run testa biva odbijeno. Promena ne deložira neusaglašene postojeće podove; zato se radi i kontrolisani restart kako bi se dokazalo da nove kreacije prolaze.kubectl label --dry-run=server --overwrite namespace secure-apps \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=v1.36 kubectl label --overwrite namespace secure-apps \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=v1.36 kubectl get namespace secure-apps --show-labelskubectl -n secure-apps rollout restart deployment/web kubectl -n secure-apps rollout status deployment/web --timeout=180s kubectl -n secure-apps get events --sort-by='.metadata.creationTimestamp' -
Simulirati regresiju i razlikovati mesto kvara. Sledeći patch postoji samo kao server-side dry-run i pokušava da vrati eskalaciju privilegija.
Warn provera workload template-a treba da prijavi prekršaj. Pošto enforce ne važi direktno za Deployment objekat, ponašanje klijenta i API verzije treba tumačiti zajedno sa konačnim testom stvarnog Pod-a. U produkciji se ne primenjuje ovaj patch samo da bi se proizveo incident.kubectl -n secure-apps patch deployment web \ --type='strategic' \ --patch='{"spec":{"template":{"spec":{"containers":[{"name":"web","securityContext":{"allowPrivilegeEscalation":true}}]}}}}' \ --dry-run=server -
Dokumentovati prihvatanje i bezbedno očistiti laboratoriju. Prihvatanje zahteva: zaključane namespace labele, uspešan server-side dry-run, dve Ready replike nakon restart-a, ne-root UID, dozvoljen upis samo u deklarisani volume, odbijeni privileged zahtev i sačuvan audit/događajni trag.
Poslednja komanda je destruktivna i briše sve namespace resurse. Izvršava se samo ako jekubectl -n secure-apps get deployment web -o yaml > accepted-deployment.yaml kubectl get namespace secure-apps -o yaml > accepted-namespace.yaml kubectl delete namespace secure-appssecure-appsnapravljen isključivo za ovu vežbu. U deljenom okruženju briše se samoDeployment/web, a namespace politika se ne menja bez zasebno odobrene promene.
Troubleshooting: Pod je odbijen ili workload nema replike
Dijagnostika počinje razdvajanjem simptoma. Ako kubectl apply odmah vraća Forbidden sa PSS detaljima, odbijen je direktni Pod zahtev ili druga admission provera. Ako je Deployment kreiran, ali READY 0/2, njegov controller možda ne može da kreira Pod. Ako Pod postoji u stanju CreateContainerConfigError ili CrashLoopBackOff, admission je već prošao i problem je u kubelet/runtime fazi, dozvolama fajlova ili aplikaciji.
kubectl -n secure-apps get deployment,replicaset,pod
kubectl -n secure-apps describe deployment web
kubectl -n secure-apps describe replicaset -l app=web
kubectl -n secure-apps get events --sort-by='.metadata.creationTimestamp'
kubectl -n secure-apps logs deployment/web --all-containers --previous
Read-only redosled je: potvrditi kontekst i namespace labele; sačuvati primljenu admission poruku; pronaći vlasnički lanac Deployment–ReplicaSet–Pod; pregledati događaje; uporediti efektivni Pod template sa PSS verzijom; zatim proveriti runtime i aplikacione logove. Tek posle postavljene hipoteze menja se manifest. Uklanjanje enforce labele ili prebacivanje namespace-a na Privileged nije dijagnostika, već proširenje attack surface-a.
| Simptom | Neposredni uzrok | Bezbedna korektivna mera |
|---|---|---|
violates PodSecurity "restricted" |
Jedno ili više polja regularnog, init ili ephemeral kontejnera krši zaključani profil. | Popisati svako navedeno polje, ispraviti sliku/manifest i ponoviti server-side dry-run. |
Deployment postoji, ReplicaSet stalno prijavljuje FailedCreate |
Controller-ov Pod zahtev odbija enforce, quota ili drugi admission plugin. | Pregledati ReplicaSet događaje i tačnu poruku; ne tražiti aplikacioni log poda koji nikada nije kreiran. |
runAsNonRoot greška za sliku |
Slika podrazumevano koristi root ili sadrži nenumerički USER koji runtime ne može potvrditi. | Izgraditi sliku sa numeričkim ne-root USER-om i pravilnim vlasništvom; runAsUser dodati tek posle testa dozvola. |
Permission denied nakon non-root promene |
Konfiguracija, volume ili aplikaciona putanja pripada root-u. | Ispraviti image build ili volume ownership; pažljivo koristiti fsGroup. Privilegovani init chown nije prihvatljiva podrazumevana zakrpa. |
| Aplikacija pada uz read-only filesystem | Pokušava upis u neidentifikovanu putanju. | Iz loga utvrditi tačnu putanju i montirati ograničen emptyDir ili odgovarajući PVC; ne vraćati ceo root FS na writable bez analize. |
Seccomp operation not permitted |
RuntimeDefault blokira syscall ili aplikacija pogrešno tumači drugi permission problem. | Korelisati runtime/kern log, syscall i verziju slike; prilagoditi aplikaciju ili kontrolisani Localhost profil. Unconfined je samo kratka izolovana dijagnostička hipoteza, ne produkciono rešenje. |
| Promena namespace labele nije ugasila stare Podove | PSA ne vrši retroaktivnu deložaciju. | Sanirati template i sprovesti kontrolisani rollout uz PDB i proveru kapaciteta. |
Incident response: neočekivani privilegovani workload
- Odrediti simptom, obim i posledicu. Identifikovati namespace, Pod UID, owner references, čvor, image digest, ServiceAccount, host namespace/volume upotrebu, capabilities i vreme kreiranja. Privileged Pod na čvoru može značiti potencijalni incident na nivou čvora, ne samo aplikacije.
- Sačuvati dokaze pre promene. Sačuvati Pod/Deployment/DaemonSet manifest, događaje, audit zapise za kreiranje i promenu namespace labela, runtime/kubelet logove, listu zahvaćenih čvorova i relevantne mrežne tokove. Secret i token vrednosti se ne kopiraju u tiket.
- Postaviti hipotezu. Razlikovati odobren infrastrukturni izuzetak, pogrešnu ili uklonjenu PSA labelu, globalni izuzetak, kompromitovan administratorski identitet, webhook fail-open i stariji Pod koji je postojao pre pooštravanja politike.
- Ograničiti štetu. Uz incidentnog vlasnika sprečiti nove kreacije vraćanjem odobrene namespace politike i uklanjanjem neovlašćenog RBAC pristupa. Izolovati čvor i workload mrežno kada rizik opravdava prekid. Brisanje poda pre prikupljanja dokaza može ukloniti važan trag i izazvati automatsko ponovno kreiranje.
- Iskoreniti uzrok. Ukloniti neodobreni controller, rotirati kompromitovane akreditacije, vratiti Git-known-good manifeste, pregledati host zbog promena i po potrebi reprovisionovati čvor iz pouzdanog izvora. Samo dodavanje PSA labele ne poništava radnje koje je privilegovani proces već izvršio.
- Oporaviti i dokazati. Ponovo rasporediti usaglašen workload, potvrditi Restricted/odobrenu politiku, negativni privileged dry-run, Ready stanje, audit kontinuitet i odsustvo neočekivanih procesa ili fajlova na čvoru.
- Sprečiti ponavljanje. Ograničiti pravo menjanja namespace labela i admission konfiguracije, uvesti alarm za privilegovane karakteristike i PSA exemptions metriku, kontrolisati image provenance i definisati rok svakom izuzetku.
Metrike, događaji, audit i logovi
API server izlaže Prometheus metrike pod_security_evaluations_total, pod_security_exemptions_total i pod_security_errors_total. Prva pokazuje broj evaluacija po odluci, režimu, nivou i verziji; druga korišćenje izuzetaka; treća interne greške evaluacije. Rast exemptions zahteva korelaciju sa odobrenim infrastrukturnim operacijama. Greška evaluacije nije isto što i legitimno odbijanje manifesta i zahteva hitnu proveru API servera.
User-facing warn poruka živi u odgovoru klijentu. Controller-ov FailedCreate obično se vidi u događajima ReplicaSet-a, DaemonSet-a ili Job-a. Audit režim dodaje anotacije audit događaju; njihov opstanak zavisi od audit nivoa i backend-a. Na kubeadm čvoru API server radi kao static Pod, pa se logovi čitaju preko container runtime-a ili centralne platforme. Journal putanja je relevantna samo kada je komponenta zaista systemd servis. Uvek se bira vremenski opseg oko requestReceivedTimestamp i korelišu korisnik, source IP, user agent, namespace, resurs, odgovor i audit ID.
kubectl -n secure-apps get events \
--field-selector=reason=FailedCreate \
--sort-by='.metadata.creationTimestamp'
kubectl get --raw='/metrics' \
| grep '^pod_security_'
Druga komanda je read-only, ali pristup /metrics je obično ograničen i izlaz može biti velik. U produkciji se preferira odobreni Prometheus scrape i dashboard sa trendovima. Ako korisnik nema pravo, ne dodeljuje mu se široka API privilegija samo radi jednokratnog čitanja metrike.
Administratorske prakse
- Inventar namespace politike. Najmanje mesečno i posle svake nadogradnje izvesti sve PSA labele, identifikovati namespace-e bez eksplicitnog enforce nivoa i povezati ih sa cluster-wide default-om. Svaki namespace mora imati vlasnika, zonu poverenja, nivo, verziju i datum sledeće revizije.
- Postepeno pooštravanje. Pre promene enforce nivoa aktivirati ciljnu politiku kroz audit/warn, prikupiti prekršaje tokom reprezentativnog poslovnog ciklusa, testirati sve controller template-e i tek zatim menjati enforce. Dry-run je obavezan, ali nije dovoljan bez stvarnog rollout testa.
- Kontrola izuzetaka. Kvartalno i posle incidenta pregledati username, RuntimeClass i namespace izuzetke, metriku
pod_security_exemptions_total, vlasnika, poslovno opravdanje i rok. Izuzetak bez aktivnog vlasnika ili dokazane potrebe uklanja se kroz odobrenu promenu. - Hardening šabloni. Održavati verzionisane Pod template obrasce sa non-root izvršavanjem,
allowPrivilegeEscalation: false,drop: ALL,RuntimeDefault, read-only root filesystem-om i isključenim ServiceAccount tokenom kada nije potreban. Obrasci se testiraju protiv svih podržanih OS/runtime kombinacija. - Zaštita kontrolne ravni. RBAC za menjanje Namespace labela, admission konfiguracije, ValidatingAdmissionPolicy i webhook objekata daje se najmanjem broju uloga. Promene se čuvaju deklarativno, prolaze peer review i korelišu sa audit zapisom.
- Nadogradnja PSS verzije. Pre minor nadogradnje uporediti PSS specifikacije stare i ciljne verzije, postaviti ciljnu verziju na audit/warn, sanirati nalaze i testirati rollback. Enforce verzija se pomera tek kada su kritični workload-i prošli kontrolisano ponovno kreiranje.
- Oporavak konfiguracije. Backup obuhvata Namespace objekte sa labelama, workload manifeste i API server AdmissionConfiguration u samoupravnom klasteru. Restore test mora dokazati i pozitivnu kreaciju usaglašenog poda i negativno odbijanje privilegovanog poda; samo postojanje YAML fajla nije dokaz funkcionalne kontrole.
Završna kontrolna tačka
- Čitalac razlikuje pod-level
PodSecurityContextod container-levelSecurityContextpolja i proverava regularne, init i ephemeral kontejnere. - Može da objasni posledice
runAsNonRoot, UID/GID,fsGroup,allowPrivilegeEscalation, capabilities, seccomp i read-only root filesystem-a. - Razlikuje PSS profile Privileged, Baseline i Restricted i zna da su kumulativni i verzionisani.
- Razlikuje standard PSS od admission mehanizma PSA i od prilagođenih CEL/webhook politika.
- Može da postavi namespace režime
enforce,auditiwarnsa eksplicitnom policy verzijom. - Razume da enforce proverava stvarne Pod zahteve i da kreiran Deployment ne garantuje kreiranje replika.
- Server-side dry-run odbija privilegovani test, a usaglašeni manifest prolazi bez Restricted upozorenja.
- Stvarni proces radi kao ne-root, nema mogućnost eskalacije, nema dodatne capabilities i koristi očekivani seccomp profil.
- Postojeći podovi su kontrolisano ponovo kreirani nakon pooštravanja politike; Ready stanje i događaji su provereni.
- Izuzeci, pravo menjanja namespace labela, audit trag, metrike i rollback imaju dokumentovane vlasnike i kriterijume.
Zaključak
SecurityContext pretvara očekivanja o privilegijama procesa u deo deklarativnog ugovora workload-a. Non-root UID, zabrana eskalacije, minimalne capabilities, seccomp, read-only root filesystem i pažljivo projektovane volume dozvole smanjuju mogućnost da kompromitovan proces preraste u kompromitovanje poda ili čvora. Međutim, ta polja vrede samo ako ih slika, runtime i OS stvarno podržavaju i ako se rezultat proveri u izvršavanju.
Pod Security Standards daju zajednički, verzionisani minimum, a Pod Security Admission taj minimum sprovodi u API putanji pre kreiranja poda. Produkciona zrelost vidi se u postepenom prelasku sa audit/warn na enforce, zaključavanju testirane verzije, odvojenim zonama poverenja i izuzetku koji je uži od problema koji rešava. Privileged namespace bez vlasnika i roka nije operativna fleksibilnost, već trajno otvorena granica napada.
Administrator posle ove lekcije ne procenjuje bezbednost po tome da li manifest sadrži nekoliko poznatih polja. On dokazuje koju politiku API server primenjuje, da li controller uspeva da kreira replike, pod kojim UID/GID proces radi, koje putanje su writable, koje operacije su odbijene, gde je audit trag i šta se dešava pri nadogradnji ili rollback-u. Time admission prestaje da bude prepreka deployment-u i postaje kontrolisana zaštita platforme.