Vanilla Kubernetes

Drugi deo: Workload platforma, mreža, podaci i bezbednost
Network Policy – kontrola mrežne komunikacije između podova
VI. Mreža, DNS i pristup aplikacijama
29. NetworkPolicy i dijagnostika mreže – segmentacija, testovi konektivnosti i dokaz uzroka

Kubernetes mrežni model podrazumevano omogućava neposrednu IP komunikaciju između podova bez obzira na to na kom se worker čvoru nalaze. Takav model pojednostavljuje povezivanje distribuiranih aplikacija, ali istovremeno znači da namespace, Service i RBAC sami po sebi ne sprečavaju jedan pod da uspostavi mrežnu vezu sa drugim podom. Namespace obezbeđuje logički opseg Kubernetes objekata, Service stabilnu adresu aplikacije, a RBAC kontroliše pristup Kubernetes API-ju; nijedan od tih mehanizama ne predstavlja mrežni firewall između workload-a.

Resurs NetworkPolicy omogućava administratoru da deklarativno odredi koji je dolazni i odlazni saobraćaj dozvoljen za izabrane podove. Standardna Kubernetes politika radi prvenstveno na trećem i četvrtom sloju mrežnog modela: bira izvore i odredišta prema labelama, namespace-ovima ili IP opsezima, a zatim ograničava protokole i portove. Njena najvažnija produkciona uloga jeste segmentacija aplikacija, smanjenje lateralnog kretanja napadača i sprovođenje principa najmanjih privilegija.

Samo postojanje NetworkPolicy objekta ne znači da se pravila zaista primenjuju. Kubernetes API čuva deklaraciju, ali stvarno filtriranje paketa mora da obezbedi instalirani mrežni dodatak, najčešće CNI komponenta sa podrškom za NetworkPolicy. Zbog toga administrator pre svake implementacije mora potvrditi ne samo da API prihvata objekat već i da mrežni sloj klastera podržava i izvršava zahtevanu vrstu politike.

Uloga NetworkPolicy resursa u Kubernetes mrežnom modelu

NetworkPolicy je namespaced resurs iz API grupe networking.k8s.io/v1. Polje spec.podSelector određuje podove na koje se politika odnosi, dok policyTypes navodi da li politika kontroliše dolazni saobraćaj, odlazni saobraćaj ili oba smera. Pravila u sekcijama ingress i egress predstavljaju dozvole. Standardni Kubernetes NetworkPolicy ne koristi eksplicitna pravila tipa „deny”; zabrana nastaje tako što se pod izoluje, a zatim se navedu samo dozvoljeni tokovi.

Pod koji nije izabran nijednom politikom za određeni smer smatra se neizolovanim u tom smeru. Na primer, pod bez ingress politike podrazumevano prihvata dolazni saobraćaj koji mreža može da mu isporuči. Kada ga makar jedna NetworkPolicy izabere za ingress, on postaje ingress-isolated i dozvoljeni su samo tokovi koje dopušta zbir svih ingress politika koje ga biraju. Isto pravilo nezavisno važi za egress.

Politike su aditivne. Redosled njihovog kreiranja nije važan, ne postoji prioritet standardnih Kubernetes NetworkPolicy objekata i jedna politika ne može da poništi dozvolu koju daje druga politika. Efektivni skup dozvoljenih konekcija predstavlja uniju pravila svih politika koje biraju pod u datom smeru. Zbog toga naizgled ispravna restriktivna politika može ostati bez efekta ako druga, šira politika već dozvoljava isti tok.

Za uspešnu vezu između dva izolovana poda moraju biti zadovoljena oba kraja komunikacije: egress pravila izvornog poda moraju dozvoliti izlaz, a ingress pravila odredišnog poda moraju dozvoliti ulaz. Dovoljno je da jedan od ta dva uslova nije ispunjen pa da konekcija ne uspe. Ova osobina je naročito važna tokom dijagnostike, jer simptom na klijentu ne pokazuje automatski na kojoj je strani paket blokiran.

Selektori podova i namespace-ova

podSelector u glavnom delu specifikacije bira podove samo unutar namespace-a u kome je NetworkPolicy kreirana. Prazan selektor podSelector: {} bira sve podove tog namespace-a. To je osnova za podrazumevane politike zabrane, ali predstavlja i visokorizičnu promenu ako se primeni bez prethodnog popisa stvarnih komunikacionih tokova.

Unutar ingress i egress pravila izvor ili odredište mogu se definisati pomoću podSelector, namespaceSelector, njihovom kombinacijom ili pomoću ipBlock. Samostalni podSelector odnosi se na podove u istom namespace-u kao politika. Samostalni namespaceSelector bira odgovarajuće namespace-ove i podrazumevano sve podove u njima. Kombinacija oba selektora u istoj stavci bira samo podove sa traženim labelama unutar izabranih namespace-ova.

Važno je razlikovati kombinaciju selektora od dve odvojene stavke liste. Jedna stavka koja sadrži i namespaceSelector i podSelector predstavlja logičko „AND”. Dve stavke, od kojih jedna sadrži namespace selektor, a druga pod selektor, predstavljaju „OR” i mogu dozvoliti znatno širi pristup nego što je administrator nameravao.

from:
  - namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: shop-prod
    podSelector:
      matchLabels:
        app.kubernetes.io/name: frontend

Prethodno pravilo dozvoljava samo podove sa labelom app.kubernetes.io/name=frontend koji se nalaze u namespace-u shop-prod. Pouzdana ugrađena labela kubernetes.io/metadata.name olakšava izbor namespace-a po imenu, ali organizacije često dodaju i sopstvene labele kao što su environment=production, tenant=finance ili network-zone=trusted. Takve labele moraju biti kontrolisane procesom upravljanja promenama, jer korisnik koji sme da ih menja potencijalno može promeniti i mrežni pristup.

Ingress, egress i izolacija podova

Ingress u NetworkPolicy terminologiji označava saobraćaj koji ulazi u izabrane podove. Ne treba ga mešati sa Kubernetes Ingress objektom, koji usmerava HTTP i HTTPS zahteve prema servisima. NetworkPolicy ingress može kontrolisati TCP, UDP i SCTP tokove koje podržavaju Kubernetes API i mrežni dodatak, bez obzira na to da li saobraćaj dolazi preko Service-a, Ingress kontrolera, Gateway-a ili direktne pod IP adrese.

Egress pravila kontrolišu veze koje izabrani podovi iniciraju prema drugim podovima, internim servisima ili spoljnim adresama. Uvođenje egress izolacije često otkriva skrivene zavisnosti: DNS rezoluciju, NTP, interne registre, sisteme identiteta, baze podataka, servise za slanje elektronske pošte, proxy servere, telemetry kolektore i spoljne API-je. Zbog toga je egress politika operativno zahtevnija od jednostavne ingress segmentacije.

Politika koja bira sve podove, navodi određeni policyTypes, a nema odgovarajuća pravila predstavlja podrazumevanu zabranu za taj smer:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress-egress
  namespace: shop-prod
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Nakon primene ove politike svi podovi u namespace-u postaju izolovani u oba smera. Komunikacija se zatim vraća preciznim allow politikama. Ovaj pristup odgovara modelu „default deny, explicit allow”, ali se ne sme uvoditi naslepo u produkciju. Pre promene moraju biti evidentirani tokovi, vlasnici aplikacija, zavisnosti, portovi, protokoli, DNS zahtevi, health provere, monitoring i putanja ulaznog saobraćaja.

Portovi, protokoli i IP opsezi

Polje ports ograničava dozvolu na određeni protokol i port. Ako se protokol izostavi, podrazumeva se TCP. Port može biti numerički ili imenovani port kontejnera, pod uslovom da implementacija mrežnog dodatka pravilno podržava tu semantiku. Numerički portovi su lakši za neposrednu analizu, dok imenovani portovi mogu smanjiti vezivanje politike za konkretnu vrednost kada se aplikacioni manifesti i politike upravljaju koordinisano.

Standardni NetworkPolicy nije aplikacioni proxy i ne razume HTTP putanje, metode, korisničke identitete, SQL operacije ili sadržaj zahteva. Dozvola za TCP port 8080 znači da je mrežna veza prema tom portu dozvoljena; ne znači da je dozvoljen samo određeni REST endpoint. Za Layer 7 kontrolu potrebne su dodatne mogućnosti mrežnog dodatka, service mesh, API gateway ili aplikaciona autorizacija.

ipBlock omogućava definisanje CIDR opsega i opcionih izuzetaka pomoću polja except. Namenjen je prvenstveno adresama izvan klastera. Korišćenje pod IP opsega kroz ipBlock uglavnom nije dobar način identifikacije workload-a, jer su pod adrese dinamične, a ponašanje u prisustvu NAT-a, Service prevođenja adresa i različitih mrežnih implementacija može se razlikovati. Administrator mora proveriti da li politika vidi originalnu ili prevedenu adresu pre nego što na IP identitetu zasnuje bezbednosnu odluku.

to:
  - ipBlock:
      cidr: 203.0.113.0/24
      except:
        - 203.0.113.128/25
  ports:
    - protocol: TCP
      port: 443

Ovaj primer dozvoljava HTTPS egress prema prvoj polovini dokumentacionog opsega 203.0.113.0/24, dok izuzima 203.0.113.128/25. U stvarnom okruženju dozvoljeni CIDR mora poticati iz zvaničnog inventara mreža. Domen se ne može pouzdano zameniti statičkim IP pravilom ako servis koristi promenljive cloud adrese, CDN ili više lokacija.

Zavisnost od CNI dodatka i granice standarda

Kubernetes definiše API i semantiku NetworkPolicy resursa, ali ne implementira samostalno filtriranje paketa. CNI ili druga mrežna komponenta mora nadgledati NetworkPolicy objekte i prevesti ih u pravila na čvorovima, virtuelnim interfejsima ili eBPF dataplane-u. Implementacije se razlikuju po arhitekturi, vidljivosti, logovanju, performansama, podršci za Windows čvorove i dodatnim funkcijama koje prevazilaze standardni API.

Prva administratorska provera jeste identifikacija aktivnog mrežnog dodatka:

kubectl get pods -n kube-system -o wide
kubectl get daemonsets -n kube-system
kubectl get customresourcedefinitions | grep -Ei 'calico|cilium|antrea|network'
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.operatingSystem}{"\n"}{end}'

Naziv sistemskih podova i prisustvo CRD objekata mogu pomoći u identifikaciji proizvoda, ali konačnu potvrdu treba tražiti u dokumentovanoj arhitekturi klastera i instalacionoj konfiguraciji. Čak i kada dodatak podržava politike, treba proveriti da li je funkcija uključena, da li svi čvorovi imaju zdrav agent i da li postoje poznata ograničenja za korišćenu verziju.

Standardni NetworkPolicy ne rešava sve bezbednosne potrebe. Ne kontroliše pristup Kubernetes API-ju, ne zamenjuje TLS, autentikaciju i autorizaciju aplikacije, ne štiti podatke u mirovanju i ne garantuje identitet procesa unutar kompromitovanog poda. Saobraćaj između kontejnera u istom podu odvija se preko zajedničkog mrežnog prostora i localhost interfejsa, pa standardna pod-orijentisana politika nije granica između tih kontejnera.

Takođe treba računati na specifičnosti saobraćaja koji potiče sa node-a ili se prevodi pre evaluacije pravila. Health provere load balancera, NodePort putanje, hostNetwork podovi, lokalni sistemski agenti i SNAT mogu se ponašati drugačije u zavisnosti od mrežnog dodatka. Takvi tokovi zahtevaju testiranje na stvarnoj platformi, a ne samo proveru validnosti YAML manifesta.

Planiranje politike u produkcionom okruženju

Dobra politika nastaje iz matrice komunikacija, a ne iz nagađanja. Za svaki workload treba evidentirati izvor, odredište, namespace, labele, protokol, port, smer iniciranja veze, poslovnu svrhu i vlasnika. Posebno se evidentiraju infrastrukturne zavisnosti: DNS, monitoring, logovanje, registry, admission i security agenti, backup, upravljanje tajnama i pristup spoljnim servisima.

Izvor Odredište Protokol/port Svrha Očekivana odluka
frontend backend TCP/8080 Aplikacioni API Dozvoli
backend database TCP/5432 PostgreSQL sesija Dozvoli
svi aplikacioni podovi klasterski DNS UDP i TCP/53 DNS rezolucija Dozvoli
frontend database TCP/5432 Neposredan pristup bazi Zabrani
neautorizovan namespace backend TCP/8080 Lateralni pristup Zabrani

Pre produkcione primene YAML treba proveriti klijentski i serverski, pregledati generisanu razliku i sačuvati postojeće stanje. kubectl diff može vratiti izlazni kod koji označava da razlika postoji, što nije isto što i greška validacije. Promena se prvo testira u reprezentativnom neprodukcijskom okruženju, a zatim uvodi kroz kontrolisani prozor uz spreman rollback i aktivan nadzor aplikacionih grešaka.

Dijagnostički model: od simptoma do dokaza uzroka

Mrežni incident ne treba započeti promenom politike, već reprodukcijom zahteva iz istog mrežnog konteksta iz kog ga aplikacija zaista šalje. Test sa administratorske radne stanice, control-plane čvora ili drugog namespace-a može pratiti drugu DNS, NAT, routing i policy putanju. Zato se najpre beleže izvorni pod i čvor, odredišno ime, Service i port, vreme testa, očekivani odgovor i tačan simptom, a zatim se proverava po jedan sloj.

SimptomNajverovatniji slojPrvi dokaz
NXDOMAIN ili ime ne postojiNaziv, namespace, Service ili DNS zapisnslookup punog imena i kubectl get svc
DNS upit ističeDNS egress, DNS Service, CoreDNS ili mrežna putanja/etc/resolv.conf, UDP i TCP/53 test i stanje CoreDNS-a
Connection refusedOdredište je dostižno, ali port ne sluša ili je pogrešno mapiranDirektan Pod IP test, container port i log aplikacije
TCP timeoutNetworkPolicy, CNI, routing, firewall, MTU ili neispravan čvorPoređenje Pod IP/ClusterIP testa i pozitivnog/negativnog policy testa
HTTP 4xx ili 5xxMrežna veza postoji; problem je u aplikaciji, autorizaciji ili zavisnostiStatus, zaglavlja, correlation ID i aplikacioni log
Kvar samo na jednom čvoruCNI agent, ruta, interfejs, MTU ili dataplane tog čvoraRaspored podova po čvorovima i log/health mrežnog agenta

Redosled provera ide od najmanje invazivnih ka privilegovanim: stanje poda i readiness, DNS ime, Service i EndpointSlice, direktan Pod IP i aplikacioni port, sve politike koje biraju izvor ili odredište, pa tek onda CNI agent, servisni proxy, rute, interfejsi, conntrack, MTU i firewall čvora. Ako Pod IP radi, a ClusterIP ne radi, fokus je na Service selektoru, targetPort, EndpointSlice-u i implementaciji Service VIP-a. Ako ne rade ni Pod IP ni ClusterIP, proveravaju se proces, port, politike i pod-mreža. Uspešan HTTP odgovor dokazuje više od samog ICMP testa: standard NetworkPolicy definiše ponašanje za TCP, UDP i opciono SCTP, dok ponašanje ICMP-a i drugih protokola nije jednoobrazno propisano između dodataka.

kubectl get pod -n shop-prod -o wide --show-labels
kubectl get svc,endpointslice -n shop-prod
kubectl get networkpolicy -n shop-prod
kubectl describe networkpolicy -n shop-prod
kubectl get events -n shop-prod --sort-by=.metadata.creationTimestamp
kubectl logs -n shop-prod deployment/backend --since=15m --timestamps

Ove komande su read-only i bezbedne su za početnu produkcionu trijažu ako nalog već ima odgovarajuća prava. kubectl describe networkpolicy prikazuje deklaraciju i selektore, ali ne dokazuje da je dataplane pravilo programirano. Standardni API nema prenosiv status koji potvrđuje završetak enforcement-a; posle kreiranja ili izmene politike postoji period eventualne konvergencije, pa se vreme promene koreliše sa funkcionalnim testovima i logovima konkretnog CNI dodatka.

Privremeno brisanje svih politika radi „provere” nije prihvatljiv produkcioni test: može otvoriti lateralno kretanje i ukloniti bezbednosnu granicu šire nego što incident zahteva. Bezbednija korekcija je vremenski ograničena, usko selektovana allow politika kroz odobrenu promenu, uz neposredan pozitivan test dozvoljenog toka i negativan test toka koji mora ostati blokiran. Osnovni uzrok se zatim dokumentuje odvojeno od privremenog zaobilaznog rešenja.

Integrisani praktikum: segmentacija troslojne aplikacije

Poslovni i tehnički kontekst. Produkciona aplikacija internet prodavnice koristi namespace shop-prod i tri sloja: frontend, backend i PostgreSQL pod database. Bez mrežnih politika svaki pod koji može da rutira do baze može pokušati vezu na port 5432. Cilj promene je da frontend pristupa samo backend servisu na TCP portu 8080, backend bazi na TCP portu 5432, a svi aplikacioni podovi klasterskom DNS servisu. Direktan frontend pristup bazi i pristup iz test namespace-a moraju biti blokirani.

Referentno okruženje i preduslovi. Klaster koristi Linux worker čvorove, funkcionalan CoreDNS ili kompatibilan DNS servis i CNI dodatak koji izvršava Kubernetes NetworkPolicy. Administrator koristi direktorijum ~/vanilla-k8s-lab/29-network-policy. Potrebna su prava za čitanje namespace-a, podova, servisa, endpoint-a i politika, kao i za kreiranje, izmenu i brisanje objekata u namespace-ovima shop-prod i shop-test. Promena CNI konfiguracije nije deo praktikuma i zahteva odvojena klasterska ovlašćenja.

  1. Proveriti identitet, kontekst i prava pristupa. Pogrešan kubeconfig kontekst može primeniti restriktivnu politiku na pogrešan klaster. Administrator prvo beleži aktivni kontekst i proverava najmanji skup potrebnih ovlašćenja.
    kubectl config current-context
    kubectl cluster-info
    kubectl auth can-i get pods -n shop-prod
    kubectl auth can-i create networkpolicies.networking.k8s.io -n shop-prod
    kubectl auth can-i delete networkpolicies.networking.k8s.io -n shop-prod
    kubectl auth can-i create deployments.apps -n shop-prod
    kubectl auth can-i create services -n shop-prod
    Validacija: kontekst i API endpoint moraju odgovarati laboratorijskom klasteru, a svaka potrebna provera mora vratiti yes. Rezultat no nije razlog za traženje cluster-admin naloga; potrebno je zatražiti namespaced ulogu sa nedostajućim glagolom i resursom.
  2. Napraviti radni direktorijum i početne namespace-ove. Deklarativni manifest čuva se kao dokaz izvedene promene i može se verzionisati.
    mkdir -p ~/vanilla-k8s-lab/29-network-policy
    cd ~/vanilla-k8s-lab/29-network-policy
    cat > 01-namespaces.yaml <<'EOF'
    apiVersion: v1
    kind: Namespace
    metadata:
      name: shop-prod
      labels:
        environment: production
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
      name: shop-test
      labels:
        environment: test
    EOF
    kubectl apply -f 01-namespaces.yaml
    Validacija:
    kubectl get namespaces shop-prod shop-test --show-labels
    Oba namespace-a moraju imati status Active; shop-prod mora sadržati labelu environment=production, a shop-test labelu environment=test.
  3. Implementirati referentnu aplikaciju. Primer koristi tri Deployment-a i tri ClusterIP servisa. PostgreSQL je laboratorijski pokrenut sa jasno označenom zamenskom lozinkom; u produkciji bi poverljiva vrednost bila isporučena preko odobrenog sistema za tajne, a ne zapisana u manifestu.
    cat > 02-application.yaml <<'EOF'
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: frontend
      namespace: shop-prod
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: frontend
      template:
        metadata:
          labels:
            app.kubernetes.io/name: frontend
            app.kubernetes.io/part-of: shop
        spec:
          containers:
            - name: web
              image: nginx:1.27-alpine
              ports:
                - name: http
                  containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: frontend
      namespace: shop-prod
    spec:
      selector:
        app.kubernetes.io/name: frontend
      ports:
        - name: http
          port: 80
          targetPort: http
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: backend
      namespace: shop-prod
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: backend
      template:
        metadata:
          labels:
            app.kubernetes.io/name: backend
            app.kubernetes.io/part-of: shop
        spec:
          containers:
            - name: api
              image: hashicorp/http-echo:1.0
              args:
                - -listen=:8080
                - -text=shop-backend-ok
              ports:
                - name: api
                  containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: backend
      namespace: shop-prod
    spec:
      selector:
        app.kubernetes.io/name: backend
      ports:
        - name: api
          port: 8080
          targetPort: api
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: database
      namespace: shop-prod
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: database
      template:
        metadata:
          labels:
            app.kubernetes.io/name: database
            app.kubernetes.io/part-of: shop
        spec:
          containers:
            - name: postgres
              image: postgres:17-alpine
              env:
                - name: POSTGRES_DB
                  value: shop
                - name: POSTGRES_USER
                  value: shop_app
                - name: POSTGRES_PASSWORD
                  value: LAB_ONLY_REPLACE_ME
              ports:
                - name: postgres
                  containerPort: 5432
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: database
      namespace: shop-prod
    spec:
      selector:
        app.kubernetes.io/name: database
      ports:
        - name: postgres
          port: 5432
          targetPort: postgres
    EOF
    kubectl apply --server-side --dry-run=server -f 02-application.yaml
    kubectl apply -f 02-application.yaml
    kubectl rollout status deployment/frontend -n shop-prod --timeout=120s
    kubectl rollout status deployment/backend -n shop-prod --timeout=120s
    kubectl rollout status deployment/database -n shop-prod --timeout=180s
    Validacija:
    kubectl get deployments,pods,services -n shop-prod -o wide
    kubectl get endpointslices.discovery.k8s.io -n shop-prod
    Sva tri Deployment-a moraju imati AVAILABLE 1, podovi stanje Running, a svaki Service najmanje jedan odgovarajući endpoint. Prazan EndpointSlice ukazuje na neusaglašene labele ili nespreman pod, a ne na problem NetworkPolicy mehanizma.
  4. Napraviti kontrolne klijente i potvrditi početno otvoreno stanje. Dva dugotrajna test poda omogućavaju ponovljive pozitivne i negativne testove bez stalnog kreiranja novih objekata.
    cat > 03-test-clients.yaml <<'EOF'
    apiVersion: v1
    kind: Pod
    metadata:
      name: frontend-client
      namespace: shop-prod
      labels:
        app.kubernetes.io/name: frontend
        app.kubernetes.io/component: test-client
    spec:
      containers:
        - name: tools
          image: nicolaka/netshoot:latest
          command: ["sh", "-c", "sleep 36000"]
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: unauthorized-client
      namespace: shop-test
      labels:
        app.kubernetes.io/name: unauthorized-client
    spec:
      containers:
        - name: tools
          image: nicolaka/netshoot:latest
          command: ["sh", "-c", "sleep 36000"]
    EOF
    kubectl apply -f 03-test-clients.yaml
    kubectl wait --for=condition=Ready pod/frontend-client -n shop-prod --timeout=120s
    kubectl wait --for=condition=Ready pod/unauthorized-client -n shop-test --timeout=120s
    kubectl exec -n shop-prod frontend-client -- curl -fsS --connect-timeout 3 http://backend:8080
    kubectl exec -n shop-prod frontend-client -- nc -zvw3 database 5432
    kubectl exec -n shop-test unauthorized-client -- curl -fsS --connect-timeout 3 http://backend.shop-prod.svc.cluster.local:8080
    Validacija: pre izolacije prvi zahtev mora vratiti shop-backend-ok, TCP provera baze mora uspeti, a klijent iz shop-test mora moći da pristupi backend-u. Time se potvrđuje početno stanje i dokazuje da kasniji neuspeh potiče od uvedene politike, a ne od neispravne aplikacije.
  5. Utvrditi DNS selektore pre pisanja egress dozvole. U mnogim klasterima DNS podovi koriste labelu k8s-app=kube-dns, ali administrator ne sme pretpostaviti naziv ili labelu.
    kubectl get services -n kube-system --show-labels
    kubectl get pods -n kube-system --show-labels | grep -Ei 'dns|coredns'
    kubectl get service kube-dns -n kube-system -o wide
    Validacija: u ovom referentnom okruženju mora postojati Service kube-dns, a njegovi podovi moraju imati labelu k8s-app=kube-dns. Ako klaster koristi druge labele, sledeći manifest mora se prilagoditi stvarnom inventaru. Pogrešan selektor bi nakon default-deny politike prekinuo DNS i proizveo simptome slične nedostupnom servisu.
  6. Kreirati default-deny i precizne allow politike. Jedan manifest uvodi osnovnu zabranu za sve podove u shop-prod, dozvoljava DNS, frontend-to-backend i backend-to-database tokove. Backend dobija ingress dozvolu samo od podova sa frontend labelom u istom produkcionom namespace-u. Database prihvata samo backend.
    cat > 04-network-policies.yaml <<'EOF'
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-ingress-egress
      namespace: shop-prod
    spec:
      podSelector: {}
      policyTypes:
        - Ingress
        - Egress
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-dns-egress
      namespace: shop-prod
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
        - to:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: kube-system
              podSelector:
                matchLabels:
                  k8s-app: kube-dns
          ports:
            - protocol: UDP
              port: 53
            - protocol: TCP
              port: 53
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-frontend-to-backend
      namespace: shop-prod
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: backend
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: frontend
          ports:
            - protocol: TCP
              port: 8080
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-frontend-egress-to-backend
      namespace: shop-prod
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: frontend
      policyTypes:
        - Egress
      egress:
        - to:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: backend
          ports:
            - protocol: TCP
              port: 8080
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-backend-to-database
      namespace: shop-prod
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: database
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: backend
          ports:
            - protocol: TCP
              port: 5432
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-backend-egress-to-database
      namespace: shop-prod
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: backend
      policyTypes:
        - Egress
      egress:
        - to:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: database
          ports:
            - protocol: TCP
              port: 5432
    EOF
    kubectl apply --server-side --dry-run=server -f 04-network-policies.yaml
    kubectl diff -f 04-network-policies.yaml || true
    kubectl apply -f 04-network-policies.yaml
    Validacija:
    kubectl get networkpolicies -n shop-prod
    kubectl describe networkpolicy -n shop-prod
    kubectl get networkpolicies -n shop-prod -o yaml > network-policies-applied.yaml
    Mora postojati šest politika. Izlaz describe mora pokazati očekivane pod selektore, smerove i portove. Sačuvani fajl predstavlja tehnički dokaz stvarno prihvaćenih API objekata, ali još ne dokazuje izvršavanje pravila u dataplane-u.
  7. Izvesti pozitivni test dozvoljene aplikacione veze. Frontend klijent mora i dalje moći da razreši DNS ime i pristupi backend servisu.
    kubectl exec -n shop-prod frontend-client -- nslookup backend.shop-prod.svc.cluster.local
    kubectl exec -n shop-prod frontend-client -- curl -fsS --connect-timeout 3 http://backend:8080
    Validacija: DNS upit mora vratiti ClusterIP servisa, a HTTP odgovor mora biti shop-backend-ok. Ako DNS ne radi, proveravaju se selektori DNS podova i TCP/UDP port 53. Ako DNS radi, a HTTP ne radi, proveravaju se labele izvornog i odredišnog poda, oba smera politike, Service endpoint-i i port 8080.
  8. Izvesti negativne testove zabranjenih tokova. Direktan pristup frontend klijenta bazi i pristup backend-u iz shop-test moraju biti blokirani.
    kubectl exec -n shop-prod frontend-client -- sh -c 'nc -zvw3 database 5432; test $? -ne 0'
    kubectl exec -n shop-test unauthorized-client -- sh -c 'curl -fsS --connect-timeout 3 http://backend.shop-prod.svc.cluster.local:8080; test $? -ne 0'
    Validacija: obe složene komande moraju završiti izlaznim kodom 0 zato što je unutrašnja konekcija očekivano neuspešna. Timeout je čest simptom tihog odbacivanja paketa. Trenutni Connection refused češće ukazuje da je paket stigao do odredišta, ali proces ne sluša na portu; zato se taj simptom ne sme automatski tumačiti kao uspešno blokiranje politikom.
  9. Potvrditi stvaran backend-to-database tok. Pošto referentni backend kontejner nema klijentske alate za PostgreSQL, privremeni dijagnostički pod dobija istu bezbednosnu labelu kao backend. Time se proverava politika identiteta zasnovana na labeli.
    kubectl run backend-diagnostic \
      -n shop-prod \
      --image=nicolaka/netshoot:latest \
      --labels=app.kubernetes.io/name=backend,app.kubernetes.io/component=diagnostic \
      --restart=Never \
      --command -- sleep 600
    kubectl wait --for=condition=Ready pod/backend-diagnostic -n shop-prod --timeout=120s
    kubectl exec -n shop-prod backend-diagnostic -- nc -zvw3 database 5432
    Validacija: TCP veza mora uspeti. Zatim treba privremeno promeniti samo labelu dijagnostičkog poda i ponoviti test:
    kubectl label pod backend-diagnostic -n shop-prod app.kubernetes.io/name=untrusted --overwrite
    kubectl exec -n shop-prod backend-diagnostic -- sh -c 'nc -zvw3 database 5432; test $? -ne 0'
    kubectl label pod backend-diagnostic -n shop-prod app.kubernetes.io/name=backend --overwrite
    Nakon promene na untrusted pristup mora biti blokiran, a vraćanjem labele ponovo dozvoljen. Ovaj test pokazuje da upravljanje labelama predstavlja bezbednosno osetljivu privilegiju.
  10. Pregledati događaje, aplikacione logove i logove mrežnog dodatka. Standardni NetworkPolicy objekat nema univerzalni status koji potvrđuje da su sva dataplane pravila programirana. Događaji i logovi zato se čitaju zajedno sa funkcionalnim testovima.
    kubectl get events -n shop-prod --sort-by=.metadata.creationTimestamp
    kubectl logs -n shop-prod deployment/backend --tail=100
    kubectl logs -n shop-prod deployment/database --tail=100
    kubectl get pods -n kube-system -o wide | grep -Ei 'calico|cilium|antrea|weave|kube-router'
    Validacija: aplikacioni logovi ne smeju pokazivati neočekivane talase timeout-a ili neuspelih konekcija. Logovi mrežnog dodatka nalaze se u sistemskim agentima na svakom čvoru, obično u namespace-u kube-system, ali tačan naziv kontejnera i nivo detalja zavise od proizvoda. Preuzimaju se ciljano, na primer:
    kubectl logs -n kube-system POD_MREZNOG_AGENTA -c KONTEJNER_AGENTA --since=15m
    Traže se greške pri parsiranju politike, programiranju pravila, sinhronizaciji sa API serverom ili radu agenta na čvoru gde se nalazi problematični pod. Odsustvo zapisa o odbijenom paketu nije dokaz da paket nije blokiran, jer drop logging često nije podrazumevano uključen.
  11. Dokumentovati kriterijume prihvatanja. Promena je prihvaćena samo ako su ispunjeni svi merljivi uslovi:
    • sva tri aplikaciona Deployment-a imaju željeni broj raspoloživih replika;
    • DNS rezolucija iz izolovanih podova uspeva preko UDP i, gde alat omogućava, TCP porta 53;
    • frontend dobija HTTP odgovor shop-backend-ok sa backend servisa na portu 8080;
    • backend-identifikovani dijagnostički pod uspostavlja TCP vezu sa bazom na portu 5432;
    • frontend ne može neposredno da pristupi bazi;
    • klijent iz shop-test ne može da pristupi backend-u;
    • nema novih kritičnih aplikacionih ili CNI grešaka tokom najmanje jednog definisanog perioda nadzora;
    • primenjeni manifest, rezultati testova, vreme promene, izvršilac i rollback postupak evidentirani su u change zapisu i repozitorijumu infrastrukture.
  12. Izvesti cleanup ili kontrolisani rollback. U laboratoriji se brišu svi kreirani objekti. U produkciji se ne briše nasumično samo default-deny politika, jer bi to moglo neprimetno vratiti potpuno otvorenu komunikaciju. Rollback mora vratiti prethodno odobren skup politika iz verzionisanog manifesta.

    Upozorenje: sledeće komande brišu laboratorijske namespace-ove i sve objekte u njima. Ne smeju se izvršavati u produkcionom klasteru.

    kubectl delete pod backend-diagnostic -n shop-prod --ignore-not-found
    kubectl delete -f 04-network-policies.yaml
    kubectl delete -f 03-test-clients.yaml
    kubectl delete -f 02-application.yaml
    kubectl delete -f 01-namespaces.yaml
    Validacija:
    kubectl get namespaces shop-prod shop-test
    kubectl get networkpolicies --all-namespaces
    Prva komanda treba da vrati da resursi nisu pronađeni nakon završetka brisanja. Druga ne sme prikazati politike iz obrisanih namespace-ova. Ako je cilj samo rollback politika uz očuvanje aplikacije, primenjuje se prethodno sačuvan i odobren manifest umesto brisanja namespace-a.

Tipične greške i dijagnostika

Administratorske prakse

Kontrolna lista

Zaključak

Administrator nakon ove lekcije treba da razume da NetworkPolicy nije dodatna lista firewall pravila, već deklarativni model izolacije podova čije izvršavanje zavisi od mrežnog dodatka. Mora razlikovati podrazumevano otvoren pod od ingress-isolated i egress-isolated poda, razumeti aditivnost politika i znati da uspešna veza između izolovanih workload-a zahteva dozvolu na oba kraja komunikacije.

Praktična veština ne završava se pisanjem YAML manifesta. Administrator mora da izradi komunikacionu matricu, potvrdi selektore nad stvarnim objektima, sačuva DNS i infrastrukturne zavisnosti, uvede default-deny kontrolisano, izvede pozitivne i negativne testove i razlikuje timeout izazvan odbacivanjem paketa od DNS, Service, EndpointSlice ili aplikacionog problema. Posebno mora razumeti da labele predstavljaju deo bezbednosnog identiteta workload-a i da prava za njihovu izmenu moraju biti ograničena i nadzirana.

Produkcioni rizici nastaju kada se politika primeni bez inventara tokova, kada CNI ne izvršava pravila, kada se zaboravi egress prema DNS-u, kada druga politika proširi dozvolu ili kada se IP adrese tumače bez razumevanja NAT putanje. Standardni NetworkPolicy ostaje Layer 3/4 mehanizam i ne zamenjuje TLS, aplikacionu autentikaciju, RBAC, admission kontrolu, zaštitu tajni niti Layer 7 autorizaciju.

Administrator je odgovoran da promenu sprovede u granicama odobrenih ovlašćenja, da ne menja klasterski mrežni dodatak bez posebnog plana i da eskalira kada ponašanje zavisi od nedokumentovanog CNI ograničenja, hostNetwork saobraćaja, NAT-a, Windows čvorova ili load balancer putanje. Ispravno implementirana politika mora biti ponovljiva, merljivo testirana, verzionisana i povezana sa poslovnim razlogom. Tek tada mrežna segmentacija postaje pouzdana produkciona kontrola, a ne samo objekat prisutan u Kubernetes API-ju.

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