Klaster Kubernetes obsługujący obronne mikroserwisy nie jest z natury zero-trust. Domyślny model sieciowy podów pozwala każdemu podowi zainicjować połączenie TCP z dowolnym innym podem w tej samej sieci klastra, co oznacza, że jeden skompromitowany kontener może dotrzeć do serwisów uwierzytelniania, magazynów danych i interfejsów dowodzenia, do których nie ma żadnego uzasadnionego powodu dostępu. Service mesh rozwiązuje ten problem nie poprzez rekonfigurację sieciowych list kontroli dostępu — które są kruche w dynamicznych środowiskach kontenerowych — ale poprzez wstrzyknięcie kryptograficznej tożsamości i egzekwowania polityki bezpośrednio do ścieżki danych każdego wywołania między serwisami. Artykuł analizuje, co oznacza to w praktyce dla klastrów obronnych: jak SPIFFE/SPIRE integruje się z architekturą zero-trust dla sieci wojskowych, jak polityka oparta na intencjach zastępuje myślenie obwodowe wewnątrz klastra oraz jak utrzymać pełną obserwowalność bez przekraczania granicy klasyfikacji przez jakąkolwiek telemetrię.
Dlaczego zabezpieczenia obwodowe zawodzą wewnątrz obronnych klastrów mikroserwisów
Model zabezpieczeń obwodowych zakłada, że ruch wewnątrz zaufanej granicy sieciowej jest bezpieczny. W aplikacji monolitycznej założenie to ma pewną zasadność: istnieje jeden proces, jedna granica zaufania i stosunkowo niewiele wewnętrznych ścieżek komunikacji do zabezpieczenia. W architekturze mikroserwisów wdrożonej w Kubernetes założenie to całkowicie się załamuje. Klaster obsługujący 20 serwisów ma rzędu 400 możliwych par serwisów. Nie wszystkie powinny się komunikować, ale bez jawnego egzekwowania na poziomie serwisu wszystkie mogą. Podatność w jednym serwisie — zależność z luką zdalnego wykonywania kodu, błędnie skonfigurowana zmienna środowiskowa ujawniająca dane uwierzytelniające, niełatany obraz bazowy — daje przeciwnikowi możliwość ruchu bocznego w całej sieci klastra.
Tryb awarii nie jest teoretyczny. Kompromitacje łańcucha dostaw wymierzone w kontenery wykazały, że gdy przeciwnik ma możliwość wykonania kodu wewnątrz poda, agresywnie sonduje sieć klastra. Bez wzajemnego uwierzytelniania nic nie powstrzymuje skompromitowanego poda od bezpośredniego wywołania wewnętrznych API: ma już prawidłowy adres IP klastra, jego żądania nadchodzą z zaufanego zakresu CIDR, a docelowy serwis nie ma możliwości weryfikacji tożsamości wywołującego. Tradycyjna segmentacja sieciowa poprzez Kubernetes NetworkPolicy pomaga na warstwie IP/portów, ale nie zapobiega podszywaniu się pod tożsamość legalnego poda w dozwolonym zakresie adresów, ani nie egzekwuje żadnych ograniczeń warstwy aplikacji dotyczących dozwolonych operacji.
Service mesh rozwiązuje to, czyniąc kryptograficzną tożsamość obciążenia obowiązkową dla każdego połączenia. Każde proxy sidecar przedstawia SPIFFE Verifiable Identity Document (SVID) — krótkotrwały certyfikat X.509 kodujący tożsamość konta serwisowego poda — i wymaga od zdalnego sidecara przedstawienia ważnego SVID w zamian, zanim przepłyną jakiekolwiek dane aplikacji. Weryfikacja tożsamości odbywa się na warstwie transportowej, transparentnie dla aplikacji, i nie może zostać ominięta przez atakującego, który kontroluje tylko proces aplikacji wewnątrz kontenera. Ruch boczny zostaje zredukowany z „dotrzyj do dowolnego poda w sieci klastra" do „dotrzyj tylko do podów, których polityka jawnie zezwala na Twój SVID jako źródło".
Wybór service mesh: Istio vs Linkerd vs Cilium w środowiskach air-gapped
Trzy implementacje service mesh są gotowe produkcyjnie do obronnych wdrożeń Kubernetes, każda z innym profilem kompromisów. Istio jest najbardziej kompletna pod względem funkcji: płaszczyzna danych oparta na Envoy obsługuje rozbudowane zarządzanie ruchem L7, w tym wstrzykiwanie błędów, ponowne próby, wyłączanie obwodów, dublowanie żądań i szczegółową politykę AuthorizationPolicy. Płaszczyzna sterowania — istiod — integruje się natywnie z SPIFFE/SPIRE do zewnętrznego wydawania certyfikatów, a jej potok telemetrii emituje metryki i ślady kompatybilne z OpenTelemetry z każdego sidecara. Ceną jest złożoność operacyjna: Istio ma stromszą krzywą uczenia się, większy ślad płaszczyzny sterowania i sidecary Envoy zużywające więcej pamięci na pod niż alternatywy. Dla klastrów z 16+ GB pamięci RAM na węzeł i zespołów doświadczonych w konfiguracji Envoy, wyrazistość polityk Istio jest trudna do zastąpienia.
Linkerd przyjmuje podejście odwrotne. Jego proxy płaszczyzny danych jest napisane w Rust i zaprojektowane jako ultralekkie: każdy sidecar linkerd-proxy używa 10–20 MB rezydentnej pamięci i dodaje mniej niż 1 ms opóźnienia do każdego skoku, w porównaniu do 50–100 MB i 0,5–3 ms dla Envoy. Bezproblemowe mTLS Linkerd jest domyślnie włączone dla całego ruchu sieciowego bez wymagania jakiegokolwiek manifestu PeerAuthentication, co zmniejsza ryzyko błędnie skonfigurowanego wyjątku plaintext. Kompromisem jest to, że model polityk Linkerd jest mniej wyrazisty na poziomie metody HTTP i ścieżki, a jego płaszczyzna sterowania integruje się z zewnętrznymi wystawcami SPIRE mniej bezpośrednio niż Istio. Dla klastrów brzegowych o ograniczonych zasobach z ograniczoną pamięcią RAM na węzeł, wydajność proxy Linkerd jest znaczącą przewagą operacyjną.
Cilium działa na warstwie eBPF, a nie jako sidecar. Osadzając egzekwowanie polityk w programach eBPF jądra Linux dołączonych do interfejsu sieciowego każdego poda, Cilium osiąga bliski zeru narzut na obciążenie dla egzekwowania polityki sieciowej i podstawowego mTLS. Zasób CiliumNetworkPolicy obsługuje reguły uwzględniające tożsamość SPIFFE poprzez integrację z API SPIRE, a komponent obserwowalności Hubble zapewnia widoczność przepływu L4/L7 wewnątrz klastra. Siłą Cilium jest wydajność i bezproblemowe zastępstwo kube-proxy; jego ograniczeniem jest to, że egzekwowanie polityk HTTP/gRPC L7 przez eBPF jest mniej dojrzałe niż równoważnik Istio oparty na Envoy w złożonych scenariuszach autoryzacji. W praktyce wiele klastrów obronnych uruchamia Cilium jako CNI dla polityki sieciowej i szyfrowania, z Istio lub Linkerd nałożonym na górze dla obserwowalności L7 i szczegółowej AuthorizationPolicy na poziomie serwisu.
Wzajemne TLS: wydawanie certyfikatów, rotacja i integracja SPIFFE/SPIRE
Wzajemne TLS w service mesh oznacza, że obie strony każdego połączenia uwierzytelniają się nawzajem za pomocą certyfikatów X.509 przed wymianą jakichkolwiek danych aplikacji. Kluczowe pytanie dla wdrożenia obronnego dotyczy sposobu wydawania tych certyfikatów, częstotliwości ich rotacji oraz tego, kto kontroluje korzeń zaufania. Natywne Istio używa własnego wbudowanego urzędu certyfikacji (istiod CA), który wydaje certyfikaty obciążeń ważne przez 24 godziny domyślnie. Dla wdrożenia obronnego jest to niewystarczające z dwóch powodów: klucz korzenia znajduje się wewnątrz klastra (skompromitowany proces istiod ujawnia wszystkie tożsamości obciążeń), a 24-godzinne TTL są zbyt długie dla środowisk, w których może być wymagane szybkie unieważnienie poświadczeń.
SPIRE zapewnia właściwą architekturę dla kontekstów obronnych. Serwer SPIRE działa jako StatefulSet z pakietem zaufania i wpisami rejestracji utrwalonymi w magazynie danych, który może być przechowywany poza klastrem. Agent SPIRE działa jako DaemonSet na każdym węźle i obsługuje poświadczanie obciążeń poprzez weryfikację tokenu konta serwisowego każdego poda względem serwera API przed wydaniem SVID. Wydane SVID mają konfigurowalne TTL — produkcyjne wdrożenia obronne używają 1-godzinnych TTL dla standardowych obciążeń i 15-minutowych TTL dla obciążeń obsługujących dane niejawne — a agent SPIRE obsługuje rotację automatycznie, przesyłając nowy SVID do gniazda obciążenia przed wygaśnięciem bieżącego. Sidecar siatki odczytuje SVID przez gniazdo SPIFFE Workload API i przedstawia go na wszystkich wychodzących połączeniach TLS bez żadnych zmian w kodzie aplikacji.
Integracja SPIRE z Istio wymaga skonfigurowania istiod do używania SPIRE jako dostawcy certyfikatów poprzez interfejs wtyczki upstream CA. Konfiguracja CustomCA Istio wskazuje istiod na port GRPC serwera SPIRE, delegując całe wydawanie i rotację SVID do SPIRE, podczas gdy Istio obsługuje egzekwowanie PeerAuthentication i AuthorizationPolicy. To rozdzielenie odpowiedzialności jest ważne: SPIRE posiada cykl życia tożsamości (poświadczanie, wydawanie, rotacja), podczas gdy Istio posiada cykl życia polityki (które tożsamości mogą komunikować się na jakich ścieżkach). Operator cofający dostęp obciążenia usuwa jego wpis rejestracji SPIRE, co powoduje niepowodzenie kolejnej rotacji SVID, odcinając obciążenie od siatki przy następnym odnawianiu certyfikatu bez konieczności wprowadzania jakichkolwiek zmian w manifeście polityki.
Polityka ruchu oparta na intencjach: tworzenie listy dozwolonych dla komunikacji wschód-zachód
Zasada domyślnej odmowy wymaga, aby żaden ruch między serwisami nie był dozwolony, jeśli nie jest jawnie zezwolony przez politykę. W Istio jest to implementowane za pomocą manifestu AuthorizationPolicy obejmującego cały klaster z pustym blokiem reguł i action: DENY — który blokuje cały ruch — a następnie wąskich polityk zezwolenia dla każdej legalnej pary serwisów. Każda polityka zezwolenia wskazuje podmiot źródłowy (URI SPIFFE konta serwisowego wywołującego serwisu), serwis docelowy oraz precyzyjny zestaw metod HTTP lub nazw serwisów gRPC, dla których wywołanie jest autoryzowane. Polityka autoryzująca serwis pozyskiwania danych z czujników do wykonywania POST do API fuzji danych, na przykład, nie zezwala mu również na pobieranie rekordów historycznych ani wywoływanie endpointu administracyjnego, nawet jeśli oba działają na tym samym porcie serwisu docelowego. Granularność jest na poziomie operacji, a nie tylko połączenia TCP.
Pisanie i utrzymywanie tego zestawu polityk wymaga dyscypliny. Klaster rozrastający się od 10 do 30 serwisów przez 18 miesięcy gromadzi setki wpisów zezwolenia, a przestarzałe wpisy dla wycofanych serwisów stają się ukrytą powierzchnią ataku. Dwie praktyki zapobiegają temu gromadzeniu. Po pierwsze, traktuj manifesty AuthorizationPolicy jak kod: przechowuj je w tym samym repozytorium kontrolowanym wersją co manifesty aplikacji, wymagaj przeglądu przez współpracownika dla każdego nowego wpisu zezwolenia i audytuj zestaw polityk kwartalnie względem listy aktualnie działających serwisów. Po drugie, używaj logowania dostępu siatki — każde odrzucone żądanie generuje wpis w dzienniku dostępu sidecara zawierający źródłowy SVID, cel i operację — aby identyfikować legalne ścieżki ruchu, którym brakuje polityki, zamiast po cichu omijać siatkę. To logowanie zapewnia również ślad forensyczny wymagany przez wiele obronnych ram bezpieczeństwa do wykazania, że decyzje kontroli dostępu są audytowalne.
Kluczowa obserwacja: Najczęstszym błędem konfiguracyjnym we wdrożeniach zero-trust service mesh jest stosowanie PeerAuthentication w trybie PERMISSIVE podczas okresu migracji i nigdy nieukończenie przejścia do trybu STRICT. W trybie PERMISSIVE akceptowane są zarówno połączenia TLS, jak i plaintext, co oznacza, że siatka zapewnia poświadczanie tożsamości tylko dla połączeń, które dobrowolnie przedstawiają certyfikat. Przeciwnik działający z poda bez siatki — skompromitowany kontener init, pod debugowania przypadkowo pozostawiony działający, obciążenie DaemonSet, które nigdy nie zostało wstrzyknięte sidecar — może komunikować się z dowolnym serwisem w plaintext, całkowicie omijając weryfikację tożsamości. Wyznacz konkretny termin dla trybu STRICT i używaj istioctl analyze do wyliczania wszelkich pozostałych ścieżek plaintext przed tą datą.
Obserwowalność bez eksfiltracji danych: metryki i śledzenie wewnątrz klastra
Service mesh automatycznie generuje bogatą telemetrię ze swoich proxy sidecar: częstotliwości żądań na serwis, wskaźniki błędów podzielone według kodu statusu HTTP i statusu gRPC, percentyle opóźnień (p50, p95, p99), liczby aktywnych połączeń oraz metryki uzgadniania TLS. W komercyjnym wdrożeniu chmurowym ta telemetria jest zazwyczaj przekazywana do platformy obserwowalności SaaS. W niejawnym klastrze wojskowym każdy potok telemetrii wysyłający dane poza granicę klasyfikacji jest potencjalnym wektorem eksfiltracji i jest architektonicznie zakazany. Cały stos obserwowalności musi być wdrożony wewnątrz granicy klastra i sam musi być kontrolowany dostępem przez te same polityki siatki, które regulują ruch aplikacji.
Standardowy stos obserwowalności wewnątrz klastra dla service mesh składa się z Prometheus do zbierania metryk, Grafana do wizualizacji oraz Jaeger lub Tempo do rozproszonego śledzenia. Prometheus zbiera endpoint statystyk Envoy (port 15090) lub endpoint metryk linkerd-proxy na każdym meshed podzie w konfigurowalnych odstępach. Grafana łączy się wyłącznie z instancjami Prometheus i Loki wewnątrz klastra — nie są konfigurowane żadne zewnętrzne połączenia źródłowe danych. Jaeger odbiera spany śladów przekazywane przez sidecar Envoy przez protokół Zipkin lub OpenTelemetry do endpointu kolektora lokalnego dla klastra. Wszystkie trwałe przechowywanie metryk, śladów i logów używa woluminów PersistentVolume lokalnych dla klastra, wspieranych przez zaszyfrowane przechowywanie blokowe. Przestrzeń nazw monitorowania sama w sobie zawiera NetworkPolicy blokującą cały egres do zewnętrznych zakresów IP, czyniąc architektonicznie niemożliwym opuszczenie klastra przez telemetrię nawet w przypadku wprowadzenia błędnie skonfigurowanego potoku.
W przypadku klastrów Kubernetes utwardzonych dla obciążeń obronnych warto zauważyć, że stos obserwowalności musi być również pobrany z rejestrów air-gapped. Obrazy Prometheus, Grafana, Jaeger i Loki muszą być zreplikowane do wewnętrznego rejestru ze zweryfikowanymi skrótami przed umieszczeniem klastra za granicą klasyfikacji. Polityki pobierania obrazów muszą być ustawione na Never lub IfNotPresent ze świadomością, że obrazy nigdy nie będą odświeżane z zewnętrznego źródła. Każda aktualizacja stosu obserwowalności wymaga formalnego procesu zarządzania zmianą, który wprowadza zaktualizowane obrazy przez procedurę air-gap, a nie prostego uaktualnienia wykresu Helm względem publicznego repozytorium wykresów.
Narzut sidecar w obronnych klastrach brzegowych o ograniczonych zasobach
Klastry brzegowe wdrożone blisko operacji — działające na serwerach o małym rozmiarze lub utwardzonych platformach obliczeniowych z 32–64 GB pamięci RAM i 4–8 węzłami — mają budżet zasobów, który czyni narzut sidecar realnym ograniczeniem inżynieryjnym, a nie kwestią teoretyczną. Sidecar Envoy Istio w stanie ustalonym zużywa 50–100 MB resident set size i do 0,5 vCPU podczas okresów wysokiej przepustowości. Klaster uruchamiający 40 podów zużywa 2–4 GB łącznej pamięci sidecar, czyli około 6–12% całkowitej pamięci 32 GB klastra. Sama płaszczyzna sterowania — istiod, Prometheus, Grafana, Jaeger — dodaje kolejne 2–4 GB, przynosząc łączny narzut infrastruktury siatki do 15–25% pamięci klastra, zanim uwzględnione zostaną jakiekolwiek obciążenia aplikacji.
Trzy opcje redukują ten narzut w ograniczonych wdrożeniach. Po pierwsze, proxy Linkerd oparte na Rust jest najbardziej wydajnym dostępnym sidecarem: 10–20 MB na pod zamiast 50–100 MB dla Envoy, a narzut opóźnienia poniżej 1 ms na skok. Dla klastrów, gdzie wymagania granularności polityki L7 mogą być spełnione przez model polityk Linkerd, oszczędności pamięci są znaczące. Po drugie, tryb ambient mesh Istio (dostępny od Istio 1.22 wzwyż) całkowicie eliminuje sidecary na pod, zastępując je proxy ztunnel na węzeł obsługującym mTLS dla wszystkich podów na węźle. Tryb ambient redukuje narzut na pod do prawie zera kosztem przeniesienia egzekwowania polityki z sidecarów na obciążenie do proxy na węzeł, co zapewnia nieco grubszą granularność polityki dla obciążeń wymagających decyzji autoryzacji na żądanie. Po trzecie, selektywne wstrzykiwanie — wstrzykiwanie sidecarów tylko do podów wymagających egzekwowania polityki L7 i używanie szyfrowania L4 opartego na eBPF Cilium dla reszty — balansuje granularność egzekwowania względem zużycia zasobów dla heterogenicznych obciążeń klastra.
Warto wyraźnie odnotować interakcję między zużyciem zasobów przez sidecar a zarządzaniem tajnymi danymi w obronnych potokach CI/CD. Sidecary wstrzykiwania tajnych danych (Vault Agent Injector, Secrets Store CSI Driver) dodają kolejny kontener na pod. Klaster brzegowy uruchamiający zarówno sidecar siatki, jak i sidecar wstrzykiwania tajnych danych na pod aplikacji ma dwa kontenery infrastruktury na każdy kontener aplikacji, podwajając liczbę kontenerów i dodając 60–120 MB narzutu infrastruktury na parę podów. Konsolidacja jest możliwa: agent SPIRE może służyć zarówno jako wystawca tożsamości siatki, jak i mechanizm dostarczania tajnych danych do obciążeń dla tajnych danych, które mogą być wyrażone jako role Vault autoryzowane przez SVID, eliminując oddzielny sidecar wstrzykiwania tajnych danych dla tych obciążeń.
Integracja polityki service mesh z RBAC i zarządzaniem tajnymi danymi
Kubernetes RBAC reguluje, kto może odczytywać i modyfikować obiekty API Kubernetes — w tym zasoby AuthorizationPolicy, PeerAuthentication i konfiguracji siatki, które definiują postawę bezpieczeństwa service mesh. Jeśli konfiguracja RBAC zezwala szerokiemu zestawowi podmiotów na modyfikację obiektów AuthorizationPolicy, warstwa kontroli dostępu siatki może zostać osłabiona przez dowolny z tych podmiotów. Prawidłowe wdrożenie obronne traktuje manifesty polityk siatki jako obiekty bezpieczeństwa wysokiej wartości: tylko konto serwisowe CI/CD, które stosuje manifesty z repozytorium polityki kontrolowanego wersją, powinno mieć dostęp do zapisu zasobów AuthorizationPolicy i PeerAuthentication, a tylko administratorzy klastra powinni być w stanie modyfikować wpisy rejestracji SPIRE. Wszystkie inne podmioty — w tym indywidualne konta serwisowe aplikacji — powinny mieć dostęp tylko do odczytu lub nie mieć dostępu do tych obiektów.
Relacja między warstwą tożsamości siatki a warstwą zarządzania tajnymi danymi jest dwukierunkowa. Siatka używa SVID SPIFFE do uwierzytelniania wywołań między serwisami na warstwie transportowej. Zarządzanie tajnymi danymi (Vault, Kubernetes Secrets Store CSI Driver) używa tych samych tożsamości opartych na SVID do określenia, które obciążenia mogą otrzymać jakie tajne dane na warstwie aplikacji. Obciążenie z SPIFFE ID spiffe://cluster.local/ns/fusion/sa/data-processor może być mapowane do roli Vault, która przyznaje dostęp do odczytu dokładnie tych poświadczeń bazy danych, których ten serwis potrzebuje — i niczego więcej. Oznacza to, że system tożsamości siatki i system dostarczania tajnych danych współdzielą jedno autorytatywne źródło tożsamości obciążenia, eliminując ryzyko niezgodności, gdzie obciążenie ma dozwolony dostęp sieciowy, ale nie tajne dane wymagane do jego użycia, lub odwrotnie. Utrzymanie tego wyrównania wymaga, aby wpisy rejestracji SPIRE, definicje ról Vault i manifesty AuthorizationPolicy siatki były aktualizowane razem przy dodawaniu, modyfikacji lub wycofywaniu serwisu.
Logowanie audytowe spaja trzy systemy dla zgodności i reagowania na incydenty. Sidecar siatki rejestruje każdą decyzję dostępu — zezwolenie lub odmowa — ze zweryfikowanym źródłowym SVID, celem, operacją i znacznikiem czasu. Vault rejestruje każdy dostęp do tajnych danych z identyfikatorem żądającego i ścieżką dostępu do tajnych danych. Dzienniki audytu serwera API Kubernetes rejestrują każde wywołanie API regulowane przez RBAC. Razem te trzy strumienie logów zapewniają pełny ślad audytowy od „które wywołanie sieciowe zostało wykonane" przez „do jakich tajnych danych uzyskano dostęp w rezultacie" do „kto zmienił politykę regulującą oba". Dla środowisk obronnych działających w ramach formalnych wymagań akredytacyjnych, ta śledzalność przez warstwy tożsamości, kontroli dostępu i tajnych danych jest często niezbędnym wymogiem zgodności, a nie wygodą operacyjną.
Bezpieczeństwo komunikacji między serwisami w środowiskach niejawnych
Corvus QUANTUM jest zbudowany na zasadach zero-trust z wzajemnym TLS w całej komunikacji między serwisami, egzekwowaną polityką ruchu i obserwowalność wewnątrz klastra spełniającą wymagania niejawnych środowisk wojskowych.
Niniejsza analiza została przygotowana przez inżynierów Corvus Intelligence, którzy tworzą aplikacje ISR i polowe o znaczeniu krytycznym dla organizacji obronnych i rządowych. Poznaj nasz zespół →