Bulut Ortamında Aşırı Yetkili Hesapları Tespit Etme ve Least Privilege Politikası Uygulama

Bulut ortamında güvenlik açıklarının büyük çoğunluğu, yazılım güvenlik açıklarından değil, yanlış yapılandırılmış kimlik ve erişim yönetiminden kaynaklanıyor. Gartner’ın tahminlerine göre 2023 yılına kadar bulut güvenlik ihlallerinin %99’u kullanıcı hatalarından, özellikle de aşırı yetkili hesaplardan kaynaklanacaktı. Bu tahmin maalesef büyük ölçüde doğrulandı. Bir sabah uyandığınızda production S3 bucket’ınızın herkese açık olduğunu veya bir geliştirici hesabının tüm VPC’yi sildiğini görmek istemiyorsanız, bu yazı tam size göre.

Aşırı Yetkili Hesap Nedir ve Neden Tehlikelidir?

Aşırı yetkili hesap, bir kullanıcının veya servisin görevini yerine getirmesi için ihtiyaç duyduğundan fazla izne sahip olması durumudur. En klasik örnek: bir developer’a “şimdilik” AdministratorAccess verilmesi ve bu “şimdilik”in kalıcı hale gelmesidir.

Bu durumun tehlikeleri birkaç farklı boyuttan geliyor:

  • İçeriden tehdit: Kötü niyetli veya dikkatsiz bir çalışan, sahip olduğu fazla yetkiyle ciddi hasar verebilir
  • Credential sızıntısı: Bir API key veya access token ele geçirildiğinde, saldırgan o hesabın tüm yetkilerine kavuşur
  • Yasal uyumluluk: GDPR, PCI-DSS, SOC2 gibi standartlar least privilege prensibini zorunlu kılar
  • Maliyet riski: Aşırı yetkili bir hesap, gereksiz kaynak oluşturarak faturanızı patlatabilir

AWS Ortamında Aşırı Yetkili Hesapları Tespit Etme

IAM Access Analyzer ile Başlayın

AWS’de ilk durağınız IAM Access Analyzer olmalı. Bu araç, harici erişime açık kaynakları tespit eder ve policy analizleri yapar.

# IAM Access Analyzer'ı CLI ile etkinleştirme
aws accessanalyzer create-analyzer 
  --analyzer-name production-analyzer 
  --type ACCOUNT 
  --region eu-west-1

# Mevcut bulguları listele
aws accessanalyzer list-findings 
  --analyzer-arn arn:aws:access-analyzer:eu-west-1:123456789:analyzer/production-analyzer 
  --filter '{"status": {"eq": ["ACTIVE"]}}'

IAM Credential Report ile Kullanılmayan Hesapları Bulun

90 günden fazla süredir kullanılmayan credential’lar ciddi bir güvenlik riski oluşturur. Aşağıdaki script ile bunları hızlıca tespit edebilirsiniz:

#!/bin/bash
# Kullanılmayan IAM kullanıcılarını tespit et

# Credential report oluştur
aws iam generate-credential-report

# Raporu indir ve parse et
aws iam get-credential-report 
  --query 'Content' 
  --output text | base64 --decode > /tmp/credential-report.csv

# 90 günden fazla giriş yapmamış kullanıcıları filtrele
awk -F',' 'NR>1 {
  user=$1
  last_used=$5
  if (last_used == "N/A" || last_used == "no_information") {
    print "INACTIVE USER: " user
  }
}' /tmp/credential-report.csv

IAM Policy Simulator ile Gerçek İzinleri Test Edin

Bir hesabın gerçekte ne yapabildiğini anlamak için Policy Simulator kullanın:

# Bir kullanıcının S3 silme iznini test et
aws iam simulate-principal-policy 
  --policy-source-arn arn:aws:iam::123456789:user/developer-john 
  --action-names s3:DeleteBucket s3:DeleteObject ec2:TerminateInstances 
  --output json | jq '.EvaluationResults[] | {Action: .EvalActionName, Decision: .EvalDecision}'

AWS Config ile Sürekli Uyumluluk Kontrolü

# Managed policy kullanan ve admin yetkisi olan kullanıcıları bul
aws iam list-users --query 'Users[*].UserName' --output text | 
  tr 't' 'n' | 
  while read username; do
    policies=$(aws iam list-attached-user-policies 
      --user-name "$username" 
      --query 'AttachedPolicies[*].PolicyName' 
      --output text)
    
    if echo "$policies" | grep -q "AdministratorAccess"; then
      echo "UYARI: $username kullanicisi AdministratorAccess yetkisine sahip!"
    fi
  done

Azure Ortamında Aşırı Yetkili Hesapları Tespit Etme

Azure’da işler biraz farklı. Azure AD ve RBAC yapısını iyi anlamak gerekiyor.

Azure PowerShell ile Yüksek Yetkili Kullanıcıları Listeleyin

# Azure CLI ile Owner ve Contributor rollerine sahip kullanıcıları listele
az role assignment list 
  --all 
  --include-inherited 
  --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor'].{User:principalName, Role:roleDefinitionName, Scope:scope}" 
  --output table

# Subscription düzeyinde owner olan service principal'ları bul
az role assignment list 
  --scope "/subscriptions/$(az account show --query id -o tsv)" 
  --role "Owner" 
  --query "[?principalType=='ServicePrincipal'].{Name:principalName, Type:principalType}" 
  --output json

Azure AD Privileged Identity Management (PIM) Durumunu Kontrol Edin

# Global Admin rolüne sahip kullanıcıları listele
az ad directory-role list --query "[?displayName=='Global Administrator'].id" -o tsv | 
  while read role_id; do
    az ad directory-role member list --id "$role_id" 
      --query "[*].{DisplayName:displayName, UPN:userPrincipalName}" 
      --output json
  done

GCP Ortamında Aşırı Yetkili Hesapları Tespit Etme

# GCP'de project düzeyinde editor ve owner rollerini listele
gcloud projects get-iam-policy $(gcloud config get-value project) 
  --flatten="bindings[].members" 
  --format='table(bindings.role,bindings.members)' 
  --filter="bindings.role:(roles/editor OR roles/owner)"

# Service account'ların project-level yetkilerini kontrol et
gcloud projects get-iam-policy $(gcloud config get-value project) 
  --format=json | 
  python3 -c "
import json, sys
policy = json.load(sys.stdin)
for binding in policy.get('bindings', []):
    role = binding['role']
    for member in binding.get('members', []):
        if 'serviceAccount' in member and ('editor' in role or 'owner' in role):
            print(f'RISK: {member} -> {role}')
"

Least Privilege Politikası Nasıl Uygulanır?

1. Adım: Mevcut Durumu Haritalayın

Politika uygulamadan önce kim ne yapıyor, neye erişiyor, bunları anlamanız lazım. Bu adımı atlamak, production’ı kırmak için en kısa yoldur.

CloudTrail log analizi yapın: Son 90 günde hangi API call’ların yapıldığını analiz ederek gerçek ihtiyaçları tespit edin.

#!/bin/bash
# CloudTrail loglarından bir kullanıcının API aktivitesini çıkar
# Bu script Athena tablosu üzerinde çalışır

ATHENA_DB="cloudtrail_logs"
USER_NAME="developer-john"
OUTPUT_LOCATION="s3://my-athena-results/"

aws athena start-query-execution 
  --query-string "
    SELECT eventname, count(*) as call_count
    FROM ${ATHENA_DB}.cloudtrail_logs
    WHERE useridentity.username = '${USER_NAME}'
    AND year = '2024'
    GROUP BY eventname
    ORDER BY call_count DESC
    LIMIT 50
  " 
  --result-configuration "OutputLocation=${OUTPUT_LOCATION}" 
  --query 'QueryExecutionId' 
  --output text

2. Adım: Rol Tabanlı Gruplandırma Yapın

Her kullanıcıya ayrı ayrı policy yazmak yönetilemez hale gelir. Bunun yerine rollere göre gruplar oluşturun:

Tipik roller ve izin seviyeleri:

  • ReadOnly: Sadece liste ve describe işlemleri, monitoring erişimi
  • Developer: Uygulama servisleri üzerinde CRUD, IAM değişikliği yok
  • DevOps: Altyapı yönetimi, production’da kısıtlı IAM yetkisi
  • SecurityAuditor: Tüm kaynaklarda read-only, CloudTrail ve Config erişimi
  • BreakGlass Admin: Acil durum için, MFA zorunlu, tüm aktiviteler loglanır

3. Adım: AWS’de Özel Minimum Yetkili Policy Oluşturun

Genel amaçlı managed policy’ler yerine, işe özel policy’ler yazın:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DeveloperS3Access",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::myapp-dev-*",
        "arn:aws:s3:::myapp-dev-*/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "eu-west-1"
        }
      }
    },
    {
      "Sid": "DenyProductionAccess",
      "Effect": "Deny",
      "Action": "*",
      "Resource": [
        "arn:aws:s3:::myapp-prod-*",
        "arn:aws:s3:::myapp-prod-*/*"
      ]
    }
  ]
}

Bu policy’yi CLI ile oluşturun ve kullanıcıya bağlayın:

# Custom policy oluştur
aws iam create-policy 
  --policy-name DeveloperS3LimitedAccess 
  --policy-document file://developer-s3-policy.json 
  --description "Developer ortami icin sinirli S3 erisimi"

# Policy'yi kullaniciya bagla
aws iam attach-user-policy 
  --user-name developer-john 
  --policy-arn arn:aws:iam::123456789:policy/DeveloperS3LimitedAccess

# Eski aşırı yetkili policy'yi kaldır
aws iam detach-user-policy 
  --user-name developer-john 
  --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

4. Adım: Zaman Sınırlı Yüksek Yetkili Erişim

Bazen yöneticilerin geçici olarak yüksek yetkiye ihtiyacı olur. Bu senaryolar için kalıcı yetki vermek yerine just-in-time erişim modelini kullanın:

#!/bin/bash
# Geçici admin erişimi için STS AssumeRole kullan
# Bu yaklaşım, erişimi loglar ve otomatik sona erdirir

ADMIN_ROLE_ARN="arn:aws:iam::123456789:role/TemporaryAdminRole"
SESSION_DURATION=3600  # 1 saat

# Geçici credential al
TEMP_CREDS=$(aws sts assume-role 
  --role-arn "$ADMIN_ROLE_ARN" 
  --role-session-name "emergency-access-$(whoami)-$(date +%Y%m%d%H%M%S)" 
  --duration-seconds $SESSION_DURATION 
  --output json)

# Environment variable'lara aktar
export AWS_ACCESS_KEY_ID=$(echo $TEMP_CREDS | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $TEMP_CREDS | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $TEMP_CREDS | jq -r '.Credentials.SessionToken')

echo "Gecici admin erisimi aktif. Sona erme: $(echo $TEMP_CREDS | jq -r '.Credentials.Expiration')"
echo "UYARI: Bu session CloudTrail'de tam olarak loglanmaktadir!"

Gerçek Dünya Senaryosu: Bir SaaS Şirketinin Güvenlik Dönüşümü

Birlikte çalıştığım bir SaaS şirketinde durum şöyleydi: 12 developer’ın tamamında AdministratorAccess vardı, çünkü “işleri kolaylaştırıyor”du. 3 adet service account da aynı yetkiye sahipti. Bir güvenlik denetimi sonrası şu adımları uyguladık:

Tespit aşaması (1. hafta): CloudTrail’den 90 günlük log analizi yaptık. Developer’ların %80’i sadece EC2, S3 ve RDS describe/list işlemleri yapıyor, birkaçı Lambda deploy ediyor, hiçbiri IAM değişikliği yapmıyordu.

Gruplandırma (2. hafta): 4 farklı custom role oluşturduk. Developer, Senior Developer, DevOps ve EmergencyAdmin. Her birinin policy’si gerçek kullanım verilerine dayanıyordu.

Geçiş (3-4. hafta): Önce non-production ortamda test ettik. Developer’lar bir şeyi yapamamaya başladığında bilet açtılar, biz de gerektiğinde policy’yi genişlettik. Production geçişi 2 hafta sonra sorunsuz tamamlandı.

Sonuç: 3 ay sonra bir developer’ın laptop’ı çalındı. Credential’ları ele geçirilmiş olsaydı bile saldırgan sadece dev ortamındaki birkaç S3 bucket’a ve Lambda’ya erişebilirdi. Eskiden bu, tüm AWS hesabının ele geçirilmesi demekti.

Otomatik Drift Tespiti ve Alerting

Least privilege uyguladıktan sonra en büyük tehlike zamanla tekrar şişmesidir. Bunu önlemek için otomatik kontroller kurun:

#!/bin/bash
# Haftalık IAM drift kontrolü - cron job olarak çalıştırın
# 0 9 * * 1 /opt/scripts/iam-drift-check.sh

SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
ALERT_THRESHOLD_DAYS=90

send_alert() {
  local message=$1
  curl -s -X POST "$SLACK_WEBHOOK" 
    -H 'Content-type: application/json' 
    --data "{"text": "IAM GUVENLIK UYARISI: $message"}"
}

# Admin yetkisi olan yeni hesapları kontrol et
aws iam list-users --query 'Users[*].UserName' --output text | 
  tr 't' 'n' | 
  while read user; do
    has_admin=$(aws iam list-attached-user-policies 
      --user-name "$user" 
      --query "AttachedPolicies[?PolicyName=='AdministratorAccess'].PolicyName" 
      --output text)
    
    if [ -n "$has_admin" ]; then
      # Bu kullanıcı onaylı admin listesinde mi?
      if ! grep -q "^$user$" /opt/scripts/approved-admins.txt; then
        send_alert "$user kullanicisi yetkisiz olarak AdministratorAccess yetkisine sahip!"
      fi
    fi
    
    # 90 gunden fazla kullanılmayan access key kontrolu
    aws iam list-access-keys 
      --user-name "$user" 
      --query 'AccessKeyMetadata[*].AccessKeyId' 
      --output text | 
      tr 't' 'n' | 
      while read key_id; do
        [ -z "$key_id" ] && continue
        last_used=$(aws iam get-access-key-last-used 
          --access-key-id "$key_id" 
          --query 'AccessKeyLastUsed.LastUsedDate' 
          --output text 2>/dev/null)
        
        if [ "$last_used" == "None" ] || [ -z "$last_used" ]; then
          send_alert "$user kullanicisinin $key_id key'i hic kullanilmamis, devre disi birakilmali!"
        fi
      done
  done

echo "IAM drift kontrolu tamamlandi: $(date)"

Terraform ile Infrastructure as Code Yaklaşımı

IAM policy’lerini manuel yönetmek hatalara açık. Terraform ile tüm yetki yapısını kod olarak yönetin:

# developer_role.tf
resource "aws_iam_policy" "developer_limited" {
  name        = "DeveloperLimitedAccess-${var.environment}"
  description = "Gelistirici ortami icin sinirli erisim - ${var.environment}"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "EC2ReadAccess"
        Effect = "Allow"
        Action = [
          "ec2:Describe*",
          "ec2:List*"
        ]
        Resource = "*"
      },
      {
        Sid    = "S3DevAccess"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:ListBucket"
        ]
        Resource = [
          "arn:aws:s3:::${var.project_name}-${var.environment}-*",
          "arn:aws:s3:::${var.project_name}-${var.environment}-*/*"
        ]
      },
      {
        Sid      = "DenyIAMChanges"
        Effect   = "Deny"
        Action   = ["iam:*"]
        Resource = "*"
      }
    ]
  })

  tags = {
    ManagedBy   = "Terraform"
    Environment = var.environment
    LastReview  = "2024-01-15"
  }
}

resource "aws_iam_group_policy_attachment" "developer_group" {
  group      = aws_iam_group.developers.name
  policy_arn = aws_iam_policy.developer_limited.arn
}

Terraform yaklaşımının avantajları:

  • Versiyon kontrolü: Her değişiklik Git’te takip edilir
  • Code review: Yetki değişiklikleri PR süreci gerektirir
  • Tekrarlanabilirlik: Dev, staging, prod ortamları tutarlı politikaya sahip olur
  • Dokümantasyon: Policy’ler kendi kendini belgeler

Uyumluluk ve Denetim İçin Raporlama

SOC2 veya ISO 27001 denetimlerine hazırlanıyorsanız, düzenli raporlar oluşturun:

#!/bin/bash
# Aylik IAM uyumluluk raporu olustur
# Denetcilere sunmak icin HTML rapor uretir

REPORT_DATE=$(date +%Y-%m)
REPORT_FILE="/tmp/iam-compliance-report-${REPORT_DATE}.txt"

{
  echo "=== IAM UYUMLULUK RAPORU - ${REPORT_DATE} ==="
  echo ""
  
  echo "--- TOPLAM KULLANICI SAYISI ---"
  aws iam list-users --query 'length(Users)' --output text
  
  echo ""
  echo "--- ADMIN YETKILI KULLANICILAR ---"
  aws iam list-users --query 'Users[*].UserName' --output text | 
    tr 't' 'n' | 
    while read user; do
      aws iam list-attached-user-policies 
        --user-name "$user" 
        --query "AttachedPolicies[?contains(PolicyName,'Admin')].PolicyName" 
        --output text | 
        grep -v "^$" | 
        awk -v user="$user" '{print user " -> " $0}'
    done
  
  echo ""
  echo "--- MFA AKTIF OLMAYAN KULLANICILAR ---"
  aws iam list-users --query 'Users[*].UserName' --output text | 
    tr 't' 'n' | 
    while read user; do
      mfa_count=$(aws iam list-mfa-devices 
        --user-name "$user" 
        --query 'length(MFADevices)' 
        --output text)
      
      if [ "$mfa_count" == "0" ]; then
        echo "MFA YOK: $user"
      fi
    done
    
  echo ""
  echo "--- 90+ GUN KULLANILMAYAN ACCESS KEY'LER ---"
  aws iam generate-credential-report > /dev/null 2>&1
  sleep 5
  aws iam get-credential-report 
    --query 'Content' 
    --output text | 
    base64 --decode | 
    awk -F',' 'NR>1 && $10!="N/A" {
      cmd = "date -d " $10 " +%s"
      cmd | getline key_date
      close(cmd)
      now = systime()
      diff = (now - key_date) / 86400
      if (diff > 90) print "ESKI KEY: " $1 " (" int(diff) " gun once kullanildi)"
    }'
    
} > "$REPORT_FILE"

echo "Rapor olusturuldu: $REPORT_FILE"

# Raporu S3'e arsivle
aws s3 cp "$REPORT_FILE" 
  "s3://company-compliance-reports/iam/${REPORT_DATE}/iam-report.txt" 
  --server-side-encryption AES256

Sonuç

Aşırı yetkili hesaplar, bulut ortamlarının sessiz katilidir. Herhangi bir alarm çalmadan var olurlar, ta ki bir incident yaşanana kadar. Least privilege prensibini uygulamak bir kez yapıp bitirilen bir iş değil, sürekli bakım gerektiren bir disiplindir.

Bugün başlamak için pratik bir yol haritası:

  • Bu hafta: IAM credential report çalıştırın, 90 gündür kullanılmayan hesapları devre dışı bırakın
  • Bu ay: CloudTrail analizi yapın, gerçek API kullanımına bakın, custom policy’leri oluşturmaya başlayın
  • Bu çeyrek: Terraform ile IAM’i kod olarak yönetmeye geçin, haftalık drift kontrollerini otomatize edin
  • Sürekli olarak: Yeni çalışanlar için onboarding sürecine least privilege’i dahil edin, ayrılanlarda hızlı offboarding yapın

Bir şeyi unutmayın: “Bu kadar kısıtlama işleri yavaşlatır” itirazını mutlaka duyacaksınız. Developer’larınız bir şeyi yapamadığında bilet açsınlar, siz de gerçekten ihtiyaç olduğunu doğrulayıp policy’yi genişletin. Bu süreç hem güvenliği artırır hem de zamanla neye gerçekten ihtiyaç duyulduğunu öğrenmenizi sağlar. Birkaç ay sonra hem güvenli hem de gereksiz karmaşıklıktan arınmış bir IAM yapısına kavuşursunuz.

Güvenlik ekibinizin en büyük kazanımı, bir ihlal yaşandığında “neyse ki o hesabın yetkileri kısıtlıydı” diyebilmektir. O güne kadar ihtiyaç olmayacak bir şey gibi görünebilir, ama o gün geldiğinde her şeyin farkını yaratır.

Bir yanıt yazın

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