Abeceda bezpečnosti v Kubernetes: autentizace, autorizace, audit

Abeceda bezpečnosti v Kubernetes: autentizace, autorizace, audit

Dříve nebo později se u každého systému objevuje otázka bezpečnosti: zajištění autentizace, rozdělení práv, auditu a dalších úkolů. Pro Kubernetes již bylo vytvořeno mnoho řešení, která umožňují dosáhnout souladu se standardy i v velmi náročných prostředích… Tento materiál se zaměřuje na základní aspekty bezpečnosti, které jsou implementovány v rámci vstavaných mechanismů K8s. Především bude užitečný pro ty, kteří začínají s Kubernetes — jako výchozí bod pro studium otázek týkajících se bezpečnosti.

Autentizace

V Kubernetes existují dva typy uživatelů:

  • Service Accounts — účty spravované Kubernetes API;
  • Uživatelé — "normální" uživatelé, spravovaní externími, nezávislými službami.

Hlavní rozdíl mezi těmito typy spočívá v tom, že pro Service Accounts existují speciální objekty v Kubernetes API (nazývají se ServiceAccounts), které jsou vázány na jmenný prostor a sadu autorizačních údajů uložených v clusteru v objektech typu Secrets. Tito uživatelé (Service Accounts) jsou určeni především k řízení přístupových práv k Kubernetes API procesů, které běží v clusteru Kubernetes.

Obyčejní uživatelé (Users) nemají záznamy v Kubernetes API: jejich správa by měla být prováděna externími mechanismy. Jsou určeni pro lidi nebo procesy, které žijí mimo cluster.

Každý požadavek na API je vázán buď na Service Account, nebo na uživatele, nebo je považován za anonymní.

Autentizační údaje uživatele zahrnují:

  • Uživatelské jméno — uživatelské jméno (závislé na velikosti písmen!);
  • UID — strojově čitelný identifikační řetězec uživatele, který je "konzistentnější a jedinečnější než uživatelské jméno";
  • Skupiny — seznam skupin, do kterých uživatel patří;
  • Extra — další pole, která mohou být použita autorizačním mechanismem.

Kubernetes může používat mnoho autentizačních mechanismů: certifikáty X509, Bearer tokeny, autentizační proxy, HTTP Basic Auth. Pomocí těchto mechanismů lze implementovat řadu autorizačních schémat: od statického souboru s hesly po OpenID OAuth2.

Navíc je povoleno používat více autorizačních schémat současně. Ve výchozím stavu jsou v clusteru používána:

  • service account tokens — pro Service Accounts;
  • X509 — pro uživatele.

Otázka správy ServiceAccounts přesahuje rámec tohoto článku a doporučuji těm, kteří mají zájem, aby se podívali na stránku oficiální dokumentace. My se nyní podíváme podrobně na problematiku certifikátů X509.

Certifikáty pro uživatele (X.509)

Klasický způsob práce s certifikáty zahrnuje:

  • generaci klíče:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/mynewuser.key 2048
  • generaci žádosti o certifikát:
    openssl req -new -key ~/mynewuser.key -out ~/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • zpracování žádosti o certifikát pomocí klíčů CA clusteru Kubernetes, získání certifikátu uživatele (pro získání certifikátu je nutné použít účet, který má přístup ke klíči certifikační autority clusteru Kubernetes, která je ve výchozím nastavení umístěna v /etc/kubernetes/pki/ca.key):
    openssl x509 -req -in ~/mynewuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ~/mynewuser.crt -days 500
  • vytvoření konfiguračního souboru:
    • popis clusteru (uvedete adresu a umístění souboru certifikátu CA konkrétní instalace clusteru):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • nebo — jak nedoporučená varianta — můžete nepoužít kořenový certifikát (pak kubectl nebude ověřovat správnost api-serveru clusteru):
      kubectl config set-cluster kubernetes --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • přidání uživatele do konfiguračního souboru:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt --client-key=.certs/mynewuser.key
    • přidání kontextu:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • nastavení výchozího kontextu:
      kubectl config use-context mynewuser-context

Po výše uvedených manipulacích bude v souboru .kube/config vytvořen konfigurační soubor následující podoby:

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

Pro usnadnění přenosu konfiguračního souboru mezi účty a servery je užitečné upravit hodnoty následujících klíčů:

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

K tomu je možné soubory uvedené v nich zakódovat pomocí base64 a zařadit je do konfiguračního souboru, přidáním k názvům klíčů příponu -data, tedy získáním certificate-authority-data atd.

Certifikáty s kubeadm

S vydáním Kubernetes 1.15 Práce se certifikáty se díky alfa verzi její podpory v nástroji kubeadm. Například takto nyní může vypadat generování konfiguračního souboru se soukromými klíči uživatele:

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

NB: Požadovaná advertise address může být zkontrolována v konfiguraci api-serveru, který je ve výchozím nastavení umístěn v /etc/kubernetes/manifests/kube-apiserver.yaml.

Výsledná konfigurace bude vytištěna do stdout. Je třeba ji uložit do ~/.kube/config uživatelského účtu nebo do souboru uvedeného v proměnné prostředí KUBECONFIG.

Ponořit se hlouběji

Pro ty, kteří chtějí hlouběji prozkoumat uvedené otázky:

Autentizace

Autorizovaný účet ve výchozím nastavení nemá oprávnění k akcím v klastru. Pro udělení oprávnění byl v Kubernetes implementován mechanismus autorizace.

Až do verze 1.6 se v Kubernetes používal typ autorizace nazývaný ABAC (Attribute-based access control). Podrobnosti o něm naleznete v oficiální dokumentaci. V současnosti je tento přístup považován za zastaralý (legacy), avšak stále jej můžete používat současně s jinými typy autorizace.

Aktuální (a flexibilnější) způsob rozdělení přístupových práv do klastru se nazývá RBAC (Role-based access control). Byl vyhlášen stabilním od verze Kubernetes 1.8. RBAC implementuje model práv, kde je vše, co není explicitně povoleno, zakázáno.
Pro aktivaci RBAC, je nutné spustit Kubernetes api-server s parametrem --authorization-mode=RBAC. Parametry se nastavují v manifestu konfigurace api-serveru, který je ve výchozím nastavení umístěn na cestě /etc/kubernetes/manifests/kube-apiserver.yaml, v sekci command. Nicméně, ve výchozím nastavení je RBAC již aktivován, takže se tím pravděpodobně nemusíte zabývat: můžete se o tom přesvědčit podle hodnoty authorization-mode (v již zmíněném kube-apiserver.yaml). Mimochodem, mezi jeho hodnotami se mohou objevovat i jiné typy autorizace (node, webhook, always allow), ale jejich zkoumání necháme mimo rámec tohoto materiálu.

Mimochodem, již jsme publikovali článek dostatečně podrobný popis principů a zvláštností práce s RBAC, takže dále se omezím na stručné shrnutí základů a příkladů.

Pro řízení přístupu v Kubernetes prostřednictvím RBAC se používají následující entity API:

  • Role a ClusterRole — role, které slouží k popisu oprávnění:
  • Role umožňuje popsat práva v rámci namespace;
  • ClusterRole — v rámci clusteru, včetně cluster-specifických objektů, jako jsou uzly, non-resources urls (tj. nesouvisejících s Kubernetes zdroji — například, /version, /logs, /api*);
  • RoleBinding a ClusterRoleBinding — slouží k propojení Role a ClusterRole s uživatelským účtem, skupinou uživatelů nebo ServiceAccount.

Entity Role a RoleBinding jsou omezeny na namespace, tj. musí se nacházet v rámci jednoho prostoru jmen. RoleBinding však může odkazovat na ClusterRole, což umožňuje vytvořit sadu typových oprávnění a spravovat přístup prostřednictvím nich.

Role popisují oprávnění pomocí sad pravidel, které obsahují:

  • API skupiny — viz oficiální dokumentaci k apiGroups a příkazu kubectl api-resources;
  • zdroje (resources: pod, namespace, deployment atd.);
  • slovesa (verbs: set, update atd.).
  • jména zdrojů (resourceNames) — pro případ, kdy je potřeba poskytnout přístup k určitému zdroji, nikoli ke všem zdrojům tohoto typu.

