Bulut Ortamında Servis Hesapları ve API Anahtarlarının Güvenli Yönetimi
Bulut ortamında çalışırken en çok göz ardı edilen güvenlik konularından biri servis hesapları ve API anahtarlarının doğru yönetilmesidir. Bir startup’ın tüm AWS altyapısının, bir geliştirici dizüstü bilgisayarına hardcode edilmiş bir API anahtarı yüzünden ele geçirildiğini ya da bir CI/CD pipeline’ında yıllarca kullanılmayan bir servis hesabının neden olduğu veri ihlaline dair haberleri hepimiz okuduk. Bu yazıda bu tür felaketlerin nasıl önleneceğini, pratik araçlar ve gerçek dünya senaryolarıyla ele alacağız.
Servis Hesapları ve API Anahtarları Neden Bu Kadar Kritik?
Uygulamalar, CI/CD sistemleri, monitoring araçları ve otomasyonlar birbirleriyle veya bulut servisleriyle konuşmak zorunda. Bu iletişimi sağlayan kimlik bilgileri, yani servis hesapları ve API anahtarları, yanlış yönetildiğinde saldırganların en çok hedef aldığı noktalara dönüşüyor.
Gerçek bir senaryo düşünelim: Ekibinizdeki bir geliştirici, yerel testler için bir AWS IAM kullanıcısı oluşturuyor, AdministratorAccess politikası ekliyor ve bu anahtarları bir Python scriptinin içine yazıyor. Script zamanla bir GitHub reposuna push’lanıyor. Repo belki private bile olsa, GitHub’ın secret scanning özelliği devreye girmeden önce bu anahtarlar onlarca bot tarafından taranmış bile olabiliyor. Sonuç: binlerce dolar EC2 faturası ve potansiyel veri ihlali.
Temel problemler şunlar:
- Aşırı geniş izinler (least privilege ihlali)
- Anahtarların kod içine gömülmesi (hardcoding)
- Kullanılmayan hesapların silinmemesi
- Rotasyon yapılmaması
- Audit log tutulmaması
Temel Prensipler: Least Privilege ve Zero Trust
Her servis hesabı sadece ihtiyacı olan izinlere sahip olmalı. Bu kulağa basit geliyor ama pratikte çoğu ekip “geliştirme sırasında geniş izin verelim, sonra daraltırız” tuzağına düşüyor. O “sonra” çoğu zaman gelmiyor.
Zero Trust yaklaşımında şu sorular sorulur:
- Bu hesap gerçekten bu kaynağa erişmeli mi?
- Bu erişim kalıcı mı yoksa geçici mi olmalı?
- Bu işlem neden bu hesap tarafından yapılıyor?
- Bu hesabın davranışı normal mi?
AWS, GCP ve Azure’un tamamında IAM (Identity and Access Management) sistemi bu prensipler üzerine kurulu ama varsayılan konfigürasyonlar güvenli değil; siz aktif olarak güvenli hale getirmelisiniz.
AWS Ortamında Güvenli Servis Hesabı Yönetimi
IAM Roller ve Instance Profile Kullanımı
EC2 instance’larına veya Lambda fonksiyonlarına erişim yetkisi vermek için asla access key kullanmayın. Bunun yerine IAM role ve instance profile kullanın.
# IAM role oluşturma (AWS CLI)
aws iam create-role
--role-name MyAppRole
--assume-role-policy-document file://trust-policy.json
--description "Uygulama sunucusu icin minimum yetkili rol"
# Trust policy dosyasi (trust-policy.json)
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# Sadece gerekli S3 bucket'a erisim icin policy ekle
aws iam put-role-policy
--role-name MyAppRole
--policy-name S3ReadOnlySpecificBucket
--policy-document file://s3-policy.json
Bu yaklaşımda uygulama sunucunuz credentials dosyası olmadan otomatik olarak geçici kimlik bilgileri alıyor. AWS SDK’ları bu instance metadata endpoint’ini (169.254.169.254) otomatik kullanıyor.
IAM Access Analyzer ile Kullanılmayan İzinleri Tespit Etme
# Access Analyzer analyzer olustur
aws accessanalyzer create-analyzer
--analyzer-name production-analyzer
--type ACCOUNT
# Kullanilmayan erisim raporunu goruntule
aws accessanalyzer list-findings
--analyzer-arn arn:aws:access-analyzer:eu-west-1:123456789:analyzer/production-analyzer
--filter '{"resourceType": {"contains": ["AWS::IAM::Role"]}}'
# Son 90 gun icinde kullanilmayan IAM kullanicilari listele
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d |
awk -F',' 'NR>1 && $5!="N/A" && $5<"2024-01-01" {print $1, $5}'
AWS Secrets Manager ile API Anahtar Rotasyonu
Uygulama kodunuzda os.environ.get('DB_PASSWORD') yazmak iyi ama bu değerin nereden geldiği daha önemli. Secrets Manager bu işi doğru yapmanın yolu.
# Secret olustur
aws secretsmanager create-secret
--name prod/myapp/database
--description "Production veritabani sifreleri"
--secret-string '{"username":"appuser","password":"ilk-guclu-sifre-buraya"}'
# Otomatik rotasyon aktif et (30 gunluk)
aws secretsmanager rotate-secret
--secret-id prod/myapp/database
--rotation-rules AutomaticallyAfterDays=30
# Uygulama icinden secret okuma (Python ornegi olarak bash ile test)
aws secretsmanager get-secret-value
--secret-id prod/myapp/database
--query SecretString
--output text | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['password'])"
Bir e-ticaret projesinde karşılaştığım gerçek bir durumu paylaşayım: Ödeme sistemi entegrasyonu için kullanılan Stripe API anahtarları hem .env dosyasında hem de environment variable olarak tanımlanmıştı. .env dosyası .gitignore‘a eklenmişti ama bir junior geliştirici bu dosyayı Docker imajına kopyaladığında imaj DockerHub’a push’landı. Anahtarlar 6 saat içinde tespit edilip kullanıldı. Secrets Manager ile çalışsaydık bu senaryo imkansız olurdu.
GCP Ortamında Servis Hesabı Güvenliği
Google Cloud’da servis hesapları oldukça güçlü ama dikkatli kullanılması gerekiyor. Workload Identity Federation modern yaklaşım olsa da hala çok sayıda ekip JSON key file kullanıyor.
Workload Identity ile Anahtarsız Kimlik Doğrulama
# GKE cluster icin Workload Identity aktif et
gcloud container clusters update my-cluster
--workload-pool=my-project.svc.id.goog
--region=europe-west1
# Kubernetes service account olustur
kubectl create serviceaccount myapp-ksa
--namespace production
# GCP service account olustur
gcloud iam service-accounts create myapp-gsa
--display-name="MyApp GKE Service Account"
--project=my-project
# Iki hesap arasinda baglanti kur
gcloud iam service-accounts add-iam-policy-binding
[email protected]
--role roles/iam.workloadIdentityUser
--member "serviceAccount:my-project.svc.id.goog[production/myapp-ksa]"
# Kubernetes service account'a annotation ekle
kubectl annotate serviceaccount myapp-ksa
--namespace production
iam.gke.io/[email protected]
GCP Secret Manager Kullanımı
# Secret olustur
echo -n "super-gizli-api-anahtari" |
gcloud secrets create my-api-key
--data-file=-
--replication-policy=user-managed
--locations=europe-west1
# Secret'a erisim ver (sadece ilgili service account)
gcloud secrets add-iam-policy-binding my-api-key
--member="serviceAccount:[email protected]"
--role="roles/secretmanager.secretAccessor"
# Secret versiyonu guncelle (rotasyon)
echo -n "yeni-api-anahtari" |
gcloud secrets versions add my-api-key --data-file=-
# Eski versiyonu devre disi birak
gcloud secrets versions disable 1 --secret=my-api-key
# Secret listesi ve son erisim bilgisi
gcloud secrets list --format="table(name,createTime,labels)"
HashiCorp Vault: Merkezi Sır Yönetimi
Birden fazla bulut sağlayıcısıyla çalışıyorsanız ya da hybrid bir altyapınız varsa, HashiCorp Vault merkezi bir sır yönetim sistemi olarak mükemmel bir çözüm sunuyor.
# Vault dynamic credentials - AWS icin
# Vault'ta AWS secrets engine aktif et
vault secrets enable aws
# AWS credentials Vault'a ekle
vault write aws/config/root
access_key=AKIAIOSFODNN7EXAMPLE
secret_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
region=eu-west-1
# Dynamic IAM user policy tanimla
vault write aws/roles/my-dynamic-role
credential_type=iam_user
[email protected]
default_ttl=1h
max_ttl=24h
# Gecici credentials al (sadece 1 saat gecerli!)
vault read aws/creds/my-dynamic-role
# Uygulama icin AppRole authentication
vault auth enable approle
vault write auth/approle/role/myapp
secret_id_ttl=10m
token_num_uses=10
token_ttl=20m
token_max_ttl=30m
secret_id_num_uses=40
policies="myapp-policy"
# Role ID ve Secret ID al
vault read auth/approle/role/myapp/role-id
vault write -f auth/approle/role/myapp/secret-id
Vault’un dinamik credential özelliği gerçekten bir oyun değiştirici. Her uygulama instance’ı kendi unique, kısa ömürlü kimlik bilgilerini alıyor. Biri ele geçirilse bile 1 saat sonra otomatik olarak geçersiz hale geliyor.
CI/CD Pipeline’larında Güvenli Kimlik Yönetimi
Pipelines özellikle riskli çünkü burada hem üretim ortamı kimlik bilgileri hem de kaynak kodun tamamına erişim bir arada bulunuyor.
GitHub Actions ile OIDC Tabanlı AWS Kimlik Doğrulama
# .github/workflows/deploy.yml
name: Production Deploy
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: AWS kimlik dogrulama (OIDC - anahtar yok!)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
role-session-name: GitHubActions-${{ github.run_id }}
aws-region: eu-west-1
- name: ECR'e push
run: |
aws ecr get-login-password | docker login --username AWS
--password-stdin 123456789.dkr.ecr.eu-west-1.amazonaws.com
docker build -t myapp .
docker push 123456789.dkr.ecr.eu-west-1.amazonaws.com/myapp:latest
Bu yaklaşımda GitHub Secrets’a hiçbir AWS anahtarı eklemenize gerek kalmıyor. GitHub Actions runner, OIDC token alıyor ve AWS bu token’ı doğrulayarak geçici kimlik bilgileri veriyor.
Audit ve Monitoring: Ne Olduğunu Bilmek
Güvenli bir sistem kurmak yeterli değil; ne zaman, kim, ne yaptı bunları da bilmeniz gerekiyor.
# AWS CloudTrail ile API cagrilarini izle
# Son 24 saatteki basarisiz authentication denemeleri
aws cloudtrail lookup-events
--lookup-attributes AttributeKey=EventName,AttributeValue=ConsoleLogin
--start-time $(date -d '24 hours ago' -u +"%Y-%m-%dT%H:%M:%SZ")
--query 'Events[?contains(CloudTrailEvent, `"errorCode"`)].{User:Username, Time:EventTime, Source:EventSource}'
--output table
# Belirli bir IAM kullanicisinin son aktiviteleri
aws cloudtrail lookup-events
--lookup-attributes AttributeKey=Username,AttributeValue=suspect-user
--start-time 2024-01-01T00:00:00Z
--query 'Events[].{Event:EventName, Time:EventTime, IP:CloudTrailEvent}'
--output json | python3 -c "
import sys, json
events = json.load(sys.stdin)
for e in events[:20]:
print(f'{e["Time"]} | {e["Event"]}')
"
# GCP audit log sorgusu
gcloud logging read
'protoPayload.serviceName="iam.googleapis.com" AND
protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"'
--limit=50
--format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.request.name)"
Pratik Güvenlik Kontrol Listesi
Ekibinizle aylık olarak geçebileceğiniz, bulut ortamınızdaki servis hesapları ve API anahtarları için pratik kontroller:
Hesap envanteri:
- Tüm servis hesapları ve API anahtarları bir envanterle takip edilmeli
- Her hesabın sahibi (owner) tanımlı olmalı
- Son kullanım tarihi kayıt altında olmalı
İzin denetimi:
- Hiçbir servis hesabı admin izni taşımamalı
- Her hesap sadece ihtiyacı olan kaynaklara erişmeli
- Cross-account roller minimum yetki prensibiyle konfigüre edilmeli
Rotasyon politikası:
- Static API anahtarları 90 günde bir rotasyona tabi olmalı
- Dinamik credential kullanımı önceliklendirilmeli
- Rotasyon otomasyonu monitoring ile doğrulanmalı
Acil müdahale:
- Ele geçirilmiş anahtar için revoke prosedürü dokümante edilmeli
- Otomatik uyarılar kurulu olmalı
- Olay müdahale planı test edilmiş olmalı
# Aktif access key'leri listele ve son kullanim kontrolu
aws iam list-users --query 'Users[].UserName' --output text |
tr 't' 'n' | while read user; do
keys=$(aws iam list-access-keys --user-name "$user"
--query 'AccessKeyMetadata[?Status==`Active`].{ID:AccessKeyId,Created:CreateDate}'
--output json 2>/dev/null)
if [ "$keys" != "[]" ]; then
echo "=== $user ==="
echo "$keys" | python3 -c "
import sys, json
from datetime import datetime, timezone
keys = json.load(sys.stdin)
for k in keys:
created = datetime.fromisoformat(k['Created'].replace('Z','+00:00'))
age = (datetime.now(timezone.utc) - created).days
status = 'UYARI: Eski anahtar!' if age > 90 else 'OK'
print(f' {k["ID"]}: {age} gun - {status}')
"
fi
done
Yaygın Hatalar ve Çözümleri
Hardcoded credentials: Kod review süreçlerinize git-secrets veya truffleHog gibi araçları entegre edin. Pre-commit hook olarak çalıştırılabilirler.
Fazla geniş izinler: Başlangıçta dar izinle başlayın, uygulama “permission denied” verdiğinde CloudTrail veya Audit Log’a bakarak tam olarak neye ihtiyaç duyduğunu anlayın. Tahmin yürütmeyin.
Paylaşılan servis hesapları: “Ortak kullanım” için oluşturulan hesaplar audit edilemez. Her uygulama, her servis kendi hesabına sahip olmalı.
Unutulan hesaplar: Çalışan ayrıldığında, proje kapandığında ya da servis değiştirildiğinde ilgili hesaplar silinmeli. Bunun için offboarding checklist’inize ekleyin.
Sonuç
Servis hesapları ve API anahtarlarının güvenli yönetimi tek seferlik bir iş değil, sürekli bir süreç. Bugün güvenli kurduğunuz sistemi iki ay sonra incelediğinizde mutlaka düzeltilmesi gereken şeyler bulacaksınız.
Öncelik sıranız şöyle olabilir: Önce mevcut durumu belgeleyin, sonra hardcoded credential’ları ortadan kaldırın, ardından rotasyon otomasyonunu kurun ve son olarak monitoring ile audit logging’i devreye alın. Bu dört adımı tamamladığınızda çoğu bulut ortamının yaşadığı güvenlik olaylarının büyük çoğunluğuna karşı ciddi ölçüde dirençli bir yapıya sahip olursunuz.
Maliyet tarafını da göz ardı etmeyin: Ele geçirilmiş bir API anahtarının neden olduğu yetkisiz kaynak kullanımı ya da veri ihlalinin maliyeti, Secrets Manager veya Vault için ödeyeceğiniz ücretten çok daha fazla olacaktır. Güvenlik yatırımını maliyet olarak değil, sigorta primi olarak düşünün.
