Validación de YAML de Kubernetes conforme a las mejores prácticas y políticas

Nota de traducción.: Con el aumento del número de configuraciones YAML para entornos K8s, se hace cada vez más relevante la necesidad de su verificación automatizada. El autor de esta revisión no solo ha seleccionado soluciones existentes para esta tarea, sino que también ha explorado, mediante el ejemplo de un Deployment, cómo funcionan. Resultó ser bastante informativo para aquellos a quienes les interesa este tema.

Validación de YAML de Kubernetes conforme a las mejores prácticas y políticas

TL;DR: El artículo compara seis herramientas estáticas de verificación y evaluación de archivos YAML de Kubernetes en relación con las mejores prácticas y requisitos.

Las cargas de trabajo de Kubernetes se definen generalmente en forma de documentos YAML. Uno de los problemas con YAML es la complejidad de establecer restricciones o relaciones entre los archivos de manifiestos.

¿Qué pasa si necesitamos asegurarnos de que todas las imágenes desplegadas en el clúster provienen de un registro de confianza?

¿Cómo prevenir el envío de Deployments al clúster para los cuales no se han establecido PodDisruptionBudgets?

La integración de pruebas estáticas permite identificar errores y violaciones de políticas aún en la fase de desarrollo. De este modo, se incrementan las garantías de corrección y seguridad de las definiciones de recursos y se aumenta la probabilidad de que las cargas de producción sigan las mejores prácticas.

El ecosistema de verificación estática de archivos YAML de Kubernetes se puede dividir en las siguientes categorías:

  • Validadores de API. Las herramientas en esta categoría validan el manifiesto YAML en conformidad con los requisitos del servidor API de Kubernetes.
  • Testers predefinidos. Las herramientas de esta categoría vienen con pruebas listas sobre seguridad, conformidad con las mejores prácticas, etc.
  • Validadores personalizados. Los representantes de esta categoría permiten crear pruebas personalizadas en varios lenguajes, como Rego y JavaScript.

En este artículo describiremos y compararemos seis herramientas diferentes:

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

¡Bien, comencemos!

Verificación de Deployments

Antes de proceder a la comparación de las herramientas, establezcamos una base sobre la cual las probaremos.

El siguiente manifiesto contiene varios errores y discrepancias con las mejores prácticas: ¿cuántos de ellos podrás encontrar?

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)

Vamos a utilizar este YAML para comparar diversas herramientas.

El manifiesto anterior base-valid.yaml y otros manifiestos de este artículo se pueden encontrar en repositorio de Git.

El manifiesto describe una aplicación web cuya tarea principal es responder con el mensaje «Hello World» en el puerto 5678. Se puede desplegar con el siguiente comando:

kubectl apply -f hello-world.yaml

Y así se prueba su funcionamiento:

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

Ahora, visita http://localhost:8080 y confirma que la aplicación está funcionando. Pero, ¿sigue las mejores prácticas? Vamos a comprobarlo.

1. Kubeval

La base de kubeval es la idea de que cualquier interacción con Kubernetes se realiza a través de su API REST. En otras palabras, se puede utilizar el esquema API para validar si el YAML dado cumple con él. Veamos un ejemplo.

Instrucciones para instalar kubeval están disponibles en el sitio del proyecto.

En el momento de redactar el artículo original, estaba disponible la versión 0.15.0.

Después de la instalación, vamos a ‘alimentarle’ el manifiesto mencionado anteriormente:

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

En caso de éxito, kubeval terminará con un código de salida 0. Se puede verificar de la siguiente manera:

$ echo $?
0

Ahora intentemos kubeval con otro manifiesto:

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)

¿Puedes identificar el problema a simple vista? Ejecutamos:

$ 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)

# verifiquemos el código de retorno
$ echo $?
1

El recurso no pasa la validación.

Los Deployments que utilizan la versión API apps/v1, deben incluir un selector que coincida con la etiqueta del pod. El manifiesto anterior no incluye un selector, por lo que kubeval informó de un error y terminó con un código diferente de cero.

Es interesante, ¿qué pasará si ejecutamos kubectl apply -f con este manifiesto?

Bueno, intentémoslo:

$ 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

Exactamente el error que advirtió kubeval. Se puede corregir añadiendo 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)

La ventaja de herramientas como kubeval es que se pueden detectar estos errores en las primeras etapas del ciclo de despliegue.

Además, para estas verificaciones no se necesita acceso al clúster: se pueden realizar sin conexión.

Por defecto, kubeval verifica los recursos para cumplir con el esquema más reciente de Kubernetes API. Sin embargo, en la mayoría de los casos, es posible que necesite realizar una verificación contra una versión específica de Kubernetes. Esto se puede hacer utilizando el flag --kubernetes-version:

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

Tenga en cuenta que la versión debe especificarse en el formato Major.Minor.Patch.

Para ver la lista de versiones para las que se admite la verificación, consulte el esquema JSON en GitHub, que kubeval utiliza para la validación. Si necesita ejecutar kubeval sin conexión, descargue los esquemas y especifique su ubicación local usando el flag --schema-location.

Además de archivos YAML individuales, kubeval también puede trabajar con directorios y stdin.

Además, Kubeval se integra fácilmente en pipelines de CI. Los interesados en realizar pruebas antes de enviar manifiestos al clúster se alegrarán de saber que kubeval admite tres formatos de salida:

  1. Texto plano;
  2. JSON;
  3. Test Anything Protocol (TAP).

Y cualquiera de los formatos se puede usar para un análisis posterior de la salida, para generar un resumen de resultados en el formato deseado.

Una de las desventajas de kubeval es que actualmente no puede verificar conformidad con las Custom Resource Definitions (CRDs). Sin embargo, se puede configurar kubeval para ignorarlas.

Kubeval es una excelente herramienta para verificar y evaluar recursos; sin embargo, cabe destacar que pasar la prueba no garantiza que el recurso cumpla con las mejores prácticas.

Por ejemplo, el uso de la etiqueta latest El contenedor no cumple con las mejores prácticas. Sin embargo, kubeval no considera esto un error y no lo reporta. Es decir, la validación de este YAML finalizará sin advertencias.

Pero, ¿qué ocurre si necesitamos evaluar el YAML y detectar infracciones como la etiqueta? latest? Как проверить YAML-файл на соответствие лучшим практикам?

2. Kube-score

Kube-score analiza los manifiestos YAML y los evalúa según pruebas incorporadas. Estas pruebas se seleccionan en base a recomendaciones de seguridad y mejores prácticas, como por ejemplo:

  • Ejecutar contenedores no como root.
  • La existencia de comprobaciones de estado de los pods.
  • Asignar solicitudes y límites de recursos.

Tras la evaluación, se emiten tres resultados: OK, ADVERTENCIA y CRÍTICO.

Kube-score se puede probar en línea o instalar localmente.

Al momento de escribir el artículo original, la versión más reciente de kube-score era 1.7.0.

Probemos con nuestro manifiesto base-valid.yaml:

$ kube-score score base-valid.yaml

apps/v1/Deployment http-echo
[CRÍTICO] Etiqueta de imagen del contenedor
  · http-echo -> Imagen con etiqueta latest
      Se recomienda usar una etiqueta fija para evitar actualizaciones accidentales
[CRÍTICO] NetworkPolicy de Pod
  · El pod no tiene una política de red coincidente
      Crea una NetworkPolicy que apunte a este pod
[CRÍTICO] Comprobaciones del Pod
  · El contenedor carece de readinessProbe
      Se debe usar un readinessProbe para indicar cuándo el servicio está listo para recibir tráfico.
      Sin él, el Pod corre el riesgo de recibir tráfico antes de haber iniciado. También se usa durante
      los despliegues y puede prevenir el tiempo de inactividad si una nueva versión de la aplicación falla.
      Más información: https://github.com/zegl/kube-score/blob/master/README_PROBES.md
[CRÍTICO] Contexto de seguridad del contenedor
  · http-echo -> Contenedor no tiene configurado un contexto de seguridad
      Establezca securityContext para ejecutar el contenedor en un contexto más seguro.
[CRÍTICO] Recursos del contenedor
  · http-echo -> Límite de CPU no está configurado
      Se recomiendan límites de recursos para evitar un DDOS de recursos. Establezca resources.limits.cpu
  · http-echo -> Límite de memoria no está configurado
      Se recomiendan límites de recursos para evitar un DDOS de recursos. Establezca resources.limits.memory
  · http-echo -> Solicitud de CPU no está configurada
      Se recomiendan solicitudes de recursos para asegurarse de que la aplicación pueda iniciar y ejecutarse sin
      fallar. Establezca resources.requests.cpu
  · http-echo -> Solicitud de memoria no está configurada
      Se recomiendan solicitudes de recursos para asegurarse de que la aplicación pueda iniciar y ejecutarse sin
      fallar. Establezca resources.requests.memory
[CRÍTICO] El despliegue tiene PodDisruptionBudget
  · No se encontró un PodDisruptionBudget coincidente
      Se recomienda definir un PodDisruptionBudget para evitar tiempos de inactividad inesperados durante las
      operaciones de mantenimiento de Kubernetes, como cuando se drena un nodo.
[ADVERTENCIA] El despliegue tiene host PodAntiAffinity
  · El despliegue no tiene un podAntiAffinity configurado
      Se recomienda establecer un podAntiAffinity que impida que múltiples pods de un despliegue se programen
      en el mismo nodo. Esto aumenta la disponibilidad en caso de que el nodo se vuelva no disponible.

El YAML pasa las verificaciones de kubeval, mientras que kube-score indica las siguientes deficiencias:

  • No se han configurado comprobaciones de disponibilidad.
  • Faltan solicitudes y límites para recursos de CPU y memoria.
  • No se han definido presupuestos de interrupción del Pod.
  • Faltan reglas de anti-affinity. (anti-afinidad) para maximizar la disponibilidad.
  • El contenedor se ejecuta bajo root.

Todo esto son observaciones razonables sobre las deficiencias que deben solucionarse para que el despliegue sea más eficiente y confiable.

Comando kube-score muestra información en un formato legible con inclusión de todas las violaciones de tipo ADVERTENCIA y CRÍTICO, lo cual es muy útil durante el desarrollo.

Quienes deseen usar esta herramienta en el marco de un pipeline de CI pueden habilitar una salida más compacta usando el flag --output-format ci (en este caso también se muestran las pruebas con resultados 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) El límite de CPU no está establecido
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) El límite de memoria no está establecido
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) La solicitud de CPU no está establecida
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) La solicitud de memoria no está establecida
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) Imagen con la etiqueta más reciente
[OK] http-echo apps/v1/Deployment
[CRITICAL] http-echo apps/v1/Deployment: El pod no tiene una política de red coincidente
[CRITICAL] http-echo apps/v1/Deployment: El contenedor carece de readinessProbe
[CRITICAL] http-echo apps/v1/Deployment: (http-echo) El contenedor no tiene un contexto de seguridad configurado
[CRITICAL] http-echo apps/v1/Deployment: No se encontró un PodDisruptionBudget coincidente
[WARNING] http-echo apps/v1/Deployment: El despliegue no tiene un podAntiAffinity host configurado
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service
[OK] http-echo v1/Service

De manera similar a kubeval, kube-score retorna un código de salida no cero si hay una prueba que falló. CRÍTICO. También se puede habilitar un manejo similar para ADVERTENCIA.

Además, existe la posibilidad de verificar recursos en conformidad con diferentes versiones de API (como en kubeval). Sin embargo, esta información está 'hardcodeada' en el propio kube-score: no se puede seleccionar otra versión de Kubernetes. Esta limitación puede ser un gran problema si planeas actualizar tu clúster o si tienes varios clústeres con diferentes versiones de K8s.

Tenga en cuenta que ya hay un issue con la propuesta de implementar esta funcionalidad.

Para más información sobre kube-score, puedes consultar el sitio web oficial.

Las pruebas de kube-score son una gran herramienta para la implementación de mejores prácticas, pero ¿qué sucede si necesitas hacer cambios en la prueba o agregar tus propias reglas? Lamentablemente, no es posible hacerlo.

Kube-score no es extensible: no se pueden agregar o ajustar políticas.

Si necesitas escribir pruebas personalizadas para comprobaciones de conformidad con las políticas adoptadas en la empresa, puedes usar una de las siguientes cuatro herramientas: config-lint, copper, conftest o polaris.

3. Config-lint

Config-lint es una herramienta para la validación de archivos de configuración en formatos YAML, JSON, Terraform, CSV y manifiestos de Kubernetes.

Se puede instalar mediante las instrucciones en el sitio del proyecto.

La versión actual al momento de escribir este artículo es 1.5.0.

Config-lint no incluye pruebas integradas para verificar los manifiestos de Kubernetes.

Para realizar cualquier prueba, es necesario crear las reglas correspondientes. Estas se escriben en archivos YAML, conocidos como 'conjuntos de reglas' (rulesets), y tienen la siguiente estructura:

version: 1
description: Reglas para archivos de especificación de Kubernetes
type: Kubernetes
files:
  - "*.yaml"
rules:
   # lista de reglas

(rule.yaml)

Examinemos esto más de cerca:

  • Campo tipo indica qué tipo de configuración utilizará config-lint. Para los manifiestos de K8s, esto es siempre Kubernetes.
  • En el campo files además de los propios archivos, se puede especificar un directorio.
  • Campo rules destinado a establecer pruebas personalizadas.

Supongamos que desea asegurarse de que las imágenes en el Deployment siempre se descarguen desde un repositorio confiable como my-company.com/myapp:1.0. La regla para config-lint que realiza esta verificación se verá de la siguiente manera:

- id: MY_DEPLOYMENT_IMAGE_TAG
  severity: FAILURE
  message: El Deployment debe usar una etiqueta de imagen válida
  resource: Deployment
  assertions:
    - every:
        key: spec.template.spec.containers
        expressions:
          - key: image
            op: starts-with
            value: "my-company.com/"

(rule-trusted-repo.yaml)

Para cada regla, deben especificarse los siguientes atributos:

  • id — identificador único de la regla;
  • severity — puede ser FAILURE, ADVERTENCIA y NON_COMPLIANT;
  • message — el contenido de esta línea se mostrará al violar la regla;
  • resource — el tipo de recurso al que se aplica esta regla;
  • assertions — lista de condiciones que se evaluarán en relación con este recurso.

En la regla anterior, la afirmación llamada every verifica que todos los contenedores en el Deployment (key: spec.templates.spec.containers) utilicen imágenes confiables (es decir, que comiencen con my-company.com/).

El conjunto de reglas completo se ve de la siguiente manera:

version: 1
description: Reglas para archivos de especificación de Kubernetes
type: Kubernetes
files:
  - "*.yaml"
rules:

 - id: DEPLOYMENT_IMAGE_REPOSITORY # !!!
    severity: FAILURE
    message: El Deployment debe usar un repositorio de imágenes válido
    resource: Deployment
    assertions:
      - every:
          key: spec.template.spec.containers
          expressions:
            - key: image
              op: starts-with
              value: "my-company.com/"

(ruleset.yaml)

Para probar la regla, guardémosla como check_image_repo.yaml. Ejecutemos la verificación en el archivo. 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": "El despliegue debe usar un repositorio de imágenes válido",
  "Status": "FALLO"
  }
]

La verificación falló. Ahora vamos a verificar el siguiente manifiesto con un repositorio de imágenes correcto:

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)

Ejecutamos la misma prueba con el manifiesto anterior. No se encontraron problemas:

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

Config-lint es un marco prometedor que permite crear pruebas personalizadas para verificar manifiestos YAML de Kubernetes utilizando YAML DSL.

¿Pero qué hacer si se requiere una lógica y pruebas más complejas? ¿No son las capacidades de YAML demasiado limitadas para eso? ¿Qué tal si se pudieran crear pruebas en un lenguaje de programación completo?

4. Copper

Copper V2 es un marco para la validación de manifiestos mediante pruebas personalizadas (similar a config-lint).

Sin embargo, se diferencia de este último en que no utiliza YAML para describir las pruebas. En su lugar, se pueden crear pruebas en JavaScript. Copper proporciona una biblioteca con varias herramientas básicas, que ayudan a leer información sobre objetos de Kubernetes y reportar errores.

La secuencia de pasos para instalar Copper se puede encontrar en documentación oficial.

2.0.1 es la versión más reciente de esta utilidad al momento de escribir el artículo original.

Al igual que config-lint, Copper no tiene pruebas integradas. Vamos a escribir una. Supongamos que verifica que los despliegues utilicen imágenes de contenedores únicamente de repositorios confiables como my-company.com.

Cree un archivo check_image_repo.js con el siguiente contenido:

$$.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',"La imagen " + $.metadata.name + " no es del repositorio my-company.com", 1)
            }
        });
    }
});

Ahora, para verificar nuestro manifiesto base-valid.yaml, use el comando copper validate:

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

Check no_company_repo failed with severity 1 due to La imagen http-echo no es del repositorio my-company.com
La validación falló

Es evidente que con copper se pueden realizar pruebas más complejas, como verificar los nombres de dominio en los manifiestos de Ingress o rechazar pods que funcionan en modo privilegiado.

Copper incluye varias funciones auxiliares:

  • DockerImage lee el archivo de entrada especificado y crea un objeto con los siguientes atributos:
    • name — nombre de la imagen,
    • tag — etiqueta de la imagen,
    • registry — registro de imágenes,
    • registry_url — protocolo (https://) y registro de imágenes,
    • fqin — ubicación completa de la imagen.
  • La función findByName ayuda a encontrar un recurso por el tipo especificado (tipo) y el nombre (name) del archivo de entrada.
  • La función findByLabels ayuda a encontrar un recurso por el tipo especificado (tipo) y las etiquetas (labels).

Puedes familiarizarte con todas las funciones auxiliares disponibles aquí.

Por defecto, carga todo el archivo YAML de entrada en una variable $$ y lo hace accesible para los scripts (un método familiar para aquellos con experiencia en jQuery).

La ventaja principal de Copper es evidente: no necesitas aprender un lenguaje especializado y puedes utilizar diversas capacidades de JavaScript para crear tus propias pruebas, como la interpolación de cadenas, funciones, etc.

También cabe destacar que la versión actual de Copper funciona con la versión ES5 del motor JavaScript, no con ES6.

Los detalles están disponibles en el sitio web oficial del proyecto.

Sin embargo, si no te gusta mucho JavaScript y prefieres un lenguaje diseñado específicamente para crear consultas y describir políticas, deberías considerar conftest.

5. Conftest

Conftest es un marco para verificar datos de configuración. Es adecuado también para pruebas/validación de manifiestos de Kubernetes. Las pruebas se describen utilizando un lenguaje de consulta especializado Rego.

Puedes instalar conftest usando las instrucciones, que se encuentran en el sitio del proyecto.

Al momento de escribir el artículo original, la última versión disponible era 0.18.2.

Al igual que config-lint y copper, conftest viene sin pruebas integradas. Vamos a probarlo y a escribir nuestra propia política. Al igual que en los ejemplos anteriores, vamos a verificar si las imágenes de los contenedores provienen de una fuente confiable.

Crea un directorio conftest-checks, y dentro, un archivo llamado check_image_registry.rego con el siguiente contenido:

package main

deny[msg] {

  input.kind == "Deployment"
  image := input.spec.template.spec.containers[_].image
  not startswith(image, "my-company.com/")
  msg := sprintf("la imagen '%v' no proviene del repositorio my-company.com", [image])
}

Ahora probemos base-valid.yaml a través de conftest:

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

FAIL - base-valid.yaml - la imagen 'hashicorp/http-echo' no proviene del repositorio my-company.com
1 pruebas, 1 aprobada, 0 advertencias, 1 fallo

La prueba falló como se esperaba, ya que las imágenes provienen de una fuente no confiable.

En el archivo Rego definimos el bloque deny. Su veracidad se considera una violación. Si hay varios bloques, deny conftest los verifica de manera independiente entre sí, y la veracidad de cualquiera de los bloques se interpreta como una violación.

Además de la salida por defecto, conftest admite JSON, TAP y formato de tabla, lo cual es una característica extremadamente útil si se necesita integrar informes en un pipeline CI existente. El formato deseado se puede establecer mediante el flag --output.

Para ayudar en la depuración de políticas, conftest tiene un flag --trace. Este muestra un seguimiento de cómo conftest analiza los archivos de políticas especificados.

Las políticas de conftest se pueden publicar y compartir en registros OCI (Open Container Initiative) como artefactos.

Comandos push y pull permiten publicar un artefacto o extraer un artefacto existente de un registro remoto. Intentemos publicar la política que creamos en un registro local de Docker utilizando conftest push.

Ejecuta un registro local de Docker:

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

En otra terminal, dirígete al directorio que creaste previamente conftest-checks y ejecuta el siguiente comando:

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

Si el comando se ejecutó correctamente, verás un mensaje del siguiente tipo:

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

Ahora crea un directorio temporal y ejecuta en él el comando conftest pull. Esto descargará el paquete creado por el comando anterior en él:

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

En el directorio temporal aparecerá un subdirectorio policy, conteniendo nuestro archivo de políticas:

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

Se pueden realizar pruebas directamente desde el repositorio:

$ 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

Desafortunadamente, DockerHub aún no es compatible. Así que consideren que tienen suerte si utilizan Azure Container Registry (ACR) o su propio registro.

El formato de los artefactos es el mismo que el de los paquetes de Open Policy Agent (OPA), lo que permite utilizar conftest para ejecutar pruebas de paquetes OPA existentes.

Más sobre el compartir políticas y otras características de conftest se puede consultar en el sitio web oficial del proyecto.

6. Polaris

La última herramienta de la que se hablará en este artículo es Polaris. (Su anuncio del año pasado ya lo hemos traducido — nota del traductor.)

Polaris se puede instalar en un clúster o utilizarse en modo línea de comandos. Como ya habrás adivinado, permite realizar un análisis estático de los manifiestos de Kubernetes.

En modo línea de comandos, hay pruebas integradas que cubren áreas como seguridad y mejores prácticas (similar a kube-score). Además, se pueden crear pruebas personalizadas (como en config-lint, copper y conftest).

En otras palabras, Polaris combina las ventajas de ambas categorías de herramientas: con pruebas integradas y personalizadas.

Para instalar Polaris en modo línea de comandos, utiliza las instrucciones en el sitio del proyecto..

Al momento de escribir este artículo, está disponible la versión 1.0.3.

Después de completar la instalación, puedes ejecutar polaris sobre el manifiesto base-valid.yaml con el siguiente comando:

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

Este comando generará una línea en formato JSON con una descripción detallada de las pruebas realizadas y sus resultados. La salida tendrá la siguiente estructura:

{
  "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": [
    /* lista larga */
  ]
}

La salida completa está disponible aquí.

Al igual que kube-score, Polaris identifica problemas en aquellas áreas donde el manifiesto no cumple con las mejores prácticas:

  • Faltan comprobaciones de salud de los pods.
  • No se especificaron etiquetas para las imágenes de los contenedores.
  • El contenedor se ejecuta bajo root.
  • No se especificaron requests y limits para la memoria y el CPU.

A cada prueba se le asigna un nivel de criticidad según sus resultados: advertencia o peligro. Para conocer más sobre las pruebas integradas disponibles, consulta la documentación.

Si no necesitas detalles, puedes agregar la bandera --format score. En este caso, Polaris mostrará un número en el rango de 1 a 100 — puntuación (es decir, la calificación):

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

Cuanto más cerca esté la calificación de 100, mayor será el grado de conformidad. Si verificas el código de salida del comando polaris audit, verás que es igual a 0.

Forzar polaris audit la salida con un código distinto de cero se puede hacer con dos banderas:

  • El flag --set-exit-code-below-score acepta como argumento un valor umbral en el rango de 1-100. En este caso, el comando finalizará con un código de salida 4 si la calificación es inferior al umbral. Esto es muy conveniente cuando tienes un valor umbral (digamos, 75) y necesitas recibir una alerta si la calificación cae por debajo.
  • El flag --set-exit-code-on-danger esto resultará en que el equipo finalice con un código 3 si alguna de las pruebas de peligro falla.

Ahora intentemos crear una prueba personalizada que verifique si la imagen proviene de un repositorio de confianza. Las pruebas personalizadas se definen en formato YAML, y la prueba en sí se describe mediante JSON Schema.

El siguiente fragmento de código YAML describe una nueva prueba llamada checkImageRepo:

checkImageRepo:
  successMessage: El registro de imágenes es válido
  failureMessage: El registro de imágenes no es válido
  category: Imágenes
  target: Contenedor
  schema:
    '$schema': http://json-schema.org/draft-07/schema
    type: object
    properties:
      image:
        type: string
        pattern: ^my-company.com/.+$

Veámoslo más de cerca:

  • successMessage — esta línea se mostrará si la prueba se completa con éxito;
  • failureMessage — este mensaje se mostrará en caso de falla;
  • category — indica una de las categorías: Imágenes, Chequeos de Salud, Seguridad, Redes y Recursos;
  • target— determina a qué tipo de objeto (spec) se aplica la prueba. Posibles valores: Contenedor, Pod o Controlador;
  • La prueba se define en el objeto schema usando el esquema JSON. En esta prueba, la palabra clave pattern se utiliza para comparar la fuente de la imagen con la requerida.

Para ejecutar la prueba anterior es necesario crear la siguiente configuración de Polaris:

checks:
  checkImageRepo: danger
customChecks:
  checkImageRepo:
    successMessage: El registro de imágenes es válido
    failureMessage: El registro de imágenes no es válido
    category: Imágenes
    target: Contenedor
    schema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(polaris-conf.yaml)

Analicemos el archivo:

  • En el campo checks definen las pruebas y su nivel de criticidad. Dado que es deseable recibir una advertencia cuando la imagen se obtiene de una fuente no confiable, establecemos aquí el nivel peligro.
  • La prueba misma checkImageRepo luego se define en el objeto customChecks.

Guarde el archivo como custom_check.yaml. Ahora se puede ejecutar polaris audit con un manifiesto YAML que requiere verificación.

Probemos nuestro manifiesto base-valid.yaml:

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

Comando polaris audit ejecutó solo la prueba personalizada definida anteriormente y no tuvo éxito.

Si se corrige la imagen a my-company.com/http-echo:1.0, Polaris completará con éxito. El manifiesto con los cambios ya está en el repositorio, así que puedes verificar el comando anterior en el manifiesto. image-valid-mycompany.yaml.

Ahora surge la pregunta: ¿cómo ejecutar pruebas integradas junto con las personalizadas? ¡Fácil! Simplemente agregue los identificadores de las pruebas integradas al archivo de configuración. Como resultado, tendrá el siguiente aspecto:

verificaciones:
  cpuRequestsMissing: advertencia
  cpuLimitsMissing: advertencia
  # Otras verificaciones integradas..
  # ..
  # verificaciones personalizadas
  checkImageRepo: peligro # !!!
verificacionesPersonalizadas:
  checkImageRepo:        # !!!
    successMessage: El registro de imágenes es válido
    failureMessage: El registro de imágenes no es válido
    category: Imágenes
    target: Contenedor
    esquema:
      '$schema': http://json-schema.org/draft-07/schema
      type: object
      properties:
        image:
          type: string
          pattern: ^my-company.com/.+$

(config_with_custom_check.yaml)

Ejemplo de archivo de configuración completo disponible aquí.

Verificar el manifiesto base-valid.yaml, utilizando pruebas integradas y personalizadas, se puede realizar con el comando:

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

Polaris complementa las pruebas integradas con pruebas personalizadas, combinando lo mejor de ambos mundos.

Por otro lado, la incapacidad de utilizar lenguajes más potentes, como Rego o JavaScript, puede convertirse en un factor limitante que impida la creación de pruebas más sofisticadas.

Más información sobre Polaris está disponible en el sitio del proyecto.

Currículum

Aunque existen muchas herramientas para verificar y evaluar archivos YAML de Kubernetes, es importante tener una comprensión clara de cómo se diseñarán y ejecutarán las pruebas.

Por ejemplo, si se toman los manifiestos de Kubernetes que pasan por el pipeline, kubeval podría ser el primer paso en dicho pipeline. Se encargaría de verificar si las definiciones de objetos cumplen con el esquema de la API de Kubernetes.

Una vez completada una verificación de este tipo, se podría avanzar hacia pruebas más sofisticadas, como la conformidad con las mejores prácticas estándar y políticas específicas. Aquí es donde serían útiles kube-score y Polaris.

Para aquellos con requisitos complejos y la necesidad de personalizar las pruebas, serían útiles copper, config-lint y conftest.

Conftest y config-lint utilizan YAML para definir pruebas personalizadas, mientras que copper proporciona acceso a un lenguaje de programación completo, lo que lo convierte en una opción bastante atractiva.

Por otro lado, ¿deberíamos usar una de estas herramientas y, por lo tanto, crear todas las pruebas manualmente, o preferir Polaris y agregarle solo lo necesario? No hay una respuesta definitiva a esta pregunta.

La tabla a continuación contiene una breve descripción de cada herramienta:

Herramienta
Propósito
Desventajas
Pruebas personalizadas

kubeval
Verifica los manifiestos YAML para la conformidad con una versión específica del esquema de la API
No puede trabajar con CRD
No

kube-score
Analiza los manifiestos YAML para verificar su conformidad con las mejores prácticas
No se puede seleccionar su versión de la API de Kubernetes para verificar recursos
No

copper
Un marco general para crear pruebas JavaScript personalizadas para manifiestos YAML
No hay pruebas integradas. Documentación escasa
Sí

config-lint
Un marco general para crear pruebas en un lenguaje orientado a objetos, integrado en YAML. Soporta varios formatos de configuración (por ejemplo, Terraform)
No hay pruebas predefinidas. Las aserciones y funciones integradas pueden ser insuficientes
Sí

conftest
Marco para crear pruebas personalizadas en Rego (un lenguaje de consultas especializado). Permite compartir políticas a través de paquetes OCI
No hay pruebas integradas. Es necesario aprender Rego. Docker Hub no está soportado para la publicación de políticas
Sí

Polaris
Analiza manifiestos YAML para asegurar el cumplimiento de las mejores prácticas estándar. Permite crear pruebas personalizadas usando JSON Schema
Las capacidades de las pruebas basadas en JSON Schema pueden ser insuficientes
Sí

Dado que estas herramientas no dependen del acceso al clúster de Kubernetes, son fáciles de instalar. Permiten filtrar archivos fuente y brindan una retroalimentación rápida a los autores de solicitudes de extracción en proyectos.

P.D. del traductor

También puedes leer en nuestro blog:

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster