L'ABC della sicurezza in Kubernetes: autenticazione, autorizzazione, controllo

L'ABC della sicurezza in Kubernetes: autenticazione, autorizzazione, controllo

Prima o poi, nel funzionamento di qualsiasi sistema, sorge il problema della sicurezza: garantire l'autenticazione, la separazione dei diritti, il controllo e altri compiti. Già creato per Kubernetes molte soluzioni, che consentono di ottenere la conformità agli standard anche in ambienti molto esigenti... Lo stesso materiale è dedicato agli aspetti fondamentali della sicurezza implementati all'interno dei meccanismi integrati dei K8. Prima di tutto, sarà utile a coloro che iniziano a familiarizzare con Kubernetes, come punto di partenza per studiare le questioni relative alla sicurezza.

autenticazione

Esistono due tipi di utenti in Kubernetes:

  • Conti di servizio — account gestiti dall'API Kubernetes;
  • Utenti — utenti “normali” gestiti da servizi esterni e indipendenti.

La differenza principale tra questi tipi è che per gli account di servizio ci sono oggetti speciali nell'API Kubernetes (si chiamano così: ServiceAccounts), che sono legati a un namespace e a un insieme di dati di autorizzazione archiviati nel cluster in oggetti di tipo Secrets. Tali utenti (account di servizio) sono destinati principalmente a gestire i diritti di accesso all'API Kubernetes dei processi in esecuzione nel cluster Kubernetes.

Gli Utenti Ordinari non hanno voci nelle API Kubernetes: devono essere gestiti da meccanismi esterni. Sono destinati a persone o processi che vivono al di fuori del cluster.

Ogni richiesta API è associata a un account di servizio, a un utente o è considerata anonima.

I dati di autenticazione dell'utente includono:

  • Nome utente — nome utente (distinzione tra maiuscole e minuscole!);
  • UID - una stringa di identificazione dell'utente leggibile meccanicamente che sia “più coerente e univoca del nome utente”;
  • ATTIVITA' E GRUPPI — elenco dei gruppi a cui appartiene l'utente;
  • Extra — campi aggiuntivi che possono essere utilizzati dal meccanismo di autorizzazione.

Kubernetes può utilizzare un gran numero di meccanismi di autenticazione: certificati X509, token Bearer, proxy di autenticazione, autenticazione HTTP di base. Utilizzando questi meccanismi, è possibile implementare un gran numero di schemi di autorizzazione: da un file statico con password a OpenID OAuth2.

Inoltre è possibile utilizzare più schemi di autorizzazione contemporaneamente. Per impostazione predefinita, il cluster utilizza:

  • token dell'account di servizio: per gli account di servizio;
  • X509 - per gli utenti.

La domanda sulla gestione degli account di servizio va oltre lo scopo di questo articolo, ma per coloro che desiderano familiarizzare con questo problema in modo più dettagliato, consiglio di iniziare con pagine di documentazione ufficiale. Daremo uno sguardo più da vicino alla questione del funzionamento dei certificati X509.

Certificati per gli utenti (X.509)

Il modo classico di lavorare con i certificati prevede:

  • generazione di chiavi:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/.certs/mynewuser.key 2048
  • generazione di una richiesta di certificato:
    openssl req -new -key ~/.certs/mynewuser.key -out ~/.certs/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • elaborare una richiesta di certificato utilizzando le chiavi della CA del cluster Kubernetes, ottenere un certificato utente (per ottenere un certificato, è necessario utilizzare un account che abbia accesso alla chiave della CA del cluster Kubernetes, che per impostazione predefinita si trova in /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
  • creando un file di configurazione:
    • descrizione del cluster (specificare l'indirizzo e il percorso del file di certificato CA per un'installazione cluster specifica):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • o come noopzione consigliata: non è necessario specificare il certificato radice (in tal caso kubectl non controllerà la correttezza del server API del cluster):
      kubectl config set-cluster kubernetes  --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • aggiunta di un utente al file di configurazione:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt  --client-key=.certs/mynewuser.key
    • aggiungendo contesto:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • assegnazione del contesto predefinito:
      kubectl config use-context mynewuser-context

Dopo le manipolazioni di cui sopra, nel file .kube/config verrà creata una configurazione come questa:

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

Per facilitare il trasferimento della configurazione tra account e server, è utile modificare i valori delle seguenti chiavi:

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

Per fare ciò, puoi codificare i file specificati in essi utilizzando base64 e registrarli nella configurazione, aggiungendo il suffisso al nome delle chiavi -data, cioè. avendo ricevuto certificate-authority-data eccetera

Certificati con kubeadm

Con il rilascio Kubernet 1.15 lavorare con i certificati è diventato molto più semplice grazie alla versione alpha del suo supporto utilità kubeadm. Ad esempio, ecco come potrebbe apparire ora la generazione di un file di configurazione con chiavi utente:

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

NB: Necessario pubblicizzare l'indirizzo può essere trovato nella configurazione del server API, che per impostazione predefinita si trova in /etc/kubernetes/manifests/kube-apiserver.yaml.

La configurazione risultante verrà inviata a stdout. Ha bisogno di essere salvato ~/.kube/config account utente o in un file specificato in una variabile di ambiente KUBECONFIG.

Scava più a fondo

Per chi vuole comprendere più approfonditamente le tematiche descritte:

Autorizzazione

L'account autorizzato predefinito non dispone dei diritti per operare sul cluster. Per concedere le autorizzazioni, Kubernetes implementa un meccanismo di autorizzazione.

Prima della versione 1.6, Kubernetes utilizzava un tipo di autorizzazione chiamato ABAC (Controllo degli accessi basato sugli attributi). I dettagli a riguardo possono essere trovati in documentazione ufficiale. Questo approccio è attualmente considerato legacy, ma puoi comunque utilizzarlo insieme ad altri tipi di autenticazione.

Viene chiamato l'attuale (e più flessibile) modo di dividere i diritti di accesso a un cluster RBAC (Controllo degli accessi in base al ruolo). È stato dichiarato stabile dalla versione Kubernet 1.8. RBAC implementa un modello di diritti in cui tutto ciò che non è esplicitamente consentito è proibito.
Per abilitare RBAC, è necessario avviare Kubernetes api-server con il parametro --authorization-mode=RBAC. I parametri vengono impostati nel manifest con la configurazione api-server, che per impostazione predefinita si trova lungo il percorso /etc/kubernetes/manifests/kube-apiserver.yaml, nella sezione command. Tuttavia, RBAC è già abilitato di default, quindi molto probabilmente non dovresti preoccuparti di questo: puoi verificarlo tramite il valore authorization-mode (nel già citato kube-apiserver.yaml). A proposito, tra i suoi significati potrebbero esserci altri tipi di autorizzazione (node, webhook, always allow), ma lasceremo la loro considerazione fuori dallo scopo del materiale.

A proposito, abbiamo già pubblicato Articolo con una descrizione abbastanza dettagliata dei principi e delle caratteristiche del lavoro con RBAC, quindi mi limiterò ulteriormente a un breve elenco delle basi e degli esempi.

Le seguenti entità API vengono utilizzate per controllare l'accesso in Kubernetes tramite RBAC:

  • Role и ClusterRole — ruoli che servono a descrivere i diritti di accesso:
  • Role ti permette di descrivere i diritti all'interno di uno spazio dei nomi;
  • ClusterRole - all'interno del cluster, inclusi oggetti specifici del cluster come nodi, URL non di risorse (ovvero non correlati alle risorse Kubernetes - ad esempio, /version, /logs, /api*);
  • RoleBinding и ClusterRoleBinding - utilizzato per la rilegatura Role и ClusterRole a un utente, gruppo di utenti o ServiceAccount.

Le entità Role e RoleBinding sono limitate dallo spazio dei nomi, ad es. deve trovarsi all'interno dello stesso spazio dei nomi. Tuttavia, un RoleBinding può fare riferimento a un ClusterRole, che consente di creare un set di autorizzazioni generiche e controllare l'accesso utilizzandole.

I ruoli descrivono i diritti utilizzando insiemi di regole contenenti:

  • Gruppi API - vedi documentazione ufficiale da apiGroups e output kubectl api-resources;
  • risorse (risorse: pod, namespace, deployment e così via.);
  • Verbi (verbi: set, update ecc).
  • nomi delle risorse (resourceNames) - nel caso in cui sia necessario fornire l'accesso a una risorsa specifica e non a tutte le risorse di questo tipo.

Un'analisi più dettagliata dell'autorizzazione in Kubernetes è disponibile nella pagina documentazione ufficiale. Invece (o meglio, in aggiunta a questo), darò degli esempi che illustrano il suo lavoro.

Esempi di entità RBAC

Semplice Role, che ti consente di ottenere un elenco e lo stato dei pod e di monitorarli nello spazio dei nomi 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"]

esempio ClusterRole, che ti consente di ottenere un elenco e lo stato dei pod e di monitorarli in tutto il cluster:

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

esempio RoleBinding, che consente all'utente mynewuser Pod "leggi" nello spazio dei nomi 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

Verifica dell'evento

Schematicamente l’architettura Kubernetes può essere rappresentata come segue:

L'ABC della sicurezza in Kubernetes: autenticazione, autorizzazione, controllo

Il componente chiave di Kubernetes responsabile dell'elaborazione delle richieste è server API. Tutte le operazioni sul cluster passano attraverso di esso. Puoi leggere di più su questi meccanismi interni nell’articolo “Cosa succede in Kubernetes quando esegui kubectl run?'.

Il controllo del sistema è una funzionalità interessante di Kubernetes, che è disabilitata per impostazione predefinita. Ti consente di registrare tutte le chiamate all'API Kubernetes. Come puoi immaginare, tutte le azioni relative al monitoraggio e alla modifica dello stato del cluster vengono eseguite tramite questa API. Una buona descrizione delle sue capacità può essere trovata (come al solito) in documentazione ufficiale K8. Successivamente, cercherò di presentare l’argomento in un linguaggio più semplice.

Così, per consentire il controllo, dobbiamo passare tre parametri richiesti al contenitore nell'api-server, descritti più dettagliatamente di seguito:

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

Oltre a questi tre parametri necessari, ci sono molte impostazioni aggiuntive relative all'auditing: dalla rotazione dei log alle descrizioni dei webhook. Esempio di parametri di rotazione del registro:

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

Ma non ci soffermeremo su di loro in modo più dettagliato: puoi trovare tutti i dettagli in documentazione di kube-apiserver.

Come già accennato, tutti i parametri sono impostati nel manifest con la configurazione api-server (per impostazione predefinita /etc/kubernetes/manifests/kube-apiserver.yaml), nella sezione command. Ritorniamo ai 3 parametri richiesti e analizziamoli:

  1. audit-policy-file - percorso del file YAML che descrive la politica di controllo. Torneremo al suo contenuto più tardi, ma per ora noterò che il file deve essere leggibile dal processo api-server. E' quindi necessario montarlo all'interno del container, per cui è possibile aggiungere il seguente codice nelle apposite sezioni della configurazione:
      volumeMounts:
        - mountPath: /etc/kubernetes/policies
          name: policies
          readOnly: true
      volumes:
      - hostPath:
          path: /etc/kubernetes/policies
          type: DirectoryOrCreate
        name: policies
  2. audit-log-path — percorso del file di registro. Il percorso deve essere accessibile anche al processo api-server, quindi descriviamo il suo montaggio allo stesso modo:
      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 — formato del registro di controllo. L'impostazione predefinita è json, ma è disponibile anche il formato testo precedente (legacy).

Politica di controllo

Ora riguardo al file menzionato che descrive la politica di registrazione. Il primo concetto di politica di audit è level, livello di registrazione. Sono i seguenti:

  • None - non effettuare il login;
  • Metadata — metadati della richiesta di registro: utente, ora della richiesta, risorsa di destinazione (pod, spazio dei nomi, ecc.), tipo di azione (verbo), ecc.;
  • Request — registrare i metadati e il corpo della richiesta;
  • RequestResponse — metadati del registro, corpo della richiesta e corpo della risposta.

Gli ultimi due livelli (Request и RequestResponse) non registrano le richieste che non hanno avuto accesso alle risorse (accessi ai cosiddetti URL non risorse).

Inoltre tutte le richieste vanno a buon fine diverse fasi:

  • RequestReceived — la fase in cui la richiesta viene ricevuta dall'incaricato del trattamento e non è ancora stata trasmessa ulteriormente lungo la catena degli incaricati del trattamento;
  • ResponseStarted — le intestazioni della risposta vengono inviate, ma prima dell'invio del corpo della risposta. Generato per query a lunga esecuzione (ad esempio, watch);
  • ResponseComplete — l'organismo di risposta è stato inviato, non verranno inviate ulteriori informazioni;
  • Panic — gli eventi vengono generati quando viene rilevata una situazione anomala.

Per saltare eventuali passaggi è possibile utilizzare omitStages.

In un file di policy, possiamo descrivere diverse sezioni con diversi livelli di registrazione. Verrà applicata la prima regola corrispondente trovata nella descrizione della policy.

Il demone kubelet monitora le modifiche nel manifest con la configurazione api-server e, se ne vengono rilevate, riavvia il contenitore con api-server. Ma c’è un dettaglio importante: le modifiche nel file delle politiche verranno ignorate. Dopo aver apportato modifiche al file dei criteri, sarà necessario riavviare manualmente il server API. Poiché api-server viene avviato come baccello statico, squadra kubectl delete non ne causerà il riavvio. Dovrai farlo manualmente docker stop su kube-masters, dove la policy di audit è stata modificata:

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

Quando si abilita il controllo, è importante ricordarlo il carico su kube-apiserver aumenta. In particolare, aumenta il consumo di memoria per la memorizzazione del contesto della richiesta. La registrazione inizia solo dopo l'invio dell'intestazione della risposta. Il carico dipende anche dalla configurazione della politica di controllo.

Esempi di politiche

Diamo un'occhiata alla struttura dei file di policy utilizzando degli esempi.

Ecco un semplice file policyper registrare tutto a livello Metadata:

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

Nella policy è possibile specificare un elenco di utenti (Users и ServiceAccounts) e gruppi di utenti. Ad esempio, in questo modo ignoreremo gli utenti del sistema, ma registreremo tutto il resto a livello 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

È anche possibile descrivere gli obiettivi:

  • spazi dei nomi (namespaces);
  • Verbi (verbi: get, update, delete e altri);
  • risorse (risorse, Come segue: pod, configmaps ecc.) e gruppi di risorse (apiGroups).

Fate attenzione! Le risorse e i gruppi di risorse (gruppi API, ovvero apiGroups), nonché le loro versioni installate nel cluster, possono essere ottenute utilizzando i comandi:

kubectl api-resources
kubectl api-versions

La seguente politica di audit viene fornita come dimostrazione delle migliori pratiche in Documentazione Alibaba Cloud:

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

Un altro buon esempio di politica di audit è profilo utilizzato in GCE.

Per rispondere rapidamente agli eventi di controllo, è possibile descrivere il webhook. Questo problema è trattato documentazione ufficiale, lo lascerò fuori dallo scopo di questo articolo.

Risultati di

L'articolo fornisce una panoramica dei meccanismi di sicurezza di base nei cluster Kubernetes, che consentono di creare account utente personalizzati, separare i loro diritti e registrare le loro azioni. Spero che possa essere utile a coloro che si trovano ad affrontare tali problemi in teoria o in pratica. Ti consiglio anche di leggere l'elenco di altri materiali sul tema della sicurezza in Kubernetes, che è riportato in "PS" - forse tra questi troverai i dettagli necessari sui problemi che ti interessano.

PS

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento