GCP Secret Manager ile Gizli Veri Yönetimi
Üretim ortamında bir veritabanı şifresi ya da API anahtarı sızdığında ne olduğunu bilen biri olarak şunu net söyleyeyim: bu tür olaylar genellikle gece yarısı patlak verir ve sabaha kadar uyutmaz. Kodun içine gömmüş .env dosyaları, Git geçmişine düşmüş credentials, Slack mesajlarında dolaşan token’lar… Bunların hepsi klasik sysadmin kâbusları. GCP Secret Manager tam da bu noktada devreye giriyor ve işleri ciddi ölçüde düzenli hale getiriyor.
GCP Secret Manager Nedir ve Neden Kullanmalısınız
Google Cloud Secret Manager, gizli verileri merkezi bir yerde, şifrelenmiş olarak saklayan ve erişimi fine-grained IAM politikalarıyla yöneten bir servistir. Sadece şifre saklamaktan ibaret değil; versiyonlama, otomatik rotasyon, audit logging ve cross-project erişim gibi enterprise seviyesinde özellikler sunuyor.
Temel kullanım senaryoları şunlardır:
- Veritabanı şifreleri ve connection string’leri
- Üçüncü parti API anahtarları (Stripe, Twilio, SendGrid vs.)
- SSL sertifikaları ve private key’ler
- OAuth client secret’ları
- Servisler arası iletişimde kullanılan token’lar
- SSH private key’leri
Peki neden başka bir çözüm değil de Secret Manager? HashiCorp Vault güçlü bir alternatif ama operasyonel yükü yüksek. Kubernetes Secrets base64 encoded, gerçek anlamda şifreli değil. Ortam değişkenleri ise process listing’de görünebilir ve yönetimi karmaşık. Secret Manager ise GCP altyapısıyla native olarak entegre çalışıyor, ekstra bir şey kurup yönetmeniz gerekmiyor.
Temel Kurulum ve Yapılandırma
API’yi Etkinleştirme ve İzinler
Önce Secret Manager API’yi etkinleştirmeniz gerekiyor. Bunu gcloud CLI üzerinden yapabilirsiniz:
# Secret Manager API'yi etkinleştir
gcloud services enable secretmanager.googleapis.com
# Mevcut projedeki etkin servisleri kontrol et
gcloud services list --enabled | grep secret
# Servis hesabı oluştur (production ortamı için)
gcloud iam service-accounts create app-secret-reader
--display-name="Application Secret Reader"
--description="Uygulama tarafindan secret okumak icin kullanilir"
IAM rollerini doğru atamak kritik önem taşıyor. Secret Manager için temel roller şunlardır:
- roles/secretmanager.admin: Tüm işlemleri yapabilir, yönetim için kullanılır
- roles/secretmanager.secretAccessor: Secret değerlerini okuyabilir, uygulamalar için ideal
- roles/secretmanager.secretVersionManager: Versiyonları yönetebilir ama değerlere erişemez
- roles/secretmanager.viewer: Metadata görüntüleyebilir, değerlere erişemez
# Servis hesabına secretAccessor rolü ver
gcloud projects add-iam-policy-binding PROJECT_ID
--member="serviceAccount:app-secret-reader@PROJECT_ID.iam.gserviceaccount.com"
--role="roles/secretmanager.secretAccessor"
# Sadece belirli bir secret için erişim ver (daha güvenli)
gcloud secrets add-iam-policy-binding my-database-password
--member="serviceAccount:app-secret-reader@PROJECT_ID.iam.gserviceaccount.com"
--role="roles/secretmanager.secretAccessor"
İlk Secret’ı Oluşturma
# Basit bir secret oluştur
echo -n "super-secret-password-123" | gcloud secrets create database-password
--data-file=-
--replication-policy="automatic"
# Dosyadan secret oluştur (SSL sertifikası gibi)
gcloud secrets create ssl-private-key
--data-file=/path/to/private.key
--replication-policy="user-managed"
--locations="europe-west1,europe-west3"
# JSON formatında çoklu değer içeren secret
cat > /tmp/db-config.json << 'EOF'
{
"host": "10.0.0.1",
"port": 5432,
"database": "production_db",
"username": "app_user",
"password": "very-secret-password"
}
EOF
gcloud secrets create database-config
--data-file=/tmp/db-config.json
--replication-policy="automatic"
# Geçici dosyayı temizle
rm /tmp/db-config.json
Secret Versiyonlama ve Yönetimi
Secret Manager’ın en güçlü özelliklerinden biri versiyonlama sistemi. Her secret güncellediğinizde eski versiyon silinmiyor, yeni bir versiyon oluşturuluyor. Bu sayede rotasyon sırasında eski versiyona dönmek mümkün oluyor.
# Mevcut bir secret'a yeni versiyon ekle
echo -n "yeni-guclu-sifre-456" | gcloud secrets versions add database-password
--data-file=-
# Secret'ın tüm versiyonlarını listele
gcloud secrets versions list database-password
# Belirli bir versiyonu oku
gcloud secrets versions access 1 --secret="database-password"
gcloud secrets versions access 2 --secret="database-password"
# Latest versiyonu oku (en yaygın kullanım)
gcloud secrets versions access latest --secret="database-password"
# Eski bir versiyonu devre dışı bırak
gcloud secrets versions disable 1 --secret="database-password"
# Bir versiyonu tamamen sil (geri alınamaz)
gcloud secrets versions destroy 1 --secret="database-password"
Versiyonlama ile ilgili önemli bir nokta: uygulamalarınızda latest yerine belirli bir versiyon numarası kullanmak daha kontrollü bir deployment süreci sağlar. Yeni şifreyi yayınlamadan önce bir versiyonu oluşturup test edebilir, sonra uygulamayı o versiyona güncelleyebilirsiniz.
Gerçek Dünya Senaryosu: Python Uygulamasında Entegrasyon
Diyelim ki Django tabanlı bir e-ticaret uygulamanız var ve veritabanı şifresini, Stripe API anahtarını ve Redis bağlantı bilgilerini Secret Manager’da saklayacaksınız.
# Gerekli secret'ları oluştur
gcloud secrets create django-secret-key
--replication-policy="automatic"
--data-file=<(python3 -c "import secrets; print(secrets.token_urlsafe(50), end='')")
gcloud secrets create stripe-api-key
--replication-policy="automatic"
# Değeri sonradan ekle (etkileşimli)
printf "sk_live_gerçek_anahtar_buraya" | gcloud secrets versions add stripe-api-key
--data-file=-
gcloud secrets create redis-url
--replication-policy="automatic"
--data-file=<(echo -n "redis://:[email protected]:6379/0")
Python tarafında google-cloud-secret-manager kütüphanesi ile entegrasyon:
# Kütüphaneyi kur
pip install google-cloud-secret-manager
# Basit bir helper script yaz
cat > /opt/app/secret_helper.py << 'PYTHON'
from google.cloud import secretmanager
import json
import os
def get_secret(secret_id, project_id=None, version_id="latest"):
"""
Secret Manager'dan secret değeri çeker.
GOOGLE_CLOUD_PROJECT env değişkeni yoksa project_id parametresi gerekli.
"""
client = secretmanager.SecretManagerServiceClient()
if project_id is None:
project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
if not project_id:
raise ValueError("Project ID belirtilmeli veya GOOGLE_CLOUD_PROJECT set edilmeli")
name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}"
try:
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("UTF-8")
except Exception as e:
raise RuntimeError(f"Secret çekilirken hata: {secret_id} - {str(e)}")
def get_json_secret(secret_id, **kwargs):
"""JSON formatındaki secret'ı dict olarak döner."""
raw = get_secret(secret_id, **kwargs)
return json.loads(raw)
# Django settings.py'de kullanım
if __name__ == "__main__":
# Test amaçlı
db_config = get_json_secret("database-config")
print(f"Veritabanı hostu: {db_config['host']}")
stripe_key = get_secret("stripe-api-key")
print(f"Stripe key başlangıcı: {stripe_key[:10]}...")
PYTHON
Kubernetes ve GKE Entegrasyonu
GKE üzerinde çalışan uygulamalar için Workload Identity kullanmak en temiz yaklaşım. Bu sayede servis hesabı JSON dosyası kullanmadan, pod’lara otomatik olarak GCP kimlik doğrulama yeteneği kazandırabilirsiniz.
# Workload Identity için Kubernetes servis hesabı oluştur
kubectl create serviceaccount app-service-account
--namespace=production
# GCP servis hesabını Kubernetes servis hesabıyla bağla
gcloud iam service-accounts add-iam-policy-binding
app-secret-reader@PROJECT_ID.iam.gserviceaccount.com
--role="roles/iam.workloadIdentityUser"
--member="serviceAccount:PROJECT_ID.svc.id.goog[production/app-service-account]"
# Kubernetes servis hesabına annotation ekle
kubectl annotate serviceaccount app-service-account
--namespace=production
iam.gke.io/gcp-service-account=app-secret-reader@PROJECT_ID.iam.gserviceaccount.com
# Secret Store CSI Driver ile Secret Manager entegrasyonu
# Önce driver'ı kur
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/secrets-store-csi-driver/main/deploy/rbac-secretproviderclass.yaml
# SecretProviderClass tanımla
cat > secret-provider-class.yaml << 'EOF'
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: app-secrets
namespace: production
spec:
provider: gcp
parameters:
secrets: |
- resourceName: "projects/PROJECT_ID/secrets/database-password/versions/latest"
fileName: "database-password"
- resourceName: "projects/PROJECT_ID/secrets/stripe-api-key/versions/latest"
fileName: "stripe-api-key"
EOF
kubectl apply -f secret-provider-class.yaml
Otomatik Rotasyon: Cloud Functions ile Şifre Döngüsü
Şifre rotasyonu genellikle ihmal edilen ama kritik öneme sahip bir güvenlik pratiği. Secret Manager, Cloud Pub/Sub üzerinden rotasyon olayları tetikleyebilir ve bunu Cloud Functions ile otomatize edebilirsiniz.
# Secret'a rotasyon ayarı ekle (her 90 günde bir)
gcloud secrets update database-password
--rotation-period="7776000s"
--next-rotation-time="2024-03-01T00:00:00Z"
# Rotasyon için Pub/Sub topic oluştur
gcloud pubsub topics create secret-rotation-topic
# Secret'ı bu topic'e bağla
gcloud secrets update database-password
--topics="projects/PROJECT_ID/topics/secret-rotation-topic"
# Cloud Function için rotasyon scripti
cat > rotation_function.py << 'PYTHON'
import functions_framework
from google.cloud import secretmanager
import base64
import json
import random
import string
def generate_password(length=32):
"""Güçlü rastgele şifre üretir."""
chars = string.ascii_letters + string.digits + "!@#$%^&*"
return ''.join(random.SystemRandom().choice(chars) for _ in range(length))
@functions_framework.cloud_event
def rotate_database_password(cloud_event):
"""
Pub/Sub olayını dinleyerek database şifresini otomatik rotate eder.
Gerçek senaryoda veritabanında da şifreyi güncellemeniz gerekir.
"""
client = secretmanager.SecretManagerServiceClient()
# Event datasını çöz
data = base64.b64decode(cloud_event.data["message"]["data"]).decode()
event_data = json.loads(data)
secret_name = event_data.get("name")
print(f"Rotasyon başlatıldı: {secret_name}")
# Yeni şifre üret
new_password = generate_password(40)
# TODO: Burada gerçek veritabanında şifreyi güncelle
# update_database_password(new_password)
# Secret'a yeni versiyon ekle
parent = secret_name
payload = new_password.encode("UTF-8")
response = client.add_secret_version(
request={
"parent": parent,
"payload": {"data": payload}
}
)
print(f"Yeni versiyon oluşturuldu: {response.name}")
# Eski versiyonları devre dışı bırak (isteğe bağlı)
# En son 2 versiyonu aktif tut, geri kalanları disable et
versions = client.list_secret_versions(request={"parent": parent})
version_list = [v for v in versions if v.state.name == "ENABLED"]
if len(version_list) > 2:
for old_version in version_list[2:]:
client.disable_secret_version(request={"name": old_version.name})
print(f"Eski versiyon devre dışı: {old_version.name}")
PYTHON
# Function'ı deploy et
gcloud functions deploy rotate-database-password
--gen2
--runtime=python311
--region=europe-west1
--source=.
--entry-point=rotate_database_password
--trigger-topic=secret-rotation-topic
--service-account=app-secret-reader@PROJECT_ID.iam.gserviceaccount.com
Audit Logging ve Monitoring
Kimin hangi secret’a ne zaman eriştiğini bilmek hem güvenlik hem de compliance açısından şart. Cloud Audit Logs bu konuda kapsamlı kayıt tutar.
# Data Access audit log'larını etkinleştir
cat > audit-policy.json << 'EOF'
{
"auditConfigs": [
{
"service": "secretmanager.googleapis.com",
"auditLogConfigs": [
{"logType": "ADMIN_READ"},
{"logType": "DATA_READ"},
{"logType": "DATA_WRITE"}
]
}
]
}
EOF
gcloud projects set-iam-policy PROJECT_ID audit-policy.json
# Belirli bir secret'a erişim loglarını sorgula
gcloud logging read
'resource.type="audited_resource" AND
resource.labels.service="secretmanager.googleapis.com" AND
protoPayload.resourceName:"secrets/database-password" AND
protoPayload.methodName="google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion"'
--limit=50
--format="json" |
jq '.[] | {time: .timestamp, caller: .protoPayload.authenticationInfo.principalEmail, secret: .protoPayload.resourceName}'
# Anormal erişim için alert policy oluştur
gcloud alpha monitoring policies create
--policy-from-file=- << 'EOF'
{
"displayName": "Secret Manager Anormal Erisim Uyarisi",
"conditions": [{
"displayName": "Yuksek secret erisim sayisi",
"conditionThreshold": {
"filter": "resource.type="audited_resource" AND protoPayload.serviceName="secretmanager.googleapis.com"",
"comparison": "COMPARISON_GT",
"thresholdValue": 100,
"duration": "300s",
"aggregations": [{
"alignmentPeriod": "300s",
"perSeriesAligner": "ALIGN_COUNT"
}]
}
}],
"alertStrategy": {
"notificationRateLimit": {"period": "3600s"}
}
}
EOF
Terraform ile Infrastructure as Code
Üretim ortamında secret’ları elle oluşturmak yerine Terraform ile yönetmek çok daha sağlıklı. Dikkat: Terraform state dosyasına asla secret değeri yazmayın.
# Terraform konfigürasyonu
cat > secrets.tf << 'EOF'
# Secret tanımı (değer olmadan, sadece metadata)
resource "google_secret_manager_secret" "database_password" {
secret_id = "database-password"
project = var.project_id
labels = {
environment = var.environment
team = "backend"
managed_by = "terraform"
}
replication {
auto {}
}
rotation {
rotation_period = "7776000s" # 90 gün
next_rotation_time = "2024-06-01T00:00:00Z"
}
topics {
name = google_pubsub_topic.secret_rotation.id
}
}
# IAM binding - Uygulama servis hesabına erişim ver
resource "google_secret_manager_secret_iam_binding" "app_reader" {
project = var.project_id
secret_id = google_secret_manager_secret.database_password.secret_id
role = "roles/secretmanager.secretAccessor"
members = [
"serviceAccount:${google_service_account.app.email}",
]
}
# Secret değerini terraform dışında set etmek için output
output "database_password_command" {
value = "gcloud secrets versions add ${google_secret_manager_secret.database_password.secret_id} --data-file=-"
description = "Secret degerini ayarlamak icin bu komutu kullanin"
}
EOF
# Terraform apply
terraform init
terraform plan -var="project_id=my-project" -var="environment=production"
terraform apply -var="project_id=my-project" -var="environment=production"
Güvenlik En İyi Pratikleri
Secret Manager’ı doğru kullanmak kadar güvenli kullanmak da önemli. Aşağıdaki pratikleri production ortamınıza mutlaka uygulayın:
Least Privilege Prensibi:
- Her uygulama veya servis sadece kendi ihtiyaç duyduğu secret’lara erişebilmeli
roles/secretmanager.adminrolünü gerçek bir insan kullanıcıya bile vermekten kaçının- Mümkünse proje seviyesinde değil, secret seviyesinde IAM binding yapın
Versiyonlama Disiplini:
- Rotasyon sırasında en az iki versiyonu aktif tutun (eski deployment’lar için)
- Artık kullanılmayan versiyonları destroy değil, önce disable edin
- Hangi versiyon numarasının production’da aktif olduğunu takip edin
Secret Naming Convention:
- Ortamı isme dahil edin:
production-database-password,staging-stripe-key - Takım veya servis bilgisini ekleyin:
backend-redis-auth,frontend-mapbox-token - Tarih bazlı isimlendirmeden kaçının, versiyon sistemi bu ihtiyacı karşılıyor
Geliştirme Ortamı Ayrımı:
# Farklı projeler için farklı secret'lar
# Development projesi
gcloud config set project my-project-dev
gcloud secrets create database-password --data-file=- <<< "dev-password"
# Production projesi
gcloud config set project my-project-prod
gcloud secrets create database-password --data-file=- <<< "production-super-secret"
# Uygulamada ortama göre proje seç
export GOOGLE_CLOUD_PROJECT=$(
if [ "$ENVIRONMENT" = "production" ]; then
echo "my-project-prod"
else
echo "my-project-dev"
fi
)
Secret Sızıntısı Durumunda Acil Müdahale:
# Tüm versiyonları acilen devre dışı bırak
for version in $(gcloud secrets versions list COMPROMISED_SECRET --format="value(name)"); do
gcloud secrets versions disable $version
echo "Devre dışı: $version"
done
# Yeni, güvenli bir versiyon oluştur
echo -n "yeni-guvenli-deger" | gcloud secrets versions add COMPROMISED_SECRET
--data-file=-
# Erişim loglarını incele - kim erişmiş?
gcloud logging read
"protoPayload.resourceName:"COMPROMISED_SECRET" AND
protoPayload.methodName:"AccessSecretVersion""
--limit=100
--format="table(timestamp, protoPayload.authenticationInfo.principalEmail)"
Sonuç
GCP Secret Manager, gizli veri yönetimini ciddi ölçüde basitleştiriyor ve güvenlik açıklarını kapatıyor. Versiyonlama sistemi sayesinde şifre rotasyonu artık bir kâbus değil, otomatize edilebilir rutin bir işlem. IAM entegrasyonu ile kimin neye erişebileceğini granüler düzeyde kontrol edebiliyorsunuz. Audit logging ile her erişimi kayıt altına alabiliyorsunuz.
Başlangıç için öncelik sıranız şöyle olabilir: önce halihazırda kod içinde veya .env dosyalarında duran credential’ları tespit edin ve bunları Secret Manager’a taşıyın. Sonra servis hesaplarınıza düzgün IAM rolleri atayın. Workload Identity ile Kubernetes pod’larınızı entegre edin. En son aşamada rotasyon otomasyonunu kurun.
Terraform ile yönetim kısmında şunu vurgulamak isterim: secret’ların tanımını (metadata) Terraform ile yönetin ama değerlerini asla Terraform state’ine yazmayın. Bu iki kavramı karıştırmak sık yapılan bir hata ve state dosyası bir git reposuna push’lanırsa felaket olur.
Secret Manager bedava değil ama maliyeti son derece makul: aylık 6 secret’a kadar ücretsiz, sonrası secret başına 0.06 dolar. 10.000 API erişiminde ise 0.03 dolar. Bir güvenlik ihlalinin potansiyel maliyetiyle kıyaslandığında bu rakamlar tartışmaya bile değmez.
