Muta recursos

En esta página, se muestra cómo mutar recursos mediante el controlador de políticas.

Esto sirve para configurar valores predeterminados. Por ejemplo, es posible que desees insertar una etiqueta para todos los recursos en un espacio de nombres específico o que el imagePullPolicy de un Pod se establezca de forma predeterminada en Always si aún no está configurado.

Antes de comenzar

Se debe habilitar la mutación mediante la configuración de spec.policyController.mutation.enabled en true en el recurso config-management:

apiVersion: configmanagement.gke.io/v1
kind: ConfigManagement
metadata:
  name: config-management
spec:
  policyController:
    enabled: true
    mutation:
      enabled: true

Si usas el comando de la herramienta gcloud, debes usar la versión Alfa para habilitar la mutación como se muestra en el siguiente ejemplo:

  # apply-spec.yaml

  applySpecVersion: 1
  spec:
    policyController:
      enabled: true
      mutationEnabled: true

Después de crear el archivo apply-spec.yaml, ejecuta el siguiente comando para aplicar la configuración:

  gcloud alpha container hub config-management apply \
      --membership=MEMBERSHIP_NAME \
      --config=CONFIG_YAML_PATH \
      --project=PROJECT_ID

Reemplaza lo siguiente:

  • MEMBERSHIP_NAME: El nombre de la membresía del clúster registrado que tiene la configuración del controlador de políticas que deseas usar
  • CONFIG_YAML_PATH: Es la ruta de acceso al archivo apply-spec.yaml
  • PROJECT_ID: El ID de tu proyecto

Definiciones

  • mutator: un recurso de Kubernetes que ayuda a configurar el comportamiento de mutación del controlador de políticas
  • system: Una disposición de varios mutadores

Ejemplo

Antes de examinar los detalles, este es un ejemplo de un mutador que establece el imagePullPolicy para todos los contenedores en todos los Pods en Always:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"

Desglose

Existen los campos de metadatos estándar de Kubernetes (apiVersion, kind, metadata.name), pero spec es el lugar en el que se configura el comportamiento del mutador.

spec.applyTo vincula el mutador a los recursos especificados. Debido a que cambiamos campos específicos en un objeto, definimos implícitamente el esquema de ese objeto. Por ejemplo, el mutador actual no tendría sentido si se aplicara a un recurso Namespace. Debido a esto, este campo es obligatorio para que el controlador de políticas sepa para qué esquemas es relevante este mutador. Los GroupVersionKinds que faltan de esta lista no se mutan.

spec.location nos dice qué campo modificar. En este caso, usamos un glob (*) para indicar que queremos modificar todas las entradas de la lista de contenedores. Ten en cuenta que los únicos tipos de listas que el campo location puede desviar son listas de tipos de mapa y se debe especificar el campo de clave para el mapa. Las listas de tipos de mapas son una construcción de Kubernetes. Encontrarás más detalles al respecto en la documentación de Kubernetes.

spec.parameters.assign.value es el valor que se asignará a location. Este campo no tiene tipo y puede tomar cualquier valor, aunque ten en cuenta que Kubernetes aún valida la solicitud después de la mutación, por lo que insertar valores con un esquema incorrecto para el objeto que se modifica da como resultado el rechazo de la solicitud.

Flujo de ejecución

Quizás el concepto más importante para comprender sobre los webhooks de mutación de Kubernetes es su política de revocación, porque el resultado de un mutador puede cambiar el comportamiento de otro mutador. Por ejemplo, si agregas un contenedor de archivo adicional nuevo, un mutador que establece la política de extracción de imágenes para todos los contenedores ahora tiene un contenedor nuevo que puede mutar.

En la práctica, esto significa que cualquier webhook de mutación del controlador de políticas de solicitudes podría llamarse más de una vez.

Para reducir la latencia, el controlador de políticas se vuelve a invocar a sí mismo para evitar solicitudes HTTP adicionales. Esto significa que la política de inserción de archivo adicional y de extracción de imagen tienen el resultado esperado.

La rutina de mutación del controlador de políticas continuará revocándose hasta que el recurso “converge”, lo que significa que las iteraciones adicionales no tengan más efectos.

Sintaxis de la ubicación

La sintaxis de la ubicación usa el descriptor de acceso de puntos (.) para desviar campos. En el caso de listas con clave, los usuarios pueden hacer referencia a objetos individuales en una lista con la sintaxis [<key>: <value>] o a todos los objetos en la lista con [<key>: *].

Los valores y los campos se pueden entrecomillar entre comillas simples (') o dobles ("). Esto es necesario cuando tienen caracteres especiales, como puntos o espacios.

En los valores entre comillas, se pueden marcar los caracteres especiales con el prefijo \. "Use \" to escape and \\\" to escape" se convierte en Use " to escape and \" to escape.

Estos son algunos ejemplos del recurso v1/Pod:

  • spec.priority hace referencia a spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly hace referencia al campo readOnly de la activación /my/mount del contenedor foo.
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly hace referencia al campo readOnly de la activación /my/mount de todos los contenedores.

Si haces referencia a una ubicación que no existe en la actualidad en un recurso, esa ubicación se crea de forma predeterminada. Este comportamiento se puede configurar mediante pruebas de ruta de acceso.

Pruebas de ruta de acceso

¿Cómo podemos realizar la configuración predeterminada, en la que evitamos modificar un valor que ya existe? Tal vez queremos configurar /secure-mount en solo lectura para todos los contenedores, pero no queremos crear un /secure-mount si aún no existe uno. Podemos realizar cualquiera de estas acciones a través de pruebas de ruta.

En este ejemplo, se evita mutar imagePullPolicy si ya está configurado:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"

Este es otro ejemplo que evita crear un contenedor sidecar vacío si aún no existe:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: 'spec.containers[name: "sidecar"].imagePullPolicy'
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]'
      condition: "MustExist"

Se pueden especificar varias pruebas de ruta de acceso, si es necesario.

subPath debe ser un prefijo de (o igual a) location.

Los únicos valores válidos para condition son MustExist y MustNotExist.

Pruebas de valor

Si solo deseas mutar valores que cumplan ciertas condiciones, es posible hacerlo con el campo spec.parameters.assignIf.

Por ejemplo, si solo deseas mutar el contexto de seguridad de un pod si configura de forma explícita el pod para que se ejecute como raíz, deberías hacer lo siguiente:

# set-non-rootyaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.securityContext.runAsUser"
  parameters:
    assign:
      value: 1111
    assignIf:
      in: [0]

assignIf tiene dos tipos de pruebas in y notIn, las cuales toman una lista de valores sin tipo. Si el valor original está en la lista de valores (o no para notIn), el valor se muta.

Coincidencia

Los mutadores también permiten la coincidencia mediante el uso de los mismos criterios que las restricciones.

Mutadores

En la actualidad, existen dos tipos de mutadores: Assign y AssignMetadata.

Asignar

Assign puede cambiar cualquier valor fuera del campo metadata de un recurso. Debido a que todos GroupVersionKinds tienen un esquema único, deben estar vinculados a un conjunto de GroupVersionKinds en particular.

Tiene el siguiente esquema:

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    kinds: # redundant because of `applyTo`, but left in for consistency
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]' # must be a prefix of `location`
      condition: "MustExist" # or "MustNotExist"
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"
    assign:
      value: "Always" # any type can go here, not just a string
    assignIf:
      in: ["mutate-if-in-this-list"]
      notIn: ["don't-mutate-if-in-this-list"]

AssignMetadata

AssignMetadata puede agregar nuevas etiquetas de metadatos. No puede cambiar el valor de las etiquetas de metadatos existentes. De lo contrario, podría escribirse un sistema de mutadores que se repetiría de forma indefinida, lo que provocaría que se agotara el tiempo de espera de las solicitudes.

Debido a que todos los recursos comparten el mismo esquema metadata, no es necesario especificar a qué recurso se aplica un AssignMetadata.

Además, debido a que AssignMetadata no puede hacer tanto, su esquema es un poco más simple.

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: AssignMetadata
metadata:
  name: set-team-name
spec:
  match:
    kinds:
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "metadata.labels.team" # must start with `metadata.labels`
  parameters:
    assign:
      value: "Always" # any type can go here, not just a string

Prácticas recomendadas

Advertencias de Kubernetes

En la documentación de Kubernetes, se enumeran algunas consideraciones importantes sobre el uso de los webhooks de mutación. Debido a que el controlador de políticas funciona como un webhook de admisión de Kubernetes, ese consejo se aplica aquí.

La sintaxis de mutación del controlador de políticas está diseñada para facilitar el cumplimiento de problemas operativos en torno a los webhooks de mutación, incluida la idempotencia.

Escribe mutadores

Atomicidad

Se recomienda que cada mutador sea lo suficientemente autosuficiente como sea posible. Debido a que Kubernetes tiene coherencia eventual, un mutador no debería confiar en que un segundo mutador se haya reconocido para que haga su trabajo de forma correcta. Por ejemplo, cuando agregas un archivo adicional, agrégalo completo, no lo construyas a través de varios mutadores.

Validación

Si hay una condición que deseas aplicar, es una buena idea que el mutador tenga una restricción coincidente. Esto ayuda a garantizar que se rechacen las solicitudes de incumplimiento y se detecten los incumplimientos preexistentes en la auditoría.

Recuperación de emergencia

La mutación se implementa como un webhook de mutación de Kubernetes. Se puede detener de la misma manera que el webhook de validación, pero el recurso relevante es un MutatingWebhookConfiguration llamado gatekeeper-mutating-webhook-configuration.

Recursos adicionales

La documentación de código abierto para la mutación de Gatekeeper tiene más explicaciones y ejemplos.