Vanilla Kubernetes

Drugi deo: Workload platforma, mreža, podaci i bezbednost
Zaštita tajni, audit politika i trag administrativne promene
VIII. Identitet, autorizacija i zaštita workload-a
38. Zaštita tajni, audit politika i trag administrativne promene

Prethodne lekcije razdvojile su ljudske i mašinske identitete, ograničile njihova prava RBAC pravilima i sprečile privilegovane workload-e admission kontrolom. Ova lekcija zatvara bezbednosnu celinu pitanjem koje ostaje i kada su identitet i autorizacija dobro projektovani: kako zaštititi akreditacije koje aplikacije moraju da koriste, kako dokazati ko je pristupio API-ju i kako povezati tehnički događaj sa odobrenom administrativnom promenom.

Kubernetes Secret je API objekat namenjen maloj količini osetljivih podataka, ali nije samostalan trezor. Base64 zapis nije enkripcija; API server, etcd, RBAC, kubelet, čvor, aplikacioni proces, CI/CD sistem i rezervna kopija pripadaju istom životnom ciklusu tajne. Audit evidencija, sa druge strane, nije samo „uključen log”: njena politika određuje koji zahtev se beleži, u kom obimu, u kojoj fazi i uz kakav rizik da se osetljivi sadržaj ponovo otkrije u log sistemu.

Secret kao API objekat i granice njegove zaštite

Secret iz osnovne API grupe core/v1 čuva parove ključa i vrednosti i ograničen je na jedan namespace. Uobičajeni tip Opaque nema dodatnu semantičku validaciju poslovnog sadržaja, dok ugrađeni tipovi, kao što su kubernetes.io/tls, kubernetes.io/dockerconfigjson i kubernetes.io/basic-auth, opisuju namenu i mogu proveriti prisustvo određenih ključeva. Tip ne pretvara sadržaj u bezbedniji kriptografski format i ne zamenjuje RBAC, rotaciju ili enkripciju u mirovanju.

Polje data sadrži base64 kodirane bajtove. Ono omogućava prenos binarnog sadržaja kroz JSON/YAML, ali svako ko može da pročita objekat može jednostavno da ga dekodira. Polje stringData prihvata običan tekst pri upisu, koji API server spaja u data; zbog toga manifest sa stringData ne sme biti sačuvan u nezaštićenom Git repozitorijumu, tiketima ili CI logovima. stringData takođe ne funkcioniše dobro sa server-side apply postupkom i ne treba ga koristiti kao osnovu GitOps vlasništva nad poljima.

apiVersion: v1
kind: Secret
metadata:
  name: inventory-db
  namespace: plat-app
type: Opaque
stringData:
  username: inventory_ro
  password: REPLACE-OUTSIDE-GIT

Ovaj manifest je obrazac za laboratoriju, a ne preporučeni način čuvanja produkcione lozinke. Komanda kubectl get secret ... -o yaml vraća kodirane vrednosti, dok kubectl get secret ... -o jsonpath zajedno sa dekodiranjem vraća čist tekst. Obe operacije zahtevaju kontrolisani terminal i sprečavanje snimanja izlaza.

Životni ciklus tajne i površine curenja

Tajna nastaje izvan ili unutar klastera, prenosi se do API servera, čuva u etcd-u, distribuira kubelet-u samo kada je potrebna podu, projektuje u kontejner, čita u memoriju procesa, rotira i na kraju opoziva. Svaka etapa ima drugačiju granicu poverenja. TLS štiti prenos do API servera, ali ne štiti od autorizovanog čitanja. Enkripcija u etcd-u štiti podatke u mirovanju, ali API server i dalje vraća čist sadržaj ovlašćenom klijentu. Read-only mount sprečava aplikaciju da menja projektovani fajl, ali ne sprečava proces u kontejneru da ga pročita.

Površina Tipičan rizik Primarna kontrola Dokaz
Manifest, Git i CI/CDČist tekst u istoriji, artefaktu ili loguSpoljni secret store, šifrovani Git format, maskiranje i kratkotrajne akreditacijeRepo skeniranje, CI audit i pregled istorije
API i RBACPreširoko get, list ili watch pravoNajmanje privilegije, namenski namespace i admission politikakubectl auth can-i i audit događaj
etcd i backupČitljiv sadržaj u bazi ili snapshot-uEncryptionConfiguration ili KMS v2, zaštita ključeva i backup enkripcijaDirektna kontrolisana provera etcd zapisa i restore test
Čvor i kubeletPrivilegovani pod ili kompromitovan čvorPod Security, zaštita hosta, minimalan raspored i izolacijaNode audit, runtime detekcija i raspored podova
Kontejner i procesEnvironment dump, core dump, debug endpoint ili logFajl mount samo potrebnom kontejneru, bez shell-a i bez ispisivanja tajniAplikacioni test i skeniranje logova
Rotacija i opozivStara vrednost ostaje važeća kod izdavaocaPreklapajuća rotacija, reload/rollout, opoziv i negativni testUspeh novog i neuspeh starog credential-a

Volume projekcija, environment promenljive i ažuriranje

Secret može biti montiran kao volume, prosleđen kao environment promenljiva ili korišćen kao image pull credential. Za aplikacione akreditacije volume je često bolji izbor: kubelet periodično sinhronizuje projektovani sadržaj i atomski menja simboličke veze kada se Secret promeni. Aplikacija ipak mora ponovo da otvara fajl ili podržava reload; proces koji je vrednost učitao jednom pri pokretanju neće automatski promeniti stanje.

Secret prosleđen kao environment promenljiva ne menja se u već pokrenutom kontejneru. Potreban je kontrolisani rollout poda. Environment je lakše nenamerno izložiti kroz dijagnostičke komande, crash izveštaje ili aplikacioni dump. Volume projektovan korišćenjem subPath takođe ne dobija automatska ažuriranja, pa ga za rotirajuće tajne treba izbegavati. Ažuriranje projektovanog volumena nije trenutno: zavisi od kubelet sinhronizacije i strategije detekcije promena.

apiVersion: v1
kind: Pod
metadata:
  name: inventory-api
  namespace: plat-app
spec:
  serviceAccountName: inventory-api
  automountServiceAccountToken: false
  containers:
    - name: application
      image: registry.example.invalid/inventory/api@sha256:REPLACE_DIGEST
      volumeMounts:
        - name: db-credentials
          mountPath: /var/run/secrets/inventory-db
          readOnly: true
  volumes:
    - name: db-credentials
      secret:
        secretName: inventory-db
        defaultMode: 0400

Oktalni režim 0400 ograničava čitanje na vlasnika fajla u okviru modela dozvola kontejnera, ali efektivni UID/GID, fsGroup, runtime i aplikaciona prava moraju se proveriti u stvarnom podu. Jedan pod ima zajedničku granicu bezbednosti: procesi u sidecar kontejnerima dele mrežni i često procesni ili volume kontekst. Ako samo signer treba privatni ključ, mount se dodeljuje samo tom kontejneru, a aplikaciona arhitektura ograničava njegov interfejs.

RBAC, namespace i indirektan pristup tajnama

Pravo get nad Secret objektom otkriva njegov sadržaj. Prava list i watch takođe vraćaju podatke svih odgovarajućih objekata, pa nisu bezbedna „metadata-only” alternativa. resourceNames može ograničiti get na konkretan naziv, ali se ne može pouzdano primeniti na create i ne rešava svaki obrazac list/watch zahteva. Administrator zato ne dodeljuje čitanje svih tajni kontroleru samo zato što mu treba jedna akreditacija.

Mogućnost kreiranja Pod-a, Deployment-a ili drugog workload-a u namespace-u često predstavlja indirektnu mogućnost čitanja tajni u tom namespace-u: napadač može kreirati pod koji montira poznati Secret ili koristi privilegovaniji ServiceAccount. Pod Security Standards sprečavaju deo host eskalacija, ali ne odlučuju koji namespace Secret workload sme da referencira. Potrebni su razdvojeni namespace-i, najmanje RBAC privilegije, admission pravila i kontrola ServiceAccount dodele.

Secret se šalje samo čvoru na kome je raspoređen pod koji ga zahteva, a kubelet ga za volume drži u tmpfs. To smanjuje nepotrebnu distribuciju, ali privilegovani workload na istom čvoru ili kompromitovan node može preći ovu granicu. Lekcija 37 zato nije odvojena tema: zabrana privilegovanih podova neposredno štiti i tajne drugih workload-a na čvoru.

Immutable Secret i operativni kompromis

Polje immutable: true sprečava naknadnu promenu data i immutable sadržaja i može smanjiti opterećenje API servera jer kubelet ne mora da održava watch za taj objekat. Ono štiti od slučajnog prepisivanja, ali ne šifruje podatke, ne sprečava čitanje i ne obezbeđuje automatsku rotaciju. Jednom postavljen immutable Secret mora se obrisati i ponovo kreirati ili zameniti novim imenom; podovi koji ga koriste moraju biti kontrolisano rekonfigurisani.

apiVersion: v1
kind: Secret
metadata:
  name: inventory-db-v2
  namespace: plat-app
type: Opaque
immutable: true
data:
  username: BASE64_VALUE
  password: BASE64_VALUE

Produkcioni obrazac sa verzionisanim imenom omogućava da stari i novi credential kratko koegzistiraju, da se workload prebaci na novu referencu i da se stari credential opozove tek nakon validacije. Rizik je gomilanje zastarelih Secret objekata; vlasnik, rok opoziva i cleanup moraju biti deo promene.

Enkripcija podataka u mirovanju

Bez eksplicitne konfiguracije API server koristi provider identity, što znači da se resurs ne šifruje pre čuvanja u etcd-u. Enkripcija diska ili storage sistema korisna je, ali nije ista kontrola kao Kubernetes encryption at rest: EncryptionConfiguration štiti vrednost na nivou API resursa pre nego što je etcd primi. I dalje su potrebni TLS, RBAC, zaštita control-plane hosta i šifrovane rezervne kopije.

Provider-i se obrađuju redom. Prvi provider služi za novi upis, a naredni za čitanje postojećeg sadržaja. identity kao poslednji fallback omogućava migraciju ranije nešifrovanih zapisa; ako ostane prvi, novi podaci se i dalje čuvaju kao čist tekst. Lokalni ključ u EncryptionConfiguration fajlu štiti od krađe samog etcd-a, ali ne i od kompromitovanja control-plane hosta koji sadrži ključ.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key-2026-07
              secret: REPLACE_WITH_BASE64_32_BYTE_KEY
      - identity: {}

Primer pokazuje format i migracioni redosled, ali lokalni aescbc nije najbolji izbor za novu produkcionu platformu: aktuelna Kubernetes dokumentacija ga označava slabijim zbog svojstava CBC režima. aesgcm zahteva disciplinovanu automatizovanu rotaciju zbog ograničenja bezbedne upotrebe nonce-a. Za novu enterprise implementaciju prednost ima stabilni KMS v2, dostupan kao stable od Kubernetes 1.29, sa ključem za šifrovanje ključeva izvan klastera. KMS v1 je deprecated od 1.28 i ne treba ga birati za novu arhitekturu.

KMS uvodi novu dostupnosnu zavisnost: API server mora komunicirati sa KMS pluginom i spoljnim sistemom ključeva. Pre produkcije proveravaju se redundansa plugina i KMS servisa, latencija, TLS ili druga zaštita transporta, prava korišćenja KEK-a, ponašanje pri prekidu, monitoring, backup konfiguracije i disaster-recovery postupak. Gubitak ključa može učiniti podatke trajno nečitljivim; dostupnost kopije etcd-a nije dovoljna za oporavak.

Uvođenje, migracija i rotacija ključa za enkripciju

EncryptionConfiguration mora biti dostupna svakom kube-apiserver procesu i konfiguracija mora biti usklađena na svim control-plane čvorovima. U kubeadm klasteru to obično znači bezbedno postavljanje fajla na svaki čvor, read-only mount u static Pod manifest i parametar --encryption-provider-config. Promena static Pod manifesta restartuje lokalni API server i zato se u HA klasteru izvodi jedan po jedan čvor, uz proveru health endpoint-a, quorum-a i klijentskog pristupa pre prelaska na sledeći.

Aktiviranje konfiguracije utiče samo na nove upise. Postojeći Secret objekti moraju se ponovo upisati da bi bili šifrovani prvim provider-om. Zvanični postupak koristi kontrolisani read/write svih Secret objekata, ali time se menja resourceVersion, može pokrenuti watch događaje i opteretiti API server i etcd. Pre migracije prave se provereni etcd snapshot i kopija konfiguracije ključeva, procenjuje broj objekata, prati latencija i bira period promene.

# State-changing: ponovo upisuje sve Secret objekte.
# Izvršava se samo u odobrenom prozoru promene i posle backup-a.
kubectl get secrets --all-namespaces -o json \
  | kubectl replace -f -

Rotacija lokalnog ključa izvodi se dodavanjem novog ključa na prvo mesto, usklađivanjem svih API servera, ponovnim upisom resursa, proverom ciphertext-a i tek zatim uklanjanjem starog ključa. Uklanjanje ključa pre potpune migracije čini preostale zapise nečitljivim. Kod KMS-a se prati dokumentovani postupak konkretnog plugina i provajdera; rotacija KEK-a, dostupnost starih verzija ključa i re-encryption nisu univerzalno isti među implementacijama.

Dokaz da je enkripcija stvarno aktivna

kubectl get secret nije validan dokaz enkripcije u mirovanju, jer API server ovlašćenom klijentu namerno dešifruje objekat. Dokaz zahteva kontrolisan direktan pregled sirovog zapisa u etcd-u ili drugi podržani mehanizam platforme. Provera se izvršava na control-plane/etcd hostu nalogom sa pristupom etcd sertifikatima, bez prikazivanja tajne u tiketu ili zajedničkom terminalu.

ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  get /registry/secrets/plat-app/encryption-check \
  --write-out=fields

Tačne putanje sertifikata i endpoint zavise od instalacije. Očekuje se prefiks koji identifikuje Kubernetes enkripcioni provider, na primer k8s:enc:kms:v2: ili odgovarajući lokalni provider, a poznata čista test vrednost ne sme biti vidljiva. Komanda nosi visoku privilegiju i ne koristi se iz aplikacionog poda. U upravljanom klasteru direktan etcd pristup najčešće nije dostupan; tada se koristi dokaz i kontrola koju objavljuje provajder, uz proveru njegove dokumentacije i odgovornosti korisnika.

Spoljni secret store i CSI integracija

Spoljni trezor može centralizovati izdavanje, rotaciju, audit i HSM/KMS zaštitu. Secrets Store CSI Driver i provider specifični plugin-i mogu montirati sadržaj u pod bez trajnog Kubernetes Secret objekta, ali to nije deo osnovnog Kubernetes API-ja i zahteva dodatne komponente, CRD objekte i podržan provider. Sinhronizovanje spoljne vrednosti nazad u Kubernetes Secret ponovo uvodi etcd kopiju i sve njene rizike.

Arhitekta proverava autentikaciju workload-a prema trezoru, dostupnost i rate limit, ponašanje pri isteku lease-a, rotaciju montiranog sadržaja, restart aplikacije, fail-closed/fail-open ponašanje, backup metapodataka i recovery kada je trezor nedostupan. Spoljni proizvod ne uklanja potrebu za najmanjim privilegijama na Kubernetes strani niti za audit korelacijom između API servera, CSI komponente i samog trezora.

Kubernetes audit arhitektura

Audit događaj nastaje u kube-apiserver-u za zahtev koji prolazi kroz API. Audit nije isto što i Kubernetes Event objekat: Event opisuje operativne događaje resursa i ima ograničen životni vek, dok audit beleži bezbednosni kontekst API zahteva. Ako --audit-policy-file nije konfigurisan, API server ne beleži audit događaje. Politika bira sadržaj, a backend ga čuva; ugrađeni backend-i su log fajl i webhook.

Zahtev može proizvesti faze RequestReceived, ResponseStarted za dugotrajne zahteve, ResponseComplete i Panic. Čuvanje svih faza stvara duplikate iz perspektive jednostavne analize i povećava obim. Često se globalno izostavlja RequestReceived, a ResponseComplete koristi kao glavni dokaz ishoda. Kod watch zahteva relevantan je i ResponseStarted.

NivoSadržajPrimena i rizik
NoneNema događajaSamo za dokazano bučne i niskorizične zahteve; stvara slepu tačku
MetadataIdentitet, vreme, verb, resurs i odgovor bez telaBezbedna početna osnova i preporučeni nivo za Secret pristup
RequestMetadata i telo zahtevaKorisno za promene, ali može zabeležiti lozinku, token ili privatni ključ
RequestResponseMetadata, zahtev i odgovorNajbogatiji dokaz i najveći rizik curenja i potrošnje prostora

Projektovanje audit politike bez curenja tajni

Pravila se obrađuju redom i prvo podudarno pravilo pobeđuje. Specifične zaštitne i visokorizične kategorije moraju zato stajati pre opšteg catch-all pravila. Za Secret objekte se koristi Metadata: telo create/update/patch zahteva ili odgovor na get/list može sadržati samu tajnu. Isto važi za TokenReview i SubjectAccessReview zahteve, određene ConfigMap objekte, exec/attach parametre i CRD resurse koji čuvaju akreditacije bez Secret tipa.

apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
  - RequestReceived
rules:
  # Nikada ne beležiti tela Secret zahteva ili odgovora.
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets"]

  # TokenReview zahtev nosi bearer token u telu.
  - level: Metadata
    resources:
      - group: authentication.k8s.io
        resources: ["tokenreviews"]

  # Detaljan zahtev za administrativne promene bez osetljivih tela.
  - level: Request
    verbs: ["create", "update", "patch", "delete", "deletecollection"]
    resources:
      - group: rbac.authorization.k8s.io
        resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"]
      - group: admissionregistration.k8s.io
        resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"]

  # Pod exec/attach je visokorizičan, ali telo nije potrebno za osnovni trag.
  - level: Metadata
    resources:
      - group: ""
        resources: ["pods/exec", "pods/attach", "pods/portforward"]

  # Svi ostali API zahtevi: osnovni dokaz.
  - level: Metadata
    omitStages:
      - RequestReceived

Politika je polazni enterprise profil, ne univerzalna gotova konfiguracija. Pre primene inventarišu se CRD objekti koji sadrže tajne, zahtevi cloud kontrolera, compliance rok čuvanja i kapacitet backend-a. Posebno se testiraju patch zahtevi: njihovo telo može sadržati vrednost koju ne želimo u logu. Ako je za istragu potrebno više detalja, ciljano podizanje nivoa mora imati vremensko ograničenje, vlasnika, procenu curenja i plan povratka.

Audit backend, dostupnost i zaštita evidencije

Log backend upisuje strukturisani JSON na putanju zadatu sa --audit-log-path; parametri ograničavaju veličinu, broj kopija, starost i kompresiju. Webhook backend šalje događaje spoljašnjem servisu preko kubeconfig-a. Režim blocking povezuje odgovor API servera sa upisom i povećava latenciju, dok batch poboljšava performanse, ali pri padu može izgubiti baferovane događaje. blocking-strict strože propagira greške backend-a i zahteva pažljivu procenu dostupnosti klastera.

--audit-policy-file=/etc/kubernetes/audit/policy.yaml
--audit-log-path=/var/log/kubernetes/audit/audit.log
--audit-log-format=json
--audit-log-mode=batch
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
--audit-log-compress=true

Putanja i vrednosti su primer; retention se određuje prema zakonu, internom standardu, obimu, SIEM kapacitetu i RPO/RTO ciljevima. Direktorijum mora postojati na hostu, biti montiran u kube-apiserver static Pod i zaštićen minimalnim filesystem pravima. Rotacija na svakom HA control-plane čvoru nije centralna evidencija: događaji se moraju pouzdano sakupiti, vremenski sinhronizovati i poslati u kontrolisani log sistem.

Audit log je bezbednosno osetljiv jer sadrži identitete, mrežne adrese, user-agent, nazive resursa i ponekad tela. Potrebni su enkripcija u prenosu i mirovanju, ograničen pristup, integritet ili WORM kontrola kada je zahtevana, detekcija prekida toka, monitoring popunjenosti diska i odvojeni administrativni domen. Administrator koji može menjati klaster ne bi trebalo neopaženo da briše i centralni dokaz sopstvene promene.

Anatomija audit događaja i korelacija

Najkorisnija polja su auditID, stage, requestURI, verb, user.username, grupe, sourceIPs, userAgent, objectRef, responseStatus, vreme prijema i završetka, kao i anotacije admission i authorization mehanizama kada postoje. auditID povezuje faze jednog API zahteva, ali nije automatski ID poslovne promene.

{
  "kind": "Event",
  "apiVersion": "audit.k8s.io/v1",
  "level": "Metadata",
  "auditID": "4f2d0c5a-EXAMPLE",
  "stage": "ResponseComplete",
  "requestURI": "/api/v1/namespaces/plat-app/secrets/inventory-db",
  "verb": "get",
  "user": {
    "username": "system:serviceaccount:plat-app:inventory-api"
  },
  "sourceIPs": ["10.244.2.17"],
  "objectRef": {
    "resource": "secrets",
    "namespace": "plat-app",
    "name": "inventory-db",
    "apiVersion": "v1"
  },
  "responseStatus": {"code": 200}
}

Primer namerno nema requestObject ni responseObject. Kod get operacije telo odgovora bi sadržalo Secret podatke. Uspešan HTTP kod dokazuje da je zahtev izvršen, ali ne dokazuje da je promena bila odobrena. Za to su potrebni Git commit, pull request, tiket promene, identitet CI/CD sistema, digest artefakta i rezultat posle implementacije.

Trag administrativne promene

Pouzdan trag odgovara na pitanja: ko je odobrio, ko ili koji sistem je izvršio, šta je bilo planirano, koji objekat i UID su promenjeni, kada, iz kog izvora konfiguracije, sa kojim rezultatom i kako se vraća prethodno stanje. Kubernetes čuva metadata.managedFields radi vlasništva nad poljima server-side apply-a, ali to nije nepromenljiv istorijski dnevnik. Anotacija kubernetes.io/change-cause i lokalna istorija kubectl takođe nisu dovoljne.

Sloj dokazaŠta dokazujeŠta ne dokazuje samostalno
Git commit i potpisani PRNameravanu deklarativnu razliku i pregledDa je baš ta verzija primenjena
CI/CD ili GitOps zapisArtefakt, identitet automatizacije i vreme deployment-aSvaku ručnu promenu van pipeline-a
Kubernetes auditStvarni API zahtev, principal, objekat i ishodPoslovno odobrenje i razlog
Tiket promeneRizik, odobrenje, prozor, test i rollbackTehničko izvršenje bez korelacije
Monitoring i testEfekat promene i oporavak uslugeTačan autor svake API operacije

Praktičan obrazac je label ili anotacija sa internim change ID-jem koja ne sadrži tajnu, uz isti ID u Git/CI zapisu. Audit politika na nivou Request može zabeležiti taj metadata podatak za bezbedne resurse. Za Secret se ostaje na Metadata, pa korelacija koristi vreme, identitet automatizacije, namespace, naziv objekta, UID/resourceVersion i spoljašnji pipeline zapis umesto tela zahteva.

Provera audit stanja i dijagnostika

Na kubeadm control-plane čvoru API server je obično static Pod. Read-only provera najpre ispituje manifest, aktivni kontejner i postojanje mount-a. Ne pretpostavlja se da je /var/log/kubernetes/audit/audit.log univerzalna putanja.

sudo grep -E -- '--audit-(policy-file|log-path|webhook-config-file)' \
  /etc/kubernetes/manifests/kube-apiserver.yaml

kubectl -n kube-system get pods -l component=kube-apiserver -o wide
sudo test -r /var/log/kubernetes/audit/audit.log && \
  sudo tail -n 5 /var/log/kubernetes/audit/audit.log | jq .

Ako fajl ne postoji, moguće je da audit nije omogućen, da se koristi druga putanja, webhook ili upravljani servis provajdera, ili da static Pod nije uspeo da montira direktorijum. Promena politike nije običan kubectl apply API objekat: ona menja konfiguraciju svakog kube-apiserver procesa i može izazvati restart. Sintaksa se proverava u laboratoriji i na jednom HA članu, zatim se validira dostupnost pre nastavka.

# Primer filtera nad lokalnim JSON-lines audit fajlom.
sudo jq -c '
  select(.stage == "ResponseComplete")
  | select(.objectRef.resource == "secrets")
  | {time: .stageTimestamp,
     user: .user.username,
     verb: .verb,
     namespace: .objectRef.namespace,
     name: .objectRef.name,
     code: .responseStatus.code,
     auditID: .auditID}' \
  /var/log/kubernetes/audit/audit.log

Integrisani praktikum: tajna, rotacija i dokaz promene

Cilj i kontekst. Aplikacija inventory-api u namespace-u secret-audit-lab koristi baznu akreditaciju montiranu kao fajl. Tim mora dokazati da ServiceAccount ne može API-jem čitati Secret, da promena tajne ostavlja audit trag bez vrednosti, da aplikacija vidi novu verziju i da se stara akreditacija može opozvati. Praktikum ne menja EncryptionConfiguration; taj control-plane postupak izdvojen je u narednu bezbednu vežbu.

Preduslovi i rizik. Potrebni su laboratorijski klaster, kubectl, pravo kreiranja namespace resursa i odobren pristup audit evidenciji. Ne koriste se stvarne lozinke. Sadržaj tajne ne unosi se u shell argument, Git niti tiket; generiše se u privremenom fajlu sa režimom 0600. Cleanup briše namespace i zato se ne izvršava u deljenom okruženju.

  1. Proveriti kontekst, identitet i audit preduslov.
    kubectl config current-context
    kubectl auth whoami
    kubectl cluster-info
    kubectl get namespace secret-audit-lab
    Očekivanje: prve tri komande potvrđuju namenski laboratorijski klaster. NotFound za namespace je prihvatljiv. Audit pristup se zasebno potvrđuje prema načinu instalacije; bez njega funkcionalni deo može da se izvede, ali kriterijum dokaza nije ispunjen.
  2. Kreirati namespace, ServiceAccount i workload bez API tokena.
    kubectl create namespace secret-audit-lab
    
    kubectl -n secret-audit-lab create serviceaccount inventory-api
    kubectl -n secret-audit-lab patch serviceaccount inventory-api \
      --type=merge \
      -p '{"automountServiceAccountToken":false}'
    Ovo su state-changing komande. Validacija:
    kubectl -n secret-audit-lab get serviceaccount inventory-api \
      -o jsonpath='{.automountServiceAccountToken}{"\n"}'
    kubectl auth can-i get secrets \
      --as=system:serviceaccount:secret-audit-lab:inventory-api \
      -n secret-audit-lab
    Očekuju se false i no. Druga komanda zahteva pravo impersonacije izvršioca.
  3. Kreirati testnu tajnu bez izlaganja kroz argument procesa.
    umask 077
    LAB_SECRET_FILE=$(mktemp)
    openssl rand -base64 24 > "$LAB_SECRET_FILE"
    
    kubectl -n secret-audit-lab create secret generic inventory-db-v1 \
      --from-file=password="$LAB_SECRET_FILE"
    
    shred -u "$LAB_SECRET_FILE"
    unset LAB_SECRET_FILE
    shred nije pouzdana garancija na copy-on-write ili journaling storage-u; ovde smanjuje laboratorijski trag, dok produkcija koristi kontrolisani secret manager/pipeline. Vrednost se ne ispisuje. Validacija:
    kubectl -n secret-audit-lab get secret inventory-db-v1 -o json \
      | jq '{name: .metadata.name, type: .type, keys: (.data | keys)}'
    Filter ne prikazuje vrednosti. Ipak, ni nazive ključeva ne treba kopirati u javni log ako otkrivaju osetljivu strukturu sistema.
  4. Kreirati Deployment koji mount-uje samo potrebni Secret.
    kubectl -n secret-audit-lab apply -f - <<'EOF'
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: inventory-api
      labels:
        app: inventory-api
        change-id: CHG-LAB-0038
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: inventory-api
      template:
        metadata:
          labels:
            app: inventory-api
            change-id: CHG-LAB-0038
        spec:
          serviceAccountName: inventory-api
          automountServiceAccountToken: false
          securityContext:
            seccompProfile:
              type: RuntimeDefault
          containers:
            - name: application
              image: registry.k8s.io/busybox:1.36.1
              command: ["sh", "-c", "while true; do sleep 3600; done"]
              securityContext:
                allowPrivilegeEscalation: false
                runAsNonRoot: true
                runAsUser: 65532
                capabilities:
                  drop: ["ALL"]
              volumeMounts:
                - name: credentials
                  mountPath: /var/run/secrets/inventory
                  readOnly: true
          volumes:
            - name: credentials
              secret:
                secretName: inventory-db-v1
                defaultMode: 0400
    EOF
    
    kubectl -n secret-audit-lab rollout status \
      deployment/inventory-api --timeout=120s
    Ako slika ne podržava zadati non-root UID ili klaster nema pristup registru, pod neće postati Ready; proveravaju se describe pod i događaji, a ne slabi securityContext.
  5. Dokazati mount bez otkrivanja vrednosti.
    POD_NAME=$(kubectl -n secret-audit-lab get pod -l app=inventory-api \
      -o jsonpath='{.items[0].metadata.name}')
    kubectl -n secret-audit-lab exec "$POD_NAME" -- \
      sh -c 'test -s /var/run/secrets/inventory/password && \
             stat -c "mode=%a bytes=%s" /var/run/secrets/inventory/password'
    
    kubectl -n secret-audit-lab exec "$POD_NAME" -- \
      sh -c 'test ! -e /var/run/secrets/kubernetes.io/serviceaccount/token'
    Očekuje se neprazan fajl režima približno 400 i odsustvo ServiceAccount tokena. Efektivni prikaz režima može zavisiti od runtime-a i securityContext-a; važan je uspešan read samo predviđenog procesa i dokumentovana interpretacija.
  6. Napraviti verziju v2 i kontrolisano prebaciti Deployment.
    umask 077
    LAB_SECRET_FILE=$(mktemp)
    openssl rand -base64 24 > "$LAB_SECRET_FILE"
    kubectl -n secret-audit-lab create secret generic inventory-db-v2 \
      --from-file=password="$LAB_SECRET_FILE"
    shred -u "$LAB_SECRET_FILE"
    unset LAB_SECRET_FILE
    
    kubectl -n secret-audit-lab patch deployment inventory-api \
      --type=json \
      -p='[{"op":"replace","path":"/spec/template/spec/volumes/0/secret/secretName","value":"inventory-db-v2"}]'
    
    kubectl -n secret-audit-lab rollout status \
      deployment/inventory-api --timeout=120s
    Očekivani rezultat: promena PodTemplate-a pokreće kontrolisani rollout i nova replika mount-uje inventory-db-v2. U produkciji se ista izmena radi u izvornom deklarativnom manifestu i prolazi Git review; direktni patch ovde služi da laboratorija jasno pokaže API promenu i audit korelaciju. Ako rollout ne uspe, kubectl rollout undo deployment/inventory-api vraća prethodni PodTemplate dok je v1 Secret još prisutan i važeći.
  7. Proveriti audit korelaciju bez tela tajne. Na audit backend-u filtrirati vreme laboratorije, namespace, Secret nazive i change ID pod-a. Primer za lokalni log:
    sudo jq -c '
      select(.stage == "ResponseComplete")
      | select(.objectRef.namespace == "secret-audit-lab")
      | select(.objectRef.resource == "secrets" or .objectRef.resource == "pods")
      | {time: .stageTimestamp,
         user: .user.username,
         verb: .verb,
         resource: .objectRef.resource,
         name: .objectRef.name,
         code: .responseStatus.code,
         auditID: .auditID,
         hasRequestBody: has("requestObject"),
         hasResponseBody: has("responseObject")}' \
      /var/log/kubernetes/audit/audit.log
    Secret događaji moraju imati hasRequestBody: false i hasResponseBody: false kada je primenjena politika iz lekcije. Ako sadrže telo, incidentno se ograničava pristup audit sistemu, procenjuje izlaganje i koriguje redosled pravila.
  8. Opozvati v1 i dokazati završno stanje. Nakon potvrde da workload koristi v2 i da backend prihvata novu akreditaciju, stara akreditacija se opoziva u stvarnom izdavaocu, zatim se briše v1 Kubernetes Secret.
    kubectl -n secret-audit-lab get deployment inventory-api \
      -o jsonpath='{.spec.template.spec.volumes[?(@.name=="credentials")].secret.secretName}{"\n"}'
    kubectl -n secret-audit-lab delete secret inventory-db-v1
    kubectl -n secret-audit-lab get secret inventory-db-v1
    Očekuje se referenca na inventory-db-v2 i zatim NotFound za v1. Brisanje Kubernetes objekta ne opoziva automatski lozinku u bazi, API ključ kod provajdera ili sertifikat kod CA; opoziv na izvoru je obavezna posebna akcija.
  9. Cleanup i rollback. Pre brisanja sačuvati samo metadata/audit dokaz bez vrednosti. U izolovanoj laboratoriji:
    kubectl delete namespace secret-audit-lab
    Komanda briše sve namespace resurse i ne izvršava se u deljenom namespace-u. Rollback produkcione rotacije vraća referencu na v1 samo dok je v1 credential i dalje važeći; nakon opoziva potreban je novi kontrolisani credential, ne „oživljavanje” kompromitovane tajne.

Kontrolisana vežba: uvođenje audit politike na kubeadm control-plane-u

Rizik. Ova vežba menja static Pod konfiguraciju kube-apiserver-a i može prekinuti API. Izvodi se samo u laboratoriji ili odobrenom HA prozoru, jedan control-plane čvor odjednom. Pre početka moraju postojati etcd snapshot, kopija static Pod manifesta, konzolni/SSH pristup i dokaz da preostali API serveri rade.

  1. Sačuvati SHA-256 hash i bezbednu kopiju postojećeg manifesta; proveriti etcd i API health.
  2. Kreirati policy i host direktorijum audit loga sa minimalnim pravima. Policy se prvo validira u neprodukcijskom okruženju.
  3. U static Pod manifest dodati hostPath/volumeMount za policy i log direktorijum i parametre iz prethodnog primera. Ne uređivati sve HA čvorove istovremeno.
  4. Pratiti lokalni container runtime i kubelet journal dok se API server ponovo kreira:
    sudo journalctl -u kubelet --since '-10 minutes' -f
    sudo crictl ps -a --name kube-apiserver
  5. Sa druge administratorske stanice potvrditi /readyz, kreirati bezopasnu ConfigMap i pronaći odgovarajući ResponseComplete audit događaj.
  6. Ako API server ne postane Ready u odobrenom roku, vratiti tačnu prethodnu kopiju manifesta preko konzolnog pristupa, sačekati static Pod recovery i tek onda analizirati YAML, mount i filesystem prava. Ne nastavljati na druge čvorove.

Incident response: otkrivena tajna ili kompromitovan audit trag

  1. Simptom i obim. Utvrditi koja vrednost, namespace, workload, spoljni sistem i period su pogođeni; razlikovati kodiranu vrednost, hash, ciphertext i stvarni credential. Proveriti Git, CI, audit, aplikacione logove, backup i terminalske snimke.
  2. Sačuvati dokaze. Sačuvati metapodatke, hash relevantnog artefakta, auditID, vreme, principal, source IP i promenu bez umnožavanja same tajne. Ograničiti pristup kompromitovanom log skladištu.
  3. Ograničiti štetu. Ukloniti nepotrebni RBAC binding, izolovati kompromitovani workload i privremeno zaustaviti distribuciju. Poslovni vlasnik odlučuje o prekidu usluge; bezbednosni tim vodi dokaz.
  4. Rotirati i opozvati na izvoru. Izdati novu akreditaciju, prebaciti potvrđene potrošače i opozvati staru kod baze, CA, registry-ja ili cloud provajdera. Promena samo Kubernetes Secret objekta nije opoziv.
  5. Validirati oporavak. Pozitivni test nove i negativni test stare vrednosti, stabilnost workload-a, odsustvo novih pristupa i konzistentan audit tok moraju biti dokazani.
  6. Trajna mera. Ukloniti tajnu iz Git istorije i keševa prema odobrenom postupku, korigovati audit nivo, uvesti skeniranje, razdvojiti namespace/identitete i dokumentovati osnovni uzrok. Sama revizija Git istorije ne ukida već kopirane credential-e.

Ako audit događaji iznenada prestanu, prvo se proveravaju health i kapacitet backend-a, disk, mreža, sertifikati webhook-a, queue/drop metrike i poslednje control-plane promene. Privremeni workaround može biti prelazak na lokalni log uz pojačan nadzor, ali trajna korekcija mora vratiti centralnu, zaštićenu evidenciju i utvrditi da li je tokom praznine bilo promena.

Tipične greške i njihovo razlikovanje

SimptomNeposredni uzrokOsnovna provera i korekcija
Base64 vrednost se smatra šifrovanomPomešano kodiranje i enkripcijaProveriti EncryptionConfiguration i sirov etcd zapis; uvesti KMS/encryption at rest
Aplikacija posle rotacije koristi staru lozinkuEnvironment promenljiva ili proces kešira fajlProveriti način projekcije; kontrolisani reload ili rollout
Secret volume se ne ažuriraKorišćen subPath, immutable objekat ili kubelet kašnjenjePregled Pod manifesta, Secret resourceVersion i kubelet loga
Novi zapisi su šifrovani, stari nisuPostojeći resursi nisu ponovo upisaniPosle backup-a izvesti kontrolisanu migraciju i ponoviti etcd proveru
API server ne čita Secret posle rotacije ključaStari ključ prerano uklonjen ili čvorovi imaju različite konfiguracijeVratiti prethodni provider/ključ, uskladiti sve API servere, zatim ponovo migrirati
Audit log sadrži lozinku ili tokenRequest/RequestResponse pravilo uhvatilo osetljivi resursOgraničiti log, rotirati izloženu tajnu i postaviti specifično Metadata pravilo pre catch-all pravila
Nema audit događajaNema policy parametra, pogrešan mount/putanja ili backend ne radiPregled static Pod args, kubelet/runtime logova, direktorijuma i webhook health-a
Ista promena se vidi više putaViše audit faza ili retry klijentaKorelisati po auditID, fazi, vremenu i objektu; ne brojati svaki red kao novu promenu
Audit pokazuje system:serviceaccount umesto osobePromenu je izvršila automatizacijaPovezati ServiceAccount sa CI/GitOps run ID-jem, commit-om i odobrenjem

Administratorske prakse

Završna kontrolna tačka

Zaključak

Kubernetes Secret uklanja potrebu da akreditacija bude ugrađena u sliku ili Pod specifikaciju, ali ne uklanja odgovornost za njeno stvaranje, skladištenje, distribuciju, korišćenje, rotaciju i opoziv. Bez encryption at rest konfiguracije etcd može čuvati čitljiv sadržaj; bez preciznog RBAC-a ovlašćeni API korisnik ga može preuzeti; bez zaštite workload-a kompromitovan proces ili čvor može ga pročitati iz izvršnog okruženja.

Audit politika pretvara API aktivnosti u proverljiv tehnički trag samo ako je dovoljno široka da pokrije važne promene, dovoljno selektivna da ne beleži same tajne i dovoljno pouzdano skladištena da preživi kvar ili zlonamernu intervenciju. Nivo RequestResponse nije automatski „bolji”; za Secret, TokenReview i druge poverljive objekte upravo je Metadata bezbedniji i profesionalniji izbor.

Administrativna odgovornost završava se tek kada namera iz odobrene promene može da se poveže sa Git/CI artefaktom, stvarnim audit događajem, monitoring rezultatom i proverljivim rollback-om. Audit ne zamenjuje change management, a change tiket ne dokazuje izvršenje. Njihova korelacija gradi dokaz da je promena bila odobrena, tehnički ispravna i bezbedno završena.

Ovom lekcijom završava se celina o identitetu, autorizaciji i zaštiti workload-a. Čitalac je pripremljen da u narednom delu pređe na dostupnost, skaliranje i observability sa važnom pretpostavkom: sistem se ne smatra pouzdanim samo zato što radi, već kada se može dokazati ko mu pristupa, šta se promenilo, kako su zaštićene akreditacije i kako će se bezbedno oporaviti.

← Prethodna lekcija Povratak na sadržaj Sledeća lekcija →