ABC-ul securității în Kubernetes: autentificare, autorizare, audit

ABC-ul securității în Kubernetes: autentificare, autorizare, audit

La un moment dat, în utilizarea oricărei sisteme, apare întrebarea securității: asigurarea autentificării, separarea drepturilor, auditul și alte sarcini. Pentru Kubernetes, există deja numeroase soluții, care permit respectarea standardelor chiar și în medii foarte exigente… Acest material se concentrează asupra aspectelor fundamentale ale securității, implementate în cadrul mecanismelor încorporate ale K8s. În primul rând, va fi util pentru cei care încep să se familiarizeze cu Kubernetes, ca un punct de plecare pentru studierea problemelor legate de securitate.

Autentificare

În Kubernetes sunt două tipuri de utilizatori:

  • Conturi de Serviciu — conturi gestionate de API-ul Kubernetes;
  • Utilizatori — utilizatori „normali”, gestionați de servicii externe, independente.

Principalul diferențiat între aceste tipuri este că pentru Conturile de Serviciu există obiecte speciale în API-ul Kubernetes (care se numesc chiar așa — ServiceAccounts), care sunt legate de spațiul de nume și setul de credențiale de autorizare stocate în cluster în obiecte de tip Secrets. Acest tip de utilizatori (Conturile de Serviciu) sunt destinate în principal gestionării drepturilor de acces la API-ul Kubernetes al proceselor care rulează în clusterul Kubernetes.

Utilizatorii obișnuiți nu au înregistrări în API-ul Kubernetes: gestionarea lor trebuie realizată prin mecanisme externe. Aceștia sunt destinați oamenilor sau proceselor care există în afara clusterului.

Fiecare cerere către API este legată fie de un Cont de Serviciu, fie de un Utilizator, fie este considerată anonimă.

Datele de autentificare ale utilizatorului includ:

  • Nume utilizator — numele utilizatorului (depinde de majuscule!);
  • UID — un șir de identificare citibil de machine, care este „mai consistent și mai unic decât numele utilizatorului”;
  • Grupuri — lista grupurilor din care face parte utilizatorul;
  • Extra — câmpuri suplimentare care pot fi folosite de mecanismul de autorizare.

Kubernetes poate folosi o varietate mare de mecanisme de autentificare: certificate X509, token-uri Bearer, proxy-uri de autentificare, HTTP Basic Auth. Prin aceste mecanisme se pot implementa o serie de scheme de autorizare: de la un fișier static cu parole, până la OpenID OAuth2.

Mai mult, se permite utilizarea mai multor scheme de autorizare simultan. În mod normal, în cluster sunt utilizate:

  • token-uri de cont de serviciu — pentru Conturile de Serviciu;
  • X509 — pentru Utilizatori.

Întrebarea despre gestionarea ServiceAccounts depășește subiectul acestui articol; pentru cei care doresc să se familiarizeze mai bine cu această problemă, recomand să înceapă cu pagina de documentație oficială. Noi vom analiza mai detaliat problema funcționării certificatelor X509.

Certificatul utilizatorilor (X.509)

Metoda clasică de lucru cu certificatele presupune:

  • generarea unei chei:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/.certs/mynewuser.key 2048
  • generarea unei cereri de certificat:
    openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • procesarea cererii pentru certificat folosind cheile CA ale clusterului Kubernetes, obținerea certificatului utilizatorului (pentru obținerea certificatului, trebuie folosit un cont care are acces la cheia centrului de certificare al clusterului Kubernetes, care este în mod default localizat în /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
  • crearea unui fișier de configurație:
    • descrierea clusterului (specificați adresa și locația fișierului certificat CA pentru instalarea specifică a clusterului):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • sau — ca nuopțiunea recomandată — nu este necesar să specificați certificatul rădăcină (atunci kubectl nu va verifica corectitudinea api-server-ului clusterului):
      kubectl config set-cluster kubernetes  --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • adăugarea utilizatorului în fișierul de configurație:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt  --client-key=.certs/mynewuser.key
    • adăugarea unui context:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • setarea contextului implicit:
      kubectl config use-context mynewuser-context

După manipulările menționate mai sus, în fișierul .kube/config va fi creat un config de tip:

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

Pentru a facilita transferul config-ului între conturi și servere, este util să editați valorile următoarelor chei:

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

Pentru aceasta, puteți codifica fișierele specificate cu base64 și le puteți adăuga în config, adăugând sufixul la numele cheilor -data, adică obținând certificate-authority-data etc.

Certificatul cu kubeadm

Cu lansarea Kubernetes 1.15 Lucrul cu certificatele a devenit semnificativ mai simplu datorită versiunii alfa a suportului său în utilitarul kubeadm. De exemplu, iată cum poate arăta acum generarea fișierului de configurare cu cheile utilizatorului:

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

NB: Necesare adresa de publicitate poate fi consultată în configurația api-server, care în mod implicit se află în /etc/kubernetes/manifests/kube-apiserver.yaml.

Configurația rezultată va fi afișată în stdout. Aceasta trebuie salvată în ~/.kube/config contul utilizatorului sau într-un fișier specificat în variabila de mediu KUBECONFIG.

Aprofundează

Pentru cei care doresc să înțeleagă mai bine subiectele descrise:

Autorizare

Contul autorizat implicit nu are drepturi de acțiune în cluster. Pentru a oferi permisiuni, Kubernetes implementează un mecanism de autorizare.

Până la versiunea 1.6, Kubernetes utiliza un tip de autorizare numit ABAC (Controlul accesului bazat pe atribute). Detalii despre acesta pot fi găsite în documentația oficială. În prezent, această abordare este considerată învechită (legacy), totuși, încă puteți folosi-o simultan cu alte tipuri de autorizare.

Metoda actuală (și mai flexibilă) de separare a drepturilor de acces în cluster se numește RBAC (Controlul accesului bazat pe roluri). A fost declarat stabil începând cu versiunea Kubernetes 1.8. RBAC implementează un model de permisiuni în care tot ceea ce nu este permis este interzis explicit.
Pentru a activa RBAC, trebuie să porniți Kubernetes api-server cu parametrul --authorization-mode=RBAC. Parametrii sunt configurați în manifestul pentru api-server, care în mod implicit se află pe calea /etc/kubernetes/manifests/kube-apiserver.yaml, în secțiunea command. Totuși, în mod implicit, RBAC este deja activat, așa că, cel mai probabil, nu este nevoie să vă faceți griji în legătură cu acest lucru: puteți verifica acest lucru prin valoarea authorization-mode (în deja menționatul kube-apiserver.yaml). Apropo, printre valorile sale pot apărea și alte tipuri de autorizare (node, webhook, always allow), dar discutarea acestora o lăsăm în afara acestui material.

Apropo, am publicat deja articol un articol suficient de detaliat despre principiile și caracteristicile lucrului cu RBAC, așa că voi limita acest lucru la o enumerare scurtă a bazelor și exemplelor.

Pentru gestionarea accesului în Kubernetes prin RBAC sunt utilizate următoarele entități API:

  • Role și ClusterRole — roluri care descriu permisiunile de acces:
  • Role permite descrierea permisiunilor în cadrul unui spațiu de nume;
  • ClusterRole — în cadrul cluster-ului, inclusiv pentru obiecte specifice cluster-ului, tipuri de noduri, url-uri non-resources (adică care nu sunt legate de resurse Kubernetes — de exemplu, /version, /logs, /api*);
  • RoleBinding și ClusterRoleBinding — servește pentru legarea Role și ClusterRole la un utilizator, grup de utilizatori sau ServiceAccount.

Entitățile Role și RoleBinding sunt limitate la spațiu de nume, adică trebuie să fie situate în interiorul aceluiași spațiu de nume. Totuși, RoleBinding poate să facă referire la ClusterRole, ceea ce permite crearea unui set de permisiuni tipice și gestionarea accesului prin intermediul lor.

Rolurile descriu permisiunile prin seturi de reguli, conținând:

  • grupuri API — vezi documentația oficială pentru apiGroups și ieșire kubectl api-resources;
  • resurse (resources: pod, namespace, deployment etc.);
  • verbe (verbs: set, update ș.a.
  • numele resurselor (resourceNames) — în cazul în care este necesar să se ofere acces la o anumită resursă, nu la toate resursele de acest tip.

O analiză mai detaliată a autorizării în Kubernetes poate fi găsită pe pagina documentația oficială. În loc de aceasta (mai precis — în plus față de aceasta), voi oferi exemple care ilustrează modul în care funcționează.

Exemple de entități RBAC

Simplu Role, permițând obținerea listei și stării pod-urilor și monitorizarea acestora în spațiul de nume 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"]

Exemplu ClusterRole, ceea ce permite obținerea listei și stării pod-urilor și monitorizarea acestora în întreg cluster-ul:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # secțiunea "namespace" nu există, deoarece ClusterRole acoperă întreg cluster-ul
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Exemplu RoleBinding, ceea ce permite utilizatorului mynewuser „a citi” pod-urile în spațiul de nume my-namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: target-namespace
subjects:
- kind: User
  name: mynewuser # numele utilizatorului depinde de caz!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # aici ar trebui să fie “Role” sau “ClusterRole”
  name: pod-reader # numele Role, care se află în același namespace,
                   # sau numele ClusterRole, utilizarea căreia
                   # dorim să o permitem utilizatorului
  apiGroup: rbac.authorization.k8s.io

Auditul evenimentelor

Schema arhitecturii Kubernetes poate fi reprezentată astfel:

ABC-ul securității în Kubernetes: autentificare, autorizare, audit

Componenta cheie a Kubernetes, responsabilă pentru procesarea cererilor, este api-server. Toate operațiunile asupra cluster-ului trec prin el. Mai multe informații despre aceste mecanisme interne pot fi găsite în articolul „Ce se întâmplă în Kubernetes atunci când se lansează comanda kubectl run?».

Auditul sistemului este o caracteristică interesantă în Kubernetes, care, în mod implicit, este dezactivată. Aceasta permite logarea tuturor apelurilor către API-ul Kubernetes. Cum era de așteptat, prin acest API se efectuează toate acțiunile legate de controlul și modificarea stării clusterului. O descriere bună a posibilităților ei poate fi găsită în documentația oficială K8s. În continuare, voi încerca să expun subiectul într-un mod mai simplu.

Deci, Pentru a activa auditul, trebuie să transmitem containerului în api-server trei parametri obligatorii, despre care se vorbește mai în detaliu mai jos:

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

Pe lângă acești trei parametri necesari, există o mulțime de setări suplimentare legate de audit: de la rotația logurilor până la descrierile webhook-urilor. Exemple de parametri pentru rotația logurilor:

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

Dar nu ne vom opri asupra lor — toate detaliile pot fi găsite în documentația pentru kube-apiserver.

Așa cum am menționat deja, toți parametrii sunt setați în manifestul cu configurația api-server (implicit, /etc/kubernetes/manifests/kube-apiserver.yaml), în secțiunea command. Să ne întoarcem la cei 3 parametri obligatorii și să îi discutăm:

  1. audit-policy-file — calea către fișierul YAML care conține descrierea politicii de audit. Vom reveni la conținutul acestuia, dar menționez acum că fișierul trebuie să fie accesibil procesului api-server-ului. Prin urmare, este necesar să-l montăm în interiorul containerului, pentru care putem adăuga următorul cod în secțiunile corespunzătoare ale configurației:
      volumeMounts:
        - mountPath: /etc/kubernetes/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — calea către fișierul de log. Această cale trebuie, de asemenea, să fie accesibilă procesului api-server-ului, așa că o descriem similar pentru montare:
      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 — formatul logului de audit. Implicit, acesta este json, dar este disponibil și un format text învechit (legacy).

Politica de audit

Acum, să discutăm despre fișierul menționat cu descrierea politicii de logare. Primul concept al audit policy este level, nivelul de logare. Acestea pot fi următoarele:

  • None — nu logați;
  • Metadata — logați metadatele cererii: utilizatorul, timpul cererii, resursa țintă (pod, namespace etc.), tipul acțiunii (verb) etc.;
  • Request — logați metadatele și corpul cererii;
  • RequestResponse — a înregistra metadatele, corpul cererii și corpul răspunsului.

Ultimele două niveluri (Request și RequestResponse) nu înregistrează cererile care nu au accesat resursele (accesările la așa-numitele url-uri non-resources).

De asemenea, toate cererile trec prin mai multe etape:

  • RequestReceived — etapa în care cererea a fost primită de procesor și încă nu a fost transmisă mai departe pe lanțul procesorilor;
  • ResponseStarted — headerele răspunsului au fost trimise, dar înainte de a trimite corpul răspunsului. Este generat pentru cererile de lungă durată (de exemplu, watch);
  • ResponseComplete — corpul răspunsului a fost trimis, nu vor mai fi trimise informații suplimentare;
  • Panic — evenimentele sunt generate când este detectată o situație anormală.

Pentru a omite anumite etape, se poate folosi omitStages.

În fișierul de politică, putem descrie mai multe secțiuni cu diferite niveluri de înregistrare. Se va aplica prima regulă adecvată găsită în descrierea policy.

Demonul kubelet monitorizează modificarea manifestului cu configurația api-server și, la detectarea acestora, repornește containerul cu api-server. Dar există un detaliu important: modificările din fișierul policy vor fi ignorate de acesta. După ce se fac modificări în fișierul policy, va fi necesară repornirea manuală a api-server-ului. Deoarece api-server-ul este pornit ca static pod, echipa kubectl delete nu va duce la repornirea acestuia. Va trebui să facem manual docker stop pe kube-masterele, unde politica auditului a fost modificată:

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

Când auditul este activat, este important să ne amintim că încărcătura pe kube-apiserver crește. În special, consumul de memorie pentru stocarea contextului cererilor crește. Înregistrarea în jurnal începe doar după trimiterea header-ului răspunsului. De asemenea, încărcătura depinde de configurația politicii de audit.

Exemple de politici

Să analizăm structura fișierelor policy prin exemple.

Iată un fișier simplu policy, pentru a înregistra totul la nivelul Metadata:

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

În policy poate fi specificată o listă de utilizatori (Utilizatori și ServiceAccounts) și grupuri de utilizatori. De exemplu, așa vom ignora utilizatorii sistemului, dar vom înregistra totul altceva la nivelul 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

De asemenea, există posibilitatea de a descrie țintele:

  • spații de nume (namespaces);
  • verbe (verbs: get, update, delete și altele);
  • resurse (resources, și anume: pod, configmaps și altele.) și grupuri de resurse (apiGroups).

Atenție! Resursele și grupurile de resurse (grupuri API, adică apiGroups), precum și versiunile lor, instalate în cluster, pot fi obținute prin comenzile:

kubectl api-resources
kubectl api-versions

Următoarea politică de audit este prezentată ca o demonstrație a celor mai bune practici în documentația Alibaba Cloud:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Nu logați etapa RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Nu logați evenimentele considerate a fi de mică importanță și inofensive:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # acesta este grupul API cu un nume gol, la care aparțin
                  # resursele de bază Kubernetes, numite „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"]
  # Nu logați cererile către URL-uri de tip read-only:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # Nu logați mesajele legate de tipul resurselor „evenimente”:
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Resursele de tip Secret, ConfigMap și TokenReview pot conține date
  # sensibile, astfel încât logăm doar metadatele cererilor
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Acțiunile de tip get, list și watch pot fi costisitoare în resurse; nu le logăm
  - 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"
  # Nivelul de logare implicit pentru resursele standard 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"
  # Nivelul de logare implicit pentru toate celelalte cereri
  - level: Metadata

Un alt exemplu bun de politică de audit — profilul utilizat în GCE.

Pentru a reacționa rapid la evenimentele de audit, există posibilitatea de a descrie un webhook. Această problemă este detaliată în documentația oficială, îl voi lăsa în afara acestui articol.

Concluzii

Articolul oferă o prezentare a mecanismelor de bază pentru asigurarea securității în clusterele Kubernetes, care permit crearea de conturi personalizate pentru utilizatori, separarea drepturilor acestora și, de asemenea, înregistrarea acțiunilor lor. Sper să fie de folos celor care s-au confruntat cu astfel de întrebări în teorie sau deja în practică. Vă recomand, de asemenea, să consultați lista altor materiale pe tema securității în Kubernetes, prezentată în „P.S.” - este posibil ca printre acestea să găsiți detalii relevante pentru problemele dumneavoastră.

P.S.

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster