ABC bezpieczeństwa w Kubernetes: uwierzytelnianie, autoryzacja, audyt

ABC bezpieczeństwa w Kubernetes: uwierzytelnianie, autoryzacja, audyt

Wcześniej czy później w działaniu dowolnego systemu pojawia się kwestia bezpieczeństwa: zapewnienie uwierzytelnienia, rozdział uprawnień, audyt i inne zadania. Już stworzony dla Kubernetes wiele rozwiązań, które pozwalają osiągnąć zgodność ze standardami nawet w bardzo wymagających środowiskach... Ten sam materiał poświęcony jest podstawowym aspektom bezpieczeństwa realizowanym w ramach wbudowanych mechanizmów K8s. Przede wszystkim przyda się osobom rozpoczynającym przygodę z Kubernetesem – jako punkt wyjścia do studiowania zagadnień związanych z bezpieczeństwem.

Uwierzytelnianie

W Kubernetesie istnieją dwa typy użytkowników:

  • Konta usług — konta zarządzane przez Kubernetes API;
  • użytkownicy — „zwykli” użytkownicy zarządzani przez zewnętrzne, niezależne usługi.

Główna różnica między tymi typami polega na tym, że dla kont usług istnieją specjalne obiekty w Kubernetes API (nazywa się je tak - ServiceAccounts), które są powiązane z przestrzenią nazw i zestawem danych autoryzacyjnych przechowywanych w klastrze w obiektach typu Secrets. Tacy użytkownicy (Konta Usług) służą przede wszystkim do zarządzania prawami dostępu do Kubernetes API procesów uruchomionych w klastrze Kubernetes.

Zwykli Użytkownicy nie mają wpisów w Kubernetes API: muszą być zarządzani przez zewnętrzne mechanizmy. Przeznaczone są dla osób lub procesów żyjących poza klastrem.

Każde żądanie API jest powiązane z Kontem Usługi, Użytkownikiem lub jest uznawane za anonimowe.

Dane uwierzytelniające użytkownika obejmują:

  • Użytkownik — nazwa użytkownika (wielkość liter ma znaczenie!);
  • UID - ciąg identyfikacyjny użytkownika nadający się do odczytu maszynowego, który jest „bardziej spójny i niepowtarzalny niż nazwa użytkownika”;
  • Grupy — lista grup, do których należy użytkownik;
  • Extra — dodatkowe pola, które może wykorzystać mechanizm autoryzacyjny.

Kubernetes może korzystać z dużej liczby mechanizmów uwierzytelniania: certyfikatów X509, tokenów okaziciela, uwierzytelniającego proxy, podstawowego uwierzytelniania HTTP. Korzystając z tych mechanizmów, można zaimplementować dużą liczbę schematów autoryzacji: od statycznego pliku z hasłami po OpenID OAuth2.

Ponadto istnieje możliwość jednoczesnego korzystania z kilku schematów uprawnień. Domyślnie klaster używa:

  • tokeny do kont serwisowych – dla Kont serwisowych;
  • X509 - dla Użytkowników.

Pytanie o zarządzanie ServiceAccounts wykracza poza zakres tego artykułu, ale dla tych, którzy chcą zapoznać się z tym zagadnieniem bardziej szczegółowo, polecam zacząć od oficjalne strony dokumentacji. Przyjrzymy się bliżej zagadnieniu działania certyfikatów X509.

Certyfikaty dla użytkowników (X.509)

Klasyczny sposób pracy z certyfikatami polega na:

  • generacja klucza:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/.certs/mynewuser.key 2048
  • generowanie żądania certyfikatu:
    openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • przetworzenie żądania certyfikatu przy użyciu kluczy CA klastra Kubernetes, uzyskanie certyfikatu użytkownika (w celu uzyskania certyfikatu należy skorzystać z konta mającego dostęp do klucza CA klastra Kubernetes, który domyślnie znajduje się w /etc/kubernetes/pki/ca.key):
    openssl x509 -req -in ~/.certs/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/.certs/mynewuser.crt -days 500
  • utworzenie pliku konfiguracyjnego:
    • opis klastra (podaj adres i lokalizację pliku certyfikatu CA dla konkretnej instalacji klastra):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • albo jak niezalecana opcja - nie musisz podawać certyfikatu głównego (wtedy kubectl nie będzie sprawdzał poprawności serwera api klastra):
      kubectl config set-cluster kubernetes  --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • dodanie użytkownika do pliku konfiguracyjnego:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt  --client-key=.certs/mynewuser.key
    • dodanie kontekstu:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • domyślne przypisanie kontekstu:
      kubectl config use-context mynewuser-context

Po powyższych manipulacjach w pliku .kube/config zostanie utworzona taka konfiguracja:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: /etc/kubernetes/pki/ca.crt
    server: https://192.168.100.200:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    namespace: target-namespace
    user: mynewuser
  name: mynewuser-context
current-context: mynewuser-context
kind: Config
preferences: {}
users:
- name: mynewuser
  user:
    client-certificate: /home/mynewuser/.certs/mynewuser.crt
    client-key: /home/mynewuser/.certs/mynewuser.key

Aby ułatwić przenoszenie konfiguracji pomiędzy kontami a serwerami warto edytować wartości następujących kluczy:

  • certificate-authority
  • client-certificate
  • client-key

W tym celu możesz zakodować określone w nich pliki za pomocą base64 i zarejestrować je w configu, dodając końcówkę do nazwy kluczy -data, tj. otrzymawszy certificate-authority-data itd.

Certyfikaty z kubeadm

Wraz z wydaniem Kubernetes 1.15 praca z certyfikatami stała się znacznie łatwiejsza dzięki wersji alfa jej obsługi w narzędzie kubeadm. Na przykład tak może teraz wyglądać generowanie pliku konfiguracyjnego z kluczami użytkownika:

kubeadm alpha kubeconfig user --client-name=mynewuser --apiserver-advertise-address 192.168.100.200

NB: Wymagany adres reklamowy można znaleźć w konfiguracji serwera api, która domyślnie znajduje się w /etc/kubernetes/manifests/kube-apiserver.yaml.

Wynikowa konfiguracja zostanie wyprowadzona na standardowe wyjście. Trzeba to zapisać w ~/.kube/config koncie użytkownika lub do pliku określonego w zmiennej środowiskowej KUBECONFIG.

Kop głębiej

Dla chcących dokładniej zrozumieć opisane zagadnienia:

autoryzacja

Domyślne autoryzowane konto nie ma uprawnień do działania w klastrze. Aby nadać uprawnienia, Kubernetes implementuje mechanizm autoryzacji.

Przed wersją 1.6 Kubernetes używał typu autoryzacji o nazwie ABAK (Kontrola dostępu oparta na atrybutach). Szczegóły na ten temat można znaleźć w oficjalna dokumentacja. To podejście jest obecnie uważane za starsze, ale nadal można go używać razem z innymi typami uwierzytelniania.

Obecny (i bardziej elastyczny) sposób podziału praw dostępu do klastra nazywa się RBAC (Kontrola dostępu oparta na rolach). Od wersji uznano ją za stabilną Kubernetes 1.8. RBAC wdraża model praw, w którym wszystko, co nie jest wyraźnie dozwolone, jest zabronione.
Aby włączyć funkcję RBAC, musisz uruchomić serwer API Kubernetes z parametrem --authorization-mode=RBAC. Parametry ustawiane są w manifeście wraz z konfiguracją serwera API, która domyślnie znajduje się wzdłuż ścieżki /etc/kubernetes/manifests/kube-apiserver.yaml, w sekcji command. Jednak funkcja RBAC jest już domyślnie włączona, więc najprawdopodobniej nie powinieneś się tym martwić: możesz to sprawdzić na podstawie wartości authorization-mode (we wspomnianym już kube-apiserver.yaml). Nawiasem mówiąc, wśród jego znaczeń mogą istnieć inne rodzaje autoryzacji (node, webhook, always allow), ale ich rozważania pozostawimy poza zakresem materiału.

Nawiasem mówiąc, już opublikowaliśmy статью z dość szczegółowym opisem zasad i cech pracy z RBAC, więc dalej ograniczę się do krótkiego zestawienia podstaw i przykładów.

Do kontroli dostępu w Kubernetes za pośrednictwem RBAC służą następujące encje API:

  • Role и ClusterRole — role służące do opisu praw dostępu:
  • Role pozwala na opisanie praw w obrębie przestrzeni nazw;
  • ClusterRole - w ramach klastra, w tym do obiektów specyficznych dla klastra, takich jak węzły, adresy URL niebędące zasobami (tj. niezwiązane z zasobami Kubernetesa - np. /version, /logs, /api*);
  • RoleBinding и ClusterRoleBinding - służy do wiązania Role и ClusterRole do użytkownika, grupy użytkowników lub konta usługi.

Jednostki Role i RoleBinding są ograniczone przestrzenią nazw, tj. muszą znajdować się w tej samej przestrzeni nazw. Jednak RoleBinding może odwoływać się do ClusterRole, co umożliwia utworzenie zestawu ogólnych uprawnień i kontrolowanie dostępu przy ich użyciu.

Role opisują uprawnienia za pomocą zestawów reguł zawierających:

  • Grupy API - patrz oficjalna dokumentacja przez apiGroups i dane wyjściowe kubectl api-resources;
  • zasoby (zasoby: pod, namespace, deployment i tak dalej.);
  • Czasowniki (czasowniki: set, update itp.).
  • nazwy zasobów (resourceNames) - w przypadku, gdy trzeba zapewnić dostęp do konkretnego zasobu, a nie do wszystkich zasobów tego typu.

Bardziej szczegółową analizę autoryzacji w Kubernetesie znajdziesz na stronie oficjalna dokumentacja. Zamiast tego (a raczej oprócz tego) podam przykłady ilustrujące jej twórczość.

Przykłady podmiotów RBAC

Proste Role, co pozwala uzyskać listę i status podów oraz monitorować je w przestrzeni nazw target-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: target-namespace
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Przykład ClusterRole, co pozwala uzyskać listę i status podów oraz monitorować je w całym klastrze:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # секции "namespace" нет, так как ClusterRole задействует весь кластер
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Przykład RoleBinding, co pozwala użytkownikowi mynewuser „czytaj” kapsuły w przestrzeni nazw my-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: target-namespace
subjects:
- kind: User
  name: mynewuser # имя пользователя зависимо от регистра!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # здесь должно быть “Role” или “ClusterRole”
  name: pod-reader # имя Role, что находится в том же namespace,
                   # или имя ClusterRole, использование которой
                   # хотим разрешить пользователю
  apiGroup: rbac.authorization.k8s.io

Audyt wydarzenia

Schematycznie architekturę Kubernetes można przedstawić w następujący sposób:

ABC bezpieczeństwa w Kubernetes: uwierzytelnianie, autoryzacja, audyt

Kluczowym komponentem Kubernetes odpowiedzialnym za przetwarzanie żądań jest serwer API. Wszystkie operacje na klastrze przechodzą przez niego. Więcej o tych wewnętrznych mechanizmach przeczytasz w artykule „Co się stanie w Kubernetesie po uruchomieniu kubectl run?".

Inspekcja systemu to interesująca funkcja w Kubernetesie, która jest domyślnie wyłączona. Umożliwia rejestrowanie wszystkich wywołań Kubernetes API. Jak można się domyślić, wszystkie akcje związane z monitorowaniem i zmianą stanu klastra realizowane są poprzez to API. Dobry opis jego możliwości można (jak zwykle) znaleźć w oficjalna dokumentacja K8. Następnie postaram się przedstawić temat prostszym językiem.

W ten sposób aby umożliwić audyt, musimy przekazać trzy wymagane parametry do kontenera w api-server, które opisano bardziej szczegółowo poniżej:

  • --audit-policy-file=/etc/kubernetes/policies/audit-policy.yaml
  • --audit-log-path=/var/log/kube-audit/audit.log
  • --audit-log-format=json

Oprócz tych trzech niezbędnych parametrów istnieje wiele dodatkowych ustawień związanych z audytem: od rotacji logów po opisy webhooków. Przykład parametrów rotacji dziennika:

  • --audit-log-maxbackup=10
  • --audit-log-maxsize=100
  • --audit-log-maxage=7

Ale nie będziemy się nad nimi rozwodzić bardziej szczegółowo - wszystkie szczegóły znajdziesz w dokumentacja kube-apiserver.

Jak już wspomniano, wszystkie parametry są ustawiane w manifeście wraz z konfiguracją serwera api (domyślnie /etc/kubernetes/manifests/kube-apiserver.yaml), w sekcji command. Wróćmy do 3 wymaganych parametrów i przeanalizujmy je:

  1. audit-policy-file — ścieżka do pliku YAML opisującego politykę audytu. Do jego zawartości wrócimy później, ale na razie zaznaczę, że plik musi być czytelny dla procesu api-server. Dlatego konieczne jest zamontowanie go wewnątrz kontenera, dla którego w odpowiednich sekcjach konfiguracji możesz dodać następujący kod:
      volumeMounts:
        - mountPath: /etc/kubernetes/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — ścieżka do pliku dziennika. Ścieżka musi być dostępna także dla procesu serwera api, dlatego jej montaż opisujemy w ten sam sposób:
      volumeMounts:
        - mountPath: /var/log/kube-audit
          name: logs
          readOnly: false
      volumes:
      - hostPath:
          path: /var/log/kube-audit
          type: DirectoryOrCreate
        name: logs
  3. audit-log-format — format dziennika audytu. Wartość domyślna to json, ale dostępny jest także starszy format tekstowy (legacy).

Polityka audytu

Teraz o wspomnianym pliku opisującym politykę logowania. Pierwszą koncepcją polityki audytu jest level, poziom logowania. Są one następujące:

  • None - nie loguj się;
  • Metadata — metadane żądania logowania: użytkownik, czas żądania, zasób docelowy (pod, przestrzeń nazw itp.), typ akcji (czasownik) itp.;
  • Request — metadane dziennika i treść żądania;
  • RequestResponse — metadane dziennika, treść żądania i treść odpowiedzi.

Ostatnie dwa poziomy (Request и RequestResponse) nie rejestruj żądań, które nie korzystały z zasobów (dostępy do tzw. adresów URL innych niż zasoby).

Wszystkie prośby również zostają zrealizowane Kilka etapów:

  • RequestReceived — etap, na którym wniosek zostaje odebrany przez podmiot przetwarzający i nie został jeszcze przekazany dalej w łańcuchu podmiotów przetwarzających;
  • ResponseStarted — nagłówki odpowiedzi są wysyłane, ale przed wysłaniem treści odpowiedzi. Generowane dla długotrwałych zapytań (na przykład watch);
  • ResponseComplete — treść odpowiedzi została wysłana, dalsze informacje nie będą przesyłane;
  • Panic — zdarzenia są generowane w przypadku wykrycia sytuacji nienormalnej.

Aby pominąć dowolne kroki, których możesz użyć omitStages.

W pliku polityki możemy opisać kilka sekcji o różnych poziomach rejestrowania. Zastosowana zostanie pierwsza pasująca reguła znaleziona w opisie polisy.

Demon kubelet monitoruje zmiany w manifeście za pomocą konfiguracji api-server i, jeśli zostaną wykryte, ponownie uruchamia kontener za pomocą api-server. Ale jest ważny szczegół: zmiany w pliku zasad zostaną przez niego zignorowane. Po wprowadzeniu zmian w pliku zasad konieczne będzie ręczne zrestartowanie serwera API. Ponieważ serwer API jest uruchamiany jako kapsuła statyczna, zespół kubectl delete nie spowoduje jego ponownego uruchomienia. Będziesz musiał to zrobić ręcznie docker stop na kube-masters, gdzie zmieniono politykę audytu:

docker stop $(docker ps | grep k8s_kube-apiserver | awk '{print $1}')

Włączając audyt, należy o tym pamiętać wzrasta obciążenie kube-apiserver. W szczególności wzrasta zużycie pamięci do przechowywania kontekstu żądania. Rejestrowanie rozpoczyna się dopiero po wysłaniu nagłówka odpowiedzi. Obciążenie zależy również od konfiguracji zasad inspekcji.

Przykłady polityk

Przyjrzyjmy się strukturze plików polityk na przykładach.

Oto prosty plik policyaby rejestrować wszystko na poziomie Metadata:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

W profilu możesz określić listę użytkowników (Users и ServiceAccounts) i grupy użytkowników. Przykładowo w ten sposób będziemy ignorować użytkowników systemu, a wszystko inne rejestrować na poziomie Request:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: None
    userGroups:
      - "system:serviceaccounts"
      - "system:nodes"
    users:
      - "system:anonymous"
      - "system:apiserver"
      - "system:kube-controller-manager"
      - "system:kube-scheduler"
  - level: Request

Możliwe jest również opisanie celów:

  • przestrzenie nazw (namespaces);
  • Czasowniki (czasowniki: get, update, delete i inni);
  • zasoby (zasobyW następujący sposób: pod, configmaps itp.) i grupy zasobów (apiGroups).

Uwaga! Zasoby i grupy zasobów (grupy API, czyli apiGroups), a także ich wersje zainstalowane w klastrze można uzyskać za pomocą poleceń:

kubectl api-resources
kubectl api-versions

Poniższa polityka audytu została przedstawiona jako demonstracja najlepszych praktyk w zakresie Dokumentacja chmury Alibaba:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Не логировать стадию RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Не логировать события, считающиеся малозначительными и не опасными:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # это api group с пустым именем, к которому относятся
                  # базовые ресурсы Kubernetes, называемые “core”
        resources: ["endpoints", "services"]
  - level: None
    users: ["system:unsecured"]
    namespaces: ["kube-system"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["configmaps"]
  - level: None
    users: ["kubelet"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    userGroups: ["system:nodes"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["nodes"]
  - level: None
    users:
      - system:kube-controller-manager
      - system:kube-scheduler
      - system:serviceaccount:kube-system:endpoint-controller
    verbs: ["get", "update"]
    namespaces: ["kube-system"]
    resources:
      - group: "" # core
        resources: ["endpoints"]
  - level: None
    users: ["system:apiserver"]
    verbs: ["get"]
    resources:
      - group: "" # core
        resources: ["namespaces"]
  # Не логировать обращения к read-only URLs:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # Не логировать сообщения, относящиеся к типу ресурсов “события”:
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Ресурсы типа Secret, ConfigMap и TokenReview могут содержать  секретные данные,
  # поэтому логируем только метаданные связанных с ними запросов
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Действия типа get, list и watch могут быть ресурсоёмкими; не логируем их
  - level: Request
    verbs: ["get", "list", "watch"]
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Уровень логирования по умолчанию для стандартных ресурсов API
  - level: RequestResponse
    resources:
      - group: "" # core
      - group: "admissionregistration.k8s.io"
      - group: "apps"
      - group: "authentication.k8s.io"
      - group: "authorization.k8s.io"
      - group: "autoscaling"
      - group: "batch"
      - group: "certificates.k8s.io"
      - group: "extensions"
      - group: "networking.k8s.io"
      - group: "policy"
      - group: "rbac.authorization.k8s.io"
      - group: "settings.k8s.io"
      - group: "storage.k8s.io"
  # Уровень логирования по умолчанию для всех остальных запросов
  - level: Metadata

Innym dobrym przykładem polityki audytu jest profil używany w GCE.

Aby szybko reagować na zdarzenia audytowe, jest to możliwe opisz webhooka. Zagadnienie to zostało omówione w oficjalna dokumentacja, pozostawię to poza zakresem tego artykułu.

Wyniki

W artykule omówiono podstawowe mechanizmy bezpieczeństwa w klastrach Kubernetes, które pozwalają na tworzenie spersonalizowanych kont użytkowników, oddzielanie ich uprawnień i rejestrowanie ich działań. Mam nadzieję, że będzie przydatna dla tych, którzy w teorii lub w praktyce stają przed takimi zagadnieniami. Polecam także zapoznać się z listą innych materiałów na temat bezpieczeństwa w Kubernetesie, która jest podana w „PS” – być może wśród nich znajdziesz niezbędne szczegóły dotyczące istotnych dla Ciebie problemów.

PS

Przeczytaj także na naszym blogu:

Źródło: www.habr.com

Dodaj komentarz