Animowane kompendium · mikroserwisy w produkcji

Kto, gdzie i z jakim hasłem? Discovery · Config · Secrets

Jak duże systemy produkcyjne odpowiadają na trzy pytania każdego serwisu: gdzie są inni, jak mam być skonfigurowany i skąd wziąć sekret. Bez wybierania „zwycięzcy” — pokazujemy opcje, między którymi naprawdę wybierają duże firmy, i ich trade-offy. Przewiń — diagramy uruchamiają się same.

każda opcja: flow + dlaczego bezpieczne 🧠
przewiń ↓

Jak czytać to kompendium

opcje, nie ranking

To nie jest lista „najlepszych narzędzi”. Każda opcja tutaj działa w produkcji u kogoś dużego. Różnią się tym, co poświęcają: spójność za dostępność, prostotę za elastyczność, wygodę za mniejszą powierzchnię ataku. Twoje zadanie: rozpoznać, który trade-off pasuje do Twojego systemu.

Każda opcja ma ten sam układ: flow (klikalny diagram sekwencji — kto do kogo i po co), box 🔒 Dlaczego to bezpieczne (uwierzytelnienie, autoryzacja, transport, co się dzieje przy awarii) i tabelę trade-offów. Poziomy: L1 podstawy, L2 praktyk, L3 ekspert. Przełącznik głębokości jest w lewym dolnym rogu.

🧠 Wysil szare komórki

Autoskaler startuje nowego poda o 3:12 w nocy. Po kilku sekundach pod obsługuje ruch, zna adres bazy, ma właściwe limity i loguje się do bazy hasłem, którego nie zna żaden człowiek. Skąd on to wszystko wie — i dlaczego atakujący, który ukradnie obraz kontenera, nie wie nic? Odpowiedź na końcu.

Trzy pytania każdego serwisu

gdzie są inni · jak mam działać · czym się uwierzytelnić

Monolit miał jeden plik konfiguracyjny i jedną bazę. 300 mikroserwisów w kilku regionach ma inne życie: instancje rodzą się i umierają co chwilę (autoskaling, deploy, awaria węzła), a każda dostaje nowy adres IP. Plik z listą adresów jest nieaktualny, zanim go zapiszesz.

Dlatego każdy serwis w dużym systemie potrzebuje trzech mechanizmów:

PytanieMechanizmJak często zmienia się odpowiedź
Gdzie jest serwis orders?Service discoverySekundy (instancje się zmieniają)
Jaki mam timeout, limit, flagę?KonfiguracjaMinuty–dni (zmiany ludzi)
Jakim hasłem/kluczem/certyfikatem się uwierzytelnić?Sekrety i tożsamośćGodziny–dni (rotacja), idealnie: krótko żyjące
▶ Dlaczego „plik z adresami” umiera zahardkodowany IP kontra autoskaling

▶ Pierwszy dzień

  1. zapisz 10.0.1.7Ktoś wpisuje adres serwisu B do pliku konfiguracyjnego serwisu A.
  2. czytaj przy starcieSerwis A czyta plik raz, przy starcie.
  3. GET /ordersDziała — B rzeczywiście jest pod 10.0.1.7.

💥 Autoskaling w nocy

  1. węzeł znikaWęzeł z instancją B pada. Orkiestrator uruchamia B gdzie indziej.
  2. B pod 10.0.9.3Nowa instancja B dostaje nowy adres. Plik o tym nie wie.
  3. GET /ordersA dalej strzela w stary adres: timeout. Ktoś musi ręcznie zmienić plik i zrestartować A.
  4. potrzebny rejestrWniosek: adres trzeba odkrywać w chwili wywołania, z miejsca, które śledzi żywe instancje. To jest service discovery.
Uproszczenie: adresy IP są ilustracyjne. W prawdziwym systemie ten sam problem dotyczy też konfiguracji (zmiana wymaga restartu) i sekretów (hasło wpisane w plik).
📌 Celne spostrzeżenia
  • Adresy, konfiguracja i sekrety zmieniają się w różnym tempie — dlatego zwykle obsługują je różne systemy.
  • Dobre rozwiązanie odpowiada w chwili użycia, a nie w chwili budowania obrazu.
Część I

Service discovery

Od DNS, przez rejestry CP i AP, Kubernetes i service mesh, po usługi chmurowe i rozwiązania hyperscalerów.

Dwa wzorce odkrywania, dwa wzorce rejestracji

kto pyta rejestr i kto go aktualizuje

Każde narzędzie z tej części to kombinacja dwóch decyzji. Pierwsza: kto pyta rejestr. W client-side discovery klient sam pobiera listę instancji i sam wybiera jedną (load balancing w kliencie). W server-side discovery klient wysyła żądanie do routera lub load balancera, a ten pyta rejestr i przekazuje ruch.

Druga: kto aktualizuje rejestr. W self-registration instancja sama się rejestruje i wysyła heartbeat. W third-party registration robi to zewnętrzny komponent, który obserwuje środowisko (np. orkiestrator). Te nazwy wzorców spopularyzował katalog microservices.io Chrisa Richardsona.

▶ Client-side vs server-side ten sam cel, inne miejsce decyzji

▶ Client-side

  1. rejestruję sięInstancja (albo platforma za nią) wpisuje swój adres do rejestru.
  2. lista instancji?Klient pyta rejestr o instancje serwisu i trzyma wynik w cache.
  3. wybór instancjiKlient sam wybiera instancję (round robin, least requests, strefa). Logika jest w bibliotece lub sidecarze.
  4. żądanie bezpośrednioRuch idzie prosto do instancji — bez dodatkowego hopu.

▶ Server-side

  1. rejestracjaInstancja trafia do rejestru (najczęściej robi to platforma: third-party registration).
  2. żądanie na stały adresKlient zna tylko jeden stabilny adres (DNS load balancera lub wirtualny IP).
  3. kto żyje?Load balancer zna zdrowe instancje z rejestru lub własnych health checków.
  4. przekazanieLoad balancer przekazuje żądanie. Klient może być „głupi” i w dowolnym języku.
Uproszczenie: w praktyce rejestr i load balancer bywają jednym systemem (np. Kubernetes Service), a „klient” bywa sidecarem obok aplikacji.
WzorzecZyskujeszPłacisz
Client-sidebrak hopu, sprytny load balancing (strefy, retry)biblioteka per język albo sidecar; klient musi znać rejestr
Server-sideklient prosty, niezależny od językadodatkowy hop i komponent do skalowania
Self-registrationprosto, instancja wie, kiedy jest gotowakod rejestracji w każdym serwisie; martwa instancja nie wyrejestruje się sama
Third-party registrationserwisy nic nie wiedzą o rejestrzepotrzebny „registrar” (orkiestrator, agent)
🔒 Dlaczego to bezpieczne (lub nie)

Sam wzorzec niczego nie zabezpiecza. Rejestr mówi tylko, gdzie jest serwis, a nie, czy to naprawdę on. Dlatego duże systemy łączą discovery z tożsamością: TLS z weryfikacją certyfikatu (najlepiej mTLS), żeby podstawiony wpis w rejestrze nie przekierował ruchu do atakującego. Zapis do rejestru też wymaga autoryzacji — inaczej każdy może „zarejestrować” swój serwer jako payments.

📌 Celne spostrzeżenia
  • Pytanie 1: czy decyzję o instancji podejmuje klient, czy pośrednik?
  • Pytanie 2: kto pilnuje, żeby rejestr znał tylko żywe instancje?
  • Discovery bez weryfikacji tożsamości to zaproszenie do podszycia się.

DNS: najstarszy rejestr świata

uniwersalny, ale z cache, którego nie kontrolujesz

Najprostsze discovery: nazwa → adres. Rekord A/AAAA daje adresy IP, a rekord SRV (RFC 2782) daje też port, priorytet i wagę. Każdy język i każda biblioteka HTTP umie DNS — to jego supermoc. Słabość: odpowiedź ma TTL i jest cache'owana po drodze (resolver, system, biblioteka, czasem sama aplikacja), więc po śmierci instancji klienci jeszcze przez chwilę dostają stary adres.

▶ Rozwiązanie nazwy i problem TTL dlaczego martwa instancja „żyje” w DNS

▶ Normalnie

  1. A? ordersKlient pyta lokalny resolver o orders.internal.
  2. brak w cacheResolver nie ma odpowiedzi, więc pyta serwer autorytatywny.
  3. A, B · TTL 30 sSerwer zwraca adresy A i B z TTL. Resolver trzyma je w cache przez TTL.
  4. połączenieKlient wybiera adres (zwykle pierwszy z listy) i łączy się.

💥 Instancja A pada

  1. A umieraInstancja A znika. Serwer DNS wkrótce usuwa jej rekord (jeśli ma health check).
  2. A? ordersResolver ma jeszcze ważną odpowiedź w cache: dalej zwraca A i B.
  3. timeoutKlient łączy się z martwym A. Do wygaśnięcia TTL część ruchu trafia w próżnię — chyba że klient ma retry na inny adres.
  4. po TTL tylko BPo wygaśnięciu cache resolver pobiera świeżą odpowiedź. Krótszy TTL skraca okno, ale zwiększa liczbę zapytań.
Uproszczenie: TTL 30 s jest ilustracyjny. Pomijamy cache w systemie operacyjnym i w bibliotekach, które potrafią ignorować TTL.
🔒 Dlaczego to bezpieczne (lub nie)

Klasyczny DNS nie uwierzytelnia odpowiedzi — spoofing i zatrucie cache to znane ataki. W sieci wewnętrznej ryzyko ogranicza się prywatnymi strefami (np. Route 53 private hosted zones, CoreDNS w klastrze) i kontrolą tego, kto może zmieniać rekordy. Właściwa ochrona jest jednak warstwę wyżej: TLS z weryfikacją nazwy w certyfikacie. Nawet jeśli DNS skieruje Cię pod zły adres, serwer atakującego nie pokaże ważnego certyfikatu orders.internal.

⚠️ Uwaga! Cache w JVM

Java ma własny cache DNS sterowany właściwością networkaddress.cache.ttl. Gdy jest zainstalowany security manager, domyślnie wyniki są cache'owane na zawsze; bez niego czas zależy od implementacji (w OpenJDK 30 s). Ustaw tę wartość świadomie, jeśli Twoje discovery opiera się na DNS.

📌 Celne spostrzeżenia
  • DNS działa wszędzie, ale „wie” o zmianie dopiero po TTL i po wszystkich cache'ach.
  • SRV niesie port; A — tylko adres.
  • Prawdziwe zabezpieczenie: TLS i weryfikacja certyfikatu, nie sam DNS.

Rejestry CP: ZooKeeper, etcd, Consul

jedna prawda, uzgodniona przez większość

Te trzy systemy trzymają rejestr w klastrze kilku serwerów, które uzgadniają każdą zmianę przez większość (quorum). ZooKeeper używa protokołu ZAB, a etcd i serwery Consula — Rafta. Skutek w języku CAP: przy podziale sieci strona bez większości nie przyjmie zapisu. Wybierasz spójność kosztem dostępności zapisu.

Kluczowy trik to wpis, który umiera razem z instancją: w ZooKeeperze ephemeral znode (znika, gdy kończy się sesja klienta), w etcd klucz przypięty do lease z TTL (znika, gdy nikt nie odnowi lease), w Consulu wpis z health checkiem wykonywanym przez lokalnego agenta. Klienci nie odpytują rejestru w kółko — zakładają watch i dostają powiadomienie o zmianie.

▶ Lease, watch i podział sieci wzorzec etcd; ZooKeeper i Consul działają analogicznie

▶ Rejestracja i śmierć

  1. grant lease TTL 10 sInstancja uwierzytelnia się (mTLS lub token) i dostaje lease z TTL.
  2. put klucz + leaseInstancja zapisuje swój adres pod kluczem przypiętym do lease.
  3. replikacjaLider zatwierdza zapis dopiero, gdy potwierdzi go większość węzłów.
  4. event PUTKlient z watchem na prefiksie /services/orders/ dostaje nowy adres.
  5. instancja zamarzaInstancja przestaje odnawiać lease (keep-alive).
  6. event DELETEPo TTL lease wygasa, a klucz znika. Klient dostaje DELETE i usuwa adres z puli.

💥 Podział sieci

  1. partycjaLider zostaje w mniejszości (np. 1 z 3 węzłów).
  2. put → błądLider w mniejszości nie zbierze quorum, więc zapis nie przechodzi. Nowa instancja nie zarejestruje się po tej stronie.
  3. nowy liderWiększość wybiera nowego lidera i dalej przyjmuje zapisy.
  4. ostatni znany stanKlienci mają w pamięci ostatnią listę. Dobra biblioteka używa jej dalej, zamiast zerować pulę.
Uproszczenie: TTL 10 s i 3 węzły są ilustracyjne. Pomijamy różnice: ZooKeeper wiąże wpis z sesją, a nie z lease; Consul dodaje agenty i gossip (blok „Głębiej” niżej).
SystemModel i APIKiedy wybierany
ZooKeeperdrzewo znode'ów, ephemeral/sequential nodes, watch; ZABekosystem Hadoop/Kafka (starsze wersje Kafki), historyczne systemy JVM (np. Finagle ServerSets)
etcdpłaski keyspace, lease, watch na prefiks, gRPC; Raftbaza stanu Kubernetesa; własne rejestry, gdy masz już etcd
Consulkatalog serwisów, health checki, DNS + HTTP API, KV, mesh; Raft + gossipwiele platform naraz (VM + Kubernetes + chmury), gotowy interfejs DNS
🔒 Dlaczego to bezpieczne

Wszystkie trzy wspierają TLS między klientem a serwerem i między węzłami. etcd umie uwierzytelniać klientów certyfikatem i ma RBAC na zakresy kluczy. Consul ma system ACL z tokenami i politykami (kto może rejestrować który serwis, kto może czytać), a ruch gossip może szyfrować współdzielonym kluczem (gossip encryption). ZooKeeper ma ACL na znode'ach i uwierzytelnianie SASL. Bezpieczeństwo awarii: wpis z lease/sesją sam znika, więc martwa instancja nie zostaje w rejestrze na zawsze.

⚠️ Uwaga!

Rejestr CP to krytyczna zależność. Jeśli klient zeruje pulę, gdy nie może połączyć się z rejestrem, awaria rejestru staje się awarią całej firmy. Dobre biblioteki trzymają ostatni znany stan i działają dalej.

🔬 Głębiej: quorum, gossip w Consulu, licencje

Quorum. Klaster z n węzłami potrzebuje większości: ⌊n/2⌋+1. Trzy węzły przeżyją awarię jednego, pięć — dwóch. Dokumentacja Consula zaleca 3 lub 5 serwerów. Parzysta liczba nie zwiększa odporności: cztery węzły też przeżyją awarię tylko jednego.

Consul = Raft + gossip. Tylko serwery Consula uczestniczą w Rafcie. Na każdym węźle działa też agent klienta; agenty tworzą sieć gossip (biblioteka Serf), która wykrywa awarie węzłów bez obciążania serwerów. Health checki uruchamia lokalny agent, więc rejestr skaluje się na tysiące węzłów.

Licencje. W sierpniu 2023 HashiCorp zmienił licencję Consula, Vaulta i Terraforma z MPL 2.0 na Business Source License. W 2025 HashiCorp przejął IBM. Dla części firm to argument za etcd lub forkami (np. OpenBao dla Vaulta).

📌 Celne spostrzeżenia
  • CP: przy podziale sieci mniejszość nie przyjmuje zapisów.
  • Wpis żyje tak długo jak sesja/lease/health check — martwa instancja znika sama.
  • Watch zamiast odpytywania; klient powinien przetrwać niedostępność rejestru.

Rejestr AP: Netflix Eureka

lepiej stara lista niż żadna

Netflix zbudował Eurekę dla AWS z odwrotnym założeniem: przy podziale sieci rejestr ma dalej odpowiadać, nawet jeśli część danych jest nieaktualna. Serwery Eureki replikują się między sobą (peer-to-peer), a klient trzyma lokalną kopię rejestru. Eureka jest dziś popularna głównie w ekosystemie Spring Cloud Netflix.

Domyślne liczby z dokumentacji: klient wysyła heartbeat co 30 s, serwer usuwa instancję po 90 s bez odnowienia, a klient pobiera zmiany rejestru co 30 s. Mechanizm self-preservation: jeśli nagle przestaje przychodzić dużo heartbeatów (poniżej progu, domyślnie 85% oczekiwanych), serwer zakłada problem z siecią, a nie masową śmierć instancji, i przestaje usuwać wpisy.

▶ Heartbeat, replikacja i self-preservation dostępność ponad spójność

▶ Normalnie

  1. registerInstancja rejestruje się w jednym z serwerów (self-registration przez bibliotekę klienta).
  2. replikacja asyncSerwer przekazuje wpis do pozostałych serwerów. Do zakończenia replikacji serwery mogą widzieć różne stany.
  3. heartbeat co 30 sInstancja odnawia lease.
  4. fetch delta co 30 sKlient pobiera zmiany z dowolnego serwera i zapisuje je w lokalnym cache.
  5. wywołanie z cacheKlient wybiera instancję z lokalnej kopii — nawet gdy Eureka chwilowo nie odpowiada.

💥 Podział sieci

  1. heartbeaty ginąPołowa heartbeatów nie dociera. Wygląda, jakby połowa floty umarła.
  2. self-preservationLiczba odnowień spadła poniżej progu, więc serwer wstrzymuje usuwanie wpisów.
  3. część wpisów martwaKlient może trafić na naprawdę martwą instancję — dlatego potrzebuje timeoutów i retry na inną instancję.
  4. ale ruch płynieAlternatywa (wyrzucić połowę floty z rejestru) byłaby gorsza: zdrowe instancje straciłyby ruch.
Liczby 30 s / 90 s / 85% to domyślne wartości z dokumentacji Eureki i Spring Cloud Netflix; można je zmienić. Pomijamy regiony i strefy.
🔒 Dlaczego to bezpieczne (lub nie)

Eureka domyślnie nie wymaga uwierzytelnienia — w Spring Cloud zabezpiecza się ją typowo Spring Security (np. HTTP Basic) i TLS, a dostęp sieciowy ogranicza do sieci prywatnej. Odporność na awarie jest tu ważniejsza niż spójność: lokalny cache klienta oznacza, że awaria całego klastra Eureki nie zatrzymuje ruchu. Ceną jest konieczność obrony przed martwymi wpisami: timeouty, retry, circuit breaker.

Przy podziale sieciCP (etcd, ZK, Consul servers)AP (Eureka)
Zapistylko po stronie większościwszędzie
Odczytspójny (lub ostatni znany stan w kliencie)może być nieaktualny
Ryzykonowe instancje niewidoczneruch do martwych instancji
📌 Celne spostrzeżenia
  • Discovery to dane, które i tak szybko się starzeją — dlatego AP bywa rozsądnym wyborem.
  • Self-preservation chroni przed „wyrzuceniem” zdrowej floty przy problemie sieci.
  • AP wymaga od klienta timeoutów i retry.

Kubernetes: discovery wbudowane w platformę

Service, EndpointSlice, kube-proxy, CoreDNS

W Kubernetesie nie budujesz rejestru — platforma jest rejestrem (third-party registration). Service wybiera pody po etykietach i dostaje stabilny wirtualny adres (ClusterIP) oraz nazwę DNS <service>.<namespace>.svc.cluster.local. Kontroler zapisuje adresy pasujących podów w obiektach EndpointSlice. kube-proxy na każdym węźle zamienia to na reguły jądra (iptables, IPVS lub nftables), które przekierowują ruch z ClusterIP do poda. To server-side discovery bez dodatkowego hopu sieciowego.

▶ Od gotowego poda do ruchu i co się dzieje, gdy readiness przestaje przechodzić

▶ Pod staje się gotowy

  1. pod ReadyKubelet raportuje, że readiness probe przechodzi.
  2. EndpointSlice += podKontroler dopisuje adres poda (ready) do EndpointSlice serwisu.
  3. watch EndpointSlicekube-proxy na każdym węźle obserwuje zmiany i aktualizuje reguły jądra.
  4. DNS → ClusterIPKlient rozwiązuje nazwę serwisu przez CoreDNS i dostaje stały ClusterIP.
  5. ruch → podReguły jądra na węźle klienta zamieniają ClusterIP na adres jednego z gotowych podów.

💥 Readiness pada

  1. pod NotReadyReadiness probe przestaje przechodzić (np. pod jest przeciążony).
  2. endpoint ready=falseKontroler oznacza endpoint jako niegotowy. ClusterIP i DNS się nie zmieniają.
  3. reguły bez podakube-proxy usuwa poda z puli. Nowe połączenia omijają go; pod nie jest restartowany (to robi liveness).
Uproszczenie: pomijamy EndpointSlice'y dzielone na części (domyślnie do 100 endpointów na slice), routing topologiczny i implementacje CNI zastępujące kube-proxy (np. eBPF w Cilium).
TypCo dajeKiedy
ClusterIPwirtualny IP + DNS, load balancing w jądrzedomyślnie, ruch wewnątrz klastra
Headless (clusterIP: None)DNS zwraca adresy podów bezpośrednioclient-side load balancing, StatefulSety (stała nazwa każdego poda)
ExternalNamealias CNAME na nazwę spoza klastraprzejściowe wskazanie zewnętrznej usługi
LoadBalancer / Gatewaywejście spoza klastraruch od klientów zewnętrznych
🔒 Dlaczego to bezpieczne

Rejestr aktualizuje tylko control plane: kubelet i kontrolery uwierzytelniają się w API serverze certyfikatami i działają w granicach RBAC. Zwykły pod nie może dopisać się do cudzego serwisu bez uprawnień do zmiany obiektów w API. Ale: domyślnie każdy pod może łączyć się z każdym podem. Ruch ogranicza NetworkPolicy (o ile CNI ją wspiera), a tożsamość i szyfrowanie daje dopiero mTLS (service mesh).

❓ Nie ma głupich pytań
P:
Czy gRPC przez ClusterIP rozkłada ruch na wszystkie pody?
O:
Nie zawsze. kube-proxy balansuje połączenia, a gRPC trzyma jedno długie połączenie HTTP/2. Wszystkie żądania mogą trafić do jednego poda. Typowe rozwiązania: headless Service + load balancing w kliencie albo service mesh, który balansuje pojedyncze żądania.
📌 Celne spostrzeżenia
  • Kubernetes to third-party registration: o rejestrze decyduje readiness, nie kod aplikacji.
  • ClusterIP jest stały; zmienia się tylko lista endpointów za nim.
  • Brak tożsamości i szyfrowania między podami „z pudełka” — to dodaje mesh.

Service mesh: discovery + tożsamość w proxy

Envoy, xDS, Istio, Linkerd

Service mesh przenosi client-side discovery z biblioteki do proxy obok aplikacji. Aplikacja wysyła zwykłe żądanie na orders, a proxy wybiera instancję, robi retry, timeouty i mTLS. Proxy dostaje wiedzę z control plane, który obserwuje rejestr (np. API Kubernetesa). Proxy Envoy powstało w Lyfcie i zostało udostępnione jako open source. Protokół, którym control plane karmi Envoya, to xDS; używa go m.in. Istio (control plane istiod) i gRPC w trybie proxyless. Linkerd ma własne proxy napisane w Ruście.

▶ Push konfiguracji i wywołanie mTLS przykład: Istio w trybie sidecar

▶ Normalnie

  1. watch Service/EndpointSliceControl plane obserwuje serwisy i endpointy.
  2. CSR + token SAAgent obok proxy A tworzy klucz prywatny i wysyła CSR z poświadczeniami (w Kubernetesie: token konta usługi). istiod weryfikuje poświadczenia i podpisuje krótko żyjący certyfikat z tożsamością SPIFFE.
  3. xDS: CDS/EDS/LDS/RDSControl plane przesyła strumieniem gRPC klastry, endpointy, listenery i trasy.
  4. mTLS handshakeProxy A i B wymieniają certyfikaty i sprawdzają nawzajem tożsamość.
  5. AuthorizationPolicyProxy B sprawdza politykę: czy tożsamość A może wołać tę ścieżkę.
  6. localhostDopiero teraz żądanie trafia do aplikacji B.

💥 Control plane padł

  1. istiod niedostępnyControl plane nie działa.
  2. brak nowych zmianDokumentacja Istio: proxy niepołączone z istiod nie dostaje żadnej nowej konfiguracji. Certyfikaty odnawia agent okresowo u CA (domyślnie istiod), więc długa awaria grozi ich wygaśnięciem.
Uproszczenie: pomijamy tryb ambient i to, że xDS może przesyłać też sekrety (SDS). Kolejność kroków 2–3 bywa inna.
🔒 Dlaczego to bezpieczne

Mesh daje to, czego brakuje DNS i rejestrom: kryptograficzną tożsamość serwisu. Każde proxy ma krótko żyjący certyfikat X.509 z tożsamością w formacie SPIFFE (w Istio: spiffe://<trust-domain>/ns/<ns>/sa/<service-account>), który jest automatycznie rotowany. Po obu stronach jest mTLS, więc podsłuch i podszycie się wymagają kradzieży klucza. Autoryzacja (kto może wołać kogo) jest w politykach, a nie w adresach IP. Linkerd włącza mTLS między podami w meshu domyślnie. Istio domyślnie działa w trybie PERMISSIVE (przyjmuje mTLS i plaintext) — tryb STRICT trzeba włączyć polityką PeerAuthentication.

OpcjaCoKiedy
Istio (sidecar)Envoy przy każdym podzie, bogate L7potrzebujesz pełnej kontroli ruchu i polityk
Istio ambientbez sidecarów: ztunnel na węźle (L4, mTLS), waypoint proxy dla L7chcesz mTLS taniej, bez wstrzykiwania sidecarów
Linkerdlekkie proxy w Ruście, mTLS domyślnieprostota i niski narzut ważniejsze niż mnogość funkcji
gRPC proxyless (xDS)biblioteka gRPC sama mówi xDSflota gRPC, chcesz uniknąć hopu przez proxy
Consul service meshEnvoy + katalog Consula, także poza Kubernetesemmieszane środowisko VM + K8s
🔬 Głębiej: typy xDS i dlaczego to standard

xDS to rodzina API: LDS (listenery — na czym proxy słucha), RDS (trasy HTTP), CDS (klastry — grupy upstreamów), EDS (endpointy w klastrze), SDS (sekrety: certyfikaty i klucze). ADS przesyła wszystkie typy jednym strumieniem, co pozwala zachować kolejność aktualizacji. Wersja „delta” wysyła tylko zmiany. Ponieważ API jest otwarte, z xDS korzystają różne control plane'y, a w Google Cloud usługa Cloud Service Mesh (dawniej Traffic Director) karmi xDS zarówno Envoye, jak i proxyless gRPC.

⚠️ Uwaga!

Mesh to nowy krytyczny system z własnym cyklem aktualizacji, narzutem CPU/pamięci na proxy i dodatkową latencją. Duże firmy wdrażają go stopniowo (namespace po namespace) i zaczynają od trybu permissive, żeby nie odciąć ruchu spoza meshu.

📌 Celne spostrzeżenia
  • Mesh = client-side discovery w proxy + mTLS + polityki.
  • xDS to otwarty protokół control plane → proxy (Envoy, gRPC).
  • Awaria control plane zatrzymuje dystrybucję zmian i odnawianie certyfikatów.

Discovery jako usługa chmurowa

AWS Cloud Map, ECS Service Connect, VPC Lattice, GCP, Azure

Firmy z ciężarem w jednej chmurze często nie utrzymują własnego rejestru. W AWS Cloud Map to rejestr nazw serwisów: instancje rejestrujesz przez API (robi to np. ECS), a odkrywasz przez DNS (Route 53) lub wywołanie API DiscoverInstances. ECS Service Connect buduje na Cloud Map i dokłada proxy przy każdym zadaniu ECS. VPC Lattice idzie dalej: to zarządzana sieć aplikacyjna między VPC i kontami, z politykami autoryzacji opartymi na IAM. AWS App Mesh (mesh na Envoyu) został wycofany: AWS zakończył jego wsparcie 30 września 2026 r. i wskazuje migrację do ECS Service Connect; dla ruchu między VPC i kontami alternatywą jest VPC Lattice.

▶ Wywołanie przez VPC Lattice z IAM discovery, routing i autoryzacja w jednym miejscu

▶ Normalnie

  1. DNS nazwy serwisuA rozwiązuje nazwę DNS serwisu Lattice z sieci VPC powiązanej z siecią serwisów (service network).
  2. żądanie z SigV4A podpisuje żądanie swoimi tymczasowymi poświadczeniami roli IAM (Signature Version 4).
  3. ewaluacja politykiLattice sprawdza auth policy: czy ten principal może wywołać tę usługę i ścieżkę.
  4. routing do celuLattice kieruje żądanie do zdrowego celu (instancja, kontener, Lambda).

💥 Obcy principal

  1. żądanie bez uprawnieńInny serwis (inna rola) próbuje wołać B.
  2. denyPolityka nie zezwala na ten principal. Lattice odrzuca żądanie, zanim dotrze do B.
Uproszczenie: pomijamy nasłuchiwacze, reguły i grupy docelowe. Lattice wspiera też tryb bez autoryzacji IAM (auth type NONE).
UsługaCoKiedy
AWS Cloud Maprejestr nazw: DNS lub API, opcjonalne health checkidiscovery dla ECS/EC2/zasobów poza K8s
ECS Service Connectdiscovery + proxy dla ECS, metryki, retryflota w ECS
Amazon VPC Latticesieć serwisów między VPC/kontami, auth IAMwiele kont i VPC, różne platformy compute
Google Service Directoryrejestr serwisów z DNS i APIdiscovery w GCP i hybrydowo
Google Cloud Service Meshzarządzany control plane xDS (Envoy / proxyless gRPC)mesh bez utrzymywania istiod
Azure Container Appswbudowane DNS-owe discovery między aplikacjami środowiskaserverless kontenery w Azure
🔒 Dlaczego to bezpieczne

Zarządzane discovery dziedziczy model IAM chmury: rejestrować i wywoływać może tylko principal z uprawnieniami, a poświadczenia są tymczasowe (role, a nie klucze). W VPC Lattice autoryzacja dzieje się przy każdym żądaniu na podstawie podpisanej tożsamości, a nie adresu IP. Trade-off: tożsamość i polityki są specyficzne dla jednej chmury — przy multi-cloud potrzebujesz warstwy niezależnej (mesh, SPIFFE).

📌 Celne spostrzeżenia
  • Chmura oferuje rejestr (Cloud Map, Service Directory) i sieć aplikacyjną (Lattice, Cloud Service Mesh).
  • Zysk: brak własnego control plane; koszt: zamknięcie w jednej chmurze.
  • App Mesh nie jest już opcją — AWS zakończył jego wsparcie.

Jak robią to hyperscalerzy

Google, Twitter, Airbnb, Lyft — z publicznych źródeł

Najwięksi zbudowali discovery, zanim istniały gotowe narzędzia. Ich rozwiązania są ciekawe, bo pokazują te same decyzje w skali ekstremalnej. Opisujemy tylko to, co opublikowali sami.

FirmaRozwiązanieIdea
GoogleBorg Name Service (BNS) + ChubbyBorg zapisuje nazwę hosta i port każdego zadania w Chubby (rozproszony serwis blokad z konsensusem Paxos) pod nazwą BNS; Chubby służy jako spójny rejestr nazw (artykuły o Borgu, EuroSys 2015, i o Chubby, OSDI 2006)
TwitterFinagle + ServerSets w ZooKeeperzeinstancje publikują się w ServerSecie w ZooKeeperze, a klient Finagle odczytuje ten ServerSet (adres zk!host!/ścieżka)
AirbnbSmartStack (Nerve + Synapse)Nerve sprawdza zdrowie i rejestruje w ZooKeeperze; Synapse konfiguruje lokalny HAProxy — wczesny „sidecar”
LyftEnvoyproxy przy każdym serwisie z dynamiczną konfiguracją z control plane — zalążek xDS i dzisiejszych meshy
NetflixEurekarejestr AP dla AWS z cache w kliencie (rozdział wyżej)
🤓 Dla dociekliwych

Widzisz ewolucję? Biblioteka w kliencie (Finagle, Ribbon) → lokalne proxy konfigurowane z rejestru (SmartStack) → proxy z otwartym protokołem sterowania (Envoy/xDS) → zarządzany control plane. Każdy krok przenosi logikę z kodu aplikacji do infrastruktury, bo w firmie z wieloma językami biblioteka per język staje się kosztem.

📌 Celne spostrzeżenia
  • Hyperscalerzy opierali rejestry na systemach z konsensusem (Chubby, ZooKeeper).
  • Trend: logika discovery wędruje z biblioteki do proxy i control plane.

Discovery: między czym naprawdę wybierasz

profil systemu → typowy wybór
ProfilTypowy wybórGłówny trade-off
Wszystko w KubernetesieService + DNS; mesh, gdy potrzebny mTLS/L7prostota vs funkcje meshu
VM + K8s + kilka chmurConsul (katalog + DNS + mesh)jeden rejestr vs kolejny system do utrzymania
Jedna chmura, wiele kontCloud Map / VPC Lattice, Service Directorybrak utrzymania vs vendor lock-in
Flota JVM na Spring CloudEureka lub Consul + biblioteka klientadojrzałość w ekosystemie vs biblioteka per język
Ekstremalna skala, wiele językówwłasny control plane xDS + Envoy / proxyless gRPCpełna kontrola vs ogromny koszt zespołu
⚠️ Anti-patterny discovery
  • Adresy IP w konfiguracji. Umierają przy pierwszym autoskalingu.
  • Klient zeruje pulę przy awarii rejestru. Awaria rejestru zamienia się w awarię wszystkiego.
  • Długi TTL bez retry. Martwe instancje dostają ruch przez cały TTL.
  • Brak readiness probe. Pod dostaje ruch, zanim jest gotowy.
  • Zaufanie do adresu zamiast tożsamości. Kto podmieni wpis, przejmie ruch — weryfikuj certyfikat.
Część II

Konfiguracja

Od zmiennych środowiskowych, przez ConfigMap i centralne serwery, po dynamiczną konfigurację, feature flags i bezpieczny rollout zmian.

Warstwy konfiguracji i 12-factor

kiedy wartość „zamarza”?

Najważniejsze pytanie o każdą wartość konfiguracyjną: kiedy zostaje ustalona. Wartość wkompilowana w obraz zmienisz tylko nowym buildem. Wartość podana przy deployu (zmienna środowiskowa, plik) — nowym deployem lub restartem. Wartość dynamiczna (centralny serwer, feature flag) — w trakcie działania, bez restartu. Im później zamarza, tym szybciej reagujesz, ale tym większe ryzyko, że zła zmiana trafi do produkcji z pominięciem testów i code review.

Manifest The Twelve-Factor App mówi: konfiguracja to wszystko, co różni się między deployami (staging, produkcja), i powinna być w zmiennych środowiskowych, a nie w kodzie. Ten sam obraz idzie wtedy na każde środowisko. To nadal standard dla wartości deploy-time — ale nie dla sekretów (część III) i nie dla wartości, które chcesz zmieniać bez restartu.

▶ Konfiguracja przez zmienne środowiskowe ten sam obraz, różne środowiska

▶ Deploy

  1. obraz + manifestCI publikuje jeden obraz. Manifest środowiska zawiera wartości, np. DB_POOL_SIZE=20.
  2. start z envOrkiestrator uruchamia proces z ustawionym środowiskiem.
  3. odczyt przy starcieAplikacja czyta zmienne raz, przy starcie, i waliduje je (fail fast przy złej wartości).

💥 Zmiana w biegu

  1. nowa wartośćOperator chce zmienić DB_POOL_SIZE na 40.
  2. proces nie widziŚrodowisko działającego procesu nie zmienia się z zewnątrz.
  3. rolling restartZmiana wymaga nowego deployu. Zaleta: zmiana przechodzi przez ten sam pipeline co kod.
Uproszczenie: wartości są ilustracyjne. Pomijamy pliki konfiguracyjne, które działają tak samo, gdy aplikacja czyta je tylko przy starcie.
WarstwaPrzykładZmiana wymaga
Build-timestałe w kodzie, plik w obrazienowego buildu
Deploy-timeenv, ConfigMap jako env, plik z wolumenu czytany przy starcierestartu / deployu
Runtime (dynamiczna)centralny serwer, AppConfig, feature flagniczego — aplikacja przeładowuje wartość
🔒 Dlaczego to bezpieczne

Konfiguracja deploy-time jest tak bezpieczna jak pipeline: zmiana przechodzi przez repozytorium, review i ten sam proces wdrożenia co kod, więc masz audyt i rollback (poprzednia wersja manifestu). Zmienne środowiskowe nie są jednak miejscem na sekrety: widzi je każdy proces potomny, narzędzia diagnostyczne i często logi przy awarii.

📌 Celne spostrzeżenia
  • Pytaj: kiedy ta wartość zamarza — build, deploy czy runtime?
  • 12-factor: jeden obraz, konfiguracja z zewnątrz.
  • Dynamiczna zmiana = szybsza reakcja, ale omija pipeline — potrzebuje własnych zabezpieczeń.

Pliki i ConfigMap w Kubernetesie

env czy wolumen — dwie różne semantyki

ConfigMap przechowuje niesekretną konfigurację jako pary klucz–wartość (do 1 MiB). Pod może ją dostać na dwa sposoby, które zachowują się inaczej przy zmianie. Jako zmienne środowiskowe: wartości są wstrzykiwane przy starcie kontenera i nie aktualizują się same. Jako wolumen: każdy klucz to plik, a kubelet aktualizuje pliki po zmianie ConfigMapy — z opóźnieniem (okres synchronizacji kubeleta plus opóźnienie cache). Wyjątek: plik zamontowany przez subPath nie jest aktualizowany.

▶ Aktualizacja ConfigMapy wolumen widzi zmianę, env nie

▶ Montaż jako wolumen

  1. kubectl applyOperator zmienia ConfigMapę. RBAC sprawdza, czy może edytować obiekty w tym namespace.
  2. syncKubelet przy okresowej synchronizacji zauważa nową wersję.
  3. nowe plikiKubelet aktualizuje pliki w wolumenie. Dokumentacja mówi, że klucze są aktualizowane „ostatecznie” (eventually) — nie natychmiast.
  4. reloadAplikacja musi sama wykryć zmianę pliku (watch na katalog) i przeładować konfigurację.

💥 ConfigMap jako env

  1. kubectl applyTa sama zmiana.
  2. stara wartośćKontener ma środowisko z chwili startu. Pody działają ze starą wartością, aż do restartu.
  3. rollout restartTypowe rozwiązanie: wymusić rolling update po każdej zmianie ConfigMapy, np. adnotacją z hashem konfiguracji w szablonie poda.
Uproszczenie: pomijamy niezmienne ConfigMapy i projekcję wielu źródeł do jednego wolumenu.
🔒 Dlaczego to bezpieczne

Dostęp do ConfigMap kontroluje RBAC Kubernetesa, a zmiany przechodzą przez API server (audyt). Gdy manifesty są w Git i wdraża je narzędzie GitOps (Argo CD, Flux), każda zmiana ma review i historię. ConfigMapa nie jest szyfrowana i nie jest przeznaczona na sekrety. Opcja immutable: true chroni przed przypadkową zmianą i zmniejsza obciążenie API servera (kubelet nie obserwuje takiej ConfigMapy).

📌 Celne spostrzeżenia
  • Env z ConfigMapy = deploy-time. Wolumen = prawie runtime (z opóźnieniem).
  • subPath nie dostaje aktualizacji.
  • Najbezpieczniej: konfiguracja w Git + rollout przy zmianie.

Centralny serwer konfiguracji

Spring Cloud Config, Consul KV, etcd

Gdy masz setki serwisów, chcesz jednego źródła konfiguracji z historią i możliwością zmiany bez deployu. Spring Cloud Config Server udostępnia konfigurację przez HTTP z backendu (najczęściej repozytorium Git, także Vault lub JDBC). Klient pobiera ją przy starcie, a beany oznaczone @RefreshScope przeładowuje po wywołaniu /actuator/refresh. Spring Cloud Bus rozgłasza odświeżenie do wszystkich instancji przez brokera (RabbitMQ lub Kafka). Poza JVM popularny wzorzec to Consul KV (lub etcd) + consul-template, który obserwuje klucze, renderuje plik i może uruchomić komendę przeładowania.

▶ Spring Cloud Config + Bus Git jako źródło prawdy, push odświeżenia

▶ Start i odświeżenie

  1. pobranie konfiguracjiInstancja pobiera konfigurację swojej aplikacji i profilu (domyślnie przy starcie), uwierzytelniając się w serwerze (np. HTTP Basic lub OAuth2).
  2. git fetchSerwer czyta pliki z repozytorium (gałąź lub tag = wersja).
  3. merge zmianyKtoś merguje PR ze zmianą timeoutu. Review i historia są w Git.
  4. /actuator/busrefreshWebhook z repozytorium (np. przez endpoint /monitor) albo operator wyzwala odświeżenie na szynie.
  5. refresh na każdej instancjiKażda instancja dostaje zdarzenie odświeżenia: czyści cache RefreshScope i ponownie wiąże @ConfigurationProperties.

💥 Config Server leży przy starcie

  1. serwer niedostępnyAwaria serwera konfiguracji.
  2. start bez konfiguracjiNowa instancja nie dostaje konfiguracji. Z fail-fast nie wystartuje; bez niego użyje wartości lokalnych.
  3. działające instancje OKUruchomione instancje mają konfigurację w pamięci. Dlatego serwer konfiguracji uruchamia się w kilku replikach, a klient ma retry.
Uproszczenie: pomijamy klucze szyfrujące i to, że serwer może mieć lokalny klon repozytorium.
🔒 Dlaczego to bezpieczne

Źródłem prawdy jest Git: każda zmiana ma autora, review i można ją cofnąć revertem. Serwer konfiguracji trzeba zabezpieczyć uwierzytelnieniem i TLS — sam z siebie udostępnia konfigurację każdemu, kto ma dostęp sieciowy. Spring Cloud Config umie trzymać wartości zaszyfrowane (prefiks {cipher}) i odszyfrowywać je po stronie serwera, ale w dużych systemach sekrety zwykle idą do dedykowanego managera (Vault jako backend).

⚠️ Uwaga!

„Odśwież wszystko naraz” to globalny deploy bez canary. Zła wartość trafia do całej floty w sekundy. Duże firmy dzielą rollout konfiguracji na etapy — o tym rozdział „Bezpieczny rollout”.

📌 Celne spostrzeżenia
  • Git + serwer konfiguracji = historia, review i zmiana bez deployu.
  • Serwer konfiguracji to zależność startowa: replikuj go, a w kliencie miej retry.
  • Rozgłoszenie do wszystkich naraz to ryzyko — rób to etapami.

Konfiguracja zarządzana w chmurze

AWS AppConfig, Parameter Store, Azure App Configuration

Chmury oferują gotowe magazyny konfiguracji. AWS Systems Manager Parameter Store to hierarchiczny magazyn parametrów (np. /prod/orders/timeout) z typami String, StringList i SecureString (szyfrowany kluczem KMS). Poziom Standard: do 10 000 parametrów, wartość do 4 KB, bez dodatkowej opłaty; Advanced: wartość do 8 KB i polityki parametrów. AWS AppConfig dodaje to, czego brakuje zwykłemu magazynowi: walidatory (JSON Schema lub funkcja Lambda), strategie wdrożenia (stopniowe udostępnianie) i automatyczny rollback, gdy w trakcie wdrożenia odpali alarm CloudWatch. Azure App Configuration trzyma klucze z etykietami (np. per środowisko), feature flagi, snapshoty i referencje do Key Vault — sekret nie jest kopiowany, aplikacja pobiera go z Key Vault własną tożsamością.

▶ Wdrożenie konfiguracji w AppConfig walidacja, stopniowy rollout, rollback na alarm

▶ Udane wdrożenie

  1. nowa wersjaOperator z uprawnieniami IAM tworzy nową wersję konfiguracji.
  2. walidacjaAppConfig uruchamia walidator. Zła składnia lub wartość spoza zakresu zatrzymuje wdrożenie, zanim cokolwiek się stanie.
  3. StartDeploymentWdrożenie ze strategią, np. liniowo co kilka minut, z czasem obserwacji (bake time) na końcu.
  4. GetLatestConfigurationAplikacje (lub AppConfig Agent obok nich) pobierają konfigurację. Każda instancja niezależnie sprawdza stan wdrożenia, więc nowa wersja trafia do rosnącej części klientów.
  5. alarmy OKAlarmy są zielone przez cały rollout, więc wdrożenie się kończy.

💥 Błędy po zmianie

  1. część klientów ma nową wersjęRollout jest w toku.
  2. błędy rosnąOdsetek błędów 5xx przekracza próg alarmu.
  3. ALARMAppConfig monitoruje alarm przypisany do środowiska.
  4. rollbackAppConfig automatycznie cofa wdrożenie; klienci wracają do poprzedniej wersji przy kolejnym odpytaniu.
Uproszczenie: pomijamy profile konfiguracji i typ „feature flags” w AppConfig. Interwały są konfigurowalne.
UsługaCoKiedy
SSM Parameter Storeprosty magazyn hierarchiczny, typ SecureStringwartości deploy-time, małe sekrety bez rotacji
AWS AppConfigwalidacja + stopniowe wdrożenie + rollback na alarmkonfiguracja zmieniana w biegu, feature flagi
Azure App Configurationkey-values z etykietami, feature flagi, snapshoty, referencje Key Vaultflota w Azure, .NET/Java z dynamicznym odświeżaniem
Google Parameter Managerparametry z wersjami i referencjami do Secret Managerflota w GCP
🔒 Dlaczego to bezpieczne

Odczyt i zapis kontroluje IAM chmury, a aplikacje używają ról zamiast kluczy. Każde wywołanie API trafia do logów audytowych (AWS CloudTrail, Azure Monitor). AppConfig dodaje bezpieczeństwo operacyjne: walidator blokuje złą wartość, a stopniowy rollout z alarmem ogranicza promień rażenia błędu do części floty.

📌 Celne spostrzeżenia
  • Magazyn (Parameter Store) ≠ system wdrożeń konfiguracji (AppConfig).
  • Walidacja przed wdrożeniem i rollback na alarm to standard dużych firm.
  • Referencja do sekretu jest lepsza niż kopia sekretu w konfiguracji.

Dynamiczna konfiguracja i feature flags

Archaius, LaunchDarkly, Unleash, OpenFeature

Feature flag to konfiguracja, która włącza kod dla wybranych użytkowników lub procentu ruchu. Oddziela deploy (kod jest na produkcji) od release (kod jest włączony). Duże platformy flagowe (LaunchDarkly, Unleash) mają wspólny wzorzec: SDK po stronie serwera trzyma reguły w pamięci i ewaluuje flagi lokalnie, bez wywołania sieciowego na każde sprawdzenie. Zmiany dostaje w tle (w LaunchDarkly przez stream). Netflix opublikował bibliotekę Archaius do dynamicznej konfiguracji (Java). OpenFeature standaryzuje API w kodzie, żeby zmiana dostawcy flag nie wymagała przepisania aplikacji.

▶ Ewaluacja lokalna i stopniowe włączanie flaga jako „pokrętło” bez deployu

▶ Włączenie dla 10%

  1. init z kluczem SDKSDK łączy się z serwisem flag kluczem SDK po stronie serwera i pobiera wszystkie reguły.
  2. rollout 10%Produkt ustawia flagę new-checkout na 10% użytkowników. Zmiana ma autora w logu audytu.
  3. stream: zmiana regułySerwis wysyła nową regułę do SDK.
  4. żądanie użytkownikaAplikacja pyta SDK o flagę dla użytkownika.
  5. hash(user) → bucketSDK liczy lokalnie: hash klucza użytkownika daje stały bucket, więc ten sam użytkownik zawsze dostaje ten sam wariant.

💥 Serwis flag niedostępny

  1. awariaSerwis flag nie odpowiada.
  2. ostatnie regułySDK ewaluuje na ostatnich znanych regułach z pamięci.
  3. zimny start → defaultNowa instancja bez połączenia dostaje wartość domyślną z kodu. Wartość domyślna musi być bezpieczna (zwykle: funkcja wyłączona).
Uproszczenie: pomijamy relay proxy i ewaluację po stronie klienta mobilnego/przeglądarki, gdzie SDK dostaje tylko wyniki, a nie reguły.
🔒 Dlaczego to bezpieczne

Klucz SDK po stronie serwera jest sekretem (pozwala czytać wszystkie reguły), a klucz po stronie klienta daje dostęp tylko do flag oznaczonych jako publiczne. Panel ma role i log audytu: wiesz, kto włączył flagę i kiedy. Bezpieczeństwo awarii daje lokalna ewaluacja i bezpieczna wartość domyślna. Flaga jest też najszybszym „wyłącznikiem awaryjnym” (kill switch) — szybszym niż rollback deployu.

⚠️ Uwaga! Dług flagowy

Każda flaga to dodatkowa gałąź w kodzie. Stare, nieusunięte flagi tworzą kombinacje, których nikt nie testował. Ustal właściciela i datę usunięcia dla każdej flagi.

📌 Celne spostrzeżenia
  • Flagi oddzielają deploy od release.
  • Ewaluacja lokalna: brak latencji i odporność na awarię serwisu flag.
  • OpenFeature = przenośne API; dostawca jest wymienny.

Config-as-code na skalę Meta

Configerator, Gatekeeper, Zeus

Artykuł „Holistic Configuration Management at Facebook” (SOSP 2015) opisuje, jak Facebook traktuje konfigurację jak kod. Inżynier pisze źródło konfiguracji w Pythonie (z typami w Thrift), kompilator generuje JSON, a walidatory sprawdzają wynik. Zmiana przechodzi code review i trafia do repozytorium Git. Dystrybucję robi Zeus (wersja ZooKeepera rozwijana w Facebooku): przez drzewo obserwatorów do proxy na każdym serwerze, które trzyma konfigurację w lokalnym cache na dysku. Aplikacja czyta konfigurację z lokalnego proxy, a nie z centrali. Na tym zbudowano Gatekeeper (stopniowe włączanie funkcji i eksperymenty), PackageVessel (duże konfiguracje przez dystrybucję peer-to-peer) i MobileConfig.

▶ Zmiana konfiguracji w modelu Configeratora konfiguracja przechodzi tę samą drogę co kod

▶ Zmiana

  1. źródło + kompilacjaInżynier zmienia źródło konfiguracji. Kompilator generuje JSON, a walidator sprawdza niezmienniki.
  2. review i commitZmiana przechodzi code review. Git zapisuje autora i historię.
  3. automatyczny canaryUsługa canary wdraża zmianę na małą grupę serwerów, potem większą, i porównuje metryki z grupą kontrolną.
  4. dystrybucjaPo udanym canary zmiana idzie przez Zeus i obserwatory do proxy na serwerach.
  5. odczyt lokalnyAplikacja dostaje nową wartość z lokalnego proxy.

💥 Centrala niedostępna

  1. proxy bez połączeniaProxy traci kontakt z obserwatorami.
  2. cache na dyskuAplikacja dalej czyta ostatnią konfigurację z lokalnego cache. Awaria systemu konfiguracji nie zatrzymuje produkcji.
Uproszczenie na podstawie artykułu z SOSP 2015; stan obecny systemów Meta może się różnić. Liczby etapów canary pomijamy.
🔒 Dlaczego to bezpieczne

Bezpieczeństwo jest tu głównie operacyjne: kompilacja i walidatory łapią błędy przed commitem, review daje drugą parę oczu, automatyczny canary ogranicza promień rażenia, a lokalny cache na każdym serwerze daje odporność na awarię centrali. Artykuł podaje, że błędy konfiguracji są ważną przyczyną incydentów — stąd tyle warstw ochrony.

📌 Celne spostrzeżenia
  • Konfiguracja jak kod: źródło, kompilacja, walidacja, review, canary.
  • Odczyt z lokalnego proxy/cache, nie z centrali.
  • Flagi (Gatekeeper) to warstwa nad tym samym systemem dystrybucji.

Bezpieczny rollout konfiguracji

konfiguracja to zmiana produkcji — tak samo groźna jak kod

Książka Google „Site Reliability Engineering” podaje, że około 70% awarii wynika ze zmian w działającym systemie. Zmiana konfiguracji to zmiana. Kilka głośnych awarii zaczęło się od danych konfiguracyjnych wdrożonych wszędzie naraz:

IncydentCo się stałoLekcja
CrowdStrike, 19.07.2024aktualizacja treści (plik kanałowy 291) dla sensora Falcon wywołała awarie Windowsa; Microsoft szacował 8,5 mln urządzeńaktualizacje treści potrzebują etapowego wdrożenia — CrowdStrike zapowiedział je w swojej analizie
Google Cloud, 12.06.2025zmiana polityki z pustymi polami replikowała się globalnie; nowy kod w Service Control nie miał flagi ani obsługi błędu i się wywracałnowa ścieżka kodu za flagą; dane konfiguracyjne propagowane etapami
▶ Etapowy rollout konfiguracji promień rażenia rośnie tylko, gdy metryki są zdrowe

▶ Zdrowa zmiana

  1. schema + testyWalidacja składni, typów i reguł biznesowych. Test, czy kod czytający konfigurację przyjmie nową wartość.
  2. canaryZmiana trafia do małej części floty. Metryki porównuje się z grupą kontrolną.
  3. jeden regionPo czasie obserwacji zmiana rozszerza się na jeden region (jedna domena awarii).
  4. globalnieDopiero na końcu wszystkie regiony.

💥 Zła wartość

  1. przechodzi walidacjęWartość jest poprawna składniowo, ale wywołuje błąd w nowym kodzie.
  2. canary chorujeWzrost błędów w grupie canary.
  3. auto-rollbackSystem cofa zmianę. Ucierpiał tylko 1% floty, a nie wszystkie regiony.
Uproszczenie: 1% i liczba etapów są ilustracyjne; każda firma dobiera je do ryzyka zmiany.
🔒 Dlaczego to bezpieczne

Etapy zamieniają „jedna zła wartość = globalna awaria” w „jedna zła wartość = mały, automatycznie cofnięty incydent”. Do tego: każda zmiana ma autora i review, poprzednia wersja jest zawsze dostępna do rollbacku, a kod czytający konfigurację odrzuca nieprawidłowe wartości i zostaje przy ostatniej dobrej (fail static), zamiast się wywracać.

📌 Celne spostrzeżenia
  • Konfiguracja wymaga tego samego co kod: review, testy, canary, rollback.
  • Przy błędnej wartości: zostań przy ostatniej dobrej, nie padaj.
  • Globalna replikacja w sekundy to cecha i jednocześnie ryzyko.

Konfiguracja: między czym wybierasz

profil → typowy wybór
PotrzebaTypowy wybórTrade-off
Wartości per środowisko, rzadkie zmianyenv / ConfigMap z Git (GitOps)restart przy zmianie vs pełny audyt i prostota
Zmiany bez deployu, jedna chmuraAWS AppConfig, Azure App Configurationgotowy rollout i rollback vs lock-in
Flota Spring/JVMSpring Cloud Config (Git) + Buswygoda w ekosystemie vs globalny refresh
Wiele platformConsul KV + consul-templateuniwersalność vs utrzymanie Consula
Włączanie funkcji dla części użytkownikówplatforma flag (LaunchDarkly, Unleash) przez OpenFeatureszybki release vs dług flagowy
Skala hyperscalerawłasny config-as-code (wzór: Configerator)pełna kontrola vs koszt budowy
⚠️ Anti-patterny konfiguracji
  • Globalny push bez canary. Zła wartość trafia wszędzie naraz.
  • Brak walidacji. Literówka w JSON wyłącza serwis.
  • Konfiguracja tylko w panelu, bez historii. Nikt nie wie, kto i kiedy zmienił timeout.
  • Sekrety w ConfigMap lub repozytorium konfiguracji. Konfiguracja nie jest szyfrowana i ma szeroki dostęp.
  • Różne obrazy per środowisko. Testujesz inny artefakt niż ten, który wdrażasz.
Część III

Sekrety

Gdzie trzymać hasła, klucze i certyfikaty, jak serwis je dostaje, jak je rotować — i jak w ogóle ich nie potrzebować.

Cykl życia sekretu i „secret zero”

każdy sekret kiedyś wycieknie — pytanie, na jak długo

Sekret to wartość, która daje dostęp: hasło do bazy, klucz API, klucz prywatny TLS, token. Każdy ma cykl życia: utworzenie → przechowanie → dystrybucja → użycie → rotacja → odwołanie. Duże firmy optymalizują dwie liczby: ile osób i systemów widzi sekret i jak długo jest ważny. Sekret ważny godzinę, znany tylko jednemu procesowi, jest wart dla atakującego niewiele.

Problem secret zero: żeby pobrać sekret z managera, serwis musi się uwierzytelnić. Czym? Jeśli kolejnym sekretem, to gdzie ten jest przechowywany? Rozwiązanie w dużych systemach: pierwszym poświadczeniem jest tożsamość nadana przez platformę (token konta usługi Kubernetesa, rola IAM instancji, atestacja węzła), a nie sekret wpisany przez człowieka.

▶ Secret zero rozwiązany tożsamością platformy człowiek nie dotyka żadnego hasła

▶ Pierwsze uruchomienie

  1. tożsamośćPlatforma wydaje serwisowi krótko żyjący, podpisany dowód tożsamości (np. token konta usługi albo poświadczenia roli).
  2. login dowodemSerwis loguje się do managera tym dowodem. Manager weryfikuje podpis u wystawcy.
  3. sekret o krótkim TTLManager wydaje sekret zgodnie z polityką dla tej tożsamości.
  4. użycieSerwis łączy się z bazą.
  5. rotacjaPrzed wygaśnięciem manager rotuje sekret; serwis pobiera nowy.

💥 Wyciek

  1. sekret w loguSekret wycieka (np. do logów).
  2. odwołanieManager odwołuje sekret; krótki TTL i tak ograniczał okno ataku.
Uproszczenie: kolejne rozdziały pokazują konkretne implementacje (Vault, menedżery chmurowe, workload identity).
🔒 Dlaczego to bezpieczne

Żaden człowiek nie zna sekretu, więc nie może go wkleić do czatu. Pierwszy dowód tożsamości jest podpisany przez platformę i krótko żyjący, więc kradzież daje krótkie okno. Każdy odczyt sekretu jest w logu audytu managera.

📌 Celne spostrzeżenia
  • Minimalizuj: liczbę widzów sekretu i czas jego ważności.
  • Secret zero rozwiązuje tożsamość platformy, nie kolejny sekret.
  • Rotacja i odwołanie muszą być zautomatyzowane.

Kubernetes Secrets: base64 to nie szyfrowanie

wygodne, ale domyślnie odkryte

Obiekt Secret wygląda jak ConfigMap na sekrety (także do 1 MiB). Wartości są zakodowane w base64 — to kodowanie, nie szyfrowanie. Domyślnie API server zapisuje Secrety w etcd niezaszyfrowane. Dokumentacja Kubernetesa wprost ostrzega: każdy, kto ma dostęp do API z prawem odczytu Secretów lub dostęp do etcd, może je przeczytać; a każdy, kto może utworzyć poda w namespace, może zamontować w nim dowolny Secret z tego namespace. Dlatego produkcja włącza szyfrowanie w spoczynku (EncryptionConfiguration, najlepiej provider kms v2) i ostre RBAC.

▶ Secret z szyfrowaniem KMS v2 kopertowe szyfrowanie przed zapisem do etcd

▶ Zapis i montaż

  1. create SecretRBAC sprawdza prawo tworzenia Secretów w namespace.
  2. szyfrowanie DEKAPI server szyfruje dane lokalnym kluczem danych (DEK).
  3. DEK zaszyfrowany KEKPlugin KMS szyfruje DEK kluczem z zewnętrznego KMS (KEK nigdy nie opuszcza KMS).
  4. szyfrogram w etcdW etcd ląduje szyfrogram i zaszyfrowany DEK. Kopia zapasowa etcd sama w sobie nie zdradza sekretu.
  5. kubelet pobieraKubelet dostaje Secret tylko dla podów na swoim węźle (Node authorizer).
  6. tmpfsSecret montowany jako wolumen trafia do tmpfs (pamięć), a nie na dysk węzła.

💥 Bez szyfrowania w spoczynku

  1. base64 w etcdSecret leży w etcd jako czytelne dane.
  2. wyciek backupuWyciek kopii zapasowej etcd = wyciek wszystkich Secretów klastra.
Uproszczenie: w KMS v2 API server generuje DEK i odświeża go według własnych reguł; pomijamy cache DEK.
🔒 Dlaczego to bezpieczne (gdy skonfigurowane)

Trzy warstwy: szyfrowanie w spoczynku z kluczem w zewnętrznym KMS chroni etcd i backupy; RBAC (bez list/watch Secretów dla zwykłych ról, bo dają dostęp do treści) chroni API; Node authorizer sprawia, że kubelet widzi tylko Secrety swoich podów. Bez tych warstw Secret to tylko „ukryta” ConfigMapa.

⚠️ Uwaga!

Prawo create pods w namespace oznacza praktycznie prawo odczytu każdego Secretu tego namespace. Rozdzielaj namespace według granic zaufania.

📌 Celne spostrzeżenia
  • base64 ≠ szyfrowanie; domyślnie etcd trzyma Secrety jawnie.
  • Produkcja: EncryptionConfiguration z KMS v2 + RBAC.
  • Secret w K8s często jest tylko „ostatnią milą” sekretu z zewnętrznego managera.

HashiCorp Vault i OpenBao

sekrety dynamiczne, lease i polityki

Vault to szeroko stosowany, niezależny od chmury manager sekretów. OpenBao to jego fork zarządzany przez OpenSSF (Linux Foundation). Model: klient loguje się metodą uwierzytelniania (Kubernetes, AWS IAM, JWT/OIDC, AppRole…) i dostaje token z politykami. Polityki są oparte na ścieżkach, a domyślnie wszystko jest zabronione. Najmocniejsza funkcja to sekrety dynamiczne: silnik bazy danych generuje poświadczenia do bazy dynamicznie, na żądanie, z lease (TTL), i odwołuje je po wygaśnięciu lease.

▶ Logowanie metodą Kubernetes i dynamiczne hasło do bazy każdy pod ma własnego, krótko żyjącego użytkownika bazy

▶ Normalnie

  1. login: rola + JWTPod wysyła do Vaulta token swojego konta usługi i nazwę roli.
  2. TokenReviewVault pyta API Kubernetesa, czy token jest ważny, i sprawdza, czy konto usługi i namespace pasują do roli.
  3. token Vaulta + politykiVault wydaje token z TTL i politykami przypisanymi do roli.
  4. read database/creds/ordersPod prosi o poświadczenia do bazy.
  5. CREATE ROLE … VALID UNTILVault tworzy w bazie nowego użytkownika według szablonu SQL z roli.
  6. user/hasło + lease 1 hPod dostaje unikalne poświadczenia z lease.

💥 Lease wygasa / wyciek

  1. hasło w loguPoświadczenia wyciekły albo pod zniknął bez odnowienia lease.
  2. revokePo wygaśnięciu lease (lub ręcznym vault lease revoke) Vault usuwa użytkownika z bazy. Wyciek dotyczy jednego poda i ma termin ważności.
Uproszczenie: TTL 1 h jest ilustracyjny. Pomijamy odnawianie lease i to, że token Vaulta też ma swój lease.
🔒 Dlaczego to bezpieczne

Brak secret zero: pod loguje się tożsamością nadaną przez Kubernetesa. Polityki domyślnie zabraniają wszystkiego, a każda rola ma minimalny dostęp. Sekrety dynamiczne są unikalne per klient i krótko żyjące, więc wyciek jest lokalny i ma termin ważności. Vault zapisuje żądania w urządzeniach audytu (audit devices), a wrażliwe wartości haszuje (HMAC). Dane w magazynie Vaulta są zaszyfrowane; po restarcie Vault jest zapieczętowany (sealed) i nie odczyta niczego bez odpieczętowania.

🔬 Głębiej: seal/unseal, transit, magazyn

Seal. Klucz główny szyfrujący dane jest chroniony. Domyślnie klucz odpieczętowujący dzieli się algorytmem Shamira na części (np. 5 części, próg 3), które mają różne osoby. W produkcji częściej stosuje się auto-unseal: klucz chroni chmurowy KMS lub HSM, więc restart nie wymaga ludzi.

Transit. Silnik transit to szyfrowanie jako usługa: aplikacja wysyła dane, a dostaje szyfrogram. Klucz domyślnie nie opuszcza Vaulta (eksport trzeba jawnie włączyć przy tworzeniu klucza), więc aplikacja nie zarządza kluczami.

Magazyn. Popularny backend to zintegrowany magazyn (Integrated Storage) oparty na Rafcie — znów quorum. W produkcji HashiCorp zaleca co najmniej 5 węzłów (3 węzły tolerują awarię tylko jednego).

📌 Celne spostrzeżenia
  • Auth method zamienia tożsamość platformy na token Vaulta.
  • Sekrety dynamiczne: unikalne, z lease, automatycznie odwoływane.
  • Vault (BSL) albo OpenBao (open source) — ten sam model.

Chmurowe menedżery sekretów

AWS Secrets Manager, Google Secret Manager, Azure Key Vault

Firmy osadzone w jednej chmurze zwykle wybierają jej natywny manager: zero utrzymania, integracja z IAM, KMS i audytem. AWS Secrets Manager szyfruje sekrety kluczem KMS i rotuje je funkcją Lambda (dla RDS i części innych usług jest gotowa, zarządzana rotacja). Wersje mają etykiety AWSCURRENT, AWSPENDING i AWSPREVIOUS. Google Secret Manager trzyma wersje sekretów z IAM na poziomie sekretu; sam nie zmienia wartości — wysyła powiadomienia o rotacji do Pub/Sub, a rotację wykonuje Twój kod. Azure Key Vault trzyma sekrety, klucze i certyfikaty, ma kontrolę dostępu Azure RBAC i soft-delete (domyślnie włączony i nie da się go wyłączyć), a dostęp z aplikacji daje managed identity.

▶ Rotacja w AWS Secrets Manager cztery kroki funkcji rotacji

▶ Rotacja według harmonogramu

  1. createSecretFunkcja generuje nowe hasło i zapisuje je jako wersję z etykietą AWSPENDING.
  2. setSecretFunkcja ustawia nowe hasło w bazie.
  3. testSecretFunkcja sprawdza, czy nowe hasło działa.
  4. finishSecretEtykieta AWSCURRENT przechodzi na nową wersję, a stara dostaje AWSPREVIOUS.
  5. GetSecretValue (cache)Aplikacja z rolą IAM pobiera bieżącą wersję, zwykle przez bibliotekę z cache odświeżanym co jakiś czas.

💥 Aplikacja z hasłem w pamięci

  1. stare hasłoW strategii jednego użytkownika stare hasło przestaje działać po rotacji. Aplikacja z hasłem w pamięci dostaje błędy logowania.
  2. ponów z nowymRozwiązanie: przy błędzie uwierzytelnienia pobierz sekret ponownie albo użyj strategii naprzemiennych użytkowników (dwa konta, rotowane na zmianę).
Uproszczenie: pomijamy replikację sekretów między regionami i rotację z zarządzanym użytkownikiem bazy.
UsługaMocne stronyKiedy
AWS Secrets Managerwbudowana rotacja (Lambda / zarządzana), replikacja między regionami, polityki zasobówflota w AWS, bazy RDS
SSM Parameter Store SecureStringtanio, hierarchia ścieżek, typ SecureStringproste sekrety bez rotacji
Google Secret Managerwersje, IAM per sekret, CMEK, powiadomienia o rotacjiflota w GCP
Azure Key Vaultsekrety + klucze + certyfikaty, Managed HSM, RBACflota w Azure, zarządzanie certyfikatami
Vault / OpenBaosekrety dynamiczne, multi-cloud, PKI, transitwiele chmur i on-prem, jedna polityka
🔒 Dlaczego to bezpieczne

Sekret jest zaszyfrowany kluczem z KMS, a dostęp do wartości wymaga uprawnień IAM (i do klucza). Dostęp ma tylko principal z polityką IAM (rola aplikacji, managed identity, konto usługi), a odczyty trafiają do logów audytu (CloudTrail, Cloud Audit Logs — w GCP po włączeniu logów Data Access, Azure Monitor). Rotacja zamyka okno ataku po cichym wycieku. Soft-delete (Key Vault) chroni przed przypadkowym usunięciem.

📌 Celne spostrzeżenia
  • Natywny manager = IAM + KMS + audyt bez utrzymania.
  • Rotacja AWS: createSecret → setSecret → testSecret → finishSecret.
  • Aplikacja musi przeżyć rotację: cache z odświeżaniem i ponowienie przy błędzie logowania.

Jak sekret trafia do serwisu

SDK, sidecar, CSI, operator — gdzie leży jawny tekst

Manager to połowa sprawy. Druga połowa: jak sekret dociera do procesu i gdzie po drodze jest jawny. Duże firmy wybierają między czterema wzorcami:

WzorzecJak działaJawny tekst wKiedy
SDK w aplikacjiaplikacja sama woła API manageratylko pamięć procesumaksymalne bezpieczeństwo, kod zna managera
Vault Agent Injectorwebhook dodaje sidecar, który loguje się i renderuje pliki do /vault/secretswolumen w pamięci podaaplikacja ma czytać plik, bez zmiany kodu
Secrets Store CSI Driversterownik CSI montuje sekret z managera jako wolumen (dostawcy: AWS, Azure, GCP, Vault)wolumen poda; opcjonalnie synchronizowany do Secretu K8swiele managerów, montaż plików
External Secrets Operatoroperator synchronizuje sekret z managera do obiektu Secret w K8sSecret w etcd + podaplikacje już używają Secretów K8s
▶ ESO kontra CSI ten sam cel, inna liczba kopii sekretu

▶ External Secrets Operator

  1. odczyt (rola operatora)Operator, według SecretStore i ExternalSecret, czyta sekret tożsamością przypisaną do SecretStore.
  2. zapis SecretOperator tworzy lub aktualizuje Secret w Kubernetesie.
  3. env / wolumenPod używa zwykłego Secretu — bez zmian w aplikacji.
  4. refreshIntervalOperator cyklicznie synchronizuje zmiany (np. po rotacji).

▶ Secrets Store CSI Driver

  1. start podaKubelet montuje wolumen CSI zdefiniowany przez SecretProviderClass.
  2. odczyt tożsamością podaDostawca pobiera sekret, zwykle tożsamością (workload identity) tego poda.
  3. plik w wolumenieSekret trafia jako plik do wolumenu poda. Bez synchronizacji nie powstaje kopia w etcd.
Uproszczenie: ESO wspiera wiele managerów i może też generować sekrety; CSI może opcjonalnie synchronizować do Secretu K8s (wtedy też powstaje kopia w etcd).
🔒 Dlaczego to bezpieczne

Wszystkie wzorce uwierzytelniają się do managera tożsamością (konto usługi, workload identity), a nie zapisanym hasłem. Różnią się powierzchnią: SDK i CSI bez synchronizacji nie zostawiają kopii w etcd, a ESO zostawia, więc wymaga szyfrowania w spoczynku i RBAC. Pliki w wolumenach z pamięci są lepsze niż zmienne środowiskowe, bo nie dziedziczą ich procesy potomne i nie widać ich w inspekcji kontenera (np. docker inspect pokazuje zmienne środowiskowe).

📌 Celne spostrzeżenia
  • Pytaj: ile kopii sekretu powstaje i gdzie są jawne?
  • ESO: wygoda (zwykłe Secrety), ale kopia w etcd.
  • Plik w tmpfs > zmienna środowiskowa.

Sekrety w GitOps: Sealed Secrets i SOPS

szyfrogram w repozytorium, klucz poza nim

W GitOps „wszystko jest w Git”. Sekret w Git jawnie to katastrofa, więc albo trzymasz w Git tylko referencję (ExternalSecret wskazujący sekret w managerze), albo szyfrogram. Sealed Secrets (Bitnami): kontroler w klastrze ma parę kluczy; kubeseal szyfruje sekret kluczem publicznym do obiektu SealedSecret, który można bezpiecznie commitować — odszyfrować go może tylko kontroler. SOPS szyfruje pliki YAML/JSON kluczami z KMS chmury, age lub PGP; Flux ma wbudowaną obsługę odszyfrowania SOPS.

▶ Sealed Secrets szyfrowanie kluczem publicznym klastra

▶ Normalnie

  1. kubesealDeveloper szyfruje Secret kluczem publicznym kontrolera. Domyślnie szyfrogram jest związany z nazwą i namespace.
  2. commit SealedSecretDo repozytorium trafia tylko szyfrogram.
  3. syncNarzędzie GitOps aplikuje SealedSecret do klastra.
  4. odszyfrowanieKontroler odszyfrowuje kluczem prywatnym i tworzy zwykły Secret.

💥 Kopiowanie do innego namespace

  1. ten sam szyfrogram, inny namespaceKtoś kopiuje SealedSecret do namespace, do którego nie powinien mieć sekretu.
  2. odmowaW domyślnym zakresie (strict) odszyfrowanie się nie uda, bo nazwa i namespace są częścią szyfrowania.
Uproszczenie: pomijamy rotację kluczy kontrolera i zakresy namespace-wide / cluster-wide.
🔒 Dlaczego to bezpieczne

W repozytorium jest tylko szyfrogram; klucz prywatny jest w klastrze (Sealed Secrets) lub w KMS (SOPS). Kradzież repozytorium nie zdradza sekretów. Trzeba jednak pamiętać, że historia Git jest wieczna: jeśli klucz kiedyś wycieknie, stare szyfrogramy da się odszyfrować — dlatego po wycieku klucza rotuje się sekrety, nie tylko klucz.

📌 Celne spostrzeżenia
  • GitOps: referencja (ESO) albo szyfrogram (Sealed Secrets, SOPS) — nigdy jawny tekst.
  • SOPS szyfruje pliki konfiguracyjne kluczami z KMS, age lub PGP.
  • Rotacja szyfrogramów w Git jest trudniejsza niż w managerze.

Szyfrowanie kopertowe, KMS i HSM

dlaczego klucz główny nigdy nie wychodzi z sejfu

Pod spodem każdy manager sekretów korzysta z tego samego wzorca. Szyfrowanie kopertowe: dane szyfrujesz lokalnie kluczem danych (DEK), a sam DEK szyfrujesz kluczem głównym (KEK), który mieszka w KMS lub HSM i nigdy go nie opuszcza. Zapisujesz szyfrogram danych razem z zaszyfrowanym DEK. Do KMS wysyłasz tylko mały DEK, więc dane mogą mieć dowolny rozmiar, a KMS nie staje się wąskim gardłem. AWS KMS chroni klucze w modułach HSM z walidacją FIPS 140-3.

▶ Szyfrowanie i odszyfrowanie kopertowe wzorzec AWS KMS GenerateDataKey

▶ Szyfrowanie

  1. GenerateDataKeyAplikacja z uprawnieniem IAM do klucza prosi KMS o klucz danych.
  2. DEK jawny + DEK zaszyfrowanyKMS zwraca DEK w dwóch postaciach.
  3. AES-GCM lokalnieAplikacja szyfruje dane jawnym DEK i od razu usuwa go z pamięci.
  4. zapis: szyfrogram + zaszyfrowany DEKZapisywane jest tylko to, co bez KMS jest bezużyteczne.

▶ Odszyfrowanie

  1. odczytAplikacja czyta szyfrogram i zaszyfrowany DEK.
  2. Decrypt(DEK)KMS sprawdza IAM i politykę klucza, loguje operację i zwraca jawny DEK.
  3. odszyfrowanie lokalneAplikacja odszyfrowuje dane.

💥 Kradzież bazy

  1. dump bazyAtakujący kradnie magazyn: ma szyfrogramy i zaszyfrowane DEK.
  2. brak dostępu do KMSBez uprawnień do klucza w KMS nie odszyfruje DEK, więc dane są bezużyteczne. Odebranie uprawnień do klucza „wyłącza” dostęp do wszystkich danych naraz.
Uproszczenie: pomijamy kontekst szyfrowania (encryption context) i cache kluczy danych w SDK.
🔒 Dlaczego to bezpieczne

KEK nie opuszcza HSM, więc nie da się go skopiować. Każde użycie klucza jest autoryzowane (IAM + polityka klucza) i zapisane w audycie. Rotacja KEK nie wymaga ponownego szyfrowania wszystkich danych. Kradzież samych danych bez dostępu do KMS jest bezużyteczna.

📌 Celne spostrzeżenia
  • DEK szyfruje dane; KEK w KMS szyfruje DEK.
  • Do KMS idzie tylko mały klucz, nie dane.
  • Ten wzorzec stoi za KMS v2 w Kubernetesie, Vaultem i chmurowymi managerami.

Tożsamość zamiast sekretu

workload identity i SPIFFE/SPIRE

Najlepszy sekret to taki, którego nie ma. Trend dużych firm: zamiast długożyjących kluczy dostępu, tożsamość workloadu wymieniana na krótko żyjące poświadczenia. W chmurach: EKS ma IRSA (projektowany token konta usługi + OIDC + sts:AssumeRoleWithWebIdentity) i nowsze EKS Pod Identity; GKE ma Workload Identity Federation for GKE; Azure ma Microsoft Entra Workload ID (federacja tokenu konta usługi). Niezależnie od chmury: SPIFFE (standard tożsamości, projekt CNCF ze statusem graduated) i SPIRE (implementacja) wydają workloadom krótko żyjące dokumenty tożsamości SVID (certyfikat X.509 lub JWT) przez lokalne Workload API.

▶ Workload identity i SPIRE poświadczenia na minuty, bez żadnego klucza w podzie

▶ EKS IRSA

  1. projektowany token SAKubelet montuje w podzie krótko żyjący token konta usługi z audience dla STS, podpisany przez klaster.
  2. AssumeRoleWithWebIdentitySDK AWS wysyła token do STS. STS weryfikuje podpis przez dostawcę OIDC klastra i warunki zaufania roli (namespace i konto usługi).
  3. poświadczenia tymczasoweSTS zwraca poświadczenia roli z terminem wygaśnięcia. SDK odświeża je samo.
  4. wywołanie APIPod woła np. S3 lub Secrets Manager. Żaden klucz dostępu nie istnieje.

▶ SPIFFE / SPIRE

  1. atestacja węzłaAgent SPIRE udowadnia serwerowi SPIRE, na jakim węźle działa (np. przez dokument tożsamości instancji chmury).
  2. Workload API (gniazdo Unix)Proces pyta lokalnego agenta o tożsamość — bez żadnego hasła.
  3. atestacja workloaduAgent sprawdza, kim jest proces (np. namespace, konto usługi i obraz z kubeleta).
  4. X.509-SVIDProces dostaje certyfikat z SPIFFE ID, np. spiffe://acme.com/ns/prod/sa/orders, rotowany automatycznie przed wygaśnięciem.
  5. mTLS / federacjaCertyfikatem robi mTLS z innym serwisem; JWT-SVID może służyć do federacji z chmurą.
Uproszczenie: kolumna „Dostawca tożsamości” to STS w IRSA i serwer SPIRE w SPIFFE. Pomijamy rejestrację wpisów w SPIRE i agenta EKS Pod Identity.
🔒 Dlaczego to bezpieczne

Nie ma czego ukraść na długo: poświadczenia żyją minuty–godziny i są rotowane automatycznie. Tożsamość wynika z atestacji (sprawdzonych faktów o węźle i procesie), a nie z posiadania sekretu. Uprawnienia są przypięte do tożsamości (rola IAM, polityka mTLS), więc odebranie dostępu to zmiana polityki, a nie polowanie na kopie klucza.

📌 Celne spostrzeżenia
  • Workload identity usuwa długożyjące klucze chmurowe z podów.
  • SPIFFE ID + SVID = przenośna tożsamość serwisu (używają jej m.in. Istio).
  • Sekret zostaje tylko tam, gdzie system docelowy nie umie federacji (np. stara baza) — i wtedy sekret dynamiczny.

Sekrety: między czym wybierasz

profil → typowy wybór
ProfilTypowy wybórTrade-off
Jedna chmuranatywny manager + workload identity (+ CSI lub ESO w K8s)zero utrzymania vs lock-in
Multi-cloud / on-premVault lub OpenBao, auth przez tożsamość platformyjedna polityka vs utrzymanie krytycznego klastra
Bazy z wieloma klientamisekrety dynamiczne (Vault) lub rotacja manageraunikalne poświadczenia vs obciążenie bazy
GitOpsESO (referencje) albo SOPS / Sealed Secretsjedno źródło w managerze vs wszystko w Git
Ruch serwis–serwismTLS z SPIFFE (mesh / SPIRE) zamiast kluczy APIbrak sekretów vs złożoność PKI
Część IV

Anti-patterny

Galeria błędów, które duże firmy wyeliminowały — zwykle po bolesnym incydencie.

Galeria anti-patternów sekretów

i co zrobić zamiast
Anti-patternDlaczego źleZamiast
Sekret w repozytoriumhistoria Git jest wieczna; usunięcie commita nie usuwa kopiiskanowanie (push protection), rotacja po wycieku, referencje do managera
Sekret w obrazie (ENV, ARG, plik)obraz trafia do rejestrów i cache; warstwy da się odczytaćsekret w runtime; przy buildzie RUN --mount=type=secret
Sekret w zmiennej środowiskowejdziedziczą go procesy potomne, widać go w docker inspect, trafia do zrzutów i logówplik w tmpfs lub SDK
Sekret w logachlogi mają szeroki dostęp i długą retencjęmaskowanie w loggerze, typy „Secret” z toString() bez wartości
Długożyjący klucz dostępuwyciek działa miesiącamiworkload identity, poświadczenia tymczasowe
Jeden sekret dla wszystkich serwisówwyciek z jednego serwisu = dostęp wszędzie; brak audytu „kto”sekret per serwis, sekrety dynamiczne
Rotacja „kiedyś ręcznie”nikt jej nie robi, a pierwsza rotacja psuje produkcjęautomatyczna rotacja i test, że aplikacja ją przeżywa
Secret K8s bez szyfrowania w spoczynkubackup etcd zdradza wszystkoEncryptionConfiguration z KMS v2
✏️ Zaostrz ołówek

Serwis w EKS czyta hasło do RDS ze zmiennej środowiskowej ustawionej z Secretu K8s, który ktoś utworzył ręcznie rok temu. Wymień trzy zmiany, które przybliżą go do standardu dużych firm.

Odpowiedź

1) Hasło w AWS Secrets Manager z automatyczną rotacją (lub IAM database authentication dla RDS). 2) Dostarczanie przez Secrets Store CSI Driver lub SDK z workload identity (IRSA / Pod Identity) zamiast zmiennej środowiskowej. 3) Aplikacja odporna na rotację: cache z odświeżaniem i ponowne pobranie sekretu przy błędzie logowania. Jeśli zostaje Secret K8s — szyfrowanie w spoczynku i RBAC.

Egzamin + ściągawka

sprawdź, co zostało w głowie
Wynik: 0 / 0

Ściągawka

Narzędzie / mechanizmCo robiWybierz, gdy
DNS A/SRVnazwa → adres (SRV: + port); cache przez TTLuniwersalność ważniejsza niż szybkość reakcji
etcd / ZooKeeper / Consulrejestr CP: lease/sesja, watch, quorumpotrzebujesz spójnego rejestru i watchy
Eurekarejestr AP, heartbeat, self-preservationflota Spring Cloud, dostępność ponad spójność
K8s Service + EndpointSliceClusterIP + DNS, readiness decyduje o ruchuwszystko w Kubernetesie
Istio / Linkerdproxy przy serwisie + mTLS + polityki dostępu/ruchupotrzebujesz mTLS i kontroli ruchu między serwisami
Cloud Map / VPC Latticezarządzany rejestr / sieć serwisów z IAMjedna chmura, wiele kont
ConfigMap (env / wolumen)env: przy starcie; wolumen: aktualizacja z opóźnieniemkonfiguracja deploy-time, GitOps
Spring Cloud Config + Buskonfiguracja z Git, @RefreshScope, refresh przez szynęflota Spring/JVM
AWS AppConfigwalidator, strategia wdrożenia, rollback na alarmzmiany w biegu z kontrolą ryzyka
OpenFeature + platforma flaglokalna ewaluacja flag, wymienny dostawcaoddzielenie deployu od release
K8s Secret + KMS v2szyfrowanie kopertowe w etcd„ostatnia mila” sekretu w klastrze
Vault / OpenBaoauth method, polityki, sekrety dynamiczne, transitmulti-cloud, unikalne poświadczenia
Secrets Manager / Secret Manager / Key Vaultnatywne IAM + KMS + audyt; rotacja: AWS — Lambda, GCP — powiadomienia Pub/Sub + Twój kodflota w jednej chmurze
ESO / Secrets Store CSIsync do Secretu / montaż pliku z manageraKubernetes + zewnętrzny manager
Sealed Secrets / SOPSszyfrogram w GitGitOps bez zewnętrznego managera
IRSA / Pod Identity / Workload Identity / SPIREtożsamość workloadu → krótkie poświadczeniazawsze, gdy cel umie federację
🎓 Gratulacje

Wracamy do poda z 3:12. Ruch: Kubernetes dodał go do EndpointSlice, gdy readiness przeszła, a proxy meshu dostały nowy endpoint przez xDS. Konfiguracja: obraz jest ten sam co wszędzie, a wartości przyszły z manifestu w Git (deploy-time) i z systemu konfiguracji z etapowym rolloutem (runtime). Hasło do bazy: pod pokazał tożsamość nadaną przez platformę (token konta usługi), wymienił ją na krótko żyjące poświadczenia (Vault lub chmurowy manager), a mTLS dał mu certyfikat SPIFFE. Atakujący z obrazem kontenera nie ma nic, bo w obrazie nie ma żadnego sekretu, a tożsamości nie da się ukraść z obrazu. Następny krok: sprawdź, który z anti-patternów z części IV jest w Twoim systemie.