Ricerca nel sito web

Implementa la governance sul tuo cluster Kubernetes


Utilizza OPA Gatekeeper per creare e applicare policy e governance per i tuoi cluster Kubernetes in modo che le risorse applicate siano conformi a tale policy.

Quando lavori con Kubernetes, diventa lentamente il tuo tempio di produzione. Investi tempo e risorse per svilupparlo e alimentarlo e inizi naturalmente a cercare modi per controllare l'utente finale Kubernetes nella tua organizzazione. Cosa può fare? Quali risorse può creare? Può etichettare due distribuzioni in un modo specifico? Quali migliori pratiche dovremmo seguire?

Incontra il Gatekeeper dell'OPA. Questo articolo ti mostrerà come utilizzarlo per creare e applicare policy e governance per i tuoi cluster Kubernetes in modo che le risorse che applichi siano conformi a tale policy.

(Noaa Barki, CC BY-SA 4.0)

Perché utilizzare OPA Gatekeeper?

OPA Gatekeeper ti fornisce due abilità fondamentali:

  • Controlla cosa può fare l'utente finale sul cluster
  • Applicare le policy aziendali nel cluster

Tuttavia, il vero potere di Gatekeeper è il suo effetto sulle organizzazioni. Gatekeeper aiuta a ridurre la dipendenza tra gli amministratori DevOps e gli sviluppatori stessi. L'applicazione delle policy della tua organizzazione può essere automatizzata, evitando agli ingegneri DevOps di preoccuparsi che gli sviluppatori commettano errori. Fornisce inoltre agli sviluppatori un feedback immediato su cosa è andato storto e cosa devono cambiare.

OPA Gatekeeper, un sottoprogetto di Open Policy Agent, è specificamente progettato per implementare OPA in un cluster Kubernetes. (Fatto curioso: il progetto è una collaborazione tra Google, Microsoft, Red Hat e Styra.) Questo articolo richiede una conoscenza di base sia di Kubernetes che di OPA, quindi se hai già familiarità con OPA, sentiti libero di saltare questa parte e andare avanti a quello successivo.

Cos'è l'OPA?

L'OPA è come un super motore. Puoi scrivere tutte le tue politiche al suo interno, quindi eseguirlo con ciascun input per verificare se viola qualche politica e, in tal caso, in che modo.

L’idea principale alla base dell’OPA è la capacità di dissociare la logica decisionale politica dall’utilizzo dell’applicazione delle politiche.

Supponiamo che tu lavori in un'architettura multiservizi. Potrebbe essere necessario prendere decisioni relative alle policy, ad esempio, quando il microservizio riceve una richiesta API (come l'autorizzazione). Questa logica si basa su regole previste nella tua organizzazione, quindi in questo caso puoi scaricare e unificare tutta la logica decisionale su un servizio dedicato: OPA.

Come utilizzare l'OPA:

  1. Integrazione con OPA: se i tuoi servizi sono scritti in Go, puoi incorporare OPA come pacchetto all'interno del tuo progetto. Altrimenti, puoi distribuire OPA come demone a livello di host.
  2. Scrivi e archivia le tue policy: per definire le tue policy in OPA, devi scriverle in Rego e inviarle a OPA. In questo modo, ogni volta che si utilizza l'OPA per l'applicazione delle policy, l'OPA interrogherà l'input rispetto a tali policy.
  3. Richiedi valutazione della policy: quando la tua applicazione deve prendere una decisione sulla policy, invierà una richiesta di query API utilizzando JSON, contenente tutti i dati richiesti tramite HTTP.

Se vuoi saperne di più su OPA e su come usarlo, e saperne di più sulle sue capacità, ti consiglio di leggere la documentazione di OPA.

Webhook di ammissione Kubernetes

Prima di approfondire il funzionamento di Gatekeeper, dobbiamo conoscere i webhook di ammissione di Kubernetes.

Quando una richiesta arriva all'API Kubernetes, passa attraverso una serie di passaggi prima di essere eseguita.

  1. La richiesta è autenticata e autorizzata.
  2. La richiesta viene elaborata da un elenco di raccolte webhook Kubernetes speciali chiamate controller di ammissione che possono mutare, modificare e convalidare gli oggetti nella richiesta.
  3. La richiesta viene mantenuta in etcd per essere eseguita.

I controller di ammissione Kubernetes sono il middleware del cluster. Controllano ciò che può procedere nel cluster. I controller di ammissione gestiscono le distribuzioni che richiedono troppe risorse, applicano le policy di sicurezza dei pod e bloccano persino la distribuzione di immagini vulnerabili.

Sotto il cofano di un controller di ammissione c'è una raccolta di callback HTTP predefiniti (ad esempio webhook), che intercettano l'API Kubernetes ed elaborano le richieste dopo che sono state autenticate e autorizzate.

Esistono due tipi di controllori di ammissione:

    MutatingAdmissionWebhook
    ValidatingAdmissionWebhook

I controller di ammissione mutanti vengono invocati per primi, poiché il loro compito è quello di applicare valori predefiniti personalizzati e, se necessario, modificare gli oggetti inviati al server API. Dopo che tutte le modifiche sono state completate e l'oggetto in entrata è stato convalidato, i controller di ammissione di convalida vengono richiamati e possono rifiutare le richieste per applicare policy personalizzate. Tieni presente che alcuni controller stanno convalidando e mutando. Se uno di questi rifiuta la richiesta, la richiesta fallirà.

È potente, gratuito e potresti già usarlo

Diversi controller di ammissione sono preconfigurati e probabilmente li usi già. LimitRanger, ad esempio, è un webhook di ammissione che impedisce l'esecuzione dei pod se il cluster ha esaurito le risorse. Per ulteriori informazioni su MutatingAdmissionWebhook, vedi "Diving into Kubernetes MutatingAdmissionWebhooks" sul blog IBM Cloud.

(Noaa Barki, CC BY-SA 4.0)

Controllo dinamico dell'ammissione

Potresti chiederti perché i controller di ammissione sono implementati con i webhook. È qui che il controllore di ammissione dà il meglio di sé ed è qui che entra in gioco il controllo di ammissione dinamico.

I webhook offrono agli sviluppatori la libertà e la flessibilità di personalizzare la logica di ammissione ad azioni come Crea, Aggiorna o Elimina su qualsiasi risorsa. Ciò è estremamente utile, perché quasi tutte le organizzazioni dovranno aggiungere/adattare le proprie politiche e migliori pratiche.

Le questioni chiave sorgono dal modo in cui operano i controllori dell’ammissione. La modifica dei controller di ammissione richiede che vengano ricompilati in Kube-apiserver e possono essere abilitati solo quando apiserver è attivato. L'implementazione dei controller di ammissione con webhook consente agli amministratori di creare webhook personalizzati e aggiungere webhook di ammissione mutanti o convalidanti alla catena di webhook di ammissione senza ricompilarli. L'apiserver Kubernetes esegue webhook registrati, che sono interfacce standard.

Per ulteriori letture sul controllo dinamico dell'ammissione, consiglio i documenti K8s.

Come funziona il Gatekeeper OPA

Gatekeeper funge da ponte tra il server API Kubernetes e OPA. In pratica, ciò significa che Gatekeeper controlla ogni richiesta che arriva nel cluster per vedere se viola qualcuna delle policy predefinite. In tal caso, apiserver lo rifiuterà.

Dietro le quinte, Gatekeeper si integra con Kubernetes utilizzando l'API di controllo dell'ammissione dinamica e viene installato come webhook ValidatingAdmission personalizzabile. Una volta installato, apiserver lo attiva ogni volta che una risorsa nel cluster viene creata, aggiornata o eliminata.

Poiché Gatekeeper opera tramite OPA, tutte le policy devono essere scritte in Rego. Fortunatamente, Kubernetes riesce a coprire tutto ciò utilizzando il framework dei vincoli OPA.

Un vincolo è un CRD che rappresenta la politica che vogliamo applicare su un tipo specifico di risorsa. Quando viene richiamato il controller ValidatingAdmission, il webhook Gatekeeper valuta tutti i vincoli e invia all'OPA la richiesta insieme alla policy per applicarla. Tutti i vincoli vengono valutati come logici e, se un vincolo non è soddisfatto, l'intera richiesta viene rifiutata.

Come utilizzare OPA Gatekeeper: uno scenario semplice

Supponiamo che tu voglia applicare una risorsa senza un'etichetta del proprietario.

Innanzitutto, installa Gatekeeper sul tuo cluster:

kubectl apply -f 
https://raw.githubusercontent.com/open-policy-agent/gatekeeper \
/release-3.4/deploy/gatekeeper.yaml

Puoi testarlo eseguendo il seguente comando:

kubectl get pods --all-namespaces

Se tutto funziona correttamente, dovresti vedere un pod in esecuzione denominato gatekeeper-controller-managerunder gatekeeper-systemnamespace.

Applica il ConstraintTemplate che richiederà la presenza di tutte le etichette descritte dal vincolo:

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        # Schema for the `parameters` field
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("you must provide labels: %v", [missing])
        }

Applica il vincolo che utilizzerà K8sRequiredLabel che hai creato prima di definire l'ambito dello spazio dei nomi, quindi ogni spazio dei nomi sarà costretto ad avere un'etichetta del proprietario:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-owner
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["owner"]

Audit

Come mi piace dire, il cluster è il tuo tempio di produzione, quindi è necessario un monitoraggio continuo per rilevare la correzione di configurazioni errate preesistenti. È qui che entra in gioco l'audit.

Quando viene richiamato il webhook Gatekeeper, memorizza i risultati dell'audit come violazioni nel campo dello stato del vincolo pertinente. Per esempio:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-owner
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["owner"]
status:
  auditTimestamp: "2019-08-06T01:46:13Z"
  byPod:
  - enforced: true
    id: gatekeeper-controller-manager-0
  violations:
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: default
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: gatekeeper-system
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: kube-public
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: kube-system

Forse vuoi creare una policy che imponga che tutti i nomi host in ingresso siano univoci. In questo caso, ti consigliamo di utilizzare la funzione di controllo. Tuttavia, il vincolo che impone un nome host di ingresso univoco deve avere accesso a tutti gli ingressi diversi dall'oggetto in valutazione. Questa situazione richiede la replica dei dati nell'OPA.

Uno scenario semplice

Per impostazione predefinita, un controllo non richiede la replica, ma esistono due modi per configurare manualmente la replica dei dati:

  1. Utilizza il meccanismo della cache OPA: imposta il flag audit-from-cache su true, che consentirà di utilizzare la cache OPA come fonte di verità per tutte le query di controllo. Qualsiasi oggetto deve prima essere memorizzato nella cache prima di poter essere controllato per eventuali violazioni dei vincoli.
  2. Utilizza una risorsa Kubernetes config : crea una risorsa config e definisci le risorse che desideri desidera essere replicato in OPA in syncOnly. Non preoccuparti, l'aggiornamento di syncOnly dovrebbe aggiornare dinamicamente tutti gli oggetti sincronizzati.

Ad esempio, la seguente configurazione replica tutti gli spazi dei nomi e le risorse pod in OPA:

apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Namespace"
      - group: ""
        version: "v1"
        kind: "Pod"

Funzionamento a secco

Puoi andare ancora oltre e testare i vincoli prima di aggiungerli e applicarli. È qui che entra in gioco la funzionalità di prova.

L'esecuzione di prova fornisce la stessa funzionalità dell'audit, consentendo di distribuire i vincoli e visualizzare tutte le violazioni dei vincoli segnalate nello stato senza apportare modifiche effettive. Per configurare un vincolo per la modalità di esecuzione di prova, tutto ciò che devi fare è utilizzare l'etichetta enforcementAction: dryrun nelle specifiche del vincolo. Per impostazione predefinita, enforcementAction è impostato su "deny", poiché il comportamento predefinito è negare le richieste di ammissione con qualsiasi violazione.

Per esempio:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-owner
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["owner"]
status:
  auditTimestamp: "2019-08-06T01:46:13Z"
  byPod:
  - enforced: true
    id: gatekeeper-controller-manager-0
  violations:
  - enforcementAction: dryrun
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: default
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: gatekeeper-system
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: kube-public
  - enforcementAction: deny
    kind: Namespace
    message: 'you must provide labels: {"hr"}'
    name: kube-system

Pensare in grande

Ora che la tua produzione è sana e salva, vorrei fermarmi un secondo e farti una domanda: in che modo Gatekeeper è diverso dai test unitari? Personalmente, quando ho sentito parlare di Gatekeeper per la prima volta, non sono riuscito a capire: "Perché dovrebbe riguardare solo la produzione?" Dopotutto, idealmente, non passa tutto attraverso la stessa pipeline? ?

Come sviluppatore, pensare alle policy Kubernetes come se fossero test unitari aveva così tanto senso che mi sono chiesto: qual è la differenza tra il mio codice e le risorse Kubernetes?

Inserisci Datree.

Datree è una soluzione CLI (interfaccia della riga di comando) che consente di testare le policy rispetto ai file YAML. La CLI è dotata di policy integrate per tutte le best practice Kubernetes e di una soluzione di gestione centralizzata per qualsiasi policy creata. Esegui Datree nell'elemento della configurazione o come hook di precommit e utilizzalo come utilizzeresti una libreria di test locale. È un progetto open source, quindi è gratuito. Puoi trovare il progetto su GitHub e ottenere tutte le informazioni sul sito di Datree.

Ti incoraggio a rivedere il nostro codice e a fornirci il tuo feedback in modo che possa essere la soluzione migliore per la tua produzione.

Riepilogo

Per come la vedo io, solo perché Kubernetes ti consente di distribuire un pod con accesso allo spazio dei nomi della rete host, ad esempio, non significa che sia una buona idea.

Quando adotti Kubernetes, cambi anche la cultura della tua organizzazione. DevOps non è qualcosa che avviene da un giorno all'altro; è un processo, ed è importante saperlo gestire, soprattutto in termini di scala.

Spero che questo ti ispiri a iniziare a pensare alle tue politiche e a come applicarle all'interno della tua organizzazione.

Questo articolo è apparso originariamente sul blog di Datree ed è stato ripubblicato con il suo permesso.

Articoli correlati: