# Validating image inputs


Las imágenes de contenedor son las entradas de compilación más comunes. Cada
instrucción `FROM` descarga una imagen, y las referencias `COPY --from` descargan
imágenes adicionales. Validar estas imágenes protege la cadena de suministro de
tu compilación frente a registros comprometidos, actualizaciones inesperadas e
imágenes base no autorizadas.

Esta guía te enseña a escribir políticas que validen las entradas de imágenes,
avanzando desde la lista de permitidos básica hasta las comprobaciones de
atestaciones avanzadas.

## 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 imágenes?

Las entradas de imágenes provienen de dos instrucciones de Dockerfile:

```dockerfile
# Instrucciones FROM
FROM alpine:3.22
FROM golang:1.25-alpine AS builder

# Referencias COPY --from
COPY --from=builder /app /app
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
```

Cada una de estas referencias desencadena la evaluación de una política. Tu
política puede inspeccionar los metadatos de la imagen, verificar las atestaciones
y aplicar restricciones antes de que continúe la compilación.

## Lista de permitidos para repositorios específicos

La política de imágenes más simple restringe qué repositorios se pueden usar. Esto
evita que los desarrolladores utilicen imágenes arbitrarias que no hayan sido
revisadas.

Crea una política que solo permita Alpine:

```rego {title="Dockerfile.rego"}
package docker

default allow := false

allow if input.local

allow if {
  input.image.repo == "alpine"
}

decision := {"allow": allow}
```

Esta política:

- Deniega todas las entradas por defecto
- Permite el contexto de compilación local
- Permite cualquier imagen del repositorio `alpine` (cualquier etiqueta o digest)

Pruébalo con un Dockerfile:

```dockerfile {title="Dockerfile"}
FROM alpine
RUN echo "hello"
```

```console
$ docker build .
```

La compilación se realiza con éxito. Intenta cambiar a `FROM ubuntu`:

```console
$ docker build .
```

La compilación falla porque `ubuntu` no coincide con el repositorio permitido.

## Comparar versiones semánticas

Restringe las imágenes a rangos de versiones específicos utilizando las funciones
`semver` de Rego:

```rego
package docker

default allow := false

allow if input.local

# Permitir Go 1.21 o posterior
allow if {
  input.image.repo == "golang"
  semver.is_valid(input.image.tag)
  semver.compare(input.image.tag, "1.21.0") >= 0
}

decision := {"allow": allow}
```

La función `semver.compare(a, b)` compara versiones semánticas y devuelve:

- `-1` si la versión `a` es menor que `b`
- `0` si las versiones son iguales
- `1` si la versión `a` es mayor que `b`

Utiliza `semver.is_valid()` para comprobar si una etiqueta es una versión
semántica válida antes de compararla.

Restringir a rangos de versiones específicos:

```rego
allow if {
  input.image.repo == "node"
  version := input.image.tag
  semver.is_valid(version)
  semver.compare(version, "20.0.0") >= 0  # 20.0.0 o posterior
  semver.compare(version, "21.0.0") < 0   # anterior a 21.0.0
}
```

Esto permite únicamente las versiones de Node.js 20.x. El patrón funciona para
cualquier imagen que utilice versionado semántico.

Estas funciones `semver` son funciones integradas estándar de Rego documentadas en
la [referencia de políticas de OPA](https://www.openpolicyagent.org/docs/latest/policy-reference/#semver).

## Requerir referencias por digest

Las etiquetas como `alpine:3.22` pueden cambiar (alguien podría subir una nueva
imagen con la misma etiqueta). Los digests como `alpine@sha256:abc123...` son
inmutables.

### Requerir que los usuarios proporcionen digests

Puedes exigir que los usuarios especifiquen siempre los digests en sus
Dockerfiles:

```rego
package docker

default allow := false

allow if input.local

allow if {
  input.image.isCanonical
}

decision := {"allow": allow}
```

El campo `isCanonical` es `true` cuando la referencia del usuario incluye un
digest. Esta política permitiría:

```dockerfile
FROM alpine@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
```

Pero rechazaría las referencias basadas únicamente en etiquetas como
`FROM alpine:3.22`.

### Fijar a digests específicos

Alternativamente (o además), puedes validar que el digest real de una imagen
coincida con un valor específico, independientemente de cómo haya escrito el
usuario la referencia:

```rego
allow if {
  input.image.repo == "alpine"
  input.image.checksum == "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}

decision := {"allow": allow}
```

Esto comprueba el digest de contenido real de la imagen descargada. Permitiría
ambos casos:

```dockerfile
FROM alpine:3.22
FROM alpine@sha256:4b7ce...
```

Siempre y cuando la imagen resuelta tenga el digest especificado. Esto es útil
para fijar imágenes base críticas a versiones conocidas como seguras.

## Restringir registros

Controla desde qué registros pueden realizar descargas tus compilaciones. Esto
ayuda a aplicar las políticas corporativas o a restringir las descargas a fuentes
de confianza.

```rego
package docker

default allow := false

allow if input.local

# Permitir imágenes de Docker Hub
allow if {
  input.image.host == "docker.io"  # Docker Hub
  input.image.repo == "alpine"
}

# Permitir imágenes de un registro interno
allow if {
  input.image.host == "registry.company.com"
}

decision := {"allow": allow}
```

El campo `host` contiene el nombre de host del registro. Las imágenes de Docker
Hub usan `"docker.io"` como valor de host. Prueba con:

```dockerfile
FROM alpine                                    # Permitido (Docker Hub)
FROM registry.company.com/myapp:latest         # Permitido (registro de la empresa)
FROM ghcr.io/someorg/image:latest              # Denegado (registro incorrecto)
```

Utiliza `fullRepo` cuando necesites la ruta completa, incluido el registro:

```rego
allow if {
  input.image.fullRepo == "docker.io/library/alpine"
}
```

## Validar restricciones de plataforma

Las imágenes multiarquitectura admiten diferentes sistemas operativos y
arquitecturas de CPU. Puedes restringir las compilaciones a plataformas
específicas:

```rego
package docker

default allow := false

allow if input.local

allow if {
  input.image.os == "linux"
  input.image.arch in ["amd64", "arm64"]
}

decision := {"allow": allow}
```

Esta política:

- Define las arquitecturas compatibles en una lista
- Comprueba que `input.image.os` coincida con Linux
- Verifica que `input.image.arch` se encuentre en la lista de compatibles

Los campos `os` y `arch` provienen del manifiesto de la imagen, reflejando la
plataforma real de la imagen. Esto funciona con la selección automática de
plataformas de Docker: las políticas validan lo que Buildx resuelve, no lo que
especificas.

## Inspeccionar metadatos de imágenes

Las imágenes contienen metadatos como variables de entorno, etiquetas y directorios
de trabajo. Puedes validarlos para asegurarte de que las imágenes cumplen con los
requisitos.

Comprobar variables de entorno específicas:

```rego
package docker

default allow := false

allow if input.local

allow if {
  input.image.repo == "golang"
  input.image.workingDir == "/go"
  some ver in input.image.env
  startswith(ver, "GOLANG_VERSION=")
  some toolchain in input.image.env
  toolchain == "GOTOOLCHAIN=local"
}

decision := {"allow": allow}
```

Esta política valida la imagen oficial de Go comprobando que:

- El directorio de trabajo es `/go`
- El entorno tiene configurado `GOLANG_VERSION`
- El entorno incluye `GOTOOLCHAIN=local`

El campo `input.image.env` es un array de cadenas en formato `CLAVE=VALOR`.
Utiliza la iteración `some` de Rego para buscar en el array.

Comprobar etiquetas de imagen:

```rego
allow if {
  input.image.labels["org.opencontainers.image.vendor"] == "Example Corp"
  input.image.labels["org.opencontainers.image.version"] != ""
}
```

El campo `labels` es un mapa, por lo que accedes a los valores utilizando la
notación de corchetes.

## Requerir atestaciones y procedencia

Las imágenes modernas incluyen [atestaciones](/build/metadata/attestations/):
metadatos legibles por máquina sobre cómo se compiló la imagen. Las atestaciones de
[procedencia (provenance)](/build/metadata/attestations/slsa-provenance/) describen el
proceso de compilación, y las de [SBOM](/build/metadata/attestations/sbom/) enumeran el
software interno.

Requerir procedencia:

```rego
package docker

default allow := false

allow if input.local

allow if {
  input.image.hasProvenance
}

decision := {"allow": allow}
```

El campo `hasProvenance` es `true` cuando la imagen tiene atestaciones de
procedencia o SBOM [attestations](/build/metadata/attestations/).

## Verificar firmas de GitHub Actions

Para imágenes compiladas con GitHub Actions, verifica que provengan de flujos de
trabajo de confianza inspeccionando los metadatos de las firmas:

```rego
allow if {
  input.image.repo == "myapp"
  input.image.hasProvenance
  some sig in input.image.signatures
  valid_github_signature(sig)
}

# Asistente para validar la firma de GitHub Actions
valid_github_signature(sig) if {
  sig.signer.certificateIssuer == "CN=sigstore-intermediate,O=sigstore.dev"
  sig.signer.issuer == "https://token.actions.githubusercontent.com"
  startswith(sig.signer.buildSignerURI, "https://github.com/myorg/")
  sig.signer.runnerEnvironment == "github-hosted"
}

decision := {"allow": allow}
```

Este patrón funciona con cualquier flujo de trabajo de GitHub Actions que utilice
firmas sin clave (keyless) de Sigstore. Los metadatos de la firma proporcionan una
prueba criptográfica del origen de la compilación. Para ver ejemplos completos de
verificación de firmas, consulta [Ejemplos de políticas](/build/policies/examples/).

## Combinar múltiples comprobaciones

Las políticas reales a menudo combinan varias comprobaciones. Múltiples
condiciones en una sola regla `allow` significan AND: todas deben ser verdaderas:

```rego
package docker

default allow := false

allow if input.local

# Las imágenes de producción necesitan todo
allow if {
  input.image.repo == "alpine"
  input.image.isCanonical
  input.image.hasProvenance
}

decision := {"allow": allow}
```

Múltiples reglas `allow` significan OR: cualquier regla puede coincidir:

```rego
package docker

default allow := false

allow if input.local

# Permitir Alpine con comprobaciones estrictas
allow if {
  input.image.repo == "alpine"
  input.image.isCanonical
}

# Permitir Go con diferentes comprobaciones
allow if {
  input.image.repo == "golang"
  input.image.workingDir == "/go"
}

decision := {"allow": allow}
```

Utiliza este patrón para aplicar diferentes requisitos a diferentes imágenes base.

## Siguientes pasos

Ahora comprendes cómo validar imágenes de contenedor en las políticas de
compilación. Para continuar aprendiendo:

- Aprende la [Validación de repositorios Git](/build/policies/validate-git/) para las entradas
  de código fuente
- Explora [Ejemplos de políticas](/build/policies/examples/) para conocer patrones de políticas
  completos
- Lee las [Funciones integradas](/build/policies/built-ins/) para la verificación de firmas y
  comprobación de atestaciones
- Consulta la [Referencia de inputs](/build/policies/inputs/) para conocer todos los campos de
  imágenes disponibles

