# 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](/build/policies/intro/): 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:

```dockerfile
# 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:

```console
$ 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:

```rego {title="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 {title="Dockerfile"}
FROM scratch
ADD https://github.com/moby/buildkit.git#v0.26.1 /
```

```console
$ 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:

```rego
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:

```rego
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:

```rego
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):

```rego
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:

```rego
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:

```rego
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):

```rego
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):

```rego
# 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:

```rego
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 == "trusted@example.com"
}

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:

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

Fijar a un SHA de commit específico:

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

## Requerir commits firmados

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

```rego
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:

```rego
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:

```rego
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:

```rego
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:
   ```console
   $ 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](/build/policies/built-ins/) 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:

```rego
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:

```console
$ docker buildx build --target=dev .
```

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

```console
$ docker buildx build --target=release .
```

## Siguientes pasos

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

- Explora [Ejemplos de políticas](/build/policies/examples/) para conocer patrones de políticas
  completos
- Lee las [Funciones integradas](/build/policies/built-ins/) para conocer las funciones de
  verificación de firmas de Git
- Consulta la [Referencia de inputs](/build/policies/inputs/) para conocer todos los campos
  de Git disponibles

