Compartir comentarios
Las respuestas se generan en base a la documentación.

Validación de repositorios Git

Los repositorios de Git a menudo aparecen en las compilaciones de Docker como entradas de código fuente. La instrucción ADD puede clonar repositorios y los contextos de compilación pueden hacer referencia a URLs de Git. Validar estas entradas garantiza que estás realizando compilaciones a partir de fuentes de confianza con versiones verificadas.

Esta guía te enseña a escribir políticas que valen las entradas de Git, desde la fijación básica de versiones hasta la verificación de commits y etiquetas firmados.

Requisitos previos

Debes comprender los conceptos básicos de las políticas a partir de la Introducción: la creación de archivos de políticas, la sintaxis básica de Rego y cómo se evalúan las políticas durante las compilaciones.

¿Qué son las entradas de Git?

Las entradas de Git provienen de instrucciones ADD que hacen referencia a repositorios de Git:

# Clonar una etiqueta específica
ADD https://github.com/moby/buildkit.git#v0.26.1 /buildkit

# Clonar una rama
ADD https://github.com/user/repo.git#main /src

# Clonar un commit
ADD https://github.com/user/repo.git#abcde123 /src

El contexto de compilación también puede ser un repositorio Git cuando compilas con:

$ docker build https://github.com/user/repo.git#main

Cada referencia de Git desencadena la evaluación de una política. Tu política puede inspeccionar las URLs de los repositorios, validar las versiones, comprobar los metadatos de los commits y verificar las firmas.

Coincidir con repositorios específicos

La política de Git más sencilla restringe qué repositorios se pueden utilizar:

Dockerfile.rego
package docker

default allow := false

allow if input.local

allow if {
  input.git.host == "github.com"
  input.git.remote == "https://github.com/moby/buildkit.git"
}

decision := {"allow": allow}

Esta política:

  • Deniega todas las entradas por defecto
  • Permite el contexto de compilación local
  • Permite únicamente el repositorio de BuildKit de GitHub

El campo host contiene el nombre de host del servidor de Git, y remote contiene la URL completa del repositorio. Pruébalo:

Dockerfile
FROM scratch
ADD https://github.com/moby/buildkit.git#v0.26.1 /
$ docker build .

La compilación se realiza con éxito. Intenta con un repositorio diferente y fallará.

Puedes hacer coincidir múltiples repositorios con reglas adicionales:

allow if {
  input.git.host == "github.com"
  input.git.remote == "https://github.com/moby/buildkit.git"
}

allow if {
  input.git.host == "github.com"
  input.git.remote == "https://github.com/docker/cli.git"
}

decision := {"allow": allow}

Pin a versiones específicas

Las etiquetas y las ramas pueden cambiar con el tiempo. Fíjalas a versiones específicas para asegurar compilaciones reproducibles:

package docker

default allow := false

allow if input.local

allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  input.git.tagName == "v0.26.1"
}

decision := {"allow": allow}

El campo tagName contiene el nombre de la etiqueta cuando la referencia de Git apunta a una etiqueta. Utiliza branch para las ramas:

allow if {
  input.git.remote == "https://github.com/user/repo.git"
  input.git.branch == "main"
}

O utiliza ref para cualquier tipo de referencia (rama, etiqueta o SHA de commit):

allow if {
  input.git.ref == "v0.26.1"
}

Utilizar listas de permitidos para versiones

Para repositorios en los que confías pero de los cuales deseas controlar las versiones, mantén una lista de permitidos:

package docker

default allow := false

allowed_versions = [
    {"tag": "v0.26.1", "annotated": true, "sha": "abc123"},
]

is_buildkit if {
    input.git.remote == "https://github.com/moby/buildkit.git"
}

allow if {
    not is_buildkit
}

allow if {
    is_buildkit
    some version in allowed_versions
    input.git.tagName == version.tag
    input.git.isAnnotatedTag == version.annotated
    startswith(input.git.commitChecksum, version.sha)
}

decision := {"allow": allow}

Esta política:

  • Define una lista de permitidos de versiones aprobadas con metadatos
  • Utiliza una regla asistente (is_buildkit) para mejorar la legibilidad
  • Permite todas las entradas que no sean de BuildKit
  • Para BuildKit, comprueba el nombre de la etiqueta, si es una etiqueta anotada y el SHA del commit frente a la lista de permitidos

La regla asistente hace que las políticas complejas sean más fáciles de mantener. Puedes ampliar la lista de permitidos a medida que se aprueben nuevas versiones:

allowed_versions = [
    {"tag": "v0.26.1", "annotated": true, "sha": "abc123"},
    {"tag": "v0.27.0", "annotated": true, "sha": "def456"},
    {"tag": "v0.27.1", "annotated": true, "sha": "789abc"},
]

Validar con patrones regex

Utiliza la coincidencia de patrones para el versionado semántico (semantic versioning):

package docker

default allow := false

allow if input.local

allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  regex.match(`^v[0-9]+\.[0-9]+\.[0-9]+$`, input.git.tagName)
}

decision := {"allow": allow}

Esto permite cualquier etiqueta de BuildKit que coincida con el patrón vX.Y.Z donde X, Y y Z son números. La expresión regular (regex) garantiza que estés utilizando versiones de lanzamiento y no etiquetas de pre-lanzamiento como v0.26.0-rc1.

Coincidir con versiones principales (major versions):

# Solo permitir lanzamientos v0.x
allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  regex.match(`^v0\.[0-9]+\.[0-9]+$`, input.git.tagName)
}

Inspeccionar metadatos de commits

El objeto commit proporciona información detallada sobre los commits:

package docker

default allow := false

allow if input.local

# Comprobar el autor del commit
allow if {
  input.git.remote == "https://github.com/user/repo.git"
  input.git.commit.author.email == "[email protected]"
}

decision := {"allow": allow}

El objeto commit incluye:

  • author.name: Nombre del autor
  • author.email: Correo electrónico del autor
  • author.when: Cuándo se creó el commit
  • committer.name: Nombre del committer
  • committer.email: Correo electrónico del committer
  • committer.when: Cuándo se registró (committed) el commit
  • message: Mensaje de commit

Validar mensajes de commit:

allow if {
  input.git.commit
  contains(input.git.commit.message, "Signed-off-by:")
}

Fijar a un SHA de commit específico:

allow if {
  input.git.commitChecksum == "abc123def456..."
}

Requerir commits firmados

Los commits firmados con GPG demuestran la autenticidad. Comprueba las firmas de los commits:

package docker

default allow := false

allow if input.local

allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  input.git.commit.pgpSignature != null
}

decision := {"allow": allow}

El campo pgpSignature es null para los commits no firmados. Para los commits firmados, contiene los detalles de la firma.

Las firmas SSH funcionan de manera similar:

allow if {
  input.git.commit.sshSignature != null
}

Requerir etiquetas firmadas

Las etiquetas anotadas pueden estar firmadas, lo que proporciona una garantía criptográfica de la versión:

package docker

default allow := false

allow if input.local

allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  input.git.tag.pgpSignature != null
}

decision := {"allow": allow}

El objeto tag solo está disponible para las etiquetas anotadas. Incluye:

  • tagger.name: Quién creó la etiqueta
  • tagger.email: Correo electrónico del etiquetador
  • tagger.when: Cuándo se creó la etiqueta
  • message: Mensaje de la etiqueta
  • pgpSignature: Firma GPG (si está firmada)
  • sshSignature: Firma SSH (si está firmada)

Las etiquetas ligeras (lightweight tags) no tienen un objeto tag, por lo que esta política exige de forma efectiva etiquetas anotadas y firmadas.

Verificar firmas con claves públicas

Utiliza la función verify_git_signature() para verificar criptográficamente las firmas de Git frente a claves públicas de confianza:

package docker

default allow := false

allow if input.local

allow if {
  input.git.remote == "https://github.com/moby/buildkit.git"
  input.git.tagName != ""
  verify_git_signature(input.git.tag, "keys.asc")
}

decision := {"allow": allow}

Esto verifica que las etiquetas de Git estén firmadas por claves contenidas en el archivo de claves públicas keys.asc. Para configurar esto:

  1. Exporta las claves públicas de los mantenedores:
    $ curl https://github.com/user.gpg > keys.asc
    
  2. Coloca keys.asc junto a tu archivo de política.

La función verifica las firmas PGP en commits o etiquetas. Consulta las Funciones integradas para más detalles.

Aplicar reglas condicionales

Utiliza diferentes reglas para diferentes contextos. Permite referencias no firmadas durante el desarrollo pero exige firmas para producción:

package docker

default allow := false

allow if input.local

is_buildkit if {
    input.git.remote == "https://github.com/moby/buildkit.git"
}

is_version_tag if {
    is_buildkit
    regex.match(`^v[0-9]+\.[0-9]+\.[0-9]+$`, input.git.tagName)
}

# Las etiquetas de versión deben estar firmadas
allow if {
    is_version_tag
    input.git.tagName != ""
    verify_git_signature(input.git.tag, "keys.asc")
}

# Referencias sin versión permitidas en desarrollo
allow if {
    is_buildkit
    not is_version_tag
    input.env.target != "release"
}

decision := {"allow": allow}

Esta política:

  • Define reglas asistentes para mejorar la legibilidad
  • Requiere etiquetas de versión firmadas por los mantenedores
  • Permite referencias no firmadas (ramas, commits) a menos que se esté compilando el target de lanzamiento (release)
  • Utiliza input.env.target para detectar el target de compilación

Compila un target de desarrollo sin firmas:

$ docker buildx build --target=dev .

Compila el target de lanzamiento, y se exigirá la firma:

$ docker buildx build --target=release .

Siguientes pasos

Ahora comprendes cómo validar repositorios Git en las políticas de compilación. Para continuar aprendiendo: