Alfabeto della sicurezza in Kubernetes: autenticazione, autorizzazione, audit

Alfabeto della sicurezza in Kubernetes: autenticazione, autorizzazione, audit

Prima o poi, nell'uso di qualsiasi sistema, sorge la questione della sicurezza: garanzia di autenticazione, separazione dei diritti, audit e altre attività. Per Kubernetes sono già stati creati molti soluzioni, che permettono di conformarsi agli standard anche in ambienti molto esigenti… Questo materiale è dedicato agli aspetti fondamentali della sicurezza, implementati nei meccanismi integrati di K8s. In primo luogo, sarà utile per coloro che iniziano a esplorare Kubernetes, come punto di partenza per approfondire le questioni legate alla sicurezza.

Autenticazione

In Kubernetes ci sono due tipologie di utenti:

  • Service Accounts — account gestiti dall'API Kubernetes;
  • Utenti — utenti "normali" gestiti da servizi esterni e indipendenti.

La principale differenza tra questi tipi è che per i Service Accounts esistono oggetti speciali nell'API Kubernetes (che si chiamano proprio così — ServiceAccounts), che sono legati a uno spazio dei nomi e a un insieme di credenziali archiviate nel cluster in oggetti di tipo Secrets. Questi utenti (Service Accounts) sono principalmente destinati alla gestione dei diritti di accesso all'API Kubernetes da parte di processi in esecuzione nel cluster Kubernetes.

Gli Users normali, d'altra parte, non hanno record nell'API Kubernetes: la loro gestione deve essere effettuata tramite meccanismi esterni. Sono destinati a persone o processi che operano al di fuori del cluster.

Ogni richiesta all'API è legata a un Service Account, a un User o è considerata anonima.

Le credenziali dell'utente includono:

  • Nome utente — nome utente (dipende dalla registrazione!);
  • UID — stringa di identificazione dell'utente leggibile dalla macchina, che è "più coerente e unica del nome utente";
  • Groups — 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, HTTP Basic Auth. Grazie a questi meccanismi, è possibile implementare un'ampia varietà di schemi di autorizzazione: da un file statico con password a OpenID OAuth2.

Inoltre, è consentito utilizzare più schemi di autorizzazione contemporaneamente. Per impostazione predefinita, nel cluster sono utilizzati:

  • token di account di servizio — per Service Accounts;
  • X509 — per Users.

La questione della gestione dei ServiceAccounts va oltre l'ambito di questo articolo, e per chi desidera approfondire l'argomento consiglio di partire dalla pagina della documentazione ufficiale. Qui analizzeremo in dettaglio il funzionamento dei certificati X509.

Certificati per utenti (X.509)

Il metodo tradizionale per lavorare con i certificati prevede:

  • generazione della chiave:
    mkdir -p ~/mynewuser/.certs/
    openssl genrsa -out ~/mynewuser.key 2048
  • generazione della richiesta di certificato:
    openssl req -new -key ~/mynewuser.key -out ~/mynewuser.csr -subj "/CN=mynewuser/O=company"
  • elaborazione della richiesta di certificato utilizzando le chiavi CA del cluster Kubernetes, ottenendo il certificato utente (per ottenere il certificato è necessario utilizzare un account con accesso alla chiave dell'autorità di certificazione del cluster Kubernetes, che di solito si trova in /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
  • creazione del file di configurazione:
    • descrizione del cluster (specificare l'indirizzo e la posizione del file del certificato CA dell'installazione specifica del cluster):
      kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.100.200:6443
    • oppure — come nonopzione raccomandata — è possibile non specificare il certificato radice (in questo caso kubectl non verificherà la correttezza dell'api-server del cluster):
      kubectl config set-cluster kubernetes --insecure-skip-tls-verify=true --server=https://192.168.100.200:6443
    • aggiunta dell'utente al file di configurazione:
      kubectl config set-credentials mynewuser --client-certificate=.certs/mynewuser.crt --client-key=.certs/mynewuser.key
    • aggiunta del contesto:
      kubectl config set-context mynewuser-context --cluster=kubernetes --namespace=target-namespace --user=mynewuser
    • designazione del contesto predefinito:
      kubectl config use-context mynewuser-context

Dopo le manovre sopra descritte, nel file .kube/config sarà creato un file di configurazione del tipo:

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ò, è possibile codificare i file specificati in formato base64 e inserirli nella configurazione, aggiungendo il suffisso al nome delle chiavi -data, ottenendo così certificate-authority-data e così via.

Certificati con kubeadm

Con il rilascio di Kubernetes 1.15 lavorare con i certificati è diventato significativamente più semplice grazie alla versione alfa del suo supporto in kubeadm. Ad esempio, ecco come può apparire ora la generazione del file di configurazione con le chiavi utente:

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

NB: Richiesto indirizzo di pubblicità può essere visualizzato nel file di configurazione dell'api-server, che per impostazione predefinita si trova in /etc/kubernetes/manifests/kube-apiserver.yaml.

Il file di configurazione risultante verrà stampato su stdout. Deve essere salvato nella ~/ .kube / config credenziale utente o in un file specificato nella variabile d'ambiente KUBECONFIG.

Approfondire

Per coloro che desiderano approfondire i temi trattati:

Autenticazione

L'account autorizzato per impostazione predefinita non ha diritti su azioni nel cluster. Per fornire autorizzazioni in Kubernetes è stato implementato un meccanismo di autorizzazione.

Fino alla versione 1.6, in Kubernetes veniva utilizzato un tipo di autorizzazione chiamato ABAC (controllo degli accessi basato su attributi). Maggiori dettagli al riguardo possono essere trovati in documentazione ufficiale. Attualmente, questo approccio è considerato obsoleto (legacy), tuttavia puoi ancora utilizzarlo insieme ad altri tipi di autorizzazione.

Il modo attuale (e più flessibile) per gestire i diritti di accesso al cluster si chiama RBAC (controllo degli accessi basato su ruoli). È stato dichiarato stabile a partire dalla versione Kubernetes 1.8. RBAC attua un modello di diritti in cui tutto è vietato, a meno che non sia esplicitamente consentito.
Per abilitare RBAC, è necessario avviare l'api-server di Kubernetes con il parametro --authorization-mode=RBAC. I parametri vengono specificati nel manifesto di configurazione dell'api-server, che per impostazione predefinita si trova nel percorso /etc/kubernetes/manifests/kube-apiserver.yaml, nella sezione command. Tuttavia, per impostazione predefinita, RBAC è già abilitato, quindi probabilmente non c'è motivo di preoccuparsi: puoi controllarlo dal valore authorization-mode (nella già citata kube-apiserver.yaml). A proposito, tra i suoi valori potrebbero esserci anche altri tipi di autorizzazione (node, webhook, sempre consentire), ma la loro trattazione sarà lasciata fuori dal materiale.

A proposito, abbiamo già pubblicato articolo un racconto abbastanza dettagliato sui principi e le caratteristiche del lavoro con RBAC, quindi per ora mi limiterò a un breve elenco delle basi e degli esempi.

Per la gestione degli accessi in Kubernetes tramite RBAC vengono utilizzate le seguenti entità API:

  • Ruolo e ClusterRole — ruoli che servono a descrivere i diritti di accesso:
  • Ruolo consente di descrivere i diritti nell'ambito dello spazio dei nomi;
  • ClusterRole — nell'ambito del cluster, inclusi gli oggetti specifici del cluster come nodi, url non risorse (cioè non correlati alle risorse Kubernetes — ad esempio, /version, /logs, /api*);
  • RoleBinding e ClusterRoleBinding — serve per legare Ruolo e ClusterRole a un utente, a un gruppo di utenti o a un ServiceAccount.

Le entità Role e RoleBinding sono limitate allo spazio dei nomi, cioè devono trovarsi all'interno di un unico spazio dei nomi. Tuttavia, RoleBinding può fare riferimento a ClusterRole, consentendo di creare un insieme di autorizzazioni tipiche e gestire l'accesso utilizzandole.

I ruoli descrivono i diritti tramite insiemi di regole, che contengono:

  • gruppi API — vedi la documentazione ufficiale per apiGroups e output kubectl api-resources;
  • risorse (resources: pod, namespace, deployment ecc.);
  • verbi (verbs: set, aggiornamento ecc.).
  • nomi delle risorse (resourceNames) — nel caso in cui sia necessario fornire accesso a una particolare risorsa e non a tutte le risorse di quel tipo.

Una descrizione più dettagliata dell'autorizzazione in Kubernetes può essere trovata nella pagina documentazione ufficiale. Invece di questo (o meglio — in aggiunta a questo) riporterò esempi che illustrano il suo funzionamento.

Esempi di entità RBAC

Semplice Ruolo, consente di ottenere l'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 consente di ottenere l'elenco e lo stato dei pod e di monitorarli in tutto il cluster:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # la sezione "namespace" non è presente, poiché ClusterRole utilizza l'intero cluster
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Esempio RoleBinding, che consente all'utente mynewuser di "leggere" i pod 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 # nome utente sensibile alle maiuscole!
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # qui deve essere “Role” o “ClusterRole”
  name: pod-reader # nome del Role, che si trova nello stesso spazio dei nomi,
                   # o nome del ClusterRole, il cui utilizzo
                   # vogliamo consentire all'utente
  apiGroup: rbac.authorization.k8s.io

Audit degli eventi

Schema dell'architettura Kubernetes può essere rappresentato come segue:

Alfabeto della sicurezza in Kubernetes: autenticazione, autorizzazione, audit

Il componente chiave di Kubernetes, responsabile della gestione delle richieste, è api-server. Tutte le operazioni sul cluster passano attraverso di esso. Maggiori dettagli su questi meccanismi interni possono essere trovati nell'articolo «Cosa succede in Kubernetes quando si esegue kubectl run?».

L'audit del sistema è una funzionalità interessante in Kubernetes, che è disabilitata per impostazione predefinita. Permette di registrare tutte le richieste all'API di Kubernetes. Come si può facilmente intuire, attraverso questa API si eseguono tutte le operazioni relative al controllo e alla modifica dello stato del cluster. Una buona descrizione delle sue capacità può essere (come al solito) trovata in documentazione ufficiale K8s. Ora cercherò di esporre l'argomento in un linguaggio più semplice.

Quindi, Per attivare l'audit, dobbiamo passare al container nell'api-server tre parametri obbligatori, di cui parlerò dettagliatamente qui 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, esistono molte altre impostazioni relative all'audit: dalla rotazione dei log alle descrizioni dei webhook. Ecco un esempio di parametri per la rotazione dei log:

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

Ma non ci soffermeremo ulteriormente su di essi: tutti i dettagli possono essere trovati nella documentazione di kube-apiserver.

Come già menzionato, tutti i parametri sono impostati nel manifesto di configurazione dell'api-server (di default /etc/kubernetes/manifests/kube-apiserver.yaml), nella sezione command. Torniamo ai 3 parametri obbligatori e analizziamoli:

  1. audit-policy-file — il percorso del file YAML contenente la politica di audit. Torneremo sul suo contenuto, ma intanto segnalo che il file deve essere leggibile dal processo dell'api-server. Pertanto, è necessario montarlo all'interno del container, per farlo si può aggiungere il seguente codice alle sezioni appropriate 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 — il percorso del file di log. Anche questo percorso deve essere accessibile dal processo dell'api-server, quindi descriviamo in modo analogo il suo montaggio:
      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 — il formato del log di audit. Di default questo è json, ma è disponibile anche un formato testuale obsoleto (legacy).

Politica di audit

Ora parliamo del file menzionato con la descrizione della politica di logging. Il primo concetto di audit policy è level, il livello di logging. I livelli possono essere i seguenti:

  • Nessuno — non registrare;
  • Metadata — registrare i metadati della richiesta: utente, ora della richiesta, risorsa target (pod, namespace, ecc.), tipo di azione (verb) e così via;
  • Request — registrare i metadati e il corpo della richiesta;
  • RequestResponse — registrare i metadati, il corpo della richiesta e il corpo della risposta.

Gli ultimi due livelli (Request e RequestResponse) non registrano le richieste che non accedono alle risorse (richieste a cosiddetti URLs non risorse).

Inoltre, tutte le richieste passano attraverso diverse fasi:

  • RequestReceived — fase in cui la richiesta è stata ricevuta dal gestore e non è ancora stata trasferita lungo la catena di gestori;
  • ResponseStarted — le intestazioni della risposta sono state inviate, ma prima dell'invio del corpo della risposta. Viene generato per richieste di lunga durata (ad esempio, watch);
  • ResponseComplete — il corpo della risposta è stato inviato, non verranno inviate ulteriori informazioni;
  • Panic — gli eventi vengono generati quando si verifica una situazione anomala.

Per saltare alcune fasi, è possibile utilizzare omitStages.

Nel file di policy possiamo descrivere diverse sezioni con vari livelli di registrazione. Sarà applicata la prima regola corrispondente trovata nella descrizione della policy.

Il demone kubelet monitora le modifiche al manifesto con la configurazione dell'api-server e, quando ne rileva, riavvia il contenitore con l'api-server. Ma c'è un dettaglio importante: le modifiche al file di policy verranno ignorate da esso. Dopo aver apportato modifiche al file di policy, sarà necessario riavviare manualmente l'api-server. Poiché l'api-server è eseguito come static pod, il comando kubectl delete non porterà al suo riavvio. Sarà necessario eseguire manualmente docker stop sui kube-master dove è stata modificata la policy di audit:

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

Quando si attiva l'audit, è importante ricordare che il carico sul kube-apiserver aumenta. In particolare, aumenta il consumo di memoria per conservare il contesto delle richieste. La registrazione nel log inizia solo dopo l'invio dell'intestazione della risposta. Inoltre, il carico dipende dalla configurazione della policy di audit.

Esempi di politiche

Analizziamo la struttura dei file di policy tramite esempi.

Ecco un semplice file policy, per registrare tutto a livello di Metadata:

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

Nella policy è possibile specificare un elenco di utenti (Utenti e ServiceAccounts) e gruppi di utenti. Ad esempio, così faremo per ignorare gli utenti di sistema, ma registrare tutto il resto a livello di 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

C'è anche la possibilità di descrivere gli obiettivi:

  • spazi dei nomi (namespace);
  • verbi (verbs: get, aggiornamento, delete e altri);
  • risorse (resources, cioè: pod, configmaps ecc.) e gruppi di risorse (apiGroups).

Attenzione! Le risorse e i gruppi di risorse (gruppi API, cioè apiGroups), così come le loro versioni, installate nel cluster, possono essere ottenute tramite i comandi:

kubectl api-resources
kubectl api-versions

La seguente audit policy è fornita come dimostrazione delle migliori pratiche nella documentazione di Alibaba Cloud:

apiVersion: audit.k8s.io/v1beta1
kind: Policy
# Non registrare la fase RequestReceived
omitStages:
  - "RequestReceived"
rules:
  # Non registrare eventi considerati di poco significato e non pericolosi:
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
      - group: "" # questo è il gruppo api con nome vuoto, a cui appartengono
                  # le risorse di base di Kubernetes, chiamate "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"]
  # Non registrare le richieste a URL di sola lettura:
  - level: None
    nonResourceURLs:
      - /healthz*
      - /version
      - /swagger*
  # Non registrare i messaggi relativi a risorse di tipo "eventi":
  - level: None
    resources:
      - group: "" # core
        resources: ["events"]
  # Le risorse di tipo Secret, ConfigMap e TokenReview possono contenere
  # dati riservati,
  # quindi registriamo solo i metadati delle richieste correlate
  - level: Metadata
    resources:
      - group: "" # core
        resources: ["secrets", "configmaps"]
      - group: authentication.k8s.io
        resources: ["tokenreviews"]
  # Le azioni di tipo get, list e watch possono essere onerose; non le registriamo
  - 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"
  # Livello di registrazione predefinito per le risorse API standard
  - 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"
  # Livello di registrazione predefinito per tutte le altre richieste
  - level: Metadata

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

Per rispondere rapidamente agli eventi di audit, c'è la possibilità di descrivere un webhook. Questa questione è trattata in documentazione ufficiale, lo lascerò fuori dai confini di questo articolo.

Conclusioni

L'articolo offre 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 sia utile a chi si è trovato di fronte a tali questioni, sia in teoria che in pratica. Raccomando anche di prendere visione dell'elenco di altri materiali sulla sicurezza in Kubernetes, riportato nel «P.S.», poiché tra di essi potreste trovare dettagli pertinenti ai vostri problemi attuali.

P.S.

Leggi anche nel nostro blog:

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster