Bir GitOps controller’ı — Argo CD, Flux — bir kümede oturur ve birçoğunu reconcile eder. Diğerlerine ulaşmak için her uzak kube-apiserver’a kimlik doğrulaması yapması gerekir. Bunu yapmanın varsayılan yolu bir kimlik bilgisini saklamaktır: kontrol kümesinde bir Kubernetes Secret olarak tutulan bir bearer token, bir client sertifikası, bir kubeconfig. Argo CD’de bunlar argocd.argoproj.io/secret-type: cluster etiketli nesnelerdir.
Dur ve o Secret’ın ne olduğuna bak. O, genelde geniş yetkili, tüm filonuza nişan almış, tek bir yerde atıl duran, uzun ömürlü bir kimlik bilgisidir. Kendi kendine rotate olmaz. Yedeklerinizdedir. Kontrol kümenizde bir saldırganın bulabileceği en değerli tek şeydir, çünkü ona sahip olmak, eriştiği her kümeye sahip olmaktır. Boring architecture disiplininin tamamı, tutmak zorunda olmadığınız yükümlülükleri tutmamakla ilgilidir — ve saklanan filo geneli bir token, sırf kullanım anında taze ve kısa ömürlü bir tane basmak daha fazla iş gibi göründüğü için tuttuğunuz bir yükümlülüktür.
Bu yazı o fazladan işi, sırasıyla yapmakla ilgili. Hedef sıfır statik yetki: controller’ı bir uzak kümeye doğrulayan hiçbir uzun ömürlü kimlik bilgisi asla yazılı kalmaz. Controller’ın sunduğu kimlik talep anında basılır, yaklaşık bir saatte sona erer ve yalnızca bellekte yaşar. Mekanizma, kimlik modeli için SPIFFE ve onu basmak için SPIRE’dır.
Her şeyden önce: bu, bir elin parmaklarıyla sayabileceğiniz üç küme için bir araç değil. SPIRE, import ettiğiniz bir kütüphane değil, işlettiğiniz bir kontrol düzlemidir — tam da yavaş üstlenmeniz gereken türden bir yetenek; tıpkı bölünmeyi hak edene kadar modüler monolitin mikroservisi yendiği gibi. Ağırlığını ancak gerçekten bir filo işlettiğinizde hak eder — çok küme, çok ekip, çalınan bir Secret’ın geçemeyeceği bir denetim gereksinimi. Tek paylaşılan bir küme size yetiyorsa, en ucuz güvenli kimlik bilgisi, basmak için makineyi hiç kurmadığınızdır. Geri kalanı, filoya gerçekten ulaştığınızı varsayar.
Tek kuralı baştan söyleyeyim
Mekanizmadan önce netleştireyim: bir küme sınırını geçen kimlik bilgisi kısa ömürlü olmalı ve asla saklanmamalı. Aşağıdaki her şey bu tek cümlenin hizmetinde. Sonunda kimlik doğrulama yolunda hâlâ bir yere diske yazılmış uzun ömürlü bir token kalıyorsa, geldiğiniz özelliği satın almadan karmaşıklık inşa etmişsinizdir — eksik bir index’ten kaçmak için read replica eklemekle aynı yanlış dönüş.
“Kısa ömürlü ve asla saklanmaz”ın burada kesin bir anlamı var. Kimlik, varsayılan ömrü bir saat olan bir X.509 sertifikası (ya da bir JWT); workload tarafından yerel bir socket’ten, sunacak kendine ait bir token’ı olmadan çekilir ve ömrünün yarısında otomatik rotate edilir. Yarın çalmaya değecek bir sır yok.
SPIFFE ve SPIRE gerçekte ne
SPIFFE bir spesifikasyon; SPIRE ise spesifikasyonun tarif ettiği şeyleri basan referans implementasyon. Dört isim tüm tasarımı taşıyor.
Bir SPIFFE ID bir URI’dir: spiffe://<trust-domain>/<path>, örneğin spiffe://prod.example.org/ns/argocd/sa/application-controller. Otorite kısmı trust domain’dir — tek bir mantıksal güven kökü, tek bir CA. Path workload’u tanımlar. Bir workload’un elinde tuttuğunu kanıtladığı isim budur.
Bir SVID (SPIFFE Verifiable Identity Document) ise kanıttır. İki biçim:
- X.509-SVID — SPIFFE ID’si URI SAN’ında (CN’de değil, ki bu sonra önemli olacak) bulunan bir X.509 sertifikası. Onunla mTLS yaparsın.
- JWT-SVID —
sub’ı SPIFFE ID,aud’unu hedef doğrulayıcıya scope’ladığın imzalı bir JWT. Onu bearer token olarak sunarsın.
Asıl mesele ömürler. SPIRE’ın sunucu konfigürasyonunda varsayılanlar default_x509_svid_ttl: 1h ve default_jwt_svid_ttl: 5m, kendi ca_ttl’i varsayılan 24h olan bir CA tarafından imzalanır. Bunlar bilerek kısadır: sızdırılmış bir SVID bir saat içinde değersizleşir, ki tam da bu yüzden eskiden token sakladığınız yerde onu kullanmak güvenlidir.
“Asla saklanmaz”ı mümkün kılan şey, bir SPIRE agent’ı tarafından yerel bir Unix domain socket üzerinden sunulan Workload API’dir (belgelenmiş varsayılan /tmp/spire-agent/public/api.sock; çoğu kurulum onu /run/spire/sockets/... altında mount eder). Workload o socket’e bağlanır ve SVID’sini ister. Bunu yapmak için hiçbir kimlik bilgisi sunmaz. Agent, çağıran process’i kernel üzerinden inceleyerek tanır — PID’sini, oradan da container’ını, namespace’ini, service account’unu — ve bir dizi selector’a karşı workload attestation yapar. Kimlik, hangi sırrı tuttuğuyla değil, process’in doğrulanabilir biçimde ne olduğuyla kurulur. Saklanacak bir şeyin olmamasının tüm nedeni o tersine çevirme.
Altında SPIRE iki bileşen ve iki attestation’dır:
- spire-server CA’yı tutar, SVID’leri imzalar ve registration entry’leri saklar. CA’sı varsayılan olarak self-signed, ya da bir
UpstreamAuthorityplugin’i aracılığıyla kurumsal PKI’nıza zincirlenmiş bir intermediate’tir — bir kez verdiğiniz ve birlikte yaşadığınız bir karar. - spire-agent her node’da çalışır. Önce node’u sunucuya kanıtlar — node attestation —
k8s_psatgibi bir plugin’le; bu, projeli bir service-account token’ını KubernetesTokenReviewAPI’si üzerinden doğrular. Node’a planlanmış bir paylaşılan sır yoktur. Sonra her yerel process için, ona bir SVID vermeden önce workload attestation yapar.
Bir registration entry, bir SPIFFE ID’yi bir parent’a (node/agent) ve bir selector kümesine (namespace, service account, image) bağlayan sunucu tarafı kayıttır. Politika odur: bu workload, bu şekilde attest edilmiş, bu kimliği alır. Bu entry’lerin yaşam döngüsünü yönetmek gerçek bir iştir ve süregelen operasyonel maliyetin çoğu oradadır.
Federasyon: başka bir kümeden gelen bir SVID’ye güvenmek
Bir trust domain, bir güven köküdür. Çok kümeli bir filo genellikle birden fazla trust domain demektir — küme başına ya da bölge başına bir tane — ve artık A domain’indeki bir workload, B domain’inde basılmış bir SVID’yi doğrulayabilmek zorundadır. İşte bu federasyon’dur ve birimi trust bundle’dır: bir domain’in SVID’lerini doğrulamanızı sağlayan public CA sertifikaları ve JWT imzalama anahtarları (bir JWKS).
Her domain bir bundle endpoint ayağa kaldırır — güncel bundle’ını sunan bir URL, OIDC’nin jwks_uri’sinin SPIFFE karşılığı — ve her peer güncel kalmak için onu poll eder. İki profil var ve fark, tam da bootstrap sorusudur:
https_web— endpoint, public bir CA’dan bir Web-PKI sertifikasıyla cephelenir. Peer onu sıradan public güven deposuyla doğrular, yani bant dışında hiçbir başlangıç bundle’ı değiştirilmesine gerek yoktur. Güven mevcut Web PKI üzerinden bootstrap olur.https_spiffe— endpoint kendi X.509-SVID’siyle kimlik doğrular. Onunla ilk kez konuşmak için peer’in o domain’in başlangıç bundle’ını zaten elinde tutması gerekir. Yani ilk bundle bant dışında gelmek zorundadır, ondan sonra ileriye dönük olarak en son çekilen bundle kullanılır.
Bir registration entry üzerinde federatesWith, bir workload’un kimlik doğrulamasına izin verilen yabancı trust domain’leri listeler; agent sonra o yabancı bundle’ları workload’a Workload API üzerinden teslim eder. Bundle’lar spiffe_refresh_hint aralığında (genelde beş dakika civarı) yenilenir ve yeni bir imzalama anahtarını, kullanmaya başlamadan birkaç yenileme döngüsü önce yayınlarsınız ki rotation indiğinde federe peer’ler onu çoktan öğrenmiş olsun.
“Sıfır statik yetki”nin dürüst şekline dikkat: federasyon bootstrap güven kararını yerini değiştirir, onu silmez. https_spiffe ile bir başlangıç bundle’ını elle gönderirsiniz; https_web ile public CA sistemine yaslanırsınız. Her iki yolda da güven kökü hâlâ bir yerden gelir — ortadan kaldırdığınız şey, uzun ömürlü, geniş kapsamlı, küme başına kimlik bilgisidir; tek seferlik güven çıpası değil. Bu doğru takastır, ama olduğu gibi adlandırın.
apiserver’a bir SVID’yi kabul ettirmek
Niyetin Kubernetes API’siyle buluştuğu ve yanılmanın kolay olduğu yer burası. İki mekanizma var ve bugün yalnızca biri temiz.
JWT-SVID-olarak-OIDC yolu (işleyen). Kubernetes’e harici bir OIDC issuer’a güvenmesi söylenebilir. SPIRE, /.well-known/openid-configuration ve SPIRE’ın JWT-SVID imzalama anahtarlarıyla beslenen bir JWKS sunan bir OIDC Discovery Provider ile gelir. Uzak (spoke) kümenin apiserver’ını o provider’a bir OIDC issuer olarak yöneltirsiniz. Controller, yerel Workload API’sinden taze bir JWT-SVID çeker ve onu bir bearer token olarak sunar; apiserver onu SPIRE’ın JWKS’ine karşı doğrular ve bir subject’e map eder. Secret yok, saklanan token yok — istek başına yeni bir JWT, dakikalar içinde sona eren.
Bu bir düşünce deneyi değil. Red Hat’in OpenShift GitOps’u (4.20+) tam olarak bunu desteklenen bir entegrasyon olarak sevk eder: Argo CD, execProviderConfig aracılığıyla bir client-go ExecCredential plugin’i (apiVersion: client.authentication.k8s.io/v1beta1) kullanır; bu plugin her çağrıda SPIFFE socket’ini (SPIFFE_ENDPOINT_SOCKET) okur, spoke apiserver’ın beklediği audience’la (SPIFFE_JWT_AUDIENCE) bir JWT-SVID ister ve onu teslim eder. Kimlik bilgisi kullanım anında imal edilir ve atılır.
X.509-SVID-ile-mTLS yolu (tuzak). İçgüdü düz mTLS yapmaktır: controller X.509-SVID’sini sunar, apiserver SPIFFE bundle’ına client CA olarak güvenir, bitti. Bitmedi. Kubernetes client-cert kimlik doğrulaması kullanıcı adını sertifikanın Subject CN’inden, grupları Subject O’sundan türetir — ve SPIFFE ID URI SAN’ında yaşar, ki apiserver client-cert kimlik doğrulaması onu okumaz. Bu yüzden ham bir SPIFFE X.509-SVID bir Kubernetes kullanıcısına map olmaz. mTLS kullanmak için apiserver’ın önüne bir proxy — Ghostunnel ya da Envoy — koyarsınız: SPIFFE mTLS’i terminate eder, URI-SAN SPIFFE ID’sini doğrular ve apiserver’ın anladığı bir kimlikle iletir. Ghostunnel kendi rotate olan SVID’leri için Workload API’yi doğrudan tüketir. Bu işler, ama her apiserver’ın önünde bir hareketli parçadır ve OIDC yolu onu hiç gerektirmez.
Olgunluk konusunda dürüst ol. Upstream Argo CD’nin native SPIFFE kimlik doğrulaması yoktur; jenerik ExecCredential plugin örüntüsüyle çalışır ve en olgun ürünleşme OpenShift’inkidir. Flux, SPIRE’ı deploy etmek için GitOps ile yönetilebilir ve OCI registry kimlik doğrulaması bir JWT-SVID tüketebilir, ama “Flux’u bir uzak kümeye kubeconfig yerine SPIFFE ile doğrula” şeklinde birinci sınıf bir özellik belgelenmemiştir — aynı OIDC/exec-credential/proxy tesisatının üzerinden gider. Native uzak-küme SPIFFE kimlik doğrulamasını, etkinleştirdiğiniz bir kutu değil, monte ettiğiniz bir örüntü olarak görün.
Ne kırılır ve maliyeti ne
Mimari diyagramların atladığı kısım bu. Saklanan bir kimlik bilgisini kaldırdınız; karşılığında bir kimlik servisini kritik yolda sıkı bir bağımlılık hâline getirdiniz ve kısa ömürlü şeyler, uzun ömürlülerin kırılmadığı yerlerde kırılır.
- SVID rotation artık bir erişilebilirlik bağımlılığı. SVID’ler kısadır — X.509 bir saat, JWT beş dakika — ve ömürlerinin yarısında rotate olur. Agent ya da Workload API düşükse, ya da agent yenilemek için sunucuya ulaşamıyorsa, SVID sona erer ve kimlik doğrulama durur. SPIRE sunucusu her basım ve yenileme için kritik yoldadır. Agent’lar kimlik bilgilerini cache’ler ve kısa sunucu kesintilerine tolerans gösterir; agent’ın
availability_targetayarı (ayarlanırsa ≥ 24h olmalı) onu erken rotate ettirerek nazik bir kesinti için pay biriktirir. Ama hata modu gerçek ve yeni: kimlik düzlemi düşükse filo kimlik doğrulaması düşüktür. - Saat kayması (clock skew) artık bir kesinti sınıfı. Beş dakikalık bir JWT affetmez. Bir controller ile bir spoke apiserver arasında birkaç dakikalık kayma, geçerli token’ları reddeder. Filo boyunca NTP disiplini, hijyen olmaktan çıkıp sıkı bir gereksinim olur.
- Kararan bir federasyon bundle endpoint’i kümeler-arası kimlik doğrulamasını kırar — sessizce, sonradan. Bir peer’in bundle endpoint’i yenileme penceresini aşacak kadar ulaşılamazsa ve o domain anahtarlarını rotate ederse, cache’lenmiş bundle’ınız bayatlar ve domain’ler-arası SVID doğrulaması başarısız olur, her iki küme tek başına sağlıklı olsa bile. Hata, kesinti anında değil rotation anında ortaya çıkar, ki bu onu teşhis etmesi kötü kılar.
- Kimlik düzlemi artık stateful, HA-kritik bir servis. HA’daki SPIRE sunucuları tek bir SQL datastore paylaşır; varsayılan SQLite tek-node’dur, yani production demek, tüm filonun kimlik doğrulayabilme yeteneğinin bağlı olduğu yüksek erişilebilir bir MySQL/PostgreSQL demektir. Çok küme için bir topoloji seçersiniz — nested SPIRE (bir root sunucunun downstream sunuculara intermediate basması, bir root kesintisinden sağ çıkması) ya da küme başına trust domain’ler arası federasyon — ve her biri kendi operasyonel yüzeyini ekler.
- Operasyonel ağırlık baştan aşağı yeni bir kontrol düzlemi. Her kümede sunucu artı agent’lar; registration-entry yaşam döngüsü (workload başına selector’lar, deployment’larla senkron tutulan); upstream-CA kararı; bundle endpoint’ler ve federasyon ilişkileri; OIDC Discovery Provider; ve mTLS yolunda apiserver başına bir proxy. Cevap “bir kubeconfig sakla” olduğunda bunların hiçbiri yoktu.
Sıra neden önemli
Adımlar bir menü değil; bir dizidir ve onu atlamak, ekiplerin maliyeti faydası olmadan nasıl aldığıdır.
1. SPIRE’ı kur: sunucu + agent’lar, node attestation (temel)
2. Controller’a Workload API üzerinden workload SVID’leri bas
3. Spoke apiserver’a SPIRE’a güvendir (önce OIDC yolu)
4. Kümeler boyunca trust domain’leri federe et
5. Saklanan kubeconfig Secret’larını sil (kazanç)
- adım tüm mesele ve yalnızca 1–4 gerçekten işlediğinde güvenli. Yaygın hata, eski
Secret’ı “yedek olarak” tutmaktır — bu da uzun ömürlü filo geneli kimlik bilgisinin hâlâ atıl, hâlâ yedeklerinizde, hâlâ saldırganın aldığı şey olması ve artık ayrıca SPIRE çalıştırıyor olmanız demektir. Kontrol düzleminin parasını ödediniz ve yükümlülüğü tuttunuz. Ya saklanan kimlik bilgisi yok olur ya da bunu yapmadınız; yarım kredi yoktur.
Tersi hata, sıra fayda verecek hâle gelmeden SPIRE’a uzanmaktır — Secret’ı gayet iyi olacak birkaç küme için federe bir kimlik düzlemi ayağa kaldırmak. Bu, güvenlik rozeti takmış ileride-lazım-olur karmaşıklığıdır: henüz sahip olmadığınız bir patlama yarıçapı için kurulmuş makine. Makineyi haklı çıkaran şey filo, denetim gereksinimi ve çalınan bir token’ın patlama yarıçapıdır. O çizginin altında, en ucuz güvenli kimlik bilgisi gerçekten de hiç basmak zorunda olmadığınız sıkıcı olanıdır.
Bu yaklaşımın gerçekten bittiği yer açık: statik bir küme-başına kimlik bilgisinin kabul edilemez bir patlama yarıçapı olduğu kadar büyük bir filo, stateful, HA, kritik-yolda bir kimlik servisini çalıştıracak ve saatlerini senkron tutacak operasyonel olgunlukla. Oraya varmış bir ekip o olgunluğun parasını zaten başka yerde öder — ve o noktada sıfır statik yetki bir süsleme değil, patlama yarıçapının baştan beri talep ettiği kimlik bilgisi modelidir. Daha önce değil.
İlgili: SPIFFE ve SPIRE belgeleri kimlik modelini ve sunucu/agent bileşenlerini tanımlar; SPIFFE Federation spec’i ise bu tasarımın dayandığı bundle-endpoint profillerini belirtir.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.