Uvod: Zašto je sigurnost kontejnera ključna u 2026. godini
Kontejnerske tehnologije postale su temelj moderne infrastrukture — tu nema nikakve dvojbe. Prema najnovijim istraživanjima, više od 92% organizacija koristi kontejnere u produkcijskim okruženjima, a Kubernetes upravlja radnim opterećenjima u gotovo svim većim podatkovnim centrima. No, evo problema: s rastućom adopcijom dolazi i dramatično povećanje površine napada.
Godina 2025. donijela je nekoliko kritičnih ranjivosti koje su ozbiljno potresle ekosustav kontejnerske sigurnosti. Iskreno, neke od njih bile su prilično zastrašujuće.
U listopadu 2025. objavljen je CVE-2025-9074, kritična ranjivost u Docker Desktopu s CVSS ocjenom 9.3, koja je omogućavala napadačima bijeg iz kontejnera (container escape) putem manipulacije montiranih volumena na Windows i macOS sustavima. Samo mjesec dana kasnije, u studenom 2025., otkrivena je serija ranjivosti u runc runtime-u: CVE-2025-31133 omogućavao je eskalaciju privilegija kroz race condition u postavljanju korisničkih imenskih prostora, CVE-2025-52565 iskorištavao je grešku u rukovanju seccomp profilima, dok je CVE-2025-52881 dozvoljavao neautorizirani pristup datotečnom sustavu domaćina kroz manipulaciju simboličkih poveznica.
Ovi incidenti jasno pokazuju da sigurnost kontejnera zahtijeva sveobuhvatan pristup — od pravilne konfiguracije runtime-a, kroz ojačavanje slika, do naprednog nadzora u produkciji. Dakle, krenimo redom. Ovaj vodič pokriva sve te aspekte s praktičnim primjerima koje možete odmah primijeniti.
Temelji izolacije kontejnera
Linux imenski prostori (namespaces)
Kontejneri nisu virtualni strojevi. To je nešto što mnogi administratori na početku ne shvaćaju u potpunosti. Oni se oslanjaju na mehanizme Linux jezgre za izolaciju procesa, a najvažniji od tih mehanizama su imenski prostori (namespaces). Svaki imenski prostor pruža izoliranu perspektivu određenog aspekta sustava:
- PID namespace — izolira identifikatore procesa; proces unutar kontejnera vidi samo procese u svom imenskom prostoru
- NET namespace — pruža izoliranu mrežnu konfiguraciju uključujući mrežna sučelja, tablice usmjeravanja i vatrozidna pravila
- MNT namespace — izolira točke montiranja datotečnog sustava, omogućujući kontejneru vlastiti pogled na datotečni sustav
- UTS namespace — izolira ime domaćina (hostname) i domenu
- IPC namespace — izolira međuprocesnu komunikaciju (semafori, redovi poruka, dijeljena memorija)
- USER namespace — mapira korisnike unutar kontejnera na različite korisnike na domaćinu, što je ključno za rootless kontejnere
- CGROUP namespace — izolira pogled na kontrolne grupe, sprječavajući kontejner da vidi cgroup hijerarhiju domaćina
Za pregled imenskih prostora pokrenutog kontejnera koristite sljedeće naredbe:
# Pronađi PID glavnog procesa kontejnera na domaćinu
docker inspect --format '{{.State.Pid}}' moj_kontejner
# Pregledaj imenske prostore tog procesa
ls -la /proc/<PID>/ns/
# Usporedi s imenskim prostorima domaćina
ls -la /proc/1/ns/
# Detaljniji pregled s lsns alatom
lsns -p <PID>
Kontrolne grupe (cgroups v2)
Dok imenski prostori pružaju izolaciju vidljivosti, cgroups (kontrolne grupe) ograničavaju pristup resursima. Cgroups v2, koji je danas standard u većini distribucija, donosi unificirano stablo hijerarhije i poboljšanu kontrolu resursa.
Pomoću cgroups možete ograničiti upotrebu procesora, memorije, I/O propusnosti i broja procesa — a u praksi, to je često jedino što stoji između vas i potpunog pada sustava zbog jednog neoptimiziranog kontejnera.
# Provjera da li sustav koristi cgroups v2
mount | grep cgroup2
# Očekivani izlaz: cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# Pregled cgroup ograničenja za kontejner
CONTAINER_ID=$(docker inspect --format '{{.Id}}' moj_kontejner)
cat /sys/fs/cgroup/system.slice/docker-${CONTAINER_ID}.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-${CONTAINER_ID}.scope/cpu.max
Razumijevanje ovih temelja je ključno jer se svaka sigurnosna mjera o kojoj ćemo govoriti nadograđuje upravo na ove mehanizme jezgre. Bez pravilne konfiguracije imenskih prostora i kontrolnih grupa, svi ostali slojevi zaštite gube na učinkovitosti.
Ojačavanje Docker okruženja
Docker je i dalje najraširenija platforma za upravljanje kontejnerima, što ga čini i najčešćom metom napada. Pravilna konfiguracija Docker okruženja prvi je — i moglo bi se reći najvažniji — korak u zaštiti vaše infrastrukture.
Pokretanje kontejnera kao neprivilegirani korisnik
Jedan od najopasnijih propusta jest pokretanje procesa unutar kontejnera kao root korisnik. I da, iznenađujuće velik broj produkcijskih kontejnera još uvijek radi upravo tako. Iako je kontejner izoliran imenskim prostorima, root unutar kontejnera i dalje ima UID 0, što u slučaju bijega iz kontejnera može značiti potpunu kompromitaciju domaćina.
Direktiva USER u Dockerfileu osigurava pokretanje procesa pod neprivilegiranim korisnikom:
FROM ubuntu:24.04
# Kreiraj neprivilegiranog korisnika
RUN groupadd -r appkorisnik && useradd -r -g appkorisnik -d /home/appkorisnik -s /sbin/nologin appkorisnik
# Postavi vlasništvo nad direktorijima aplikacije
COPY --chown=appkorisnik:appkorisnik ./app /home/appkorisnik/app
# Prebaci se na neprivilegiranog korisnika
USER appkorisnik
WORKDIR /home/appkorisnik/app
CMD ["./pokreni-aplikaciju"]
Uklanjanje Linux mogućnosti (capabilities)
Linux capabilities dijele tradicionalne root privilegije u granularne jedinice. Docker prema zadanim postavkama dodjeljuje kontejnerima skup mogućnosti koji uključuje potencijalno opasne kao što su NET_RAW, SYS_CHROOT i CHOWN. Sigurnosna preporuka je jednostavna: uklonite sve mogućnosti i dodajte samo one koje su zaista potrebne.
# Ukloni sve mogućnosti i dodaj samo potrebne
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE moja_slika
# Provjera mogućnosti unutar kontejnera
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE moja_slika capsh --print
Datotečni sustav samo za čitanje i ostale zaštite
Montiranjem korijenskog datotečnog sustava kontejnera u načinu samo za čitanje sprječavate napadača da modificira binarne datoteke ili konfiguraciju unutar kontejnera. Za direktorije koji zahtijevaju pisanje (npr. /tmp ili /var/run) koristite tmpfs volumene:
# Datotečni sustav samo za čitanje s tmpfs za privremene datoteke
docker run \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--tmpfs /var/run:rw,noexec,nosuid,size=16m \
moja_slika
Sveobuhvatni primjer sigurnog pokretanja kontejnera
Ovo je naredba koju osobno koristim kao polaznu točku za gotovo svaki produkcijski kontejner. Kombinira sve sigurnosne zastavice u jednu cjelovitu konfiguraciju:
docker run -d \
--name sigurna_aplikacija \
--user 1000:1000 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--tmpfs /var/run:rw,noexec,nosuid,size=16m \
--security-opt=no-new-privileges:true \
--security-opt apparmor=docker-podrazumijevano \
--pids-limit 100 \
--memory 512m \
--memory-swap 512m \
--cpus 1.0 \
--restart unless-stopped \
--health-cmd "curl -f http://localhost:8080/health || exit 1" \
--health-interval 30s \
--health-timeout 10s \
--health-retries 3 \
--network moja_izolirana_mreza \
-p 127.0.0.1:8080:8080 \
moja_slika:v1.2.3
Obratite posebnu pozornost na zastavicu --security-opt=no-new-privileges:true — ona sprječava procese unutar kontejnera da steknu nove privilegije putem setuid/setgid binarnih datoteka. Ograničenje PID-ova (--pids-limit) štiti od fork bombi, a identična ograničenja memorije i swap prostora efektivno onemogućuju korištenje swap memorije. Zašto je to bitno? Jer se time sprječava curenje osjetljivih podataka na disk.
Rootless kontejneri
Rootless kontejneri predstavljaju paradigmatski pomak u sigurnosti kontejnera. Umjesto da se Docker daemon pokreće kao root (što je tradicionalni model i, iskreno rečeno, uvijek me malo nervirao iz sigurnosne perspektive), rootless način rada pokreće cijeli kontejnerski stack pod neprivilegiranim korisnikom. Rezultat? Čak i u slučaju potpunog bijega iz kontejnera, napadač nema root pristup domaćinu.
Docker rootless način rada
# Instalacija Docker rootless načina rada
# Preduvjeti: uidmap paket mora biti instaliran
sudo apt-get install -y uidmap dbus-user-session
# Pokretanje instalacijskog skripte (kao neprivilegirani korisnik, NE kao root)
dockerd-rootless-setuptool.sh install
# Konfiguracija okruženja (dodaj u ~/.bashrc)
export PATH=/home/$USER/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
# Provjera da rootless Docker radi
docker info 2>&1 | grep -i "root"
# Očekivani izlaz: rootless: true
Podman kao rootless alternativa
Podman je od samog početka dizajniran za rad bez root privilegija i bez centralnog demona, što ga čini inherentno sigurnijim od tradicionalnog Docker-a. Koristi iste OCI standarde i može pokretati identične kontejnerske slike, pa je prijelaz relativno bezbolan:
# Instalacija Podmana
sudo apt-get install -y podman
# Pokretanje kontejnera kao neprivilegirani korisnik (bez sudo!)
podman run -d --name web_server \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--security-opt=no-new-privileges:true \
--userns=auto \
docker.io/library/nginx:alpine
# Provjera mapiranja korisničkih ID-ova
podman top web_server huser user
# Prikazuje mapiranje UID-ova između domaćina i kontejnera
# Generiranje systemd jedinice za kontejner
podman generate systemd --new --name web_server > ~/.config/systemd/user/web_server.service
systemctl --user enable --now web_server.service
Zastavica --userns=auto automatski dodjeljuje raspon UID/GID-ova iz konfiguracije /etc/subuid i /etc/subgid, osiguravajući da svaki kontejner koristi jedinstvene identifikatore korisnika koji se ne preklapaju s pravim korisnicima sustava.
Seccomp profili
Seccomp (Secure Computing Mode) je mehanizam Linux jezgre koji filtrira sistemske pozive koje proces smije izvršiti. Docker prema zadanim postavkama primjenjuje seccomp profil koji blokira oko 44 od 300+ dostupnih sistemskih poziva, uključujući opasne pozive poput reboot, kexec_load i mount.
Međutim — i ovo je važno — zadani profil je kompromis između kompatibilnosti i sigurnosti. Za maksimalnu zaštitu trebate kreirati prilagođene profile.
Kako seccomp funkcionira
Seccomp profili koriste BPF (Berkeley Packet Filter) programe za filtriranje sistemskih poziva na razini jezgre. Svaki poziv može biti dopušten (SCMP_ACT_ALLOW), zabranjen s greškom (SCMP_ACT_ERRNO), ili može uzrokovati slanje signala procesu (SCMP_ACT_TRAP). Profili se definiraju u JSON formatu i primjenjuju pri pokretanju kontejnera.
Kreiranje prilagođenog seccomp profila
Sljedeći profil implementira pristup s eksplicitnim dopuštenjima (whitelist) — blokira sve sistemske pozive osim onih koji su izričito dopušteni:
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
]
}
],
"syscalls": [
{
"names": [
"accept4", "access", "arch_prctl", "bind", "brk",
"clock_gettime", "clone", "close", "connect",
"epoll_create1", "epoll_ctl", "epoll_wait",
"execve", "exit", "exit_group",
"fcntl", "fstat", "futex",
"getdents64", "getpid", "getppid", "getsockname",
"getsockopt", "gettid", "ioctl",
"listen", "lseek", "madvise", "mmap", "mprotect",
"munmap", "nanosleep", "newfstatat",
"openat", "pipe2", "poll", "prctl",
"pread64", "pwrite64", "read", "recvfrom",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"sched_getaffinity", "sched_yield",
"sendto", "set_robust_list", "setsockopt",
"sigaltstack", "socket", "stat",
"tgkill", "uname", "wait4", "write", "writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Spremite ovaj profil kao prilagodeni-seccomp.json i primijenite ga pri pokretanju kontejnera:
# Primjena prilagođenog seccomp profila
docker run --security-opt seccomp=prilagodeni-seccomp.json moja_slika
# Za otkrivanje potrebnih sistemskih poziva koristite strace
# (u razvojnom okruženju, NIKAD u produkciji)
strace -c -f -p $(docker inspect --format '{{.State.Pid}}' moj_kontejner)
# Ili koristite OCI seccomp bpf hook za automatsko generiranje profila
# na temelju stvarnog ponašanja aplikacije
Preporučujem započeti s restriktivnim profilom i postupno dodavati potrebne sistemske pozive na temelju funkcionalnosti koju aplikacija zahtijeva. Alati poput oci-seccomp-bpf-hook mogu vam automatski generirati seccomp profil praćenjem stvarnih sistemskih poziva aplikacije tijekom testiranja — što je izuzetno praktično.
AppArmor i SELinux za kontejnere
Dok seccomp filtrira sistemske pozive, AppArmor i SELinux pružaju obveznu kontrolu pristupa (MAC — Mandatory Access Control) na razini datotečnog sustava, mrežnih operacija i mogućnosti procesa. Zamislite ih kao dodatni sloj obrane koji djeluje neovisno o izolaciji imenskih prostora.
AppArmor profil za Docker kontejnere
AppArmor koristi profile koji definiraju što proces smije i ne smije raditi. Docker prema zadanim postavkama primjenjuje profil docker-default, ali za kritične aplikacije svakako biste trebali kreirati specifične profile:
# Profil: /etc/apparmor.d/docker-web-aplikacija
#include <tunables/global>
profile docker-web-aplikacija flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/nameservice>
# Mrežni pristup
network inet tcp,
network inet udp,
network inet6 tcp,
network inet6 udp,
# Zabrani raw sockete (sprječava mrežno snifanje)
deny network raw,
deny network packet,
# Pristup datotečnom sustavu
/ r,
/app/** r,
/app/data/** rw,
/tmp/** rw,
/var/run/** rw,
# Zabrani pristup osjetljivim lokacijama
deny /proc/*/mem rw,
deny /proc/sysrq-trigger rw,
deny /proc/kcore r,
deny /sys/firmware/** r,
deny /sys/kernel/** r,
# Zabrani montiranje datotečnih sustava
deny mount,
deny umount,
# Zabrani učitavanje modula jezgre
deny @{PROC}/sys/kernel/modules_disabled w,
# Dopusti pokretanje potrebnih binarnih datoteka
/usr/bin/node ix,
/usr/local/bin/node ix,
}
# Učitavanje AppArmor profila
sudo apparmor_parser -r /etc/apparmor.d/docker-web-aplikacija
# Pokretanje kontejnera s prilagođenim AppArmor profilom
docker run --security-opt apparmor=docker-web-aplikacija moja_web_slika
# Provjera primijenjenog profila
docker inspect --format '{{.HostConfig.SecurityOpt}}' moj_kontejner
SELinux politike za kontejnere
Na sustavima temeljenim na Red Hat distribucijama (RHEL, CentOS, Fedora), SELinux pruža još granularniju kontrolu kroz sustav oznaka (labels). Docker i Podman automatski dodjeljuju SELinux kontekst container_t procesima kontejnera:
# Provjera SELinux statusa
getenforce
# Očekivano: Enforcing
# Pregled SELinux konteksta kontejnerskog procesa
ps -eZ | grep container_t
# Pokretanje kontejnera s prilagođenim SELinux oznakama
docker run --security-opt label=type:container_strict_t \
--security-opt label=level:s0:c100,c200 \
moja_slika
# Zabrana pristupa kontejnera određenim kategorijama podataka
# putem MCS (Multi-Category Security) oznaka
docker run --security-opt label=level:s0:c1,c2 kontejner_a
docker run --security-opt label=level:s0:c3,c4 kontejner_b
# Kontejneri A i B ne mogu pristupiti podacima jedan drugog
# Kreiranje prilagođenog SELinux modula za kontejner
cat > moj_kontejner.te <<'SELINUX_POLICY'
policy_module(moj_kontejner, 1.0)
require {
type container_t;
type httpd_port_t;
class tcp_socket { name_bind name_connect };
}
# Dopusti kontejneru vezanje na HTTP portove
allow container_t httpd_port_t:tcp_socket { name_bind name_connect };
SELINUX_POLICY
# Kompilacija i instalacija modula
checkmodule -M -m -o moj_kontejner.mod moj_kontejner.te
semodule_package -o moj_kontejner.pp -m moj_kontejner.mod
sudo semodule -i moj_kontejner.pp
Generalno, preporuka je koristiti AppArmor na Ubuntu/Debian sustavima i SELinux na RHEL/Fedora sustavima. Oba sustava pružaju značajan dodatni sloj zaštite koji može spriječiti eksploataciju čak i kada napadač pronađe ranjivost u kontejnerskom runtime-u.
Sigurnost kontejnerskih slika
Kontejnerska slika je temelj svega. Ako slika sadrži ranjivosti, zlonamjerni softver ili nepotrebne komponente, nikakva runtime zaštita neće to u potpunosti kompenzirati. To je kao stavljati brave na vrata kuće koja ima rupe u zidovima.
Sigurna izgradnja slika zahtijeva minimizaciju površine napada, višestupanjsku izgradnju i kontinuirano skeniranje.
Minimalne bazne slike
Tradicionalne bazne slike poput ubuntu ili debian sadrže stotine paketa koji nikada neće biti potrebni vašoj aplikaciji, ali mogu sadržavati ranjivosti. Koristite minimalne alternative:
- Distroless slike (Google) — sadrže samo runtime aplikacije bez ljuske (shell), upravitelja paketa ili bilo kakvih sistemskih alata
- Alpine Linux — kompletna distribucija u samo ~5 MB, koristi musl libc i BusyBox
- Chainguard Images — potpuno provjerene minimalne slike s SBOM-om (Software Bill of Materials)
- Scratch — potpuno prazna bazna slika, idealna za statički kompilirane Go binarne datoteke
Višestupanjska izgradnja (multi-stage build)
Višestupanjska izgradnja omogućuje odvajanje razvojnog okruženja od produkcijske slike. Time se drastično smanjuje veličina konačne slike i eliminiraju razvojni alati koji bi napadaču mogli poslužiti kao oružje:
# Stupanj 1: Izgradnja aplikacije
FROM golang:1.23-alpine AS graditelj
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s" -o /app/server ./cmd/server
# Stupanj 2: Produkcijska slika
FROM gcr.io/distroless/static-debian12:nonroot
# Kopiraj samo kompiliranu binarnu datoteku
COPY --from=graditelj /app/server /server
# Kopiraj potrebne konfiguracijske datoteke
COPY --from=graditelj /src/config/produkcija.yaml /config/produkcija.yaml
# Distroless slike prema zadanom koriste nonroot korisnika (UID 65532)
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
CMD ["--config", "/config/produkcija.yaml"]
Skeniranje slika s Trivy-jem
Trivy je trenutno vodeći alat otvorenog koda za skeniranje ranjivosti u kontejnerskim slikama, datotečnim sustavima, repozitorijima koda i Kubernetes klasterima. Integrira se izravno u CI/CD cjevovod, što ga čini nezamjenjivim dijelom moderne sigurnosne prakse:
# Instalacija Trivy-ja
sudo apt-get install -y wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | \
sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install -y trivy
# Skeniranje kontejnerske slike za ranjivosti
trivy image --severity HIGH,CRITICAL moja_slika:v1.2.3
# Skeniranje s izlazom u JSON formatu za CI/CD integraciju
trivy image --format json --output rezultati.json moja_slika:v1.2.3
# Skeniranje Dockerfilea za pogrešne konfiguracije
trivy config --severity HIGH,CRITICAL ./Dockerfile
# Postavljanje praga: prekid CI/CD ako postoje kritične ranjivosti
trivy image --exit-code 1 --severity CRITICAL moja_slika:v1.2.3
# Skeniranje SBOM-a (Software Bill of Materials)
trivy image --format spdx-json --output sbom.json moja_slika:v1.2.3
Potpisivanje slika s Cosign/Sigstore
Potpisivanje kontejnerskih slika osigurava integritet i autentičnost — garantira da slika koju pokrećete u produkciji zaista dolazi od pouzdanog izvora i nije bila modificirana. Cosign, dio Sigstore ekosustava, čini taj proces prilično jednostavnim:
# Instalacija Cosigna
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Generiranje para ključeva za potpisivanje
cosign generate-key-pair
# Potpisivanje kontejnerske slike
cosign sign --key cosign.key moj-registar.hr/moja_slika:v1.2.3
# Provjera potpisa prije pokretanja
cosign verify --key cosign.pub moj-registar.hr/moja_slika:v1.2.3
# Keyless potpisivanje s OIDC (preporučeno za CI/CD)
# Koristi Fulcio CA i Rekor transparentni dnevnik
COSIGN_EXPERIMENTAL=1 cosign sign moj-registar.hr/moja_slika:v1.2.3
Integracija skeniranja ranjivosti i provjere potpisa u CI/CD cjevovod trebala bi biti obavezna. Cilj je jednostavan: nijedna nepotpisana ili ranjiva slika ne smije dospjeti u produkciju.
Kubernetes Pod Security Standards
Kubernetes je de facto standard za orkestraciju kontejnera, a s verzijom 1.25 stabiliziran je Pod Security Admission kontroler koji zamjenjuje zastarjele Pod Security Policies (PSP). Ako još uvijek koristite PSP-ove — krajnje je vrijeme za migraciju.
Sustav definira tri razine sigurnosnih profila:
- Privileged — neograničena politika, dopušta sve eskalacije privilegija. Koristite samo za sistemske komponente (npr. CNI plugine, log kolektore)
- Baseline — minimalno restriktivna politika koja sprječava poznate eskalacije privilegija. Zabranjuje privilegirane kontejnere, hostNetwork, hostPID i hostIPC
- Restricted — najrestriktivnija politika koja slijedi trenutne najbolje prakse ojačavanja. Zahtijeva pokretanje kao non-root, uklanjanje svih mogućnosti, read-only datotečni sustav
Konfiguracija imenskog prostora s Pod Security Admission
# Označi imenski prostor za primjenu restricted profila
kubectl label namespace produkcija \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/audit-version=latest \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/warn-version=latest
YAML manifest za imenski prostor s punom konfiguracijom:
apiVersion: v1
kind: Namespace
metadata:
name: produkcija
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
Primjer Pod specifikacije za restricted profil
Evo kompletnog primjera Deployment manifesta koji zadovoljava restricted profil. Možete ga koristiti kao predložak za vlastite aplikacije:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sigurna-web-aplikacija
namespace: produkcija
labels:
app: web-aplikacija
spec:
replicas: 3
selector:
matchLabels:
app: web-aplikacija
template:
metadata:
labels:
app: web-aplikacija
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: moj-registar.hr/web-app:v1.2.3@sha256:abc123...
ports:
- containerPort: 8080
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 65532
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
livenessProbe:
httpGet:
path: /zdravlje
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /spremnost
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir:
sizeLimit: 64Mi
- name: cache
emptyDir:
sizeLimit: 128Mi
Obratite pozornost na nekoliko ključnih elemenata. Korištenje digest-a slike (@sha256:...) umjesto samo oznake sprječava podmetnute slike. Postavka automountServiceAccountToken: false onemogućuje automatsko montiranje Kubernetes tokena koji bi napadač mogao iskoristiti za pristup API serveru. A seccompProfile: RuntimeDefault primjenjuje zadani seccomp profil kontejnerskog runtime-a.
Runtime zaštita i nadzor
Preventivne mjere su neophodne, ali nedostatne — i to je nešto što se u praksi prečesto zaboravlja. Potrebna vam je i sposobnost detekcije anomalnog ponašanja u stvarnom vremenu. Tu na scenu stupa Falco, projekt CNCF inkubatora i vodeći alat za runtime detekciju prijetnji u kontejnerskim okruženjima.
Kako Falco funkcionira
Falco prati sistemske pozive korištenjem eBPF (extended Berkeley Packet Filter) sondi u jezgri Linuxa, bez potrebe za modifikacijom kontejnera ili aplikacija. Pravila se definiraju u YAML formatu i pokrivaju scenarije poput neovlaštenog pristupa datotekama, pokretanja ljuske unutar kontejnera, mrežnih anomalija i eskalacije privilegija.
Ono što Falco čini posebno atraktivnim jest minimalan utjecaj na performanse zahvaljujući eBPF tehnologiji, što ga čini idealnim za produkcijska okruženja.
Praktična Falco pravila
# /etc/falco/pravila_prilagodena.yaml
# Pravilo 1: Detekcija pokretanja ljuske unutar kontejnera
- rule: Ljuska pokrenuta u kontejneru
desc: >
Detektira pokretanje interaktivne ljuske unutar kontejnera,
što može ukazivati na kompromitaciju ili neovlašteni pristup
condition: >
spawned_process and
container and
proc.name in (bash, sh, zsh, dash, ksh, csh, fish) and
not proc.pname in (crond, sshd, sistemski_servis)
output: >
UPOZORENJE: Ljuska pokrenuta u kontejneru
(korisnik=%user.name kontejner=%container.name
slika=%container.image.repository
proces=%proc.name roditeljski_proces=%proc.pname
naredba=%proc.cmdline)
priority: WARNING
tags: [kontejner, ljuska, sumnjiva_aktivnost]
# Pravilo 2: Detekcija čitanja osjetljivih datoteka
- rule: Čitanje osjetljivih datoteka u kontejneru
desc: Detektira pristup osjetljivim datotekama unutar kontejnera
condition: >
open_read and
container and
fd.name in (/etc/shadow, /etc/sudoers, /root/.ssh/authorized_keys,
/run/secrets/kubernetes.io/serviceaccount/token)
output: >
KRITIČNO: Čitanje osjetljive datoteke u kontejneru
(datoteka=%fd.name korisnik=%user.name kontejner=%container.name
slika=%container.image.repository)
priority: CRITICAL
tags: [kontejner, datotecni_sustav, osjetljivi_podaci]
# Pravilo 3: Detekcija neočekivane odlazne mrežne veze
- rule: Neocekivana odlazna veza iz kontejnera
desc: >
Detektira odlazne mrežne veze na nestandardne portove,
što može ukazivati na C2 komunikaciju ili eksfiltraciju podataka
condition: >
outbound and
container and
not fd.sport in (80, 443, 8080, 8443, 53, 5432, 6379) and
not k8s.ns.name in (kube-system, monitoring)
output: >
UPOZORENJE: Neočekivana odlazna veza iz kontejnera
(kontejner=%container.name slika=%container.image.repository
veza=%fd.name sport=%fd.sport)
priority: WARNING
tags: [kontejner, mreza, sumnjiva_aktivnost]
Revizijsko praćenje (Audit Logging)
Uz Falco, bitno je omogućiti i pravilno konfigurirati revizijsko praćenje na razini Docker demona i Kubernetes API servera:
# Docker daemon konfiguracija za revizijsko praćenje
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5",
"labels": "produkcija_status",
"env": "os,customer"
},
"userns-remap": "default",
"no-new-privileges": true,
"live-restore": true,
"userland-proxy": false,
"seccomp-profile": "/etc/docker/seccomp-profil.json"
}
Kombinacija preventivnih mjera (seccomp, AppArmor/SELinux, capabilities) s detekcijskim alatima (Falco, revizijsko praćenje) i reaktivnim mehanizmima (automatizirano izoliranje kompromitiranih kontejnera) čini osnovu strategije obrane u dubinu. Nijedan od ovih slojeva sam po sebi nije dovoljan, ali zajedno stvaraju ozbiljnu barijeru za napadače.
Mrežna sigurnost kontejnera
Evo nečega što iznenadi mnoge ljude: prema zadanim postavkama, svi kontejneri u Docker mreži ili Kubernetes klasteru mogu komunicirati jedni s drugima bez ikakvih ograničenja. To je kao da imate uredsku zgradu gdje svaka soba ima otvorena vrata — kompromitacija jednog kontejnera omogućuje lateralno kretanje napadača kroz cijelo okruženje.
Mrežne politike su ključne za implementaciju načela najmanje privilegije na mrežnoj razini.
Docker mrežna izolacija
Docker omogućuje kreiranje izoliranih mreža za grupiranje kontejnera prema funkciji:
# Kreiranje izoliranih mreža za različite slojeve aplikacije
docker network create --driver bridge \
--opt com.docker.network.bridge.enable_icc=false \
--subnet 172.20.0.0/24 \
mreza_frontend
docker network create --driver bridge \
--internal \
--subnet 172.21.0.0/24 \
mreza_backend
docker network create --driver bridge \
--internal \
--subnet 172.22.0.0/24 \
mreza_baza_podataka
# Frontend kontejner - pristup internetu i backend mreži
docker run -d --name nginx --network mreza_frontend nginx:alpine
docker network connect mreza_backend nginx
# API kontejner - samo backend i baza podataka, bez interneta
docker run -d --name api --network mreza_backend moja_api_slika
docker network connect mreza_baza_podataka api
# Baza podataka - potpuno izolirana, samo interna komunikacija
docker run -d --name postgres --network mreza_baza_podataka postgres:16-alpine
Zastavica --internal onemogućuje pristup vanjskoj mreži, dok enable_icc=false (Inter-Container Communication) sprječava izravnu komunikaciju između kontejnera na istoj mreži osim putem eksplicitno objavljenih portova.
Kubernetes NetworkPolicy
U Kubernetes okruženju, NetworkPolicy resursi pružaju deklarativnu kontrolu mrežnog prometa na razini Poda. Važno je napomenuti da NetworkPolicy zahtijeva mrežni plugin (CNI) koji ga podržava — Calico, Cilium ili Weave Net su najčešći izbori:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: politika-api-servera
namespace: produkcija
spec:
# Primijeni na Podove s oznakom app: api-server
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
# Dopusti dolazni promet samo od frontend Podova na portu 8080
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: produkcija
podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
# Dopusti odlazni promet samo prema bazi podataka
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
# Dopusti DNS rezoluciju
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Za produkcijska okruženja preporučujem započeti sa zadanom politikom koja blokira sav promet (deny-all), a zatim eksplicitno dopustiti samo potrebnu komunikaciju:
# Zadana politika: zabrani sav dolazni i odlazni promet
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: zadana-zabrana-svega
namespace: produkcija
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Primjena ove politike na imenski prostor znači da nijedan Pod neće moći primati niti slati mrežni promet dok ne definirate eksplicitne dozvole. Ovaj pristup — zadano zabrani, eksplicitno dopusti — fundamentalni je princip mrežne sigurnosti koji značajno otežava lateralno kretanje napadača.
Zaključak: Strategija obrane u dubinu
Sigurnost kontejnera nije jedna mjera ili jedan alat — to je sveobuhvatna strategija obrane u dubinu (defense in depth) koja obuhvaća svaki sloj tehnološkog stoga. Od razine Linux jezgre, kroz kontejnerski runtime, slike, orkestraciju, pa do mrežnih politika i runtime nadzora — svaki sloj dodaje dodatnu prepreku napadaču.
Ranjivosti otkrivene krajem 2025. godine (CVE-2025-9074 u Docker Desktopu te serija runc ranjivosti) najbolji su dokaz zašto se ne smijemo oslanjati na samo jednu razinu zaštite. Kada je runc ranjivost omogućavala zaobilaženje seccomp profila, organizacije koje su imale AppArmor/SELinux profile i Falco runtime detekciju bile su u znatno boljoj poziciji.
Kontrolna lista za sigurnost kontejnera u produkciji
Koristite sljedeću kontrolnu listu kao polaznu točku za procjenu sigurnosti vašeg kontejnerskog okruženja:
- Izgradnja slike — koristite minimalne bazne slike (distroless, Alpine, scratch), primjenjujte višestupanjsku izgradnju, skenirajte slike u CI/CD cjevovodu i potpišite ih s Cosignom
- Konfiguracija kontejnera — pokretanje kao non-root korisnik, uklanjanje svih Linux mogućnosti osim nužnih, datotečni sustav samo za čitanje, zastavica no-new-privileges
- Runtime izolacija — primjena prilagođenih seccomp profila, AppArmor ili SELinux politika, korisničkih imenskih prostora (rootless kontejneri)
- Ograničenje resursa — postavljanje limita za CPU, memoriju, broj procesa i I/O propusnost putem cgroups v2
- Orkestracija — primjena Kubernetes Pod Security Standards (restricted profil), onemogućavanje automatskog montiranja tokena, korištenje digest-a slike umjesto oznaka
- Mrežna sigurnost — implementacija deny-all politike mrežnog prometa, eksplicitno dopuštanje samo potrebne komunikacije
- Nadzor i detekcija — deployment Falca za runtime detekciju, konfiguracija revizijskog praćenja, centralizirano prikupljanje dnevničkih zapisa
- Upravljanje ranjivostima — redovito skeniranje pokrenutih kontejnera, automatizirano ažuriranje baznih slika, praćenje sigurnosnih obavijesti
Za kraj, zapamtite: sigurnost kontejnera je kontinuirani proces, ne jednokratna aktivnost. Nove ranjivosti se otkrivaju svakodnevno, tehnike napada se usavršavaju, a ekosustav alata se neprestano razvija. Implementirajte mjere opisane u ovom vodiču, prilagodite ih specifičnim potrebama vašeg okruženja i izgradite kulturu sigurnosti u vašem timu. Jer u svijetu kontejnera, sigurnost je zaista odgovornost svih — od razvojnih inženjera do operativnog tima.