Podrobnější rozbor autorizace v Kubernetes lze nalézt na stránce oficiální dokumentaci. Místo toho (přesněji — jako doplněk k tomu) uvedu příklady, které ilustrují její fungování.

Příklady entit RBAC

Jednoduchá Role, umožňující získat seznam a stav podů a sledovat je v prostoru jmen 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"]

Příklad ClusterRole, což umožňuje získat seznam a stav podů a sledovat je v celém clusteru:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # sekce "namespace" není, protože ClusterRole pokrývá celý cluster
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Příklad RoleBinding, což umožňuje uživateli mynewuser "číst" podí v prostoru jmen my-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: target-namespace
subjects:
- kind: User
  name: mynewuser # jméno uživatele je citlivé na velikost písmen!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # zde by mělo být “Role” nebo “ClusterRole”
  name: pod-reader # jméno Role, které se nachází ve stejném namespace,
                   # nebo jméno ClusterRole, jejíž užití
                   # chceme uživateli povolit
  apiGroup: rbac.authorization.k8s.io

Audit událostí

Schématicky lze architekturu Kubernetes představit následovně:

Abeceda bezpečnosti v Kubernetes: autentizace, autorizace, audit

Klíčová komponenta Kubernetes, odpovědná za zpracování požadavků, je api-server. Všechny operace s klastrem probíhají přes něj. Více o těchto vnitřních mechanismech se můžete dočíst v článku „Co se v Kubernetes děje při spuštění kubectl run?».

Auditing systému je zajímavá funkce v Kubernetes, která je ve výchozím nastavení vypnuta. Umožňuje logovat všechny požadavky na API Kubernetes. Jak asi tušíte, přes toto API se provádějí všechny akce související s kontrolou a změnou stavu klastru. Dobré popisy jejích možností lze (jak obvykle) najít v oficiální dokumentaci K8s. Dále se pokusím téma vysvětlit jednodušším jazykem.

Takže, abychom zapli audit, musíme předat kontejneru v api-serveru tři povinné parametry, o kterých se více dozvíte níže:

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

Kromě těchto tří nezbytných parametrů existuje mnoho dalších nastavení týkajících se auditu: od rotace logů po popisy webhooků. Příklad parametrů rotace logů:

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

Ale nebudeme se na ně podrobně zastavovat — všechny detaily lze nalézt v dokumentaci k kube-apiserver.

Jak již bylo zmíněno, všechny parametry se nastavují v manifestu konfigurace api-serveru (ve výchozím nastavení /etc/kubernetes/manifests/kube-apiserver.yaml), v sekci command. Vraťme se k třem povinným parametrům a rozeberme je:

  1. audit-policy-file — cesta k YAML souboru s popisem auditní politiky. K jeho obsahu se ještě vrátíme, ale prozatím podotknu, že soubor musí být přístupný procesu api-serveru. Proto je třeba jej namontovat do kontejneru, k čemuž lze přidat následující kód do příslušných sekcí konfigurace:
      volumeMounts:
        - mountPath: /etc/kubernetes/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — cesta k souboru s logy. Cesta musí být také přístupná procesu api-serveru, proto ji stejně popisujeme jako namontování:
      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 — formát logu auditu. Ve výchozím nastavení je to json, ale k dispozici je také zastaralý textový formát (legacy).

Auditní politika

Teď k zmiňovanému souboru s popisem logovací politiky. První pojem audit policy — to je úroveň, logování. Ty mohou být následující:

  • None — neprovádět logování;
  • Metadata — logovat metadata požadavku: uživatel, čas požadavku, cílový zdroj (pod, namespace atd.), typ akce (verb) atd.;
  • Request — logovat metadata a tělo požadavku;
  • RequestResponse — logovat metadata, tělo požadavku a tělo odpovědi.

Poslední dvě úrovně (Request a RequestResponse) nelogují požadavky, které se netýkají zdrojů (žádosti na tzv. non-resources urls).

Také všechny požadavky procházejí několika fázemi:

  • RequestReceived — fáze, kdy byl požadavek přijat zpracovatelem a ještě nebyl předán dále do řetězce zpracovatelů;
  • ResponseStarted — hlavičky odpovědi byly odeslány, ale před odesláním těla odpovědi. Generováno pro dlouhé požadavky (například, watch);
  • ResponseComplete — tělo odpovědi bylo odesláno, více informací již nebude odesláno;
  • Panic — události jsou generovány, když dojde k nečekané situaci.

Pro vynechání jakýchkoli fází lze použít omitStages.

V souboru politiky můžeme popsat několik sekcí s různými úrovněmi logování. Použije se první vhodné pravidlo nalezené v popisu policy.

Démon kubelet sleduje změny manifestu s konfigurací api-serveru a při jejich zjištění restartuje kontejner s api-serverem. Ale je tu důležitý detail: změny v souboru policy budou ignorovány. Po provedení změn v souboru policy bude nutné api-server ručně restartovat. Protože api-server běží jako static pod, příkaz kubectl delete nezpůsobí jeho restart. Bude nutné ručně provést docker stop na kube-masterech, kde byla změněna auditní politika:

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

Při zapnutí auditu je důležité mít na paměti, že zvyšuje zátěž na kube-apiserveru. Zejména se zvyšuje spotřeba paměti pro uložení kontextu požadavků. Záznam do logu začíná až po odeslání hlavičky odpovědi. Také zátěž závisí na konfiguraci auditní politiky.

Příklady politik

Prozkoumejme strukturu souborů policy na příkladech.

Tady je jednoduchý soubor policy, který loguje všechno na úrovni Metadata:

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

V policy lze uvést seznam uživatelů (Uživatelé a ServiceAccounts) a skupin uživatelů. Například, takto budeme ignorovat systémové uživatele, ale logovat vše ostatní na úrovni. 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

Existuje také možnost popsat cílové:

  • jmenné prostory (jmenné prostory);
  • slovesa (verbs: získat, update, odstranit a další);
  • zdroje (resources, konkrétně: pod, configmaps a podobně) a skupiny zdrojů (apiGroups).

Všimněte si! Zdroje a skupiny zdrojů (API skupiny, tj. apiGroups), a také jejich verze nainstalované v klastru, lze získat pomocí příkazů:

kubectl api-resources
kubectl api-versions

Následující politiky auditů jsou uvedeny jako příklad nejlepších praktik v dokumentaci Alibaba Cloud:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Nezaznamenávat fázi RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Nezaznamenávat události považované za bezvýznamné a nebezpečné:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # toto je api skupina s prázdným jménem, které se týká
                  # základních zdrojů Kubernetes, nazývaných "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"]
  # Nezaznamenávat přístupy k read-only URL:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # Nezaznamenávat zprávy týkající se typu zdrojů "události":
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Zdroje typu Secret, ConfigMap a TokenReview mohou obsahovat tajné údaje,
  # proto zaznamenáváme pouze metadata související s těmito požadavky
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Akce typu get, list a watch mohou být náročné na zdroje; nezaznamenáváme je
  - 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"
  # Výchozí úroveň protokolování pro standardní API zdroje
  - 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"
  # Výchozí úroveň protokolování pro všechny ostatní požadavky
  - level: Metadata

Další dobrý příklad audit policy — profil, který se používá v GCE.

Pro rychlou reakci na události auditu je možnost popis webhooku. Toto téma je podrobněji vysvětleno v oficiální dokumentaci, nechám to mimo rámec tohoto článku.

Závěry

Článek poskytuje přehled mechanismů základního zabezpečení v clusterech Kubernetes, které umožňují vytvářet personalizované uživatelské účty, rozdělovat jejich práva a také registrovat jejich činnosti. Doufám, že to bude užitečné pro ty, kteří se s těmito otázkami setkali teoreticky nebo již v praxi. Doporučuji se také podívat na seznam dalších materiálů na téma zabezpečení v Kubernetes, který je uveden v "P.S.", – možná mezi nimi najdete potřebné podrobnosti o problémech, které vás zajímají.

P.S.

Přečtěte si také náš blog:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster