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.
