GitOps yaparken en zor “doğru problem”, secret yönetimidir. Uygulama manifest’lerini Git’te tutarsınız; ama secret’ı Git’e koymak istemezsiniz. Bu ikilemi çözen pratik yaklaşımlardan biri: Git’te şifreli secret tutup, sadece doğru yerde (CI veya cluster) açmaktır.
Bu rehberde SOPS + age ile, sahada “yürüyen” bir secret akışını kuruyoruz: PR süreci bozulmadan, “kopyala‑yapıştır secret” refleksi üretmeden.
0) Ön koşullar ve kararlar
Bu yazı şu varsayımı yapar:
- Secret’lar Git’te şifreli olarak duracak
- Şifre çözme anahtarı Git’te durmayacak
- Açma işlemi yalnızca CI veya cluster içinde, kontrollü şekilde olacak
Neden age?
- Basit, hızlı, dosya bazlı şifreleme akışına uygun
- “Anahtar dosyası” ile operasyonu kolaylaştırır
Not: Cloud KMS ile SOPS kullanmak da güçlüdür. Burada özellikle “hafif ve taşınabilir” bir başlangıç için age anlatıyorum.
1) age key üretimi (ve saklama disiplini)
İlk adım age anahtarını üretmektir:
age-keygen -o age.key
Public key’i çıkartın:
age-keygen -y age.key
Pratik saklama seçenekleri:
- Secret manager (Vault, cloud secret store)
- CI secret store (repository/environment secret’ları)
- HSM/KMS üzerinde “anahtarı sarmalama” (ileri seviye)
2) SOPS konfigürasyonu: .sops.yaml
Repo köküne .sops.yaml koyarak hangi dosyaların nasıl şifreleneceğini standardize edin.
Örnek:
creation_rules:
- path_regex: '.*\\.enc\\.ya?ml$'
age: 'age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Bu sayede ekip “hangi komutla şifreleyeceğiz?” tartışmasından çıkar; dosya isminden akış anlaşılır.
3) Şifreli secret dosyası üretimi
Örnek bir Kubernetes secret manifest’i oluşturun (plaintext):
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
DATABASE_URL: "postgres://user:pass@db:5432/app"
Bunu şifreli hale getirin:
sops -e app-secrets.yaml > app-secrets.enc.yaml
Sonra plaintext dosyayı repo dışına alın veya silin.
4) CI’de “decrypt + apply” yaklaşımı (en sade başlangıç)
GitOps aracı kullanmıyorsanız bile, ilk adım olarak CI’de şifre çözüp uygulayabilirsiniz.
Örnek akış:
- CI,
age.key’i secret store’dan çeker SOPS_AGE_KEY_FILEenv’i ayarlanırsops -dile decrypt edilirkubectl applyyapılır
export SOPS_AGE_KEY_FILE=./age.key
sops -d app-secrets.enc.yaml | kubectl apply -f -
5) GitOps araçlarıyla decrypt (operasyonel notlar)
Cluster içinde decrypt yapacaksanız iki risk sınıfını netleştirin:
- Anahtar nerede duruyor? (controller pod’unda mı, node secret store’da mı?)
- Hangi namespace/servis hangi secret’a erişebiliyor?
Benim sahada hedeflediğim denge:
- “Tek anahtar her şeyi açar” yerine scope’lanmış anahtarlar
- Anahtarı tutan bileşen için:
- node affinity / toleration (kritik node’lar)
- resource limit
- audit log
6) Rotasyon: “bir gün lazım olacak” değil, bugün planlanmalı
Rotasyon planı yoksa, secret yönetimi sürdürülemez olur.
Pratik rotasyon adımları:
- Yeni age key üret
.sops.yamliçine yeni key’i ekle (geçiş dönemi için iki key)- Eski dosyaları yeniden şifrele:
sops -r -i app-secrets.enc.yaml
- CI/cluster anahtar kaynağını güncelle
- Eski key’i kaldır ve erişimini iptal et
7) Runbook: “decrypt çalışmıyor”
En sık görülen nedenler:
SOPS_AGE_KEY_FILEyanlış path- Yanlış public key ile şifrelenmiş dosya
- CI secret store güncellenmiş ama runner cache’li
- Dosyada merge conflict sonrası SOPS metadata bozulmuş
Kontrol listesi:
sops -d file.enc.yamllokal (güvenli ortamda) çalışıyor mu?sops --status file.enc.yamlmetadata sağlıklı mı?- CI’de
age.keygerçekten doğru içerikle geliyor mu? (hash ile doğrulayın, içeriği loglamayın)
Kapanış
SOPS + age; GitOps’ta secret yönetimini “yasaklar ve istisnalar” yerine, tekrar edilebilir ve denetlenebilir bir sürece taşır. Asıl değer şifreleme değil; standardizasyon, rotasyon ve incident anında hızlı teşhis üretmesidir.