Validarea YAML Kubernetes pentru conformitatea cu cele mai bune practici și politici

Nota traducătorului.: Odată cu creșterea numărului de configurații YAML pentru medii K8s, devine tot mai relevantă necesitatea verificării automate a acestora. Autorul acestui rezumat a selectat nu doar soluțiile existente pentru această sarcină, ci a examinat și cum funcționează acestea în cazul unui Deployment. A rezultat o prezentare foarte informativă pentru cei interesați de acest subiect.

Validarea YAML Kubernetes pentru conformitatea cu cele mai bune practici și politici

TL;DR: Articolul compară șase instrumente statice de verificare și evaluare a fișierelor YAML Kubernetes, în funcție de cele mai bune practici și cerințe.

Sarcinile de lucru Kubernetes sunt de obicei definite sub formă de documente YAML. Una dintre problemele cu YAML este dificultatea de a impune restricții sau relații între fișierele manifest.

Ce facem dacă trebuie să ne asigurăm că toate imaginile desfășurate în cluster provin dintr-un registru de încredere?

Cum prevenim trimiterea în cluster a Deployment-urilor pentru care nu sunt definite PodDisruptionBudgets?

Integrarea testării statice permite identificarea erorilor și a încălcărilor politicilor încă din stadiul de dezvoltare. Astfel, se îmbunătățește garanția corectitudinii și siguranței definițiilor de resurse și se crește șansa ca sarcinile de producție să respecte cele mai bune practici.

Ecosistemul de verificare statică a fișierelor YAML Kubernetes poate fi împărțit în următoarele categorii:

  • Validatori API. Instrumentele din această categorie verifică manifestul YAML pentru conformitate cu cerințele serverului API Kubernetes.
  • Testerii gata făcuți. Instrumentele din această categorie vin cu teste gata făcute pentru securitate, conformitate cu cele mai bune practici etc.
  • Validatori personalizați. Reprezentanții acestei categorii permit crearea de teste personalizate în diverse limbaje, cum ar fi Rego și Javascript.

În acest articol vom descrie și compara șase instrumente diferite:

  1. kubeval;
  2. kube-score;
  3. config-lint;
  4. copper;
  5. conftest;
  6. Polaris.

Ei bine, să începem!

Verificarea Deployment-urilor

Înainte de a începe comparația instrumentelor, să creăm o bază pe care să le testăm.

Manifestul de mai jos conține o serie de erori și neconformități cu cele mai bune practici: câte dintre ele puteți găsi?

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)

Vom folosi acest YAML pentru a compara diferite instrumente.

Manifestul menționat anterior base-valid.yaml și alte manifeste din acest articol pot fi găsite în depozitul public Git.

Manifestul descrie o aplicație web a cărei sarcină principală este de a răspunde cu mesajul „Hello World” pe portul 5678. Poate fi desfășurat cu următoarea comandă:

kubectl apply -f hello-world.yaml

Iată cum să verifici funcționarea acestuia:

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

Acum, te rog să mergi la http://localhost:8080 și confirmă că aplicația funcționează. Dar respectă aceasta cele mai bune practici? Hai să verificăm.

1. Kubeval

În centrul kubeval stă ideea că orice interacțiune cu Kubernetes are loc prin API-ul său REST. Cu alte cuvinte, putem folosi schema API pentru a verifica dacă acest YAML este conform. Să luăm un exemplu.

Instrucțiunile de instalare kubeval sunt disponibile pe site-ul proiectului.

La momentul scrierii articolului original, versiunea 0.15.0 era disponibilă.

După instalare, haideți să „alimentăm” manifestul menționat mai sus:

$ kubeval base-valid.yaml
PASS - base-valid.yaml conține un Deployment valid (http-echo)
PASS - base-valid.yaml conține un Service valid (http-echo)

În caz de succes, kubeval se va încheia cu un cod de ieșire 0. Îl poți verifica astfel:

$ echo $?
0

Hai să încercăm acum kubeval cu un alt manifest:

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)

Poți identifica problema cu ochiul liber? Rulăm:

$ kubeval kubeval-invalid.yaml
WARN - kubeval-invalid.yaml conține un Deployment invalid (http-echo) - selector: selector este necesar
PASS - kubeval-invalid.yaml conține un Service valid (http-echo)

# să verificăm codul de întoarcere
$ echo $?
1

Resursa nu trece testul.

Deploymentele care folosesc versiunea API apps/v1, trebuie să includă un selector care să corespundă etichetei pod-ului. Manifestul de mai sus nu include selectorul, de aceea kubeval a raportat o eroare și a ieșit cu un cod de ieșire non-zero.

Interesant ce se va întâmpla dacă rulăm kubectl apply -f cu acest manifest?

Ei bine, haideți să încercăm:

$ kubectl apply -f kubeval-invalid.yaml
eroare: eroare validând "kubeval-invalid.yaml": eroare validând datele: ValidationError(Deployment.spec):
lipsește câmpul obligatoriu "selector" în io.k8s.api.apps.v1.DeploymentSpec; dacă alegi să ignori aceste erori,
oprește validarea cu --validate=false

Aceasta este exact eroarea de care a avertizat kubeval. O poți corecta adăugând un selector:

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)

Avantajul instrumentelor precum kubeval este că astfel de erori pot fi detectate în stadii timpurii ale ciclului de desfășurare.

În plus, pentru aceste verificări nu este necesar accesul la cluster: acestea pot fi efectuate offline.

În mod implicit, kubeval verifică resursele pentru conformitatea cu cea mai recentă schemă a API-ului Kubernetes. Cu toate acestea, în cele mai multe cazuri, s-ar putea să fie nevoie să efectuezi verificări pentru a respecta o versiune specifică a Kubernetes. Acest lucru se poate face cu ajutorul flag-ului --kubernetes-version:

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

Observați că versiunea trebuie specificată în formatul Major.Minor.Patch.

Pentru a vizualiza lista versiunilor pentru care se suportă verificarea, consultați schema JSON pe GitHub, pe care kubeval o folosește pentru validare. Dacă trebuie să rulezi kubeval offline, descarcă schemele și specifică locația lor locală folosind flag-ul --schema-location.

În plus față de fișierele YAML individuale, kubeval poate funcționa și cu directoare și stdin.

De asemenea, Kubeval se integrează ușor în pipeline-ul CI. Cei care doresc să efectueze teste înainte de a trimite manifeștele în cluster vor fi încântați să afle că kubeval suportă trei formate de ieșire:

  1. Text simplu;
  2. JSON;
  3. Test Anything Protocol (TAP).

Și oricare dintre formatele poate fi utilizat pentru un parsing ulterior al ieșirii, pentru a forma un rezumat al rezultatelor dorite.

Unul dintre dezavantajele kubeval este că, în prezent, acesta nu poate verifica conformitatea cu definițiile resurselor personalizate (CRD-uri). Cu toate acestea, kubeval poate fi configurat să le ignore.

Kubeval este un instrument excelent pentru verificarea și evaluarea resurselor; totuși, trebuie subliniat că trecerea unui test nu garantează că resursa respectă cele mai bune practici.

De exemplu, utilizarea etichetei latest în container nu respectă cele mai bune practici. Totuși, kubeval nu consideră aceasta o eroare și nu o raportează. Cu alte cuvinte, verificarea acestui YAML se va încheia fără avertismente.

Dar ce se întâmplă dacă trebuie să evaluăm YAML-ul și să identificăm încălcări cum ar fi eticheta latest? Как проверить YAML-файл на соответствие лучшим практикам?

2. Kube-score

Kube-score analizează manifeste YAML și le evaluează pe baza testelor integrate. Aceste teste sunt selectate pe baza recomandărilor de securitate și a celor mai bune practici, de exemplu:

  • Rularea containerului fără privilegii de root.
  • Existența verificărilor de sănătate pentru pod-uri.
  • Definirea cerințelor și limitelor resurselor.

În urma testului se emit trei rezultate: OK, AVERTISMENT și CRITIC.

Kube-score poate fi încercat online sau instalat local.

La momentul redactării articolului original, cea mai recentă versiune kube-score era 1.7.0.

Să-l testăm pe manifestul nostru base-valid.yaml:

$ kube-score score base-valid.yaml

apps/v1/Deployment http-echo
[CRITIC] Eticheta imaginii containerului
  · http-echo -> Imagine cu eticheta latest
      Se recomandă utilizarea unei etichete fixe pentru a evita upgrade-uri accidentale
[CRITIC] Pod NetworkPolicy
  · Pod-ul nu are o politică de rețea corespunzătoare
      Creați o NetworkPolicy care vizează acest pod
[CRITIC] Verificări Pod
  · Containerul lipsește o readinessProbe
      O readinessProbe ar trebui să fie utilizată pentru a indica când serviciul este pregătit să primească trafic.
      Fără aceasta, Pod-ul riscă să primească trafic înainte de a fi pornit. De asemenea, este utilizată în timpul
      desfășurărilor și poate preveni timpul de nefuncționare dacă o nouă versiune a aplicației eșuează.
      Mai multe informații: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRITIC] Contextul de securitate al containerului
  · http-echo -> Containerul nu are un context de securitate configurat
      Setează securityContext pentru a rula containerul într-un context mai sigur.
[CRITIC] Resursele containerului
  · http-echo -> Limită CPU nu este setată
      Se recomandă limitele de resurse pentru a evita DDOS-ul de resurse. Setează resources.limits.cpu
  · http-echo -> Limită de memorie nu este setată
      Se recomandă limitele de resurse pentru a evita DDOS-ul de resurse. Setează resources.limits.memory
  · http-echo -> Cerere CPU nu este setată
      Se recomandă cererile de resurse pentru a asigura că aplicația poate porni și rula fără
      a se bloca. Setează resources.requests.cpu
  · http-echo -> Cerere de memorie nu este setată
      Se recomandă cererile de resurse pentru a asigura că aplicația poate porni și rula fără a se bloca.
      Setează resources.requests.memory
[CRITIC] Desfășurarea are PodDisruptionBudget
  · Nu a fost găsit un PodDisruptionBudget corespunzător
      Se recomandă definirea unui PodDisruptionBudget pentru a evita timpul de nefuncționare neașteptat în timpul
      operațiunilor de întreținere Kubernetes, cum ar fi atunci când se drenează un nod.
[AVERTISMENT] Desfășurarea are PodAntiAffinity pe gazdă
  · Desfășurarea nu are setat podAntiAffinity pe gazdă
      Se recomandă setarea unui podAntiAffinity care să împiedice programarea mai multor pod-uri dintr-o desfășurare
      pe același nod. Acest lucru crește disponibilitatea în cazul în care nodul devine indisponibil.

YAML-ul trece testele kubeval, în timp ce kube-score indică următoarele deficiențe:

  • Nu sunt configurate verificările de disponibilitate.
  • Lipsesc cererile și limitele pentru resursele CPU și memorie.
  • Nu sunt stabilite bugetele de întrerupere pentru Pod.
  • Lipsesc regulile de anti-aggresie. (anti-afinitate) pentru a maximiza disponibilitatea.
  • Contenitorul rulează sub root.

Toate acestea sunt observații rezonabile despre deficiențele care ar trebui corectate pentru ca Deployment să devină mai eficient și mai fiabil.

Comanda kube-score afișează informațiile într-un format ușor de citit, incluzând toate neconformitățile de tip AVERTISMENT și CRITIC, ceea ce ajută foarte mult în timpul dezvoltării.

Cei care doresc să folosească acest instrument în cadrul pipeline-ului CI pot activa o ieșire mai compactă cu ajutorul flag-ului --output-format ci (în acest caz, sunt afișate și testele cu rezultatul 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) Limită CPU nu este setată
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Limită de memorie nu este setată
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Cerere CPU nu este setată
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Cerere de memorie nu este setată
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Imagine cu eticheta latest
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: Pod-ul nu are o politică de rețea corespunzătoare
[CRITICAL] http-echo apps/v1/Deployment: Contenitorul lipsește un readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Contenitorul nu are un context de securitate configurat
[CRITICAL] http-echo apps/v1/Deployment: Niciun PodDisruptionBudget corespunzător nu a fost găsit
[WARNING] http-echo apps/v1/Deployment: Deployment nu are un host podAntiAffinity setat
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

