Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Przyp. tłum.: Autor artykułu — Reuven Harrison — ma ponad 20-letnie doświadczenie w tworzeniu oprogramowania, a obecnie jest dyrektorem technicznym i współzałożycielem firmy Tufin, która tworzy rozwiązania do zarządzania politykami bezpieczeństwa. Rozważając polityki sieciowe Kubernetes jako wystarczająco potężne narzędzie do segmentacji sieci w klastrze, jednocześnie uważa, że nie są one tak łatwe w zastosowaniu w praktyce. Ten materiał (dość obszerny) ma na celu zwiększenie świadomości specjalistów w tej kwestii i pomoc im w tworzeniu niezbędnych konfiguracji.

Dziś wiele firm coraz częściej wybiera Kubernetes do uruchamiania swoich aplikacji. Zainteresowanie tym oprogramowaniem jest na tyle wysokie, że niektórzy nazywają Kubernetes „nowym systemem operacyjnym dla centrów danych”. Stopniowo Kubernetes (lub k8s) zaczyna być postrzegany jako krytyczna część biznesu, która wymaga organizacji dojrzałych procesów biznesowych, w tym zapewnienia bezpieczeństwa sieciowego.

Dla specjalistów ds. bezpieczeństwa, którzy zmagają się z pracą z Kubernetes, prawdziwym odkryciem może być domyślna polityka tej platformy: zezwalaj na wszystko.

Ten przewodnik pomoże zrozumieć wewnętrzną strukturę polityk sieciowych; wyjaśni, czym różnią się od zasad dla zwykłych zapór ogniowych. Opisane będą również niektóre pułapki oraz udzielone zalecenia, które pomogą zabezpieczyć aplikacje w Kubernetes.

Polityki sieciowe Kubernetes

Mechanizm polityk sieciowych Kubernetes pozwala zarządzać interakcjami wdrożonych na platformie aplikacji na poziomie sieciowym (trzecim w modelu OSI). Polityki sieciowe nie mają niektórych zaawansowanych funkcji nowoczesnych zapór ogniowych, takich jak kontrola na 7 poziomie OSI i wykrywanie zagrożeń, jednak zapewniają podstawowy poziom bezpieczeństwa sieci, który stanowi dobry punkt wyjścia.

Polityki sieciowe kontrolują komunikację między podami

Obciążenia w Kubernetes są rozdzielane na pod'y, które składają się z jednego lub kilku kontenerów wspólnie wdrożonych. Kubernetes przypisuje każdemu pod'owi adres IP, dostępny z innych pod'ów. Polityki sieciowe Kubernetes określają uprawnienia dostępu dla grup pod'ów w sposób analogiczny do grup zabezpieczeń w chmurze, które służą do zarządzania dostępem do instancji maszyn wirtualnych.

Określenie polityk sieciowych

Podobnie jak inne zasoby Kubernetes, polityki sieciowe są definiowane w języku YAML. W poniższym przykładzie aplikacja balance uzyskuje dostęp do postgres:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: balance
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

(Przyp. tłum.: ten zrzut ekranu, podobnie jak wszystkie następne podobne, został utworzony nie za pomocą natywnych narzędzi Kubernetes, lecz przy użyciu narzędzia Tufin Orca, za rozwojem którego stoi firma autora oryginalnego artykułu i które jest wspomniane na końcu materiału.

Aby określić własną politykę sieciową, potrzebna będzie podstawowa znajomość YAML. Ten język opiera się na wcięciach (ustawianych spacjami, a nie tabulacjami). Element z wcięciem należy do najbliższego elementu z wcięciem powyżej. Nowy element listy rozpoczyna się od myślnika, pozostałe elementy mają postać klucz-wartość.

Opisując politykę w YAML, użyj kubectl, aby utworzyć ją w klastrze:

kubectl create -f policy.yaml

Specyfikacja polityki sieciowej

Specyfikacja polityki sieciowej Kubernetes zawiera cztery elementy:

  1. podSelector: określa pod'y, na które ta polityka ma wpływ (cele) — obowiązkowy;
  2. policyTypes: wskazuje, jakie typy polityk są zawarte: ingress i/lub egress — opcjonalny, jednak zalecam jego wyraźne określenie we wszystkich przypadkach;
  3. ingress: definiuje dozwolony przychodzący ruch do docelowych pod'ów — opcjonalny;
  4. egress: definiuje dozwolony ruch wychodzący z docelowych pod'ów — opcjonalny.

Przykład pochodzi ze strony Kubernetes (zmieniłem role na app), pokazuje, jak używane są wszystkie cztery elementy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:    # <<<
    matchLabels:
      app: db
  policyTypes:    # <<<
  - Ingress
  - Egress
  ingress:        # <<<
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:         # <<<
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.
Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Zauważ, że wszystkie cztery elementy nie są obowiązkowe. Obowiązkowy jest tylko podSelector, pozostałe parametry mogą być używane według uznania.

Jeśli pominiesz policyTypes, polityka będzie interpretowana w następujący sposób:

  • Domyślnie zakłada się, że definiuje stronę ingress. Jeśli w polityce nie ma wyraźnych wskazówek w tej sprawie, system uzna, że cały ruch jest zabroniony.
  • Zachowanie na stronie egress będzie określane przez obecność lub brak odpowiedniego parametru egress.

Aby uniknąć błędów, zalecam zawsze wyraźnie wskazywać policyTypes.

Zgodnie z powyższą logiką, jeśli parametry ingress i/lub egress są pominięte, polityka będzie zabraniać całego ruchu (patrz 'Reguła czyszczenia' poniżej).

Polityka domyślna — zezwól

Jeśli polityki nie są zdefiniowane, Kubernetes domyślnie zezwala na cały ruch. Wszystkie pod’y mogą swobodnie wymieniać informacje między sobą. Z punktu widzenia bezpieczeństwa może to wydawać się nielogiczne, ale pamiętaj, że Kubernetes był pierwotnie tworzony przez programistów z zamiarem zapewnienia interakcji aplikacji. Polityki sieciowe zostały dodane później.

Przestrzenie nazw

Przestrzenie nazw (Namespaces) to mechanizm wspólnej pracy Kubernetes. Służą do izolowania logicznych środowisk od siebie, przy czym wymiana danych między przestrzeniami jest domyślnie dozwolona.

Podobnie jak większość komponentów Kubernetes, polityki sieciowe znajdują się w określonym przestrzeni nazw. W bloku metadata można określić, do któregokolwiek przestrzeni należy polityka:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: my-namespace  # <<<
spec:
...

Jeśli przestrzeń nazw w metadanych nie jest wyraźnie określona, system użyje namespace podanego w kubectl (domyślnie namespace=default):

kubectl apply -n my-namespace -f namespace.yaml

Zalecam wyraźnie określać namespace, chyba że piszesz politykę przeznaczoną od razu dla wielu przestrzeni nazw.

Podstawowy element podSelector w polityce będzie wybierał pod’y z przestrzeni nazw, do której przynależy polityka (nie ma dostępu do pod’ów z innych przestrzeni nazw).

Podobnie podSelector’y w blokach ingress i egress mogą wybierać pod’y tylko ze swojej przestrzeni nazw, chyba że połączysz je z pomocą namespaceSelector o tym mowa w rozdziale „Filtruj według przestrzeni nazw i pod’ów”.

Zasady nazewnictwa polityk

Nazwy polityk są unikalne w ramach jednej przestrzeni nazw. Nie mogą występować dwie polityki o tej samej nazwie w jednej przestrzeni, ale mogą istnieć polityki o identycznych nazwach w różnych przestrzeniach. Jest to wygodne, gdy chcesz ponownie zastosować tę samą politykę w kilku przestrzeniach.

Szczególnie podoba mi się jeden ze sposobów nazewnictwa. Polega on na łączeniu nazwy przestrzeni nazw z docelowymi pod’ami. Na przykład:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres  # <<<
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: admin
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Etykiety

Można przypinać niestandardowe etykiety do obiektów Kubernetes, takich jak pod’y i przestrzenie nazw. Etykiety (labels — oznaczenia) są ekwiwalentem tagów w chmurze. Polityki sieciowe Kubernetes używają etykiet do wybierania pod’y, do których są stosowane:

podSelector:
  matchLabels:
    role: db

… lub przestrzeni nazw, do których są stosowane. W tym przykładzie wybierane są wszystkie pod’y w przestrzeniach nazw z odpowiednimi etykietami:

namespaceSelector:
  matchLabels:
    project: myproject

Jedno ostrzeżenie: przy użyciu namespaceSelector upewnij się, że wybierane przestrzenie nazw zawierają wymagane etykiety. Pamiętaj, że wbudowane przestrzenie nazw, takie jak default i kube-system, domyślnie nie zawierają etykiet.

Etykietę do przestrzeni można dodać w następujący sposób:

kubectl label namespace default namespace=default

Przy czym namespace w sekcji metadata powinien odnosić się do faktywnego nazwy przestrzeni, a nie do etykiety:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default   # <<<
spec:
...

Źródło i adresat

Polityki zapory składają się z reguł z adresatami i źródłami. Polityki sieciowe Kubernetes są określane dla celów — zestawu podów, do których są stosowane, a następnie ustalają reguły dla ruchu przychodzącego (ingress) i/lub wychodzącego (egress). W naszym przykładzie celem polityki będą wszystkie pody w przestrzeni nazw default z etykietą z kluczem app i wartością db:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: db   # <<<
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.
Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Podsekcja ingress w tej polityce otwiera ruch przychodzący do docelowych podów. Innymi słowy, ingress działa jako źródło, a cel — odpowiedni adresat. Podobnie egress jest adresatem, a cel — jego źródłem.

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

To odpowiada dwóm regułom dla zapory: Ingress → Cel; Cel → Egress.

Egress i DNS (ważne!)

Ograniczając ruch wychodzący, szczególną uwagę zwróć na DNS — Kubernetes używa tej usługi do mapowania usług na adresy IP. Na przykład, następująca polityka nie zadziała, ponieważ nie zezwoliłeś aplikacji balance na dostęp do DNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  policyTypes:
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Możesz to poprawić, otwierając dostęp do usługi DNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:               # <<<
    ports:            # <<<
    - protocol: UDP   # <<<
      port: 53        # <<<
  policyTypes:
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Ostatni element to — pusty, więc pośrednio wybiera wszystkie pody we wszystkich przestrzeniach nazw, pozwalając balance wysyłać zapytania DNS do odpowiedniej usługi Kubernetes (zwykle działa w przestrzeni kube-system).

To podejście działa, jednak jest nadmiernie zezwalające i niebezpieczne, ponieważ pozwala kierować zapytania DNS poza klaster.

Można je poprawić w trzech kolejnych krokach.

1. Zezwolić na zapytania DNS tylko wciąż do klastra, dodając namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:
    - namespaceSelector: {} # <<<
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

2. Zezwól na zapytania DNS tylko w określonej przestrzeni nazw kube-system.

Aby to zrobić, musisz dodać etykietę do przestrzeni nazw kube-system: kubectl label namespace kube-system namespace=kube-system — i umieścić ją w polityce za pomocą namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgres
  - to:
    - namespaceSelector:         # <<<
        matchLabels:             # <<<
          namespace: kube-system # <<<
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

3. Paranoicy mogą pójść jeszcze dalej i ograniczyć zapytania DNS do określonej usługi DNS w kube-system. W sekcji „Filtrowanie według przestrzeni nazw i podów” omówimy, jak to osiągnąć.

Inną opcją jest zezwolenie na DNS na poziomie przestrzeni nazw. W takim przypadku nie trzeba go otwierać dla każdej usługi:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.dns
  namespace: default
spec:
  podSelector: {} # <<<
  egress:
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
  policyTypes:
  - Egress

Pusty podSelector wybiera wszystkie pody w przestrzeni nazw.

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Pierwsze dopasowanie i kolejność reguł

W zwykłych zaporach ogniowych działanie („Zezwól” lub „Zabroń”) w odniesieniu do pakietu jest określane przez pierwszą regułę, która jest spełniona. W Kubernetes kolejność polityk nie ma znaczenia.

Domyślnie, gdy nie określono polityk, komunikacja między podami jest dozwolona i mogą one swobodnie wymieniać informacje. Gdy tylko zaczynasz formułować polityki, każdy pod, który jest objęty przynajmniej jedną z nich, staje się izolowany w zgodzie z dysjunkcją (logicznym LUB) wszystkich polityk, które go wybrały. Pody, które nie są objęte żadną polityką, pozostają otwarte.

Można zmienić to zachowanie za pomocą reguły oczyszczania.

Reguła oczyszczania („Zabroń”)

Polityki zapór ogniowych zazwyczaj zabraniają dowolnego ruchu, który nie jest wyraźnie dozwolony.

W Kubernetes nie ma akcji „zabroń” (deny),, jednak podobny efekt można osiągnąć za pomocą zwykłej (zezwalającej) polityki, wybierając pustą grupę podów źródłowych (ingress):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Ta polityka wybiera wszystkie pod'y w przestrzeni nazw i pozostawia ingress nieokreślony, zabraniając całego ruchu przychodzącego.

Podobnie można ograniczyć cały ruch wychodzący z przestrzeni nazw:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Uwaga, że jakiekolwiek dodatkowe polityki, które zezwalają na ruch do pod'ów w przestrzeni nazw, będą miały priorytet nad tą regułą (analogicznie do dodawania reguły zezwalającej przed zabraniającą w konfiguracji zapory).

Zezwól na wszystko (Any-Any-Any-Allow)

Aby stworzyć politykę 'Zezwól na wszystko', należy uzupełnić powyższą politykę zabraniającą pustym elementem ingress:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
  namespace: default
spec:
  podSelector: {}
  ingress: # <<<<
  - {}     # <<<<
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Otwiera dostęp z wszystkich pod'ów we wszystkich przestrzeniach nazw (i wszystkich adresów IP) do dowolnego pod'a w przestrzeni nazw default. Takie zachowanie jest domyślne, więc zazwyczaj nie trzeba go definiować dodatkowo. Jednak czasami może być konieczne tymczasowe wyłączenie niektórych specyficznych zezwoleń w celu diagnostyki problemu.

Regułę można zawęzić i zezwolić na dostęp tylko do określonego zestawu pod'ów (app:balance) w przestrzeni nazw default:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-to-balance
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: balance
  ingress: 
  - {}
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Następująca polityka zezwala na cały ruch przychodzący (ingress) i wychodzący (egress), w tym dostęp do dowolnego adresu IP poza klastrem:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}
  egress:
  - {}
  policyTypes:
  - Ingress
  - Egress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.
Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Łączenie wielu polityk

Polityki łączą się za pomocą logicznego LUB na trzech poziomach; zezwolenia każdego pod'a są ustalane na podstawie dysjunkcji wszystkich polityk, które go dotyczą:

1. W polach od i to można określić trzy typy elementów (wszystkie są kombiowane za pomocą LUB):

  • namespaceSelector — wybiera całą przestrzeń nazw;
  • podSelector — wybiera pod'y;
  • ipBlock — wybiera podsieć.

Przy tym liczba elementów (nawet tych samych) w sekcjach od/to nie jest ograniczona. Wszystkie będą połączone logicznym LUB.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    - podSelector:
        matchLabels:
          app: admin
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

2. W obrębie polityki sekcja ingress może mieć wiele elementów od (łączą się za pomocą logicznego LUB). Podobnie sekcja egress może zawierać wiele elementów to (również łączą się za pomocą dysjunkcji):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
  - from:
    - podSelector:
        matchLabels:
          app: admin
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

3. Różne polityki są również łączone za pomocą logicznego LUB

Ale podczas ich łączenia istnieje jedno ograniczenie, na które zaznaczył Chris Cooney: Kubernetes może łączyć polityki tylko z różnymi policyTypes (Ingress lub Egress). Polityki określające ingress (lub egress) nadpiszą się nawzajem.

Relacja między przestrzeniami nazw

Domyślnie wymiana informacji między przestrzeniami nazw jest dozwolona. Można to zmienić za pomocą polityki blokującej, która ograniczy wychodzący i/lub przychodzący ruch do przestrzeni nazw (zob. "Reguła czyszcząca" powyżej).

Blokując dostęp do przestrzeni nazw (zob. "Reguła czyszcząca" powyżej), możesz wprowadzić wyjątki w polityce blokującej, zezwalając na połączenia z określonej przestrzeni nazw za pomocą namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector: # <<<
        matchLabels:
          namespace: default
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

W rezultacie wszystkie pod’y w przestrzeni nazw default uzyskają dostęp do pod’ów postgres w przestrzeni nazw database. Ale co, jeśli chcesz otworzyć dostęp do postgres tylko konkretnych pod’ów w przestrzeni nazw default?

Filtr według przestrzeni nazw i pod’ów

Kubernetes w wersji 1.11 i wyższej pozwala na łączenie operatorów namespaceSelector i podSelector za pomocą logicznego I. Wygląda to następująco:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          namespace: default
      podSelector: # <<<
        matchLabels:
          app: admin
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Dlaczego to jest interpretowane jako I zamiast zwykłego LUB?

Zauważ, że podSelector nie zaczyna się od myślnika. W YAML oznacza to, że podSelector i stojący przed nim namespaceSelector należą do tego samego elementu listy. Dlatego łączą się za pomocą logicznego I.

Dodanie myślnika przed podSelector spowoduje powstanie nowego elementu listy, który będzie łączony z poprzednim namespaceSelector przy użyciu logicznego LUB.

Aby wybrać pod’y z określoną etykietą we wszystkich przestrzeniach nazw, wpisz pusty namespaceSelector:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database.postgres
  namespace: database
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
  - from:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          app: admin
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Wielokrotne etykiety są łączone z AND

Reguły dla zapory ogniowej z wieloma obiektami (hostami, sieciami, grupami) są łączone przy użyciu logicznego LUB. Następująca reguła zadziała, jeśli źródło pakietu odpowiada Host_1 LUB Host_2:

| Źródło | Cel | Usługa | Działanie |
| ----------------------------------------|
| Host_1 | Subnet_A    | HTTPS   | Zezwól  |
| Host_2 |             |         |        |
| ----------------------------------------|

Przeciwnie, w Kubernetes różne etykiety w podSelector lub namespaceSelector są łączone logicznym AND. Na przykład, następująca reguła wybierze pod’y, które mają obie etykiety, role=db I version=v2:

podSelector:
  matchLabels:
    role: db
    version: v2

Ta sama logika ma zastosowanie do wszystkich typów operatorów: selektorów celów polityki, selektorów pod’ów i selektorów przestrzeni nazw.

Podsieci i adresy IP (IPBlocks)

Do segmentacji sieci zapory ogniowe używają VLAN, adresów IP i podsieci.

W Kubernetes adresy IP są automatycznie przypisywane pod’om i mogą często się zmieniać, dlatego do wyboru pod’ów i przestrzeni nazw w politykach sieciowych używa się etykiet.

Podsieci (ipBlocks) są używane do zarządzania połączeniami przychodzącymi (ingress) lub wychodzącymi (egress) zewnętrznymi (North-South). Na przykład ta polityka otwiera wszystkim pod’om z przestrzeni nazw default dostęp do serwisu DNS Google:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-dns
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 8.8.8.8/32
    ports:
    - protocol: UDP
      port: 53

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Pusty selektor pod’ów w tym przykładzie oznacza „wybierz wszystkie pod’y w przestrzeni nazw”.

Ta polityka otwiera dostęp tylko do 8.8.8.8; dostęp do jakiegokolwiek innego IP jest zablokowany. Tak więc, zasadniczo zablokowałeś dostęp do wewnętrznej usługi DNS Kubernetes. Jeśli chcesz to jednak otworzyć, zrób to wyraźnie.

Zazwyczaj ipBlocks i podSelectors są wykluczające, ponieważ wewnętrzne adresy IP pod’ów nie są używane w ipBlocks. Określając wewnętrzne IP pod’ów, w rzeczywistości zezwolisz na połączenia z/od pod’ów z tymi adresami. W praktyce nie będziesz wiedzieć, jaki adres IP użyć, dlatego nie należy ich stosować do wybierania pod’ów.

Jako kontropis przykład, następna polityka obejmuje wszystkie adresy IP i tym samym zezwala na dostęp do wszystkich innych pod’ów:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-any
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Można otworzyć dostęp tylko do zewnętrznych adresów IP, wykluczając wewnętrzne adresy IP pod’ów. Na przykład, jeśli podsieć twojego pod’a to 10.16.0.0/14:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-any
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 10.16.0.0/14

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Porty i protokoły

Zazwyczaj pod’y nasłuchują na jednym porcie. Oznacza to, że można po prostu nie podawać numerów portów w politykach i pozostawić wszystko domyślnie. Niemniej jednak polityki zaleca się, aby były jak najbardziej restrykcyjne, dlatego w niektórych przypadkach warto jednak podać porty:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    - podSelector:
        matchLabels:
          app: admin
    ports:             # <<<
      - port: 443      # <<<
        protocol: TCP  # <<<
      - port: 80       # <<<
        protocol: TCP  # <<<
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Zauważ, że selektor porty jest stosowany do wszystkich elementów w bloku to lub od, w którym się znajduje. Aby wskazać różne porty dla różnych zestawów elementów, podziel je ingress lub egress na kilka podsekcji z to lub od i w każdej podaj swoje porty:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default.postgres
  namespace: default
spec:
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: indexer
    ports:             # <<<
     - port: 443       # <<<
       protocol: TCP   # <<<
  - from:
    - podSelector:
        matchLabels:
          app: admin
    ports:             # <<<
     - port: 80        # <<<
       protocol: TCP   # <<<
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
  - Ingress

Wprowadzenie do polityk sieciowych Kubernetes dla specjalistów ds. bezpieczeństwa.

Działanie portów domyślnie:

  • Jeśli całkowicie pomijasz definicję portów (porty), oznacza to wszystkie protokoły i wszystkie porty;
  • Jeśli pomijasz definicję protokołu (protocol), oznacza to TCP;
  • Jeśli pomijasz definicję portu (port), oznacza to wszystkie porty.

Najlepsza praktyka: nie polegaj na wartościach domyślnych, podawaj to, czego potrzebujesz, wyraźnie.

Zwróć uwagę, że należy używać portów podów, a nie usług (więcej na ten temat w następnym akapicie).

Czy polityki są zdefiniowane dla podów czy usług?

Zazwyczaj pody w Kubernetes komunikują się ze sobą za pośrednictwem usługi — wirtualnego load balancera, który przekierowuje ruch do podów realizujących usługę. Można by pomyśleć, że polityki sieciowe kontrolują dostęp do usług, ale tak nie jest. Polityki sieciowe Kubernetes działają na portach podów, a nie usług.

Na przykład, jeśli usługa nasłuchuje na porcie 80, ale przekierowuje ruch na port 8080 swoich podów, w polityce sieciowej należy podać właśnie 8080.

Taki mechanizm można uznać za suboptymalny: przy zmianie wewnętrznej struktury usługi (porty, na których nasłuchują pody) będzie trzeba aktualizować polityki sieciowe.

Nowe podejście architektoniczne z wykorzystaniem Service Mesh (na przykład, zobacz Istio poniżej — przyp. tłum.) pozwala poradzić sobie z tym problemem.

Czy konieczne jest zdefiniowanie zarówno Ingress, jak i Egress?

Krótką odpowiedzią jest — tak, aby pod A mógł skontaktować się z pod B, należy zezwolić mu na nawiązanie połączenia wychodzącego (w tym celu należy skonfigurować politykę egress), a pod B musi mieć możliwość przyjęcia połączenia przychodzącego (w tym celu, odpowiednio, potrzebna jest polityka ingress).

Jednak w praktyce można polegać na domyślnej polityce, która zezwala na połączenia w jednym lub obu kierunkach.

Jeśli jakiś pod-ClickHouse-Ninja/Proton zostanie wybrany przez jedną lub kilka egress-polityk, nałożone na niego ograniczenia będą określane ich dysjunkcją. W takim przypadku trzeba będzie jawnie zezwolić na połączenie z podem-adresatem. Jeśli pod nie zostanie wybrany przez żadną politykę, jego wychodzący (egress) ruch jest domyślnie dozwolony.

Podobnie, los poda-adresata, który został wybrany przez jedną lub kilka ingress-polityk, będzie określany ich dysjunkcją. W takim przypadku konieczne jest jawne zezwolenie mu na odbieranie ruchu z poda-źródła. Jeśli pod nie zostanie wybrany przez żadną politykę, cały przychodzący (ingress) ruch dla niego jest domyślnie dozwolony.

Zob. punkt „Stateful czy Stateless” poniżej.

Logi

Polityki sieciowe Kubernetes nie potrafią rejestrować ruchu. Utrudnia to określenie, czy polityka działa poprawnie, i znacznie utrudnia analizę w zakresie bezpieczeństwa.

Kontrola ruchu do zewnętrznych usług

Polityki sieciowe Kubernetes nie pozwalają na wskazywanie pełnej nazwy domeny (DNS) w sekcjach egress. To powoduje znaczne niedogodności przy próbie ograniczenia ruchu do zewnętrznych adresatów, które nie mają stałego adresu IP (takich jak aws.com).

Weryfikacja polityki

Zapory sieciowe powiadomią Cię lub nawet odmówią przyjęcia błędnej polityki. Kubernetes również dokonuje pewnej weryfikacji. Przy określaniu polityki sieciowej za pomocą kubectl, Kubernetes może zadeklarować, że jest ona nieprawidłowa i odmówi jej przyjęcia. W innych przypadkach Kubernetes przyjmie politykę i uzupełni ją brakującymi szczegółami. Można je zobaczyć za pomocą polecenia:

kubernetes get networkpolicy  -o yaml

Pamiętaj, że system weryfikacji Kubernetes nie jest nieomylny i może przeoczyć niektóre typy błędów.

Wykonanie

Kubernetes nie zajmuje się wdrażaniem polityk sieciowych samodzielnie, a jedynie jest bramką API, która nakłada obowiązek kontroli na podległy system, zwany Container Networking Interface (CNI). Ustalanie polityk w klastrze Kubernetes bez odpowiedniego przypisania CNI jest podobne do tworzenia polityk na serwerze zarządzania zaporą bez ich późniejszego zainstalowania w zaporach. Musisz sam upewnić się, że masz odpowiednie CNI lub, w przypadku platform Kubernetes osadzonych w chmurze, (można zapoznać się z listą dostawców tutaj — przyp. red.), aktywować polityki sieciowe, które zainstalują CNI za Ciebie.

Zwróć uwagę, że Kubernetes nie powiadomi Cię, jeśli ustalisz politykę sieciową bez odpowiedniego wspomagającego CNI.

Stanowy czy bezstanowy?

Wszystkie CNI Kubernetes, z którymi miałem do czynienia, przechowują stan (na przykład Calico używa Linux conntrack). Umożliwia to podowi odbieranie odpowiedzi dla nawiązanych przez niego połączeń TCP bez konieczności ponownego ich ustanawiania. Nie znam jednak standardu Kubernetes, który gwarantowałby utrzymanie stanu (statefulness).

Zaawansowane zarządzanie polityką bezpieczeństwa

Oto kilka sposobów na zwiększenie efektywności wykonania polityki bezpieczeństwa w Kubernetes:

  1. Wzorzec architektoniczny Service Mesh wykorzystuje kontenery sidecar do zapewnienia szczegółowej telemetrii i kontroli ruchu na poziomie serwisów. Jako przykład można podać Istio.
  2. Niektórzy dostawcy CNI rozszerzyli swoje narzędzia, aby wykraczać poza polityki sieciowe Kubernetes.
  3. Tufin Orca zapewnia przezroczystość i automatyzację polityk sieciowych Kubernetes.

Pakiet Tufin Orca zarządza politykami sieciowymi Kubernetes (i służy jako źródło zrzutów ekranowych podanych powyżej).

Dodatkowe informacje

Podsumowanie

Polityki sieciowe Kubernetes oferują dobrą gamę narzędzi do segmentacji klastrów, jednak są intuicyjnie nieczytelne i mają wiele niuansów. Uważam, że z powodu tej złożoności polityki wielu istniejących klastrów zawierają błędy. Możliwymi rozwiązaniami tego problemu są automatyzacja definicji polityk lub zastosowanie innych narzędzi do segmentacji.

Mam nadzieję, że ten przewodnik pomoże wyjaśnić pewne kwestie i rozwiązać problemy, z którymi możesz się spotkać.

P.S. od tłumacza

Przeczytaj także na naszym blogu:

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster