Çoklu Bulut Ortamında Güvenlik Yönetimi

Birden fazla bulut sağlayıcısıyla çalışmak başlangıçta özgürleştirici hissettiriyor. AWS’de compute, Azure’da Active Directory entegrasyonu, GCP’de makine öğrenmesi workload’ları… Her platform kendi güçlü yanını sunuyor. Ancak bu esnekliğin bedeli ağır olabiliyor: her platformun kendi güvenlik modeli, kendi IAM yapısı, kendi log formatı var. Birini yönetmek bile başlı başına bir iş iken üçünü aynı anda güvenli tutmak ciddi bir mimari düşünce gerektiriyor.

Bu yazıda gerçek dünyada karşılaştığım senaryolar üzerinden çoklu bulut ortamında güvenlik yönetimini ele alacağız. Teori değil, çalışan scriptler ve pratik yaklaşımlar.

Çoklu Bulut Güvenliğinin Temel Zorlukları

İlk büyük sorun kimlik yönetimi. AWS’de IAM kullanıcıları var, Azure’da Entra ID (eski adıyla AAD), GCP’de Cloud Identity. Bir kullanıcı üç platformda da erişime ihtiyaç duyduğunda üç ayrı kimlik oluşturuyorsunuz. Biri işten ayrıldığında üçünü de kapatmanız lazım. Birini unutursanız güvenlik açığı açılıyor.

İkinci sorun görünürlük eksikliği. AWS CloudTrail, Azure Monitor, GCP Cloud Audit Logs… Hepsi farklı formatlarda log üretiyor. Bir güvenlik olayını araştırırken üç farklı konsola girip üç farklı query dili öğrenmek zorunda kalıyorsunuz.

Üçüncü sorun konfigürasyon sapması. Bir platformda security group’u doğru yapılandırdınız, diğerinde NSG kuralını unuttuğunuz bir port açık kaldı. İnsan hatası kaçınılmaz oluyor.

Kimlik ve Erişim Yönetimini Merkezileştirme

Her şeyden önce tek bir Identity Provider (IdP) üzerinden tüm platformları federe etmeniz gerekiyor. Okta, Azure Entra ID veya Keycloak bu amaçla kullanılabilir. Pratikte en yaygın senaryo Entra ID’yi merkezi IdP olarak kullanıp AWS ve GCP’yi buna federe etmek.

AWS tarafında SAML federasyonu kurmak için önce Entra ID’de enterprise application oluşturuyorsunuz, sonra AWS tarafında identity provider tanımlıyorsunuz:

# AWS tarafında SAML identity provider oluşturma
aws iam create-saml-provider 
  --saml-metadata-document file://entra-metadata.xml 
  --name AzureEntraIDProvider

# Federe erişim için rol oluşturma
aws iam create-role 
  --role-name EntraID-DevOps-Role 
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789:saml-provider/AzureEntraIDProvider"
      },
      "Action": "sts:AssumeRoleWithSAML",
      "Condition": {
        "StringEquals": {
          "SAML:aud": "https://signin.aws.amazon.com/saml"
        }
      }
    }]
  }'

GCP tarafında workload identity federation ile Entra ID’yi bağlamak için:

# GCP workload identity pool oluşturma
gcloud iam workload-identity-pools create "entra-id-pool" 
  --project="my-project-id" 
  --location="global" 
  --display-name="Azure Entra ID Pool"

# OIDC provider ekleme
gcloud iam workload-identity-pools providers create-oidc "entra-id-provider" 
  --project="my-project-id" 
  --location="global" 
  --workload-identity-pool="entra-id-pool" 
  --display-name="Entra ID OIDC Provider" 
  --attribute-mapping="google.subject=assertion.sub,attribute.groups=assertion.groups" 
  --issuer-uri="https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0"

Bu yapıyı kurduktan sonra kullanıcı yönetimi tek noktadan yapılıyor. Birisi işten ayrıldığında Entra ID’de hesabı devre dışı bırakmak yeterli oluyor.

Policy as Code ile Konfigürasyon Tutarlılığı

Konfigürasyon sapmasının önüne geçmenin en etkili yolu güvenlik politikalarını kod olarak yönetmek. Open Policy Agent (OPA) bu konuda platform bağımsız çalışan güçlü bir araç. Terraform ile birlikte kullanıldığında deploy öncesinde kural ihlallerini yakalayabiliyorsunuz.

Örneğin tüm bulut kaynaklarının belirli tag’lere sahip olmasını zorunlu kılan bir OPA politikası:

# policies/required_tags.rego
package terraform.required_tags

required_tags := ["Environment", "Owner", "CostCenter", "DataClassification"]

deny[msg] {
  resource := input.resource_changes[_]
  resource.change.actions[_] == "create"
  
  missing_tag := required_tags[_]
  not resource.change.after.tags[missing_tag]
  
  msg := sprintf(
    "Kaynak '%s' zorunlu '%s' tag'ini icermiyor",
    [resource.address, missing_tag]
  )
}

Bu politikayı CI/CD pipeline’ınıza entegre etmek için:

#!/bin/bash
# ci/security-check.sh

set -e

echo "Terraform plan olusturuluyor..."
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json

echo "OPA politika kontrolu yapiliyor..."
opa eval 
  --input tfplan.json 
  --data policies/ 
  --format pretty 
  "data.terraform.required_tags.deny" | tee opa-results.json

# Hata varsa pipeline'i durdur
VIOLATIONS=$(cat opa-results.json | jq '. | length')
if [ "$VIOLATIONS" -gt "0" ]; then
  echo "HATA: $VIOLATIONS politika ihlali bulundu!"
  cat opa-results.json
  exit 1
fi

echo "Tum politika kontrolleri gecti."

Ayrıca AWS Config, Azure Policy ve GCP Organization Policy’yi aynı anda yönetmek için Terraform modülleri yazmak işleri çok kolaylaştırıyor. Örneğin S3 bucket’larının public olmasını engelleyen AWS Config kuralı:

# Mevcut AWS hesabindaki açik S3 bucket'larini listele
aws s3api list-buckets --query 'Buckets[*].Name' --output text | 
  tr 't' 'n' | while read bucket; do
    PUBLIC=$(aws s3api get-bucket-policy-status 
      --bucket "$bucket" 
      --query 'PolicyStatus.IsPublic' 
      --output text 2>/dev/null || echo "false")
    
    if [ "$PUBLIC" == "True" ]; then
      echo "UYARI: $bucket bucket'i herkese acik!"
    fi
  done

Merkezi Log Toplama ve SIEM Entegrasyonu

Üç platformdan gelen logları tek bir yerde toplamak hem güvenlik analizi hem de compliance açısından kritik. Elasticsearch üzerinde çalışan bir SIEM veya bulut tabanlı bir çözüm olabilir. Ben genellikle küçük ve orta ölçekli ortamlar için Grafana + Loki + Promtail kombinasyonunu tercih ediyorum çünkü maliyet avantajı var.

AWS CloudTrail loglarını S3’ten çekip normalize etmek için:

#!/usr/bin/env python3
# scripts/normalize_logs.py

import boto3
import json
import gzip
from datetime import datetime

def normalize_aws_event(event):
    """AWS CloudTrail eventini ortak formata donustur"""
    return {
        "timestamp": event.get("eventTime"),
        "platform": "aws",
        "account": event.get("recipientAccountId"),
        "region": event.get("awsRegion"),
        "actor": event.get("userIdentity", {}).get("arn", "unknown"),
        "action": event.get("eventName"),
        "resource": event.get("requestParameters", {}),
        "source_ip": event.get("sourceIPAddress"),
        "user_agent": event.get("userAgent"),
        "error_code": event.get("errorCode"),
        "severity": "HIGH" if event.get("errorCode") else "INFO"
    }

def process_cloudtrail_logs(bucket_name, prefix):
    s3 = boto3.client('s3')
    paginator = s3.get_paginator('list_objects_v2')
    
    for page in paginator.paginate(Bucket=bucket_name, Prefix=prefix):
        for obj in page.get('Contents', []):
            response = s3.get_object(Bucket=bucket_name, Key=obj['Key'])
            
            with gzip.open(response['Body'], 'rt') as f:
                log_data = json.load(f)
                
            for record in log_data.get('Records', []):
                normalized = normalize_aws_event(record)
                # SIEM'e gonder veya dosyaya yaz
                print(json.dumps(normalized))

if __name__ == "__main__":
    process_cloudtrail_logs("my-cloudtrail-bucket", "AWSLogs/")

Azure Activity Log’larını Event Hub üzerinden almak için Logic App veya direkt API kullanabilirsiniz. GCP’de ise Pub/Sub ile log sink oluşturmak standart yaklaşım:

# GCP log sink olusturma - tum kritik olaylari Pub/Sub'a gonder
gcloud logging sinks create security-audit-sink 
  pubsub.googleapis.com/projects/my-project/topics/security-logs 
  --log-filter='
    protoPayload.methodName=~"(create|delete|update|setIamPolicy)" AND
    resource.type=~"(gcs_bucket|cloudsql_database|compute_instance|gke_cluster)"
  ' 
  --project=my-project

# Pub/Sub topic olustur
gcloud pubsub topics create security-logs --project=my-project

# Sink service account'una publisher yetkisi ver
SINK_SA=$(gcloud logging sinks describe security-audit-sink 
  --project=my-project 
  --format='value(writerIdentity)')

gcloud pubsub topics add-iam-policy-binding security-logs 
  --member="$SINK_SA" 
  --role="roles/pubsub.publisher"

Güvenlik Açığı Tarama Otomasyonu

Çoklu bulut ortamında zafiyet yönetimi için platform bağımsız araçlar kullanmak mantıklı. Trivy hem container image hem de Infrastructure as Code dosyalarını tarayabiliyor:

#!/bin/bash
# scripts/security-scan.sh
# Tum bulut kaynak tanimlarini tara

REPORT_DIR="./security-reports/$(date +%Y%m%d)"
mkdir -p "$REPORT_DIR"

echo "=== IaC Guvenlik Taramasi ==="

# Terraform dosyalarini tara
echo "Terraform taranıyor..."
trivy config 
  --format json 
  --output "$REPORT_DIR/terraform-findings.json" 
  --severity HIGH,CRITICAL 
  ./terraform/

# Kubernetes manifestleri tara
echo "Kubernetes manifestleri taranıyor..."
trivy config 
  --format json 
  --output "$REPORT_DIR/k8s-findings.json" 
  --severity HIGH,CRITICAL 
  ./k8s/

# Container image'larini tara
echo "Container image'lari taranıyor..."
while IFS= read -r image; do
  IMAGE_NAME=$(echo "$image" | tr '/:' '_')
  trivy image 
    --format json 
    --output "$REPORT_DIR/image-${IMAGE_NAME}.json" 
    --severity HIGH,CRITICAL 
    "$image"
done < images.txt

# Ozet rapor olustur
CRITICAL_COUNT=$(find "$REPORT_DIR" -name "*.json" -exec 
  jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length' {} ; | 
  paste -sd+ | bc)

echo "Toplam CRITICAL bulgu: $CRITICAL_COUNT"

if [ "$CRITICAL_COUNT" -gt "0" ]; then
  echo "Kritik bulgular mevcut! Detaylar: $REPORT_DIR"
  # Slack veya Teams'e bildirim gonder
  curl -s -X POST "$SLACK_WEBHOOK_URL" 
    -H 'Content-type: application/json' 
    --data "{"text":"UYARI: $CRITICAL_COUNT kritik guvenlik acigi tespit edildi! Detaylar icin $REPORT_DIR dizinine bakin."}"
fi

Network Segmentasyonu ve Zero Trust Yaklaşımı

Çoklu bulut ortamında network güvenliği için her platformda benzer segmentasyon kuralları uygulamak gerekiyor. Bunu manuel yapmak yerine Terraform ile tüm platformlarda tutarlı network politikaları deploy edebilirsiniz.

Pratik bir senaryo: Production ve development workload’larının birbirinden tamamen izole olmasını sağlamak. AWS’de ayrı VPC, Azure’da ayrı VNet, GCP’de ayrı VPC projesi kullanmak ve aralarında sadece gerekli portların açık olduğundan emin olmak.

Hangi servisler arası iletişime izin verildiğini takip etmek için basit bir script:

#!/bin/bash
# scripts/check-network-exposure.sh
# Tum platformlarda acik portlari kontrol et

echo "=== AWS Security Groups Kontrolu ==="
aws ec2 describe-security-groups 
  --query 'SecurityGroups[*].[GroupName, IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]]]' 
  --output json | jq -r '.[] | 
    select(.[1] | length > 0) | 
    "SG: (.[0]) - Acik portlar mevcut!"'

echo ""
echo "=== Azure NSG Kontrolu ==="
az network nsg list --query '[*].name' -o tsv | while read nsg; do
  OPEN_RULES=$(az network nsg show 
    --name "$nsg" 
    --resource-group "$(az network nsg list --query "[?name=='$nsg'].resourceGroup" -o tsv)" 
    --query 'securityRules[?sourceAddressPrefix==`*` && access==`Allow`].name' 
    -o tsv)
  
  if [ -n "$OPEN_RULES" ]; then
    echo "NSG $nsg - Herkese acik kurallar: $OPEN_RULES"
  fi
done

echo ""
echo "=== GCP Firewall Rules Kontrolu ==="
gcloud compute firewall-rules list 
  --format='json' | jq -r '.[] | 
    select(.sourceRanges[]? == "0.0.0.0/0") | 
    "Kural: (.name) - Hedefler: (.targetTags // ["tum instance'"'"'lar"] | join(","))"'

Incident Response Otomasyonu

Bir güvenlik olayı tespit edildiğinde hızlı tepki kritik önem taşıyor. Çoklu bulut ortamında bu tepkiyi otomatize etmek için platform agnostik bir playbook scripti:

#!/bin/bash
# scripts/incident-response.sh
# Kullanim: ./incident-response.sh <platform> <kaynak-id> <olay-turu>

PLATFORM=$1
RESOURCE_ID=$2
INCIDENT_TYPE=$3
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/incident-response/${TIMESTAMP}_${PLATFORM}.log"

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

isolate_aws_instance() {
  local instance_id=$1
  log "AWS instance izole ediliyor: $instance_id"
  
  # Tum inbound/outbound trafiği engelleyen bos security group olustur
  SG_ID=$(aws ec2 create-security-group 
    --group-name "INCIDENT-ISOLATION-${TIMESTAMP}" 
    --description "Incident isolation - ${TIMESTAMP}" 
    --query 'GroupId' --output text)
  
  # Instance'a izolasyon security group'unu ata
  aws ec2 modify-instance-attribute 
    --instance-id "$instance_id" 
    --groups "$SG_ID"
  
  log "Instance $instance_id izole edildi. Isolation SG: $SG_ID"
  
  # Snapshot al (forensic analiz icin)
  VOLUMES=$(aws ec2 describe-instances 
    --instance-ids "$instance_id" 
    --query 'Reservations[0].Instances[0].BlockDeviceMappings[*].Ebs.VolumeId' 
    --output text)
  
  for vol in $VOLUMES; do
    SNAP_ID=$(aws ec2 create-snapshot 
      --volume-id "$vol" 
      --description "INCIDENT-${TIMESTAMP}-forensic" 
      --query 'SnapshotId' --output text)
    log "Forensic snapshot olusturuldu: $SNAP_ID"
  done
}

case "$PLATFORM" in
  "aws")
    isolate_aws_instance "$RESOURCE_ID"
    ;;
  "azure")
    log "Azure VM izole ediliyor: $RESOURCE_ID"
    az vm deallocate --ids "$RESOURCE_ID"
    log "Azure VM durduruldu: $RESOURCE_ID"
    ;;
  "gcp")
    log "GCP instance izole ediliyor: $RESOURCE_ID"
    gcloud compute instances add-tags "$RESOURCE_ID" 
      --tags="isolated,incident-${TIMESTAMP}"
    log "GCP instance tag'lendi ve firewall kurali uygulanacak"
    ;;
  *)
    log "HATA: Bilinmeyen platform: $PLATFORM"
    exit 1
    ;;
esac

# Ekibi bilgilendir
log "Olay kaydedildi. Dosya: $LOG_FILE"

Compliance ve Denetim Raporu Üretimi

SOC 2, ISO 27001 veya PCI DSS gibi standartlara uyumluluk için düzenli denetim raporları gerekiyor. Bu raporları manuel hazırlamak hem zaman alıcı hem de hata payı yüksek. Basit bir compliance check scripti:

#!/bin/bash
# scripts/compliance-check.sh
# CIS Benchmark temel kontrolleri

PASS=0
FAIL=0
WARN=0

check() {
  local control=$1
  local description=$2
  local result=$3
  
  case "$result" in
    "PASS") 
      echo "PASS [$control] $description"
      ((PASS++))
      ;;
    "FAIL")
      echo "FAIL [$control] $description"
      ((FAIL++))
      ;;
    "WARN")
      echo "WARN [$control] $description"
      ((WARN++))
      ;;
  esac
}

# AWS CIS 1.1 - Root account MFA kontrolu
ROOT_MFA=$(aws iam get-account-summary 
  --query 'SummaryMap.AccountMFAEnabled' 
  --output text)
[ "$ROOT_MFA" == "1" ] && 
  check "CIS-AWS-1.1" "Root account MFA aktif" "PASS" || 
  check "CIS-AWS-1.1" "Root account MFA aktif" "FAIL"

# AWS CIS 2.1 - CloudTrail aktif mi
TRAIL_COUNT=$(aws cloudtrail describe-trails 
  --query 'trailList | length(@)' 
  --output text)
[ "$TRAIL_COUNT" -gt "0" ] && 
  check "CIS-AWS-2.1" "CloudTrail etkin" "PASS" || 
  check "CIS-AWS-2.1" "CloudTrail etkin" "FAIL"

# AWS - Public S3 bucket kontrolu
PUBLIC_BUCKETS=$(aws s3api list-buckets --query 'Buckets[*].Name' --output text | 
  tr 't' 'n' | while read b; do
    aws s3api get-bucket-policy-status --bucket "$b" 
      --query 'PolicyStatus.IsPublic' --output text 2>/dev/null
  done | grep -c "True" || true)

[ "$PUBLIC_BUCKETS" -eq "0" ] && 
  check "CIS-AWS-2.3" "Herkese acik S3 bucket yok" "PASS" || 
  check "CIS-AWS-2.3" "Herkese acik S3 bucket yok ($PUBLIC_BUCKETS bucket)" "FAIL"

echo ""
echo "=== OZET ==="
echo "PASS: $PASS | FAIL: $FAIL | WARN: $WARN"
echo "Toplam kontrol: $((PASS + FAIL + WARN))"

[ "$FAIL" -gt "0" ] && exit 1 || exit 0

Maliyet ile Güvenlik Dengesini Kurmak

Güvenlik yatırımları her zaman doğrudan ROI göstermiyor. Ancak çoklu bulut ortamında bazı güvenlik kontrolleri hem güvenliği artırıyor hem de maliyeti düşürüyor. Kullanılmayan IAM kullanıcıları ve gereksiz açık kaynaklar hem güvenlik riski hem de maliyet kaynağı.

Kullanılmayan IAM kullanıcılarını tespit eden script:

#!/bin/bash
# scripts/unused-iam-cleanup.sh
# Son 90 gunde giris yapmayan IAM kullanicilari listele

THRESHOLD_DAYS=90
TODAY=$(date +%s)

echo "Son $THRESHOLD_DAYS gunde aktif olmayan IAM kullanicilari:"
echo "---"

aws iam list-users --query 'Users[*].UserName' --output text | 
  tr 't' 'n' | while read username; do
    LAST_USED=$(aws iam get-user 
      --user-name "$username" 
      --query 'User.PasswordLastUsed' 
      --output text 2>/dev/null)
    
    if [ "$LAST_USED" == "None" ] || [ -z "$LAST_USED" ]; then
      echo "INACTIVE: $username (hic giris yapilmamis)"
    else
      LAST_USED_TS=$(date -d "$LAST_USED" +%s 2>/dev/null || 
        date -j -f "%Y-%m-%dT%H:%M:%S+00:00" "$LAST_USED" +%s)
      DAYS_SINCE=$(( (TODAY - LAST_USED_TS) / 86400 ))
      
      if [ "$DAYS_SINCE" -gt "$THRESHOLD_DAYS" ]; then
        echo "INACTIVE: $username (son giris: $DAYS_SINCE gun once)"
      fi
    fi
  done

Sonuç

Çoklu bulut güvenliği tek bir ürün veya tek bir script ile çözülecek bir sorun değil. Başarılı bir güvenlik duruşu için gereken üç temel unsur var:

Merkezileşme: Kimlik yönetimi, log toplama ve politika yönetimi tek noktadan yapılmalı. Dağıtık kimlik yönetimi operasyonel kördür ve kaçınılmaz olarak açıklar doğurur.

Otomasyon: Elle yapılan güvenlik kontrolleri ölçeklenmiyor. Compliance check’leriniz, zafiyet taramalarınız ve incident response prosedürleriniz otomatik çalışmalı. İnsan gözden geçirmesi için uyarı sistemi olsun ama ilk adımlar otomatik atılsın.

Tutarlılık: Üç platformda farklı güvenlik standartları uygulamak en zayıf halkadan saldırılmanıza zemin hazırlıyor. Policy as Code yaklaşımı bu tutarlılığı kod düzeyinde garanti altına alıyor.

Buraya kadar anlattıklarımın hepsini ilk günden uygulamanıza gerek yok. Önce IAM federasyonuyla başlayın, sonra merkezi loglama ekleyin, ardından otomatik compliance checkları devreye alın. Her adım bir öncekinin üzerine inşa ediliyor. Küçük adımlarla başlamak büyük bir güvenlik duruşuna götürüyor, bir gecede her şeyi değiştirmeye çalışmak ise genellikle hiçbir şeyi değiştiremiyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir