Crop bearded ethnic male computer geek in hoodie chatting on cellphone while typing on netbook in dark room

Bir banka iştiraki SaaS müşterimiz, AKS cluster’ını PCI DSS denetimi için hardening yapmak istedi. Mevcut cluster: 28 node, 340 pod, root-as-default container’lar, network policy yok, secret’lar plaintext envar olarak. 4 ay süren hardening sonunda: Pod Security Standards (restricted), Cilium Network Policy (deny-by-default), Workload Identity, Defender for Containers. Bu yazı o projeden notlar.

Tehdit Modeli

AKS cluster’ında düşülen güvenlik açıkları:

  • Container escape: Privileged container kernel’a erişiyor, host’a bulaşıyor
  • Lateral movement: Bir pod compromise olunca tüm cluster pod’larına erişiyor (network policy yok)
  • Credential leak: Service principal credentials secret olarak, container içinden okunuyor
  • Image vulnerability: Out-of-date base image, CVE’li paketler
  • Excessive RBAC: Cluster-admin role çok kişiye verilmiş
  • Sensitive data exposure: ConfigMap’lerde DB password

Pod Security Standards

Kubernetes 1.25+’da PodSecurityPolicy (PSP) deprecated. Yerine Pod Security Standards: namespace seviyesinde 3 profil: privileged, baseline, restricted.

Profil Kullanım
Privileged Sistem workload (CSI driver, network plugin)
Baseline Çoğu uygulama (ortalama güvenlik)
Restricted Hardened uygulamalar (PCI/HIPAA)

Banka uygulamalarında restricted şart. Namespace label ile enforce:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Restricted profil enforce edildiğinde:

  • runAsNonRoot: true (root user yasak)
  • readOnlyRootFilesystem: true (root FS read-only)
  • allowPrivilegeEscalation: false (sudo yok)
  • capabilities drop ALL
  • seccompProfile: RuntimeDefault
  • hostNetwork, hostPID, hostIPC: false

Bu kuralları sağlamayan pod deploy denenince admission controller block ediyor.

Container Hardening

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: api
          image: registry.example.com/api:v2026.04
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: cache
              mountPath: /app/cache
      volumes:
        - name: tmp
          emptyDir: {}
        - name: cache
          emptyDir: {}

readOnlyRootFilesystem true ise uygulamanın yazma ihtiyaç duyduğu yerler emptyDir mount ile.

Network Policy: Deny-by-Default

Default’ta tüm pod’lar birbirine erişebilir. Network policy ile mikrosegmentasyon:

# Deny all egress (default)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# API pod'una sadece gateway pod'undan erişim
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-gateway
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: gateway
      ports:
        - port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - port: 5432
    - to:  # DNS izni
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - port: 53
          protocol: UDP

API pod’u sadece gateway’den ingress alır, sadece postgres + DNS’e egress yapar. Compromise olsa bile etki sınırlı.

Workload Identity: Secret-less Azure Auth

Eski yöntem: Service principal credentials secret olarak pod’a env. Compromise riski yüksek. Modern yaklaşım: Workload Identity. Pod, Azure AD federated identity ile direkt Azure kaynaklarına erişiyor, secret yok.

# AKS cluster Workload Identity enable
az aks update -g rg-banka-saas -n aks-prod 
  --enable-oidc-issuer 
  --enable-workload-identity

# Federated credential
az identity federated-credential create 
  --name api-federated 
  --identity-name id-api 
  --resource-group rg-banka-saas 
  --issuer $(az aks show -g rg-banka-saas -n aks-prod --query oidcIssuerProfile.issuerUrl -o tsv) 
  --subject system:serviceaccount:production:api-sa
# Service account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-sa
  namespace: production
  annotations:
    azure.workload.identity/client-id: "550e8400-e29b-41d4-a716-446655440000"
---
# Pod
spec:
  serviceAccountName: api-sa
  containers:
    - name: api
      env:
        - name: AZURE_CLIENT_ID
          value: "550e8400-e29b-41d4-a716-446655440000"

Pod içinde DefaultAzureCredential() ile Key Vault, Storage, Cosmos DB’ye direkt auth, hiçbir secret yok.

Image Security: Scan + Signing

Azure Container Registry (ACR) image scan ile her push’ta CVE tarama. Defender for Containers ile findings:

# ACR image scan sonuç
az acr task run 
  --registry myregistry 
  --name image-scan 
  --image api:v2026.04

# Critical CVE varsa pipeline fail
az security assessment list 
  --resource-id /subscriptions/.../registries/myregistry/repositories/api/manifests/v2026.04 
  --query "[?status.code=='Unhealthy']"

Image signing (Notation + Cosign) ile sadece signed image deploy izinli. Unsigned image admission controller’da block.

Defender for Containers

Defender for Containers etkin: runtime threat detection (cryptominer, suspicious network call, container escape attempt), advisory + block. Sentinel’e log akar, SOC ekibi alarm alır.

Azure Policy ile Compliance Enforcement

# PCI DSS initiative atama
az policy assignment create 
  --name pci-dss-aks 
  --policy-set-definition "/providers/Microsoft.Authorization/policySetDefinitions/PCI_DSS_v4" 
  --scope /subscriptions/.../resourceGroups/rg-banka-saas 
  --enforcement-mode Default

Non-compliant resource’lar dashboard’da görünür, deny-by-policy ile yeni non-compliant resource oluşturulamaz.

Sahada Düşülen Üç Tuzak

  1. Hardening’i tek seferde uygulamak: Restricted profil aktif edilince mevcut pod’lar fail. Aşamalı: önce audit mode (sadece log), sonra warn, en son enforce.
  2. Network policy testi yapmamak: Yanlış selector ile prod pod’lar konuşamıyor. Önce dev/staging’de test, sonra prod.
  3. Workload Identity’yi tüm cluster’da değil tek namespace’te tutmak: Bazı namespace eski secret + service principal modelde kalıyor. Cluster genelinde migrate.

CloudSpark olarak AKS hardening, Pod Security Standards uygulama, Workload Identity migration, Network Policy tasarımı ve PCI DSS uyum projeleri için danışmanlık veriyoruz.

🇹🇷 Türkçe🇬🇧 English🇩🇪 Deutsch🇫🇷 Français🇸🇦 العربية🇷🇺 Русский🇪🇸 Español