Konteyner Orkestrasyon (Edge)
TEKNİK REHBER KONTEYNER GÜVENLİK 2026

Konteyner Güvenliği —
Cosign, Trivy & Distroless.

Gömülü ve edge sistemlerde konteyner güvenliği: Sigstore Cosign ile image imzalama ve doğrulama, Trivy ile CVE tarama, distroless ve scratch image yapıları, çok aşamalı Dockerfile, seccomp ve AppArmor profili — üretim odaklı sertleştirme rehberi.

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.

Tehdit Katmanları
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
Image güveni
Cosign imzalama + Policy Controller (Connaisseur, Kyverno) ile yalnızca imzalı image'lar çalıştırılır.
CVE yönetimi
Trivy, Grype veya Snyk ile her build'de tarama. CVSS >= 7.0 kritik bulgu varsa pipeline durur.
Minimum saldırı yüzeyi
Distroless veya scratch image; shell, paket yöneticisi, geliştirme araçları yok. Exploit edilebilir bileşenler azalır.
Çalışma zamanı sınırlandırma
seccomp profili, AppArmor/SELinux, non-root user, read-only filesystem, capability drop ile kapsamlı sertleştirme.

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

bash
## 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

bash — private key 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)

bash — keyless (Fulcio + Rekor)
## 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

bash — metadata ekle
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

bash — cosign verify
## 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

kyverno-policy.yaml
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)

bash — containerd policy plugin
## /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

bash
## 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

bash — trivy image
## 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

bash — fs ve config 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.

GitHub Actions — trivy-scan.yml
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

bash — CVSS eşiği ile tarama
## 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

Distroless Image Tablosu
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

Dockerfile — Go scratch
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"]
bash — boyut karşılaştırma
## 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

Dockerfile — C++ distroless
## 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

Dockerfile — Python distroless
## 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

Dockerfile — BuildKit secret mount
## --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

seccomp-edge-app.json
{
  "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

bash
## 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

bash — profil otomatik keşif
## 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

/etc/apparmor.d/docker-edge-app
#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

bash
## 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

bash — complain → enforce akışı
## Ö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

Non-root kullanıcı
Dockerfile'da USER direktifi ile root dışı kullanıcı ayarlanmalı (UID >= 1000). distroless:nonroot otomatik sağlar.
Read-only filesystem
docker run --read-only ile dosya sistemi salt okunur yapılır. Yazılabilir dizinler --tmpfs ile eklenir.
Capability drop
--cap-drop=ALL --cap-add=NET_BIND_SERVICE ile minimum capability. Çoğu uygulama hiçbir capability gerektirmez.
No new privileges
--security-opt no-new-privileges=true ile setuid/setgid binary'lerin privilege yükseltmesi engellenir.
Resource limits
--memory, --cpus, --pids-limit ile kaynak tüketim saldırıları engellenir.

Tam güvenli çalıştırma örneği

bash — sertleştirilmiş container çalıştırma
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

DevSecOps Pipeline
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