00 Konteyner güvenlik tehdit modeli
Konteynerler, izolasyon sağlarken saldırı yüzeyini tamamen ortadan kaldırmaz. Tehdit modeli, yazılım tedarik zincirinden çalışma zamanı kaçışlarına kadar birçok katmanı kapsar.
Kaynak Kodu
├── Tedarik zinciri saldırısı (bağımlılık zehirleme)
↓
Dockerfile / Build
├── Güvensiz base image (CVE'li paketler)
├── Gizli bilgi sızıntısı (secret leak)
↓
Container Registry
├── İmaj değiştirme / sahte imaj (MITM)
├── İmza eksikliği
↓
Runtime (containerd / crun)
├── Ayrıcalık yükseltme (privilege escalation)
├── Kernel exploit (container kaçışı)
├── Yetki dışı sistem çağrısı
└── Ağ izolasyon ihlali
01 Cosign ile image imzalama
Cosign, Sigstore projesi kapsamındaki açık kaynaklı konteyner imzalama aracıdır. OCI registry'ye şeffaf bir imza ekler; private key, Fulcio (keyless) veya KMS destekler.
Cosign kurulum
## Linux AMD64
curl -L -o cosign \
https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign && sudo mv cosign /usr/local/bin/
## ARM64 (edge cihaz)
curl -L -o cosign \
https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-arm64
chmod +x cosign && sudo mv cosign /usr/local/bin/
cosign version
Anahtar çifti ile imzalama
## Anahtar çifti oluştur
cosign generate-key-pair
# Enter password for private key: ****
# Private key written to cosign.key
# Public key written to cosign.pub
## Image build ve push
docker build -t registry.example.com/myapp:v1.2.3 .
docker push registry.example.com/myapp:v1.2.3
## Image'ı imzala (digest ile — tag değişse bile güvenli)
cosign sign --key cosign.key \
registry.example.com/myapp:v1.2.3
## Veya digest doğrudan:
IMAGE_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' \
registry.example.com/myapp:v1.2.3)
cosign sign --key cosign.key "$IMAGE_DIGEST"
Keyless imzalama (OIDC — GitHub Actions)
## GitHub Actions CI ortamında (OIDC token mevcut)
cosign sign \
--yes \
--oidc-issuer=https://token.actions.githubusercontent.com \
registry.example.com/myapp:v1.2.3
## İmza Rekor şeffaflık günlüğüne kaydedilir
## Doğrulama sırasında kimlik (email/OIDC) kontrol edilir
Ek bilgi (annotations) ile imzalama
cosign sign --key cosign.key \
-a "repo=github.com/org/myapp" \
-a "workflow=ci-build" \
-a "git_sha=$(git rev-parse HEAD)" \
registry.example.com/myapp:v1.2.3
02 Cosign ile image doğrulama ve policy
İmzalı image'ların yalnızca yetkili anahtarla imzalandığını deployment öncesi veya admission webhook ile doğrulayın.
Manuel doğrulama
## Public key ile doğrula
cosign verify --key cosign.pub \
registry.example.com/myapp:v1.2.3
# Verification for registry.example.com/myapp:v1.2.3 --
# The following checks were performed on each of these signatures:
# - The cosign claims were validated
# - The signatures were verified against the specified public key
# [{"critical":{"identity":{"docker-reference":"..."},...}]
## Keyless doğrulama (GitHub Actions kimliği)
cosign verify \
--certificate-identity-regexp="https://github.com/org/.*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
registry.example.com/myapp:v1.2.3
Kyverno ile Kubernetes admission policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
background: false
rules:
- name: check-image-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
containerd ile policy (cri-o / gVisor)
## /etc/containerd/config.toml
## image_policy plugin (containerd 2.x)
[plugins."io.containerd.transfer.v1.local".unpack_config]
[plugins."io.containerd.image.v1".signer]
disable_partial_content = true
## Cosign Connaisseur ile Helm deploy
helm repo add connaisseur https://sse-secure-systems.github.io/connaisseur/charts
helm install connaisseur connaisseur/connaisseur \
--set validators[0].name=default \
--set validators[0].type=cosign \
--set validators[0].trust_roots[0].name=default \
--set validators[0].trust_roots[0].key="$(cat cosign.pub)"
03 Trivy ile CVE tarama
Trivy, Aqua Security tarafından geliştirilen açık kaynaklı güvenlik açığı tarayıcısıdır. Container image, dosya sistemi, kod deposu ve Kubernetes cluster'larını tarayabilir.
Trivy kurulum
## Ubuntu / Debian
sudo apt install wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key \
| gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
https://aquasecurity.github.io/trivy-repo/deb generic main" \
| sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt update && sudo apt install trivy
## ARM64 (binary)
wget https://github.com/aquasecurity/trivy/releases/latest/download/\
trivy_Linux_ARM64.tar.gz
tar xf trivy_Linux_ARM64.tar.gz && sudo mv trivy /usr/local/bin/
Image tarama
## Temel tarama
trivy image registry.example.com/myapp:v1.2.3
## Yalnızca kritik ve yüksek severity
trivy image --severity HIGH,CRITICAL \
registry.example.com/myapp:v1.2.3
## JSON çıktısı (CI/CD için)
trivy image --format json \
--output trivy-report.json \
registry.example.com/myapp:v1.2.3
## Belirli CVE'leri yoksay (.trivyignore)
cat .trivyignore
# CVE-2023-44487 # HTTP/2 rapid reset — mitigated by LB
# CVE-2024-0001 # not applicable to ARM
trivy image --ignorefile .trivyignore myapp:v1.2.3
Dosya sistemi ve Dockerfile tarama
## Yerel dizin tarama
trivy fs --scanners vuln,secret,misconfig .
## Secret leak tespiti (kaynak kodda API key, password)
trivy fs --scanners secret . 2>&1 | grep -A3 "CRITICAL\|HIGH"
## Dockerfile misconfig
trivy config Dockerfile
# Dockerfile (docker)
# MEDIUM: Specify a non-root user in the last USER directive
# MEDIUM: Do not expose sensitive ports
04 Trivy CI/CD entegrasyonu
Trivy'yi CI pipeline'ına entegre ederek her build'de otomatik güvenlik taraması yapılır. Kritik CVE bulunursa pipeline durdurulur.
name: Security Scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy scan (CRITICAL/HIGH → fail)
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
Threshold ile esnek politika
## CVSS >= 7.0 bulgu varsa çık kodu 1 döner
trivy image \
--exit-code 1 \
--severity CRITICAL,HIGH \
--ignore-unfixed \ # düzeltme yok CVE'leri atla
--vuln-type os,library \
myapp:latest
echo "Trivy çıkış kodu: $?"
# 0 → temiz veya yalnızca LOW/MEDIUM bulgu
# 1 → HIGH/CRITICAL bulgu var → pipeline dur
05 Distroless ve scratch image
Distroless image'lar (Google tarafından sağlanan), shell, paket yöneticisi ve işletim sistemi araçları içermez; yalnızca çalışma zamanı bağımlılıkları bulunur. Bu, saldırı yüzeyini dramatik biçimde azaltır.
Distroless image seçenekleri
Image Boyut Kabuk glibc Dil
──────────────────────────────────────────────────────────────────
scratch 0 KB - - ham binary
gcr.io/distroless/static:nonroot 2 MB - - Go static
gcr.io/distroless/base:nonroot 20 MB - ✓ C/C++
gcr.io/distroless/cc:nonroot 21 MB - ✓ C++ (libstdc++)
gcr.io/distroless/python3 ~50 MB - ✓ Python3
gcr.io/distroless/java17:nonroot ~200MB - ✓ Java 17
debian:12-slim ~80 MB ✓ ✓ Genel amaç
alpine:3.20 ~7 MB ash musl Genel amaç
Go uygulaması için scratch image
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# CGO_ENABLED=0: statik binary, glibc bağımlılığı yok
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \
go build -ldflags="-s -w" -o /server ./cmd/server
# Final: scratch — sıfır boyut, sadece binary
FROM scratch
COPY --from=builder /server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt \
/etc/ssl/certs/
EXPOSE 8080
USER 65534:65534 # nobody
ENTRYPOINT ["/server"]
## Scratch image
docker build -t myapp:scratch -f Dockerfile.scratch .
docker image ls myapp:scratch
# myapp scratch abc123 5.2MB
## golang:alpine base kullansaydık:
# myapp alpine-base def456 310MB
## Trivy tarama — scratch'te sıfır CVE beklenir
trivy image myapp:scratch
# Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
06 Çok aşamalı Dockerfile (multi-stage)
Multi-stage build, build araçlarını final image'a dahil etmeden küçük ve güvenli image üretir. Her aşama bağımsız bir FROM ile başlar; yalnızca seçilen artifactlar kopyalanır.
C++ edge uygulaması — multi-stage
## Aşama 1: Bağımlılık derleme
FROM debian:12-slim AS deps
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake ninja-build gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
libssl-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /deps
COPY vcpkg.json .
RUN vcpkg install --triplet arm64-linux-release
## Aşama 2: Uygulama derleme
FROM deps AS builder
WORKDIR /app
COPY CMakeLists.txt .
COPY src/ src/
RUN cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=/deps/vcpkg/scripts/buildsystems/vcpkg.cmake
RUN ninja -C build
## Aşama 3: Final — distroless C++ runtime
FROM gcr.io/distroless/cc:nonroot
COPY --from=builder /app/build/edge_app /usr/local/bin/edge_app
# Konfigürasyon
COPY --from=builder /app/config/default.json /etc/edge_app/config.json
USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/edge_app"]
Python ML servisi — multi-stage
## Aşama 1: Bağımlılık kurulumu
FROM python:3.12-slim AS builder
WORKDIR /install
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
## Aşama 2: Runtime
FROM gcr.io/distroless/python3:nonroot
COPY --from=builder /root/.local /root/.local
COPY app/ /app/
ENV PYTHONPATH="/root/.local/lib/python3.12/site-packages"
WORKDIR /app
USER nonroot
CMD ["/app/main.py"]
BuildKit ile gizli bilgi güvenliği
## --mount=type=secret: derleme zamanında gizli bilgiye erişim
## Final image'a dahil edilmez
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install --production
## Kullanımı:
docker build --secret id=npmrc,src=$HOME/.npmrc -t myapp .
07 seccomp profili ile sistem çağrı kısıtlama
seccomp (Secure Computing Mode), bir sürecin yapabileceği sistem çağrılarını beyaz liste ile kısıtlar. Konteyner kaçışı ve privilege escalation saldırılarını önemli ölçüde zorlaştırır.
Özel seccomp profili oluşturma
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"architectures": ["SCMP_ARCH_AARCH64", "SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": [
"read", "write", "open", "close", "stat", "fstat",
"poll", "lseek", "mmap", "mprotect", "munmap",
"brk", "rt_sigaction", "rt_sigprocmask", "ioctl",
"access", "socket", "connect", "accept", "sendto",
"recvfrom", "bind", "listen", "getsockname",
"getpeername", "setsockopt", "getsockopt",
"clone", "fork", "execve", "exit", "wait4",
"getpid", "getuid", "getgid", "gettimeofday",
"epoll_create", "epoll_ctl", "epoll_wait",
"signalfd4", "timerfd_create", "timerfd_settime",
"eventfd2", "futex", "set_robust_list",
"openat", "getdents64", "newfstatat",
"nanosleep", "clock_gettime", "clock_nanosleep",
"exit_group", "prctl", "arch_prctl", "set_tid_address"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Docker / Podman ile seccomp uygulama
## Docker
docker run --security-opt seccomp=seccomp-edge-app.json \
myapp:latest
## Podman
podman run --security-opt seccomp=seccomp-edge-app.json \
myapp:latest
## Kubernetes pod spec
# securityContext:
# seccompProfile:
# type: Localhost
# localhostProfile: profiles/seccomp-edge-app.json
oci-seccomp-bpf-hook ile otomatik profil üretme
## Uygulama çalışırken kullanılan syscall'ları yakala
sudo apt install oci-seccomp-bpf-hook
## Test ortamında çalıştır
podman run --annotation io.containers.trace-syscall=of:profile.json \
myapp:latest /bin/sh -c "uygulama_testi.sh"
## Üretilen profili düzenle ve uygula
cat profile.json | jq '.syscalls[0].names | sort' | head -20
08 AppArmor profili ile konteyner sınırlandırma
AppArmor, seccomp'u tamamlayan zorunlu erişim kontrolü (MAC) sistemidir. Dosya erişimi, ağ işlemleri ve capability kullanımını profil tabanlı kısıtlar.
AppArmor profili yazma
#include <tunables/global>
profile docker-edge-app flags=(attach_disconnected, mediate_deleted) {
#include <abstractions/base>
## Binary çalıştırma
/usr/local/bin/edge_app rix,
## Konfigürasyon dosyaları (salt okunur)
/etc/edge_app/** r,
## Veri dizini (okuma/yazma)
/var/lib/edge_app/** rw,
## Ağ (TCP/UDP)
network tcp,
network udp,
## /proc okuma (health check için)
/proc/sys/kernel/hostname r,
/proc/self/** r,
## Loglar
/dev/null rw,
/dev/urandom r,
## YASAK: shell çalıştırma, /etc/passwd yazma, vs.
deny /bin/sh x,
deny /bin/bash x,
deny /etc/passwd w,
deny /etc/shadow rw,
deny /root/** rw,
}
Profili yükle ve Docker'a uygula
## Profili yükle
sudo apparmor_parser -r -W /etc/apparmor.d/docker-edge-app
## Aktif profiller
sudo aa-status | grep docker-edge-app
## Docker konteynerine uygula
docker run --security-opt apparmor=docker-edge-app \
myapp:latest
## Podman
podman run --security-opt apparmor=docker-edge-app \
myapp:latest
aa-complain modu ile profil geliştirme
## Önce complain modunda çalıştır (engelleme yok, log var)
sudo aa-complain /etc/apparmor.d/docker-edge-app
## Uygulama çalıştır, logları topla
docker run --security-opt apparmor=docker-edge-app myapp:latest
## Loglardaki redleri profil kuralına dönüştür
sudo aa-logprof
# Öneri: allow /dev/tty rw → onay ver veya reddet
## Enforce moduna geç
sudo aa-enforce /etc/apparmor.d/docker-edge-app
09 Güvenlik kontrol listesi ve araç zinciri özeti
Üretim konteyner ortamı için kapsamlı güvenlik kontrol listesi ve araç zincirleri.
Image güvenlik kontrol listesi
Tam güvenli çalıştırma örneği
docker run \
--name edge-app \
--read-only \
--tmpfs /tmp:size=64m,noexec,nosuid \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt no-new-privileges=true \
--security-opt seccomp=seccomp-edge-app.json \
--security-opt apparmor=docker-edge-app \
--user 1000:1000 \
--memory 256m \
--cpus 0.5 \
--pids-limit 100 \
--network app-net \
--restart unless-stopped \
registry.example.com/myapp:v1.2.3
Araç zinciri özeti
Kod Yazma
└── Trivy fs --scanners secret (gizli bilgi tespiti)
│
Build (docker buildx)
└── Multi-stage Dockerfile → distroless/scratch image
│
Test
└── Trivy image --severity CRITICAL,HIGH
│ (CRITICAL bulgu → pipeline dur)
│
Sign
└── Cosign sign --key cosign.key image:tag
│
Push → Registry
└── Cosign verify (admission webhook / Kyverno)
│
Deploy (Kubernetes / k3s)
└── seccomp + AppArmor + non-root + read-only