Sysdig
Learn Cloud Native

Sign up to receive our newsletter

Come e perché proteggere i pod con i contesti di protezione di Kubernetes

In che modo si può garantire che ciascuna risorsa in Kubernetes sia in possesso delle autorizzazioni di cui necessita evitando al contempo che le vengano assegnate autorizzazioni eccessive? In altre parole: come definire le autorizzazioni su base granulare per aderire al principio dei privilegi minimi?

La risposta è: utilizzando i contesti di protezione di Kubernetes. Un contesto di protezione è uno strumento che consente agli amministratori di definire parametri di sicurezza per ogni singola risorsa. Come tale, consente di assegnare a ciascuna risorsa le autorizzazioni specifiche di cui ha bisogno per accedere alle risorse sul server host, negandole però l’accesso alle risorse di cui non ha specifico bisogno.

Questo articolo fornisce una panoramica dei contesti di protezione di Kubernetes, spiega come funzionano, in che modo definirli e quali sono i limiti da considerare quando si utilizzano.

Cos’è un contesto di protezione di Kubernetes?

In Kubernetes, un contesto di protezione definisce i privilegi di singoli pod o container. Può essere utilizzato per assegnare ai pod o ai container autorizzazioni quali il diritto di accedere a un file esterno o di funzionare in modalità privilegiata.

Contesti di protezione interni vs. esterni

I contesti di protezione di Kubernetes sono piuttosto complicati, poiché alcune delle regole definite vengono applicate internamente da Kubernetes stesso, mentre altre si integrano con strumenti di sicurezza esterni, ad esempio AppArmor e SELinux.

Quindi, possiamo pensare ai contesti di protezione di Kubernetes come a un modo per definire alcune autorizzazioni per pod e container e per integrare Kubernetes con strumenti di sicurezza esterni eseguiti sull’host e non su Kubernetes.

Contesti di protezione vs. RBAC

Un contesto di protezione è simile ma non è uguale al Role-Based Access Control di Kubernetes, o RBAC. Di seguito presentiamo le loro principali differenze:

  • Risorse interessate: RBAC può essere applicato a diverse risorse Kubernetes, ad esempio pod, nodi e perfino a interi cluster. Un contesto di protezione assegna autorizzazioni solo ai pod.
  • Azioni: RBAC può garantire diverse autorizzazioni in base a “verbi” che gli amministratori definiscono nelle policy RBAC. Un contesto di protezione è più restrittivo, poiché consente agli amministratori solo di assegnare tipi specifici di capacità predefinite, ad esempio l’esecuzione in modalità privilegiata (sebbene i contesti di protezione siano più flessibili se si definiscono regole utilizzando SELinux o AppArmor).
  • Estendibilità: Come già indicato, i contesti di protezione possono essere estesi ricorrendo alle integrazioni con strumenti esterni, quali SELinux e AppArmor. RBAC di Kubernetes non può utilizzare strumenti esterni per definire le policy.

Quindi, possiamo pensare ai contesti di protezione come a un modo per definire ulteriori tipi di autorizzazioni di sicurezza per container e pod che RBAC non è in grado di gestire. Per molti ambienti Kubernetes, sarà necessario utilizzare i contesti di protezione e RBAC contestualmente, poiché sono complementari.

Contesti di protezione vs. Pod Security Policies

Molte regole di sicurezza definibili mediante i contesti di protezione possono anche essere configurate attraverso le pod security policies, uno strumento diverso.

Perché Kubernetes offre supporto sia ai contesti di protezione che alle pod security policies? La risposta è che i contesti di protezione sono di fatto un sostituto delle pod security policies. Le pod security policies, che possono essere utilizzate per configurare le autorizzazioni per tutti i pod in esecuzione in un cluster, consentono un minor controllo granulare rispetto ai contesti di protezione, i quali possono essere applicati ai singoli pod.

Dalla versione 1.21 di Kubernetes, le pod security policies sono considerate obsolete, sebbene per il momento siano ancora supportate. Verranno completamente eliminate con Kubernetes 1.25, pertanto da quel momento eventuali policy definite verranno ignorate.

Come utilizzare un contesto di protezione di Kubernetes?

Utilizzare un contesto di protezione in Kubernetes è piuttosto semplice (soprattutto se si lavora solo con contesti di protezione interni non integrati con SELinux o AppArmor). Basta includere un blocco di codice per il contesto di protezione all’interno del file di distribuzione creato durante la distribuzione del pod.

Per esempio, il seguente blocco comunica a Kubernetes di eseguire un pod con ID utente pari a 1000. Assegna inoltre l’ID gruppo 2000 a tutti i container nel pod:

Spec: securityContext: runAsUser: 1000 fsGroup: 2000

Diversamente da RBAC, il contesto di protezione non richiede di definire più tipi di file (come Roles e RoleBindings) per applicare una regola di sicurezza. Basta aggiungere il codice del contesto di protezione richiesto quando si definisce una distribuzione e Kubernetes applicherà automaticamente le regole.

Per una panoramica completa dei tipi di autorizzazioni che possono essere assegnate (o negate) tramite i contesti di protezione, consultare la documentazione Kubernetes. Oltre alle autorizzazioni relative a ID utente e di gruppo, molti amministratori troveranno molto utili i parametri che consentono di eseguire il pod o il container in modalità privilegiata. Un container eseguito in modalità privilegiata dispone di quasi tutti i diritti di accesso alle risorse a livello kernel nell’host, quasi come i processi eseguiti come root, quindi in generale è consigliabile non consentire la modalità privilegiata. Al contrario, è meglio definire le autorizzazioni su base granulare, ad esempio consentendo a un container o pod di rimanere limitato a una certa porta o di eseguire un certo file binario esterno negando allo stesso tempo l’accesso a risorse esterne al container.

Lavorare con i contesti di protezione esterni

Utilizzare i contesti di protezione in base a strumenti esterni quali SELinux e AppArmor richiede un po’ più di impegno. Ecco di seguito i passaggi fondamentali.

Caricare il modulo SELinux o AppArmor

Per prima cosa, è necessario assicurarsi che il modulo kernel associato allo strumento in uso (cioè SELinux o AppArmor) sia installato e caricato sul nodo o sui nodi che ospitano i container o i pod.

Dal momento che nella maggior parte dei casi Kubernetes assegna automaticamente i pod o i container ai nodi, non è possibile sapere in anticipo quale nodo ospiterà quale container o pod. Di conseguenza, è di solito necessario installare AppArmor o SELinux su ciascun nodo nel cluster se si desidera definire i contesti di protezione attraverso uno di questi strumenti.

Caricare un profilo

Dopo aver caricato i moduli, è necessario caricare il profilo AppArmor o SELinux che verrà utilizzato per definire le autorizzazioni. Per esempio, questo profilo AppArmor nega la possibilità di scrivere file:

#include <tunables/global> profile k8s-apparmor-example-deny-write flags=(attach_disconnected) { #include <abstractions/base> file, # Deny all file writes. deny /** w, }
Code language: PHP (php)

Bisogna salvare il profilo nel filesystem del nodo in un punto in cui Kubernetes possa leggerlo.

Di nuovo, poiché probabilmente non si conosce in anticipo quale nodo eseguirà quale container o pod, sarà necessario caricare il profilo su ciascun nodo del cluster. Per svolgere questa operazione in modo efficiente (ovvero per evitare di dover stabilire una SSH in ciascun nodo e applicare i profili manualmente), consultare la documentazione di Kubernetes sui profili dei nodi.

Applicare il profilo in Kubernetes

Infine, è necessario aggiungere un blocco per il contesto di protezione ai file di distribuzione per comunicare a Kubernetes di fare riferimento al profilo esterno e di applicarlo alla distribuzione:

apiVersion: v1 kind: Pod metadata: name: apparmor-demo annotations: container.apparmor.security.beta.kubernetes.io/hello: /path/to/policy-file
Code language: JavaScript (javascript)

Applicare la dichiarazione con:

kubectl create -f ./deployment.yml

A questo punto, Kubernetes applicherà la policy configurata attraverso AppArmor o SELinux come farebbe un contesto di protezione configurato direttamente in un file di distribuzione.

Limitazioni dei contesti di protezione

I contesti di protezione di Kubernetes sono uno strumento potente per definire alcuni tipi di autorizzazione su base granulare. Tuttavia, sono soggetti ad alcune limitazioni importanti.

Nessun supporto per Windows

Innanzitutto, gli strumenti per i contesti di protezione attualmente gestiscono solo privilegi e autorizzazioni validi su un server Linux. Se si eseguono container Windows in Kubernetes, i contesti di protezione non saranno utili.

Sicurezza solo a livello di pod/container

I contesti di protezione permettono di definire autorizzazioni solo per pod o container. Non possono controllare privilegi in altri livelli dello stack.

Ovviamente, molte delle regole che possono essere applicate con i contesti di protezione hanno senso solo se si riferiscono a container o pod. Per esempio, non avrebbe alcun senso dire a un nodo di avviarsi in modalità privilegiata.

Detto questo, è importante sottolineare che i contesti di protezione sono utili per gestire problemi di sicurezza solo a livello di pod o container. Saranno necessari altri strumenti (come RBAC) per proteggere nodi, utenti, account di servizio, eccetera.

Il futuro cambia

I contesti di protezione sono una funzione piuttosto nuova per Kubernetes e sono in continua evoluzione. Alcuni parametri (come fsGroupChangePolicy, introdotto con supporto beta in Kubernetes 1.20) non sono ancora completamente supportati e in futuro potrebbero essere sviluppate nuove definizioni.

Pertanto, sebbene oggi sia possibile utilizzare i contesti di protezione in cluster di produzione, è importante seguire attentamente l’evoluzione di questa funzione poiché alcuni tipi di definizioni potrebbero cambiare nelle future release.

Strumenti inefficienti

Come abbiamo già sottolineato, una grande limitazione dei contesti di protezione (in particolare, quelli che utilizzano i profili SELinux o AppArmor) risiede nel fatto che essi richiedono l’impiego di risorse esterne su ciascun nodo nel cluster. Per quanto sia possibile automatizzare questo processo, configurare le automazioni richiede uno sforzo piuttosto grande. La distribuzione potrebbe anche avvenire disordinatamente in presenza di nodi eseguiti su diverse distribuzioni Linux, nel qual caso potrebbe essere necessario personalizzare il setup di AppArmor o SELinux per ciascuna disribuzione.

Si può sperare che, in futuro, vengano sviluppati strumenti per semplificare l’applicazione dei profili delle policy nei nodi. Ma per ora, non dobbiamo sottovalutare gli sforzi da impiegare per configurare i nodi.

Nonostante questo svantaggio, i contesti di protezione costituiscono una risorsa importante che consente di colmare eventuali lacune nel controllo degli accessi nei cluster di Kubernetes. Sebbene siano limitati in termini di applicazione e non siano una soluzione completa, sarebbe utile sfruttare al massimo i contesti di protezione per rinforzare la sicurezza complessiva del cluster.