La fel ca kubeval, kube-score returnează un cod de ieșire nenul în cazul în care un test finalizat cu o eroare. CRITICDe asemenea, se poate activa o astfel de procesare și pentru AVERTISMENT.

În plus, există posibilitatea verificării resurselor pentru conformitate cu diferite versiuni de API (așa cum se întâmplă și în kubeval). Totuși, această informație este 'hardcode' în kube-score: nu se poate alege o altă versiune de Kubernetes. O asemenea restricție poate deveni o problemă majoră dacă doriți să actualizați cluster-ul sau dacă aveți mai multe clustere cu versiuni diferite de K8s.

Rețineți că există deja o problemă cu o propunere de a implementa această posibilitate.

Despre kube-score se poate afla mai multe pe site-ul oficial.

Testele kube-score sunt un instrument excelent pentru implementarea celor mai bune practici, dar ce se întâmplă dacă trebuie să faceti modificări la test sau să adăugați propriile reguli? Din păcate, acest lucru nu este posibil.

Kube-score nu este extensibil: nu pot fi adăugate politici sau adaptate.

Dacă este necesar să scrieți teste personalizate pentru verificarea conformității cu politicile acceptate de companie, puteți folosi unul dintre următoarele patru instrumente: config-lint, copper, conftest sau polaris.

3. Config-lint

Config-lint este un instrument pentru validarea fișierelor de configurare în format YAML, JSON, Terraform, CSV și a manifestelor Kubernetes.

Îl puteți instala folosind instrucțiunile de pe site-ul proiectului.

Versiunea curentă la momentul scrierii articolului original este 1.5.0.

Config-lint nu conține teste încorporate pentru verificarea manifestelor Kubernetes.

Pentru a efectua orice teste, este necesar să creați reguli corespunzătoare. Acestea sunt scrise în fișiere YAML, numite «seturi de reguli» (rulesets), și au următoarea structură:

version: 1
description: Reguli pentru fișierele de specificație Kubernetes
type: Kubernetes
files:
  - "*.yaml"
rules:
   # lista regulilor

(rule.yaml)

Să o examinăm mai atent:

  • Câmp type indică ce tip de configurație va folosi config-lint. Pentru manifestele K8s, aceasta este întotdeauna Kubernetes.
  • În câmpul files în afară de fișierele propriu-zise, poate fi specificat un director.
  • Câmp rules este destinat pentru definirea testelor personalizate.

Să presupunem că doriți să vă asigurați că imaginile din Deployment sunt întotdeauna descărcate dintr-un depozit de încredere, cum ar fi my-company.com/myapp:1.0. Regula pentru config-lint, care efectuează o astfel de verificare, va arăta astfel:

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: Deployment trebuie să folosească un tag de imagine valid
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

Pentru fiecare regulă trebuie specificate următoarele atribute:

  • id — un identificator unic al regulii;
  • severity — poate fi FAILURE, AVERTISMENT și NON_COMPLIANT;
  • message — în cazul încălcării regulii se va afișa conținutul acestei linii;
  • resource — tipul resursei căreia i se aplică această regulă;
  • assertions — o listă de condiții care vor fi evaluate în raport cu această resursă.

În regula de mai sus assertion called every verifică dacă toate containerele din Deployment (key: spec.templates.spec.containers) utilizează imagini de încredere (adică, care încep cu my-company.com/).

Setul complet de reguli arată astfel:

version: 1
description: Reguli pentru fișierele de specificație Kubernetes
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    severity: FAILURE
    message: Deployment trebuie să folosească un depozit de imagini valid
    resource: Deployment
    assertions:
      - every:
          key: spec.template.spec.containers
          expressions:
            - key: image
              op: starts-with
              value: "my-company.com/"

(ruleset.yaml)

Pentru a testa regula, să o salvăm ca check_image_repo.yaml. Să executăm verificarea pe fișier base-valid.yaml:

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

[
  {
  "AssertionMessage": "Fiecare expresie eșuează: Expresia And eșuează: imaginea nu începe cu 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": "Implementarea trebuie să utilizeze un repository de imagini valid",
  "Status": "FAILURE"
  }
]

Verificarea a eșuat. Acum să verificăm următorul manifest cu un repository de imagini corect:

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)

Executăm aceeași testare cu manifestul de mai sus. Nu s-au detectat probleme:

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

Config-lint este un cadru promițător care permite crearea de teste personalizate pentru verificarea manifestelor YAML Kubernetes cu ajutorul YAML DSL.

Dar ce se întâmplă dacă avem nevoie de o logică și teste mai complexe? Nu cumva capabilitățile YAML sunt prea limitate pentru asta? Ce-ar fi dacă am putea crea teste într-un limbaj de programare complet?

4. Copper

Copper V2 — este un cadru pentru validarea manifestelor folosind teste personalizate (similar cu config-lint).

Cu toate acestea, se deosebește prin faptul că nu utilizează YAML pentru a descrie teste. În schimb, testele pot fi create în JavaScript. Copper oferă o librărie cu câteva instrumente de bază, care ajută la citirea informațiilor despre obiectele Kubernetes și la raportarea erorilor.

Secvența pașilor pentru instalarea Copper poate fi găsită în documentația oficială.

2.0.1 — cea mai recentă versiune a acestui utilitar la momentul redactării articolului original.

La fel ca și config-lint, Copper nu are teste încorporate. Să scriem unul. Acesta să verifice că implementările folosesc imagini de container exclusiv din repository-uri de încredere precum my-company.com.

Creați un fișier check_image_repo.js cu următorul conținut:

$$.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',"Imaginea " + $.metadata.name + " nu este din repository-ul my-company.com", 1)
            }
        });
    }
});

Acum, pentru a verifica manifestul nostru base-valid.yaml, folosiți comanda copper validate:

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

Verificarea no_company_repo a eșuat cu severitate 1 din cauza că Imaginea http-echo nu este din repository-ul my-company.com
Validarea a eșuat

Este clar că, cu ajutorul copper, pot fi efectuate teste mai complexe - de exemplu, verificarea numelui domeniilor în manifestele Ingress sau respingerea pod-urilor care funcționează în mod privilegiat.

Copper include diverse funcții de asistență:

  • DockerImage citește fișierul de intrare specificat și creează un obiect cu următoarele atribute:
    • name — numele imaginii,
    • tag — eticheta imaginii,
    • registry — registry-ul imaginilor,
    • registry_url — protocolul (https://) și registry-ul imaginilor,
    • fqin — locația completă a imaginii.
  • Funcția findByName ajută la găsirea unei resurse după tipul specificat (kind) și nume (name) din fișierul de intrare.
  • Funcția findByLabels ajută la găsirea unei resurse după tipul specificat (kind) și etichete (labels).

Cu toate funcțiile de asistență disponibile, puteți consulta aici.

Implicit, acesta încarcă întregul fișier YAML de intrare într-o variabilă $$ și îl face disponibil pentru scripturi (o metodă cunoscută pentru cei care au experiență cu jQuery).

Principalul avantaj al Copper este evident: nu trebuie să învățați un limbaj specializat și puteți utiliza diverse funcționalități JavaScript pentru a crea propriile teste, cum ar fi interpolarea șirurilor, funcțiile etc.

De asemenea, este important de menționat că versiunea actuală a Copper funcționează cu versiunea ES5 a motorului JavaScript, nu cu ES6.

Detalii sunt disponibile pe situl oficial al proiectului.

Cu toate acestea, dacă nu sunteți foarte pasionat de JavaScript și preferați un limbaj destinat în mod special creării de interogări și descrierii politicilor, ar trebui să luați în considerare conftest.

5. Conftest

Conftest este un cadru pentru verificarea datelor de configurare. Este potrivit și pentru testarea/verificarea manifestelor Kubernetes. Testele sunt descrise folosind un limbaj specializat de interogare Rego.

Puteți instala conftest folosind instrucțiunile, disponibile pe site-ul proiectului.

La momentul redactării articolului original, cea mai recentă versiune disponibilă era 0.18.2.

În mod similar cu config-lint și copper, conftest vine fără teste încorporate. Să încercăm și să scriem propria politică. Ca în exemplele anterioare, vom verifica dacă imaginile containerelor sunt preluate dintr-o sursă de încredere.

Creează un director conftest-checks, iar în acesta - un fișier cu numele check_image_registry.rego cu următorul conținut:

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])
}

Acum să testăm base-valid.yaml prin 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

Testul a eșuat așa cum era de așteptat, deoarece imaginile provin dintr-o sursă nesigură.

În fișierul Rego, definim blocul deny. Valabilitatea sa este considerată o încălcare. Dacă există mai multe blocuri, deny conftest le verifică independent unul de celălalt, iar valabilitatea oricărui bloc este interpretată ca o încălcare.

Pe lângă ieșirea implicită, conftest suportă JSON, TAP și format tabelar — o caracteristică extrem de utilă, dacă trebuie să integrați rapoartele în pipeline-ul CI existent. Puteți seta formatul dorit folosind flagul --output.

Pentru a facilita depanarea politicilor, conftest include un flag --trace. Acesta va afișa o trasare a modului în care conftest parcurge fișierele de politici specificate.

Politicile conftest pot fi publicate și partajate în registre OCI (Open Container Initiative) sub formă de artefacte.

Comenzi push și pull permit publicarea unui artefact sau extragerea unui artefact existent dintr-un registru la distanță. Să încercăm să publicăm politica pe care am creat-o în registrul local Docker folosind conftest push.

Rulați registrul local Docker:

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

Într-un alt terminal, navigați la directorul creat anterior conftest-checks și executați următoarea comandă:

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

Dacă comanda a fost executată cu succes, veți vedea un mesaj de tipul următor:

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

Acum creați un director temporar și executați în el comanda conftest pull. Aceasta va descărca pachetul creat de comanda anterioară:

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

În directorul temporar va apărea un subdirector policy, care va conține fișierul nostru de politici:

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

Teste pot fi efectuate direct din repository:

$ conftest test --update 127.0.0.1:5000/amitsaha/opa-bundle-example:latest base-valid.yaml
..
FAIL - base-valid.yaml - imaginea 'hashicorp/http-echo' nu provine din registrul my-company.com
2 teste, 1 trecut, 0 avertismente, 1 eșec

Din păcate, DockerHub nu este în prezent suportat. Deci, considerați-vă norocoși dacă folosiți Azure Container Registry (ACR) sau propriul dvs. registru.

Formatul artefactelor este același ca și cel al pachetelor Open Policy Agent (OPA), ceea ce permite utilizarea conftest pentru a rula teste din pachete OPA existente.

Mai multe despre partajarea politicilor și alte caracteristici ale conftest puteți afla pe situl oficial al proiectului.

6. Polaris

Ultimul instrument despre care vom discuta în acest articol este Polaris. (Anunțul său de anul trecut l-am tradus deja — notă de traducere.)

Polaris poate fi instalat într-un cluster sau utilizat în modul linie de comandă. Așa cum ați ghicit, acesta permite analiza statică a manifestelor Kubernetes.

Atunci când se lucrează în modul linie de comandă, sunt disponibile teste încorporate care acoperă domenii precum securitatea și cele mai bune practici (similar cu kube-score). De asemenea, puteți crea teste personalizate (ca în config-lint, copper și conftest).

Cu alte cuvinte, Polaris combină avantajele ambelor categorii de instrumente: cu teste încorporate și personalizate.

Pentru a instala Polaris în modul linie de comandă, utilizați instrucțiunile de pe site-ul proiectului.

La momentul redactării acestui articol, versiunea disponibilă este 1.0.3.

După finalizarea instalării, puteți rula polaris pe manifestul base-valid.yaml folosind comanda următoare:

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

Aceasta va produce un șir în format JSON cu o descriere detaliată a testelor efectuate și rezultatele lor. Iată cum va arăta structura de ieșire:

{
  "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": [
    /* listă lungă */
  ]
}

Ieșirea completă este disponibilă aici.

Similar cu kube-score, Polaris identifică probleme în domeniile în care manifestul nu respectă cele mai bune practici:

  • Lipsesc verificările de sănătate pentru poduri.
  • Nu sunt specificate etichetele pentru imaginile containere.
  • Contenitorul rulează sub root.
  • Nu sunt specificate cerințele și limitele pentru memorie și CPU.

Fiecărui test îi este atribuit un grad de gravitate în funcție de rezultatele sale: warning sau danger. Pentru a afla mai multe despre testele încorporate disponibile, consultați documentation.

Dacă nu aveți nevoie de detalii, puteți specifica flag-ul --format score. În acest caz, Polaris va produce un număr în intervalul de la 1 la 100 — score (adică evaluarea):

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

Cu cât evaluarea este mai aproape de 100, cu atât mai ridicată este gradul de conformitate. Dacă verificați codul de ieșire al comenzii polaris audit, veți observa că acesta este 0.

Pentru a face polaris audit să se încheie cu un cod de ieșire diferit de zero, puteți folosi două flag-uri:

  • Steagul --set-exit-code-below-score acceptă ca argument un prag în intervalul 1-100. În acest caz, comanda se va încheia cu codul de ieșire 4, dacă evaluarea se află sub prag. Acest lucru este foarte util atunci când aveți un anumit prag (să spunem, 75) și aveți nevoie de o alertă dacă evaluarea scade sub această valoare.
  • Steagul --set-exit-code-on-danger va duce la finalizarea echipei cu codul 3, dacă unul dintre testele de tip danger eșuează.

Acum haideți să încercăm să creăm un test personalizat care verifică dacă imaginea provine dintr-un repository de încredere. Testele personalizate sunt definite în format YAML, iar testul în sine este descris folosind JSON Schema.

Următorul fragment de cod YAML descrie un nou test numit checkImageRepo:

checkImageRepo:
  successMessage: Registry-ul imaginii este valid
  failureMessage: Registry-ul imaginii nu este valid
  category: Imagini
  target: Container
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Să ne uităm mai îndeaproape la acesta:

  • successMessage — acest text va fi afișat dacă testul se finalizează cu succes;
  • failureMessage — acest mesaj va fi afișat în caz de eșec;
  • category — indică una dintre categorii: Imagini, Verificări de sănătate, Securitate, Networking și Resurse;
  • destinația— definește tipul de obiect la care se aplică testul (spec) Posibile valori: Container, Pod sau Controller;
  • Testul este definit într-un obiect schema folosind JSON schema. În acest test, cuvântul cheie pattern este folosit pentru a compara sursa imaginii cu cea cerută.

Pentru a rula testul de mai sus, trebuie să creați următoarea configurație Polaris:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: Registry-ul imaginii este valid
    failureMessage: Registry-ul imaginii nu este valid
    category: Imagini
    target: Container
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(polaris-conf.yaml)

Să analizăm fișierul:

  • În câmpul checks defineste testele și nivelul lor de criticitate. Deoarece este de dorit să primim un avertisment atunci când imaginea provine dintr-o sursă nesigură, setăm aici nivelul danger.
  • Testul în sine checkImageRepo apoi este scris în obiectul customChecks.

Salvați fișierul ca custom_check.yaml. Acum putem rula polaris audit cu un manifest YAML care necesită verificare.

Să testăm manifestul nostru base-valid.yaml:

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

Comanda polaris audit a efectuat doar testul personalizat definit mai sus, iar acesta nu a avut succes.

Dacă corectăm imaginea la my-company.com/http-echo:1.0, Polaris va finaliza cu succes. Manifestul cu modificările este deja disponibil în repository, așa că puteți verifica comanda anterioară pe manifest. image-valid-mycompany.yaml.

Acum apare întrebarea: cum să rulați testele încorporate împreună cu cele personalizate? Ușor! Trebuie doar să adăugați identificatorii testelor încorporate în fișierul de configurație. Ca rezultat, acesta va avea următorul aspect:

verificări:
  cpuRequestsMissing: avertizare
  cpuLimitsMissing: avertizare
  # Alte verificări integrate..
  # ..
  # verificări personalizate
  checkImageRepo: pericol # !!!
verificăriPersonalizate:
  checkImageRepo:        # !!!
    successMessage: Registrul de imagini este valid
    failureMessage: Registrul de imagini nu este valid
    category: Imagini
    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)

Un exemplu complet al fișierului de configurare este disponibil aici.

Verifică manifestul base-valid.yaml, utilizând teste integrate și personalizate, se poate face cu comanda:

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

Polaris completează testele integrate cu cele personalizate, combinând astfel cele mai bune aspecte din ambele lumi.

Pe de altă parte, imposibilitatea de a folosi limbaje mai puternice, cum ar fi Rego sau JavaScript, poate deveni un factor limitativ în crearea de teste mai sofisticate.

Informații suplimentare despre Polaris sunt disponibile pe site-ul proiectului.

Rezumat

Deși există numeroase instrumente pentru verificarea și evaluarea fișierelor YAML Kubernetes, este important să ai o înțelegere clară despre cum vor fi proiectate și executate testele.

De exemplu, dacă luăm manifestele Kubernetes care trec prin pipeline, kubeval ar putea fi primul pas într-un astfel de pipeline. Ar urmări dacă definițiile obiectelor respectă schema API Kubernetes.

După finalizarea unei astfel de verificări, s-ar putea trece la teste mai sofisticate, cum ar fi conformitatea cu cele mai bune practici standard și politici specifice. Și aici s-ar putea folosi kube-score și Polaris.

Celor care au cerințe complexe și necesitatea de a personaliza în detaliu testele li s-ar potrivi copper, config-lint și conftest.

Conftest și config-lint folosesc YAML pentru a defini teste personalizate, iar copper oferă acces la un limbaj de programare complet, ceea ce îl face o alegere destul de atrăgătoare.

Pe de altă parte, merită să folosești unul dintre aceste instrumente și, prin urmare, să creezi toate testele manual, sau să preferi Polaris și să adaugi doar ceea ce este necesar? Nu există un răspuns definitiv la această întrebare.

Tabelul de mai jos conține o descriere scurtă a fiecărui instrument:

Instrument
Scop
Dezavantaje
Teste personalizate

kubeval
Verifică manifestele YAML pentru conformitatea cu o anumită versiune a schemei API
Nu poate lucra cu CRD
Nu

kube-score
Analizează manifestele YAML pentru conformitatea cu cele mai bune practici
Nu poți alege propria versiune API Kubernetes pentru a verifica resursele
Nu

copper
Un cadru comun pentru crearea propriilor teste JavaScript pentru manifestele YAML
Nu există teste încorporate. Documentație limitată
Da

config-lint
Un cadru comun pentru crearea testelor în limbajul orientat pe obiecte, încorporat în YAML. Suportă diverse formate de configurații (de exemplu, Terraform)
Nu există teste gata făcute. Este posibil să nu fie suficiente aserțiuni și funcții încorporate
Da

conftest
Cadru pentru crearea propriilor teste în Rego (un limbaj specializat de interogare). Permite partajarea politicilor prin OCI bundles
Nu există teste încorporate. Este nevoie să înveți Rego. Docker Hub nu este acceptat la publicarea politicilor
Da

Polaris
Analizează manifestele YAML pentru conformitatea cu cele mai bune practici standard. Permite crearea de teste proprii folosind JSON Schema
Capacitățile testelor bazate pe JSON Schema pot să nu fie suficiente
Da

Deoarece aceste instrumente nu depind de accesul la clusterul Kubernetes, sunt ușor de instalat. Permit filtrarea fișierelor sursă și oferă feedback rapid autorilor pull request-urilor din proiecte.

P.S. de la traducător

Citiți și în blogul nostru:

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster