Azure Key Vault'tan Python ile Secret Okumak

Selamlar, bu yazımda Python uygulamamızdan Azure Key Vault'a nasıl temiz bir bağlantı kurduğumuza, secret'lari nasıl set/get/list yaptığımıza ve cache tarafında ne yapsak iyi olur sorusuna bakacağız. Konuyu çok dağıtmayalım, ama 'kopyala-yapıştır da çalıştır' havasında da olmasın istiyorum; sebebini anlattığımda bence daha çok işinize yarar. Hadi başlayalım.

Önce şunu söyleyeyim: parolanızı .env dosyasında, koda gömülü olarak ya da Slack'te DM olarak tutmak yerine merkezi bir yerde tutmak istiyorsanız Key Vault tam aradığınız şey. Production'da rotation gerektiğinde tek yerden değiştirip yola devam etmek bayağı rahat, kabul edelim.

Kurulum ve kimlik

İki paket yeter. azure-identity kimlik doğrulamayı, azure-keyvault-secrets ise secret işlerini hallediyor.

pip install azure-identity azure-keyvault-secrets

Kimlik tarafında en sevdiğim parça DefaultAzureCredential. Şöyle ki, lokalde az login ile Azure CLI'ya girdiyseniz onu kullanır; container içindeyseniz environment variable'lardan service principal'i alır; AKS veya App Service'te managed identity'yi otomatik bulur. Yani kodu hiç değiştirmeden her ortamda aynı credential nesnesi çalışır. AI havasında 'modern kimlik doğrulama yaklaşımı' demeden söyleyeyim: bu sayede 'lokalde mi, prod'da mı?' diye if/else yazmak zorunda kalmıyorsunuz.

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
vault_url = 'https://payment-vault.vault.azure.net/'
client = SecretClient(vault_url=vault_url, credential=credential)

RBAC mi access policies mi?

Eski Key Vault'larda yetkilendirme 'access policies' ile yapılırdı. Yeni vault oluştururken --enable-rbac-authorization true bayrağını verip RBAC moduna geçmenizi şiddetle tavsiye ederim. Bence access policies artık miras kalmış bir mekanizma; başka her yerde RBAC kullanırken Key Vault'ta ayrı bir model tutmanın anlamı yok.

Uygulamanız sadece okuyacaksa Key Vault Secrets User rolü yeter; yazma da yapacaksa Key Vault Secrets Officer lazım. Least privilege bu işin kalbi - prod uygulamasına Officer vermek gibi bir alışkanlık edinmeyin.

Secret okumak ve yazmak

API gerçekten sade. Bir örnek:

client.set_secret('database-password', 'S3cur3P@ssw0rd!')

secret = client.get_secret('database-password')
print(secret.value)
print(secret.properties.created_on)

set_secret'a tags, content_type ve expires_on da geçebilirsiniz. Etiketleri 'environment' ve 'service' gibi alanlarla doldurmak ileride hangi secret'in nereye ait olduğunu hatırlamak için altın değerinde. Expiration ise rotation'ı zorlayan bir baskı; vermek size huzur verir.

Listelemeyi yaparken Key Vault size değerleri vermez, sadece property'leri verir. Bir tasarım kararı bu, ve doğru bir karar. Değer için her secret'i ayrıca get_secret ile çekmeniz gerekir.

for prop in client.list_properties_of_secrets():
    print(prop.name, prop.tags)

Cache stratejisi

Burası kritik. Her HTTP request'te Key Vault'a gitmek hem yavaş hem de pahalı; rate limit'e takılırsanız bütün uygulama kilitlenir. O yüzden secret'leri uygulama açılışında bir kere okuyup hafızada tutmak en sade çözüm.

class SecretsCache:
    def __init__(self, vault_url: str):
        self._client = SecretClient(
            vault_url=vault_url,
            credential=DefaultAzureCredential(),
        )
        self._store: dict[str, str] = {}

    def get(self, name: str) -> str:
        if name not in self._store:
            self._store[name] = self._client.get_secret(name).value
        return self._store[name]

Bu kadar yeter mi? Çoğu uygulama için evet. Ama secret rotation sırasında uygulamayı yeniden başlatmadan yeni değeri almak istiyorsanız 5-15 dakikalık bir TTL ekleyin. Sahte deneyim uydurmadan söyleyeyim: TTL'siz cache rotation günü kâbus olur, çünkü değişimi pod restart gelene kadar görmüyorsunuz.

Soft delete ve sürüm

Key Vault sildiğiniz secret'i hemen yok etmiyor; varsayılan 90 günlük 'soft delete' süresi var. Yanlışlıkla sildiğinizde begin_recover_deleted_secret ile geri çağırabilirsiniz. Sürümler de otomatik tutuluyor; her set_secret yeni bir version yaratıyor, eskisi denetim için yerinde duruyor. Bu iki özelliği kapatmayın, prod'da hayat kurtarıyor.

Sık karşılaşılan tuzaklar

  • DefaultAzureCredential timeout'u: VM'de managed identity etkin değilse credential sırayla bütün metodları deneyip yavaş açılışa neden olur. Ortama göre ManagedIdentityCredential ya da EnvironmentCredential ile daraltın.
  • Cache'i sonsuz tutmak: Rotation yaptığınızda uygulama eski değeri kullanmaya devam eder. TTL ekleyin ya da rotation sonrası restart akışınız olsun.
  • Vault URL'inde tipo: vault.azure.net ile vault.azure.com farkı kullanıcıyı saatlerce ararken bulur. URL'i config'ten okuyun, koda gömmeyin.
  • Listing'de değer beklemek: list_properties_of_secrets değer dönmez. Her birini ayrıca get_secret ile çekin.

Kapanış

Secret yönetimini erken halletmek sonradan retrofit etmekten her zaman kolay. Bence Key Vault + DefaultAzureCredential ikilisi Python tarafında en az sürtünmeli kombinasyon; üstüne küçük bir cache koyduğunuzda hem hızlı hem güvenli oluyorsunuz. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.