Convalida Kubernetes YAML rispetto alle best practice e alle policy

Nota. trad.: Con il crescente numero di configurazioni YAML per ambienti K8, la necessità della loro verifica automatizzata diventa sempre più urgente. L'autore di questa recensione non solo ha selezionato le soluzioni esistenti per questa attività, ma ha anche utilizzato la distribuzione come esempio per vedere come funzionano. Si è rivelato molto istruttivo per coloro che sono interessati a questo argomento.

Convalida Kubernetes YAML rispetto alle best practice e alle policy

TL; DR: questo articolo mette a confronto sei strumenti statici per convalidare e valutare i file YAML Kubernetes rispetto alle best practice e ai requisiti.

I carichi di lavoro Kubernetes sono generalmente definiti sotto forma di documenti YAML. Uno dei problemi con YAML è la difficoltà di specificare vincoli o relazioni tra i file manifest.

Cosa succede se dobbiamo assicurarci che tutte le immagini distribuite nel cluster provengano da un registro attendibile?

Come posso impedire che le distribuzioni che non dispongono di PodDisruptionBudget vengano inviate al cluster?

L'integrazione dei test statici consente di identificare errori e violazioni delle policy in fase di sviluppo. Ciò aumenta la garanzia che le definizioni delle risorse siano corrette e sicure e aumenta la probabilità che i carichi di lavoro di produzione seguano le migliori pratiche.

L'ecosistema di ispezione dei file YAML statici Kubernetes può essere suddiviso nelle seguenti categorie:

  • Validatori API. Gli strumenti di questa categoria controllano il manifest YAML rispetto ai requisiti del server API Kubernetes.
  • Tester pronti. Gli strumenti di questa categoria vengono forniti con test già pronti per la sicurezza, la conformità alle migliori pratiche, ecc.
  • Validatori personalizzati. I rappresentanti di questa categoria ti consentono di creare test personalizzati in varie lingue, ad esempio Rego e Javascript.

In questo articolo descriveremo e confronteremo sei diversi strumenti:

  1. Kubeval;
  2. punteggio kube;
  3. configurazione-lint;
  4. rame;
  5. contesa;
  6. Polare.

Bene, cominciamo!

Controllo delle distribuzioni

Prima di iniziare a confrontare gli strumenti, creiamo uno sfondo su cui testarli.

Il manifesto riportato di seguito contiene una serie di errori e non conformità alle migliori pratiche: quanti ne puoi trovare?

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: http-echo
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(base-valid.yaml)

Utilizzeremo questo YAML per confrontare diversi strumenti.

Il manifesto di cui sopra base-valid.yaml e altri manifesti di questo articolo possono essere trovati in Repository Git.

Il manifest descrive un'applicazione web il cui compito principale è rispondere con un messaggio "Hello World" alla porta 5678. Può essere distribuito con il seguente comando:

kubectl apply -f hello-world.yaml

E quindi - controlla il lavoro:

kubectl port-forward svc/http-echo 8080:5678

Ora vai a http://localhost:8080 e confermare che l'applicazione funziona. Ma segue le migliori pratiche? Controlliamo

1. Kubeval

Al cuore kubeval L'idea è che qualsiasi interazione con Kubernetes avvenga tramite la sua API REST. In altre parole, puoi utilizzare uno schema API per verificare se un determinato YAML è conforme ad esso. Diamo un'occhiata a un esempio.

Istruzioni per l'installazione kubeval sono disponibili sul sito web del progetto.

Al momento della stesura dell'articolo originale, era disponibile la versione 0.15.0.

Una volta installato, diamogli in pasto il manifest qui sopra:

$ kubeval base-valid.yaml
PASS - base-valid.yaml contains a valid Deployment (http-echo)
PASS - base-valid.yaml contains a valid Service (http-echo)

In caso di successo, kubeval uscirà con il codice di uscita 0. Puoi verificarlo come segue:

$ echo $?
0

Proviamo ora kubeval con un manifest diverso:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(kubeval-invalid.yaml)

Riesci a individuare il problema a occhio? Lanciamo:

$ kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml contains an invalid Deployment (http-echo) - selector: selector is required
PASS - kubeval-invalid.yaml contains a valid Service (http-echo)

# проверим код возврата
$ echo $?
1

La risorsa non è in fase di verifica.

Distribuzioni utilizzando la versione API apps/v1, deve includere un selettore che corrisponda all'etichetta del pod. Il manifest sopra non include il selettore, quindi kubeval ha segnalato un errore ed è uscito con un codice diverso da zero.

Mi chiedo cosa accadrebbe se lo facessi kubectl apply -f con questo manifesto?

Bene, proviamo:

$ kubectl apply -f kubeval-invalid.yaml
error: error validating "kubeval-invalid.yaml": error validating data: ValidationError(Deployment.spec):
missing required field "selector" in io.k8s.api.apps.v1.DeploymentSpec; if you choose to ignore these errors,
turn validation off with --validate=false

Questo è esattamente l'errore da cui kubeval ha messo in guardia. Puoi risolverlo aggiungendo un selettore:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:          # !!!
    matchLabels:     # !!!
      app: http-echo # !!!
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo
        args: ["-text", "hello-world"]
        ports:
        - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: http-echo
spec:
  ports:
  - port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app: http-echo

(base-valid.yaml)

Il vantaggio di strumenti come kubeval è che errori come questi possono essere rilevati nelle prime fasi del ciclo di distribuzione.

Inoltre, questi controlli non richiedono l'accesso al cluster e possono essere eseguiti offline.

Per impostazione predefinita, kubeval controlla le risorse rispetto allo schema API Kubernetes più recente. Tuttavia, nella maggior parte dei casi potrebbe essere necessario effettuare un confronto con una specifica versione di Kubernetes. Questo può essere fatto usando la bandiera --kubernetes-version:

$ kubeval --kubernetes-version 1.16.1 base-valid.yaml

Tieni presente che la versione deve essere specificata nel formato Major.Minor.Patch.

Per un elenco delle versioni per le quali è supportata la verifica, fare riferimento a Schema JSON su GitHub, che kubeval utilizza per la convalida. Se devi eseguire kubeval offline, scarica gli schemi e specifica la loro posizione locale utilizzando il flag --schema-location.

Oltre ai singoli file YAML, kubeval può funzionare anche con directory e stdin.

Inoltre, Kubeval si integra facilmente nella pipeline CI. Coloro che desiderano eseguire test prima di inviare manifest al cluster saranno lieti di sapere che kubeval supporta tre formati di output:

  1. Testo semplice;
  2. JSON;
  3. Test Anything Protocol (TAP).

E qualsiasi formato può essere utilizzato per un'ulteriore analisi dell'output per generare un riepilogo dei risultati del tipo desiderato.

Uno degli svantaggi di kubeval è che attualmente non è in grado di verificare la conformità con le Custom Resource Definitions (CRD). Tuttavia è possibile configurare kubeval ignorali.

Kubeval è un ottimo strumento per controllare e valutare le risorse; Va però sottolineato che il superamento del test non garantisce che la risorsa sia conforme alle best practices.

Ad esempio, utilizzando il tag latest in un contenitore non segue le migliori pratiche. Tuttavia kubeval non lo considera un errore e non lo segnala. Cioè, la verifica di tale YAML verrà completata senza avvisi.

Ma cosa succede se desideri valutare YAML e identificare violazioni come il tag latest? Come posso verificare un file YAML rispetto alle migliori pratiche?

2. Punteggio Kube

Punteggio Kube analizza i manifesti YAML e li valuta rispetto ai test integrati. Questi test vengono selezionati in base alle linee guida e alle migliori pratiche di sicurezza, come ad esempio:

  • Esecuzione del contenitore non come root.
  • Disponibilità di controlli sanitari dei pod.
  • Impostazione di richieste e limiti per le risorse.

In base ai risultati del test, vengono forniti tre risultati: OK, AVVERTIMENTO и CRITICA.

Puoi provare Kube-score online o installarlo localmente.

Al momento della stesura dell'articolo originale, l'ultima versione di kube-score era la 1.7.0.

Proviamolo sul nostro manifest base-valid.yaml:

$ kube-score score base-valid.yaml

apps/v1/Deployment http-echo
[CRITICAL] Container Image Tag
  · http-echo -> Image with latest tag
      Using a fixed tag is recommended to avoid accidental upgrades
[CRITICAL] Pod NetworkPolicy
  · The pod does not have a matching network policy
      Create a NetworkPolicy that targets this pod
[CRITICAL] Pod Probes
  · Container is missing a readinessProbe
      A readinessProbe should be used to indicate when the service is ready to receive traffic.
      Without it, the Pod is risking to receive traffic before it has booted. It is also used during
      rollouts, and can prevent downtime if a new version of the application is failing.
      More information: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRITICAL] Container Security Context
  · http-echo -> Container has no configured security context
      Set securityContext to run the container in a more secure context.
[CRITICAL] Container Resources
  · http-echo -> CPU limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.cpu
  · http-echo -> Memory limit is not set
      Resource limits are recommended to avoid resource DDOS. Set resources.limits.memory
  · http-echo -> CPU request is not set
      Resource requests are recommended to make sure that the application can start and run without
      crashing. Set resources.requests.cpu
  · http-echo -> Memory request is not set
      Resource requests are recommended to make sure that the application can start and run without crashing.
      Set resources.requests.memory
[CRITICAL] Deployment has PodDisruptionBudget
  · No matching PodDisruptionBudget was found
      It is recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes
      maintenance operations, such as when draining a node.
[WARNING] Deployment has host PodAntiAffinity
  · Deployment does not have a host podAntiAffinity set
      It is recommended to set a podAntiAffinity that stops multiple pods from a deployment from
      being scheduled on the same node. This increases availability in case the node becomes unavailable.

YAML supera i test kubeval, mentre kube-score evidenzia i seguenti difetti:

  • I controlli di idoneità non sono configurati.
  • Non ci sono richieste o limiti per le risorse della CPU e della memoria.
  • I budget per l'interruzione dei pod non sono specificati.
  • Non esistono regole di separazione (anti-affinità) per massimizzare la disponibilità.
  • Il contenitore viene eseguito come root.

Questi sono tutti punti validi sulle carenze che devono essere affrontate per rendere la distribuzione più efficiente e affidabile.

Squadra kube-score visualizza le informazioni in formato leggibile dall'uomo, comprese tutte le violazioni di tipo AVVERTIMENTO и CRITICA, che aiuta molto durante lo sviluppo.

Coloro che desiderano utilizzare questo strumento all'interno della pipeline CI possono abilitare un output più compresso utilizzando il flag --output-format ci (in questo caso vengono visualizzate anche le prove con il risultato OK):

$ kube-score score base-valid.yaml --output-format ci

[OK] http-echo apps/v1/Deployment
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory limit is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) CPU request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Memory request is not set
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Image with latest tag
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: The pod does not have a matching network policy
[CRITICAL] http-echo apps/v1/Deployment: Container is missing a readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Container has no configured security context
[CRITICAL] http-echo apps/v1/Deployment: No matching PodDisruptionBudget was found
[WARNING] http-echo apps/v1/Deployment: Deployment does not have a host podAntiAffinity set
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

Similmente a kubeval, kube-score restituisce un codice di uscita diverso da zero quando un test fallisce CRITICA. Puoi anche abilitare un'elaborazione simile per AVVERTIMENTO.

Inoltre è possibile verificare la conformità delle risorse con diverse versioni API (come in kubeval). Tuttavia, queste informazioni sono codificate nel kube-score stesso: non è possibile selezionare una versione diversa di Kubernetes. Questa limitazione può rappresentare un grosso problema se intendi aggiornare il tuo cluster o se disponi di più cluster con versioni diverse di K8.

Si noti che c'è già un problema con una proposta per realizzare questa opportunità.

Maggiori informazioni su kube-score sono disponibili all'indirizzo il sito ufficiale.

I test Kube-score sono un ottimo strumento per implementare le migliori pratiche, ma cosa succede se è necessario apportare modifiche al test o aggiungere le proprie regole? Ahimè, questo non può essere fatto.

Kube-score non è estensibile: non è possibile aggiungervi policy o modificarle.

Se è necessario scrivere test personalizzati per verificare la conformità alle politiche aziendali, è possibile utilizzare uno dei quattro strumenti seguenti: config-lint, rame, conftest o polaris.

3.Config-lint

Config-lint è uno strumento per convalidare file di configurazione YAML, JSON, Terraform, CSV e manifest Kubernetes.

Puoi installarlo usando Istruzioni sul sito web del progetto.

La versione corrente al momento della stesura dell'articolo originale è la 1.5.0.

Config-lint non dispone di test integrati per la convalida dei manifest Kubernetes.

Per condurre qualsiasi test, è necessario creare regole appropriate. Sono scritti in file YAML chiamati "rulesets" (regole), e hanno la seguente struttura:

version: 1
description: Rules for Kubernetes spec files
type: Kubernetes
files:
  - "*.yaml"
rules:
   # список правил

(rule.yaml)

Studiamolo più da vicino:

  • Campo type specifica quale tipo di configurazione utilizzerà config-lint. Per i manifesti K8 questo è sempre Kubernetes.
  • Nel campo files Oltre ai file stessi, è possibile specificare una directory.
  • Campo rules destinato all'impostazione dei test utente.

Supponiamo che tu voglia assicurarti che le immagini in Deployment vengano sempre scaricate da un repository attendibile come my-company.com/myapp:1.0. Una regola config-lint che esegue tale controllo sarebbe simile a questa:

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: Deployment must use a valid image tag
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

Ogni regola deve avere i seguenti attributi:

  • id — identificatore univoco della regola;
  • severity - Forse FALLIMENTO, AVVERTIMENTO и NON CONFORME;
  • message — se una regola viene violata, viene visualizzato il contenuto di questa riga;
  • resource — il tipo di risorsa a cui si applica questa regola;
  • assertions — un elenco delle condizioni che verranno valutate in relazione a questa risorsa.

Nella regola sopra assertion dal titolo every controlla che tutti i contenitori siano in Distribuzione (key: spec.templates.spec.containers) utilizzano immagini attendibili (ovvero iniziando con my-company.com/).

Il set di regole completo è simile al seguente:

version: 1
description: Rules for Kubernetes spec files
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    severity: FAILURE
    message: Deployment must use a valid image repository
    resource: Deployment
    assertions:
      - every:
          key: spec.template.spec.containers
          expressions:
            - key: image
              op: starts-with
              value: "my-company.com/"

(ruleset.yaml)

Per provare il test, salviamolo con nome check_image_repo.yaml. Eseguiamo un controllo sul file base-valid.yaml:

$ config-lint -rules check_image_repo.yaml base-valid.yaml

[
  {
  "AssertionMessage": "Every expression fails: And expression fails: image does not start with my-company.com/",
  "Category": "",
  "CreatedAt": "2020-06-04T01:29:25Z",
  "Filename": "test-data/base-valid.yaml",
  "LineNumber": 0,
  "ResourceID": "http-echo",
  "ResourceType": "Deployment",
  "RuleID": "DEPLOYMENT_IMAGE_REPOSITORY",
  "RuleMessage": "Deployment must use a valid image repository",
  "Status": "FAILURE"
  }
]

Il controllo è fallito. Ora controlliamo il seguente manifest con il repository di immagini corretto:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: http-echo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: http-echo
  template:
    metadata:
      labels:
        app: http-echo
    spec:
      containers:
      - name: http-echo
         image: my-company.com/http-echo:1.0 # !!!
         args: ["-text", "hello-world"]
         ports:
         - containerPort: 5678

(image-valid-mycompany.yaml)

Eseguiamo lo stesso test con il manifest sopra. Nessun problema riscontrato:

$ config-lint -rules check_image_repo.yaml image-valid-mycompany.yaml
[]

Config-lint è un framework promettente che ti consente di creare i tuoi test per convalidare i manifest YAML Kubernetes utilizzando YAML DSL.

Ma cosa succede se hai bisogno di logica e test più complessi? YAML non è troppo limitato per questo? E se potessi creare test in un linguaggio di programmazione completo?

4. Rame

Rame V2 è un framework per convalidare i manifest utilizzando test personalizzati (simile a config-lint).

Tuttavia, differisce da quest'ultimo in quanto non utilizza YAML per descrivere i test. I test possono invece essere scritti in JavaScript. Copper fornisce una libreria con diversi strumenti di base, che ti aiutano a leggere informazioni sugli oggetti Kubernetes e a segnalare errori.

I passaggi per l'installazione di Copper sono disponibili in documentazione ufficiale.

2.0.1 è l'ultima versione di questa utility al momento della stesura dell'articolo originale.

Come config-lint, Copper non dispone di test integrati. Scriviamone uno. Lascia che controlli che le distribuzioni utilizzino immagini del contenitore esclusivamente da repository attendibili come my-company.com.

Crea un file check_image_repo.js con il seguente contenuto:

$$.forEach(function($){
    if ($.kind === 'Deployment') {
        $.spec.template.spec.containers.forEach(function(container) {
            var image = new DockerImage(container.image);
            if (image.registry.lastIndexOf('my-company.com/') != 0) {
                errors.add_error('no_company_repo',"Image " + $.metadata.name + " is not from my-company.com repo", 1)
            }
        });
    }
});

Ora testiamo il nostro manifest base-valid.yaml, usa il comando copper validate:

$ copper validate --in=base-valid.yaml --validator=check_image_tag.js

Check no_company_repo failed with severity 1 due to Image http-echo is not from my-company.com repo
Validation failed

È chiaro che con l'aiuto di Copper è possibile eseguire test più complessi, ad esempio controllare i nomi di dominio nei manifest Ingress o rifiutare i pod in esecuzione in modalità privilegiata.

Il rame ha varie funzioni di utilità integrate:

  • DockerImage legge il file di input specificato e crea un oggetto con i seguenti attributi:
    • name - nome dell'immagine,
    • tag - tag immagine,
    • registry - registro delle immagini,
    • registry_url - protocollo (https://) e registro delle immagini,
    • fqin — posizione completa dell'immagine.
  • Funzione findByName aiuta a trovare una risorsa in base a un determinato tipo (kind) e nome (name) dal file di input.
  • Funzione findByLabels aiuta a trovare una risorsa in base a un tipo specificato (kind) ed etichette (labels).

È possibile visualizzare tutte le funzioni di servizio disponibili qui.

Per impostazione predefinita carica l'intero file YAML di input in una variabile $$ e lo rende disponibile per lo scripting (una tecnica familiare per chi ha esperienza con jQuery).

Il vantaggio principale di Copper è evidente: non è necessario padroneggiare un linguaggio specializzato e puoi utilizzare varie funzionalità JavaScript per creare i tuoi test, come l'interpolazione di stringhe, funzioni, ecc.

Va inoltre notato che l'attuale versione di Copper funziona con la versione ES5 del motore JavaScript, non con ES6.

Dettagli disponibili su il sito ufficiale del progetto.

Tuttavia, se non ti piace molto JavaScript e preferisci un linguaggio specificamente progettato per creare query e descrivere politiche, dovresti prestare attenzione a conftest.

5.Contest

Conftest è un framework per testare i dati di configurazione. Adatto anche per testare/verificare i manifest Kubernetes. I test vengono descritti utilizzando un linguaggio di query specializzato Rego.

Puoi installare conftest usando Istruzionielencati sul sito web del progetto.

Al momento della stesura dell'articolo originale, l'ultima versione disponibile era la 0.18.2.

Similmente a config-lint e Copper, conftest viene fornito senza test integrati. Proviamolo e scriviamo la nostra politica. Come negli esempi precedenti, controlleremo se le immagini del contenitore provengono da una fonte affidabile.

Crea una directory conftest-checks, e al suo interno c'è un file denominato check_image_registry.rego con il seguente contenuto:

package main

deny[msg] {

  input.kind == "Deployment"
  image := input.spec.template.spec.containers[_].image
  not startswith(image, "my-company.com/")
  msg := sprintf("image '%v' doesn't come from my-company.com repository", [image])
}

Ora proviamo base-valid.yaml attraverso conftest:

$ conftest test --policy ./conftest-checks base-valid.yaml

FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
1 tests, 1 passed, 0 warnings, 1 failure

Il test, prevedibilmente, è fallito perché le immagini provenivano da una fonte non attendibile.

Nel file Rego definiamo il blocco deny. La sua verità è considerata una violazione. Se blocchi deny diversi, conftest li controlla indipendentemente l'uno dall'altro e la verità di uno qualsiasi dei blocchi viene trattata come una violazione.

Oltre all'output predefinito, conftest supporta JSON, TAP e il formato tabella: una funzionalità estremamente utile se è necessario incorporare report in una pipeline CI esistente. È possibile impostare il formato desiderato utilizzando il flag --output.

Per rendere più semplice il debug delle politiche, conftest ha un flag --trace. Fornisce una traccia di come conftest analizza i file di policy specificati.

Le politiche del concorso possono essere pubblicate e condivise nei registri OCI (Open Container Initiative) come artefatti.

comandi push и pull consentono di pubblicare un artefatto o recuperare un artefatto esistente da un registro remoto. Proviamo a pubblicare la policy che abbiamo creato nel registro Docker locale utilizzando conftest push.

Avvia il registro Docker locale:

$ docker run -it --rm -p 5000:5000 registry

In un altro terminale, vai alla directory che hai creato in precedenza conftest-checks ed esegui il seguente comando:

$ conftest push 127.0.0.1:5000/amitsaha/opa-bundle-example:latest

Se il comando ha avuto successo, vedrai un messaggio come questo:

2020/06/10 14:25:43 pushed bundle with digest: sha256:e9765f201364c1a8a182ca637bc88201db3417bacc091e7ef8211f6c2fd2609c

Ora crea una directory temporanea ed esegui il comando al suo interno conftest pull. Scaricherà il pacchetto creato dal comando precedente:

$ cd $(mktemp -d)
$ conftest pull 127.0.0.1:5000/amitsaha/opa-bundle-example:latest

Verrà visualizzata una sottodirectory nella directory temporanea policycontenente il nostro file di policy:

$ tree
.
└── policy
  └── check_image_registry.rego

I test possono essere eseguiti direttamente dal repository:

$ conftest test --update 127.0.0.1:5000/amitsaha/opa-bundle-example:latest base-valid.yaml
..
FAIL - base-valid.yaml - image 'hashicorp/http-echo' doesn't come from my-company.com repository
2 tests, 1 passed, 0 warnings, 1 failure

Sfortunatamente, DockerHub non è ancora supportato. Quindi considerati fortunato se lo usi Registro Azure Container (ACR) o il proprio registro.

Il formato dell'artefatto è lo stesso di Aprire i pacchetti dell'agente policy (OPA), che consente di utilizzare conftest per eseguire test da pacchetti OPA esistenti.

Puoi saperne di più sulla condivisione delle policy e altre funzionalità di conftest su il sito ufficiale del progetto.

6. Polaris

L'ultimo strumento che verrà discusso in questo articolo è Polaris. (Il suo annuncio dell'anno scorso noi già tradotto - ca. traduzione)

Polaris può essere installato in un cluster o utilizzato in modalità riga di comando. Come avrai intuito, ti consente di analizzare staticamente i manifest Kubernetes.

Quando si esegue in modalità riga di comando, sono disponibili test integrati che coprono aree come la sicurezza e le migliori pratiche (simili a kube-score). Inoltre, puoi creare i tuoi test (come in config-lint, rame e conftest).

In altre parole, Polaris combina i vantaggi di entrambe le categorie di strumenti: con test integrati e personalizzati.

Per installare Polaris in modalità riga di comando, utilizzare istruzioni sul sito web del progetto.

Al momento della stesura dell'articolo originale, è disponibile la versione 1.0.3.

Una volta completata l'installazione è possibile eseguire polaris sul manifest base-valid.yaml con il seguente comando:

$ polaris audit --audit-path base-valid.yaml

Verrà restituita una stringa in formato JSON con una descrizione dettagliata dei test eseguiti e dei relativi risultati. L'output avrà la seguente struttura:

{
  "PolarisOutputVersion": "1.0",
  "AuditTime": "0001-01-01T00:00:00Z",
  "SourceType": "Path",
  "SourceName": "test-data/base-valid.yaml",
  "DisplayName": "test-data/base-valid.yaml",
  "ClusterInfo": {
    "Version": "unknown",
    "Nodes": 0,
    "Pods": 2,
    "Namespaces": 0,
    "Controllers": 2
  },
  "Results": [
    /* длинный список */
  ]
}

Uscita completa disponibile qui.

Come kube-score, Polaris identifica i problemi nelle aree in cui il manifest non soddisfa le migliori pratiche:

  • Non sono previsti controlli sanitari per i pod.
  • I tag per le immagini del contenitore non sono specificati.
  • Il contenitore viene eseguito come root.
  • Le richieste e i limiti per memoria e CPU non sono specificati.

Ad ogni test, a seconda dei suoi risultati, viene assegnato un grado di criticità: identificazione dei warning o pericolo. Per ulteriori informazioni sui test integrati disponibili, fare riferimento a documentazione.

Se i dettagli non sono necessari, è possibile specificare il flag --format score. In questo caso, Polaris emetterà un numero compreso tra 1 e 100 − Punto (cioè valutazione):

$ polaris audit --audit-path test-data/base-valid.yaml --format score
68

Più il punteggio si avvicina a 100, maggiore è il grado di accordo. Se controlli il codice di uscita del comando polaris audit, risulta che è uguale a 0.

fare polaris audit Puoi terminare il lavoro con codice diverso da zero utilizzando due flag:

  • bandiera --set-exit-code-below-score accetta come argomento un valore di soglia compreso tra 1 e 100. In questo caso, il comando uscirà con il codice di uscita 4 se il punteggio è inferiore alla soglia. Questo è molto utile quando hai un certo valore di soglia (diciamo 75) e hai bisogno di ricevere un avviso se il punteggio scende al di sotto.
  • bandiera --set-exit-code-on-danger farà sì che il comando fallisca con il codice 3 se uno dei test di pericolo fallisce.

Ora proviamo a creare un test personalizzato che controlli se l'immagine è presa da un repository attendibile. I test personalizzati sono specificati nel formato YAML e il test stesso è descritto utilizzando lo schema JSON.

Il seguente frammento di codice YAML descrive un nuovo test chiamato checkImageRepo:

checkImageRepo:
  successMessage: Image registry is valid
  failureMessage: Image registry is not valid
  category: Images
  target: Container
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Diamo un'occhiata più da vicino:

  • successMessage — questa riga verrà stampata se il test viene completato con successo;
  • failureMessage — questo messaggio verrà visualizzato in caso di guasto;
  • category — indica una delle categorie: Images, Health Checks, Security, Networking и Resources;
  • target--- determina quale tipo di oggetto (spec) viene applicato il test. Valori possibili: Container, Pod o Controller;
  • Il test stesso è specificato nell'oggetto schema utilizzando lo schema JSON. La parola chiave in questo test è pattern utilizzato per confrontare la sorgente dell'immagine con quella richiesta.

Per eseguire il test precedente, è necessario creare la seguente configurazione Polaris:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: Image registry is valid
    failureMessage: Image registry is not valid
    category: Images
    target: Container
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(polaris-conf.yaml)

Analizziamo il file:

  • Nel campo checks vengono prescritti i test e il loro livello di criticità. Poiché è auspicabile ricevere un avviso quando un'immagine viene presa da una fonte non attendibile, impostiamo qui il livello danger.
  • La prova stessa checkImageRepo quindi registrato nell'oggetto customChecks.

Salva il file con nome custom_check.yaml. Ora puoi correre polaris audit con un manifest YAML che richiede la verifica.

Mettiamo alla prova il nostro manifesto base-valid.yaml:

$ polaris audit --config custom_check.yaml --audit-path base-valid.yaml

Squadra polaris audit ha eseguito solo il test utente specificato sopra e non è riuscito.

Se aggiusti l'immagine su my-company.com/http-echo:1.0, Polaris verrà completato correttamente. Il manifesto con le modifiche è già arrivato repositorycosì puoi controllare il comando precedente sul manifest image-valid-mycompany.yaml.

Ora sorge la domanda: come eseguire i test integrati insieme a quelli personalizzati? Facilmente! Devi solo aggiungere gli identificatori di test integrati al file di configurazione. Di conseguenza, assumerà la seguente forma:

checks:
  cpuRequestsMissing: warning
  cpuLimitsMissing: warning
  # Other inbuilt checks..
  # ..
  # custom checks
  checkImageRepo: danger # !!!
customChecks:
  checkImageRepo:        # !!!
    successMessage: Image registry is valid
    failureMessage: Image registry is not valid
    category: Images
    target: Container
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(config_with_custom_check.yaml)

È disponibile un esempio di file di configurazione completo qui.

Controlla il manifesto base-valid.yamlutilizzando test integrati e personalizzati, puoi utilizzare il comando:

$ polaris audit --config config_with_custom_check.yaml --audit-path base-valid.yaml

Polaris integra i test integrati con quelli personalizzati, combinando così il meglio di entrambi i mondi.

D’altra parte, l’impossibilità di utilizzare linguaggi più potenti come Rego o JavaScript può essere un fattore limitante che impedisce la realizzazione di test più sofisticati.

Maggiori informazioni su Polaris sono disponibili su sito del progetto.

Riassunto

Sebbene siano disponibili molti strumenti per ispezionare e valutare i file YAML Kubernetes, è importante avere una chiara comprensione di come verranno progettati ed eseguiti i test.

Per esempio, se si prendono i manifest Kubernetes attraverso una pipeline, kubeval potrebbe essere il primo passo in tale pipeline. Monitorerebbe se le definizioni degli oggetti sono conformi allo schema API Kubernetes.

Una volta completata tale revisione, si potrebbe passare a test più sofisticati, come la conformità alle migliori pratiche standard e alle politiche specifiche. È qui che kube-score e Polaris torneranno utili.

Per coloro che hanno requisiti complessi e necessitano di personalizzare i test in dettaglio, rame, config-lint e conftest sarebbero adatti.

Conftest e config-lint utilizzano YAML per definire test personalizzati e Copper ti dà accesso a un linguaggio di programmazione completo, rendendolo una scelta piuttosto interessante.

D'altronde vale la pena utilizzare uno di questi strumenti e, quindi, creare tutti i test manualmente, oppure preferire Polaris e aggiungervi solo ciò che serve? Non esiste una risposta chiara a questa domanda.

La tabella seguente fornisce una breve descrizione di ciascuno strumento:

Strumento
destino
Limitazioni
Test utente

kubeval
Convalida i manifesti YAML rispetto a una versione specifica dello schema API
Impossibile lavorare con CRD
No

kube-score
Analizza i manifest YAML rispetto alle migliori pratiche
Impossibile selezionare la versione dell'API Kubernetes per controllare le risorse
No

rame
Un framework generale per la creazione di test JavaScript personalizzati per i manifest YAML
Nessun test integrato. Documentazione scarsa

config-lint
Un framework generale per la creazione di test in un linguaggio specifico del dominio incorporato in YAML. Supporta vari formati di configurazione (ad esempio Terraform)
Non esistono test già pronti. Le asserzioni e le funzioni integrate potrebbero non essere sufficienti

conftest
Un framework per creare i tuoi test utilizzando Rego (un linguaggio di query specializzato). Consente la condivisione di policy tramite bundle OCI
Nessun test integrato. Devo imparare Rego. Docker Hub non è supportato durante la pubblicazione delle policy

Polaris
Esamina i manifest YAML rispetto alle best practice standard. Ti consente di creare i tuoi test utilizzando JSON Schema
Le funzionalità di test basate sullo schema JSON potrebbero non essere sufficienti

Poiché questi strumenti non si basano sull'accesso al cluster Kubernetes, sono facili da installare. Consentono di filtrare i file di origine e fornire un rapido feedback agli autori delle richieste pull nei progetti.

PS da traduttore

Leggi anche sul nostro blog:

Fonte: habr.com

Aggiungi un commento