Merge pull request #23782 from dvdksn/build-input-policy

build: rego source policies
This commit is contained in:
David Karlsson
2026-01-28 10:48:41 +01:00
committed by GitHub
15 changed files with 3627 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
(?i)[A-Z]{2,}'?s
jq
untracked
ripgrep
exfiltration
sandboxing
@@ -13,6 +14,7 @@ armhf
[Aa]uditability
auditable
autolock
[Aa]llowlist(ing)?
Azure
Azure AD
bootup
@@ -172,6 +174,7 @@ Qualcomm
Quickview
rebalance
reimplement
Rego
Rekor
ROCm
rollback
@@ -183,6 +186,7 @@ scrollable
SELinux
Slack
snapshotters?
Sigstore
Snyk
Solr
SonarQube

View File

@@ -1,12 +1,7 @@
---
title: Checking your build configuration
linkTitle: Build checks
params:
sidebar:
badge:
color: green
text: New
weight: 30
weight: 20
description: Learn how to use build checks to validate your build configuration.
keywords: build, buildx, buildkit, checks, validate, configuration, lint
---

View File

@@ -0,0 +1,167 @@
---
title: Validating build inputs with policies
linkTitle: Validating builds
description: Secure your Docker builds by validating images, Git repositories, and dependencies with build policies
keywords: build policies, opa, rego, docker security, supply chain, attestations
weight: 70
params:
sidebar:
badge:
color: blue
text: Experimental
---
Building with Docker often involves downloading remote resources. These
external dependencies, such as Docker images, Git repositories, remote files,
and other artifacts, are called build inputs.
For example:
- Pulling images from a registry
- Cloning a source code repository
- Fetching files from a server over HTTPS
When consuming build inputs, it's a good idea to verify the contents are what
you expect them to be. One way to do this is to use the `--checksum` option for
the `ADD` Dockerfile instruction. This lets you verify the SHA256 checksum of a
remote resource when pulling it into a build:
```dockerfile
ADD --checksum=sha256:c0ff3312345… https://example.com/archive.tar.gz /
```
If the remote `archive.tar.gz` file does not match the checksum that the
Dockerfile expects, the build fails.
Checksums verify that content matches what you expect, but only for the `ADD`
instruction. They don't tell you anything about where the content came from or
how it was produced. You can't use checksums to enforce constraints like
"images must be signed" or "dependencies must come from approved sources."
Build policies solve this problem. They let you define rules that validate all
your build inputs, enforcing requirements like provenance attestations,
approved registries, and signed Git tags across your entire build process.
## Prerequisites
Build policies is currently an experimental feature. To try it out, you'll
need:
- Buildx 0.31.0 or later - Check your version: `docker buildx version`
- BuildKit 0.27.0 or later - Verify with: `docker buildx inspect --bootstrap`
If you're using Docker Desktop, ensure you're on a version that includes these
updates.
## Build policies
Buildx version 0.31.0 added support for build policies. Build policies are
rules for securing your Docker build supply chain, and help protect against
upstream compromises, malicious dependencies, and unauthorized modifications to
your build inputs.
Build policies let you enforce extended verifications on inputs used to build
your projects, such as:
- Docker images must use digest references (not tags alone)
- Images must have provenance attestations and cosign signatures
- Git tags are signed by maintainers with a PGP public key
- All remote artifacts must use HTTPS and include a checksum for verification
Build policies are defined in a declarative policy language, called Rego,
created for the [Open Policy Agent (OPA)](https://www.openpolicyagent.org/).
The following example shows a minimal build policy in Rego.
```rego {title="Dockerfile.rego"}
package docker
default allow := false
# Allow any local inputs for this build
# For example: a local build context, or a local Dockerfile
allow if input.local
# Allow images, but only if they have provenance attestations
allow if {
input.image.hasProvenance
}
decision := {"allow": allow}
```
If the Dockerfile associated with this policy references an image with no
provenance attestation in a `FROM` instruction, the policy would be violated
and the build would fail.
## How policies work
When you run `docker buildx build`, Buildx:
1. Resolves all build inputs (images, Git repos, HTTP downloads)
2. Looks for a policy file matching your Dockerfile name (e.g.,
`Dockerfile.rego`)
3. Evaluates each input against the policy before the build starts
4. Allows the build to proceed only if all inputs pass the policy
Policies are written in Rego (Open Policy Agent's policy language). You don't
need to be a Rego expert - the [Introduction](./intro.md) tutorial teaches you
everything needed.
Policy files live alongside your Dockerfile:
```text
project/
├── Dockerfile
├── Dockerfile.rego
└── src/
```
No additional configuration is needed - Buildx automatically finds and loads
the policy when you build.
## Use cases
Build policies help you enforce security and compliance requirements on your
Docker builds. Common scenarios where policies provide value:
### Enforce base image standards
Require all production Dockerfiles to use specific, approved base images with
digest references. Prevent developers from using arbitrary images that haven't
been vetted by your security team.
### Validate third-party dependencies
When your build downloads files, libraries, or tools from the internet, verify
they come from trusted sources and match expected checksums or signatures. This
protects against supply chain attacks where an upstream dependency is
compromised.
### Ensure signed releases
Require that all dependencies have valid signatures from trusted parties.
- Check GPG signatures for Git repositories you clone in your builds
- Verify provenance attestation signatures with Sigstore
### Meet compliance requirements
Some regulatory frameworks require evidence that you validate your build
inputs. Build policies give you an auditable, declarative way to demonstrate
you're checking dependencies against security standards.
### Separate development and production rules
Apply stricter validation for production builds while allowing more flexibility
during development. The same policy file can contain conditional rules based on
build context or target.
## Get started
Ready to start writing policies? The [Introduction](./intro.md) tutorial walks
you through creating your first policy and teaches the Rego basics you need.
For practical usage guidance, see [Using build policies](./usage.md).
For practical examples you can copy and adapt, see the [Example
policies](./examples.md) library.

View File

@@ -0,0 +1,205 @@
---
title: Built-in functions
linkTitle: Built-in functions
description: Buildx includes built-in helper functions to make writing policies easier
keywords: build policies, built-in functions, rego functions, signature verification, policy helpers
weight: 90
---
Buildx provides built-in functions, in addition to the [Rego
built-ins](#rego-built-in-functions), to extend Rego policies with
Docker-specific operations like loading local files, verifying Git signatures,
and pinning image digests.
## Rego built-in functions
The functions [documented on this page](#buildx-built-in-functions) are
Buildx-specific functions, distinct from [Rego's standard built-in
functions](https://www.openpolicyagent.org/docs/policy-language#built-in-functions)
Buildx also supports standard Rego built-in functions, but only a subset. To
see the exact list of supported functions, refer to the Buildx [source
code](https://github.com/docker/buildx/blob/master/policy/builtins.go).
## Buildx built-in functions
Buildx provides the following custom built-in functions for policy development:
- [`print`](#print)
- [`load_json`](#load_json)
- [`verify_git_signature`](#verify_git_signature)
- [`pin_image`](#pin_image)
### `print`
Outputs debug information during policy evaluation.
Parameters:
- Any number of values to print
Returns: The values (pass-through)
Example:
```rego
allow if {
input.image.repo == "alpine"
print("Allowing alpine image:", input.image.tag)
}
```
Debug output appears when building with `--progress=plain`.
### `load_json`
Loads and parses JSON data from local files in the build context.
Parameters:
- `filename` (string) - Path to JSON file relative to policy directory
Returns: Parsed JSON data as Rego value
Example:
```rego
# Load approved versions from external file
approved_versions = load_json("versions.json")
allow if {
input.image.repo == "alpine"
some version in approved_versions.alpine
input.image.tag == version
}
```
File structure:
```text
project/
├── Dockerfile
├── Dockerfile.rego
└── versions.json
```
versions.json:
```json
{
"alpine": ["3.19", "3.20"],
"golang": ["1.21", "1.22"]
}
```
The JSON file must be in the same directory as the policy or in a
subdirectory accessible from the policy location.
### `verify_git_signature`
Verifies PGP signatures on Git commits or tags.
Parameters:
- `git_object` (object) - Either `input.git.commit` or `input.git.tag`
- `keyfile` (string) - Path to PGP public key file (relative to policy
directory)
Returns: Boolean - `true` if signature is valid, `false` otherwise
Example:
```rego
# Require signed Git tags
allow if {
input.git.tagName != ""
verify_git_signature(input.git.tag, "maintainer.asc")
}
# Require signed commits
allow if {
input.git.commit
verify_git_signature(input.git.commit, "keys/team.asc")
}
```
Directory structure:
```text
project/
├── Dockerfile.rego
└── maintainer.asc # PGP public key
```
Or with subdirectory:
```text
project/
├── Dockerfile.rego
└── keys/
├── maintainer.asc
└── team.asc
```
Obtaining public keys:
```console
$ gpg --export --armor user@example.com > maintainer.asc
```
### `pin_image`
Pins an image to a specific digest, overriding the tag-based reference. Use
this to force builds to use specific image versions.
Parameters:
- `image_object` (object) - Must be `input.image` (the current image being
evaluated)
- `digest` (string) - Target digest in format `sha256:...`
Returns: Boolean - `true` if pinning succeeds
Example:
```rego
# Pin alpine 3.19 to specific digest
alpine_3_19_digest = "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
allow if {
input.image.repo == "alpine"
input.image.tag == "3.19"
pin_image(input.image, alpine_3_19_digest)
}
```
Automatic digest replacement:
```rego
# Replace old digests with patched versions
replace_map = {
"3.22.0": "3.22.2",
"3.22.1": "3.22.2",
}
alpine_digests = {
"3.22.0": "sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be023728e11715",
"3.22.2": "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412",
}
allow if {
input.image.repo == "alpine"
some old_version, new_version in replace_map
input.image.checksum == alpine_digests[old_version]
print("Replacing", old_version, "with", new_version)
pin_image(input.image, alpine_digests[new_version])
}
```
This pattern automatically upgrades old image versions to patched releases.
## Next steps
- Browse complete examples: [Example policies](./examples.md)
- Learn policy development workflow: [Using build policies](./usage.md)
- Reference input fields: [Input reference](./inputs.md)

View File

@@ -0,0 +1,188 @@
---
title: Debugging build policies
linkTitle: Debugging
description: Debug policies during development with inspection and testing tools
keywords: build policies, debugging, policy troubleshooting, log-level, policy eval, rego debugging
weight: 70
---
When policies don't work as expected, use the tools available to inspect policy
evaluation and understand what's happening. This guide covers the debugging
techniques and common gotchas.
## Quick reference
Essential debugging commands:
```console
# See complete input data during builds (recommended)
$ docker buildx build --progress=plain --policy log-level=debug .
# See policy checks and decisions
$ docker buildx build --progress=plain .
# Explore input structure for different sources
$ docker buildx policy eval --print .
$ docker buildx policy eval --print https://github.com/org/repo.git
$ docker buildx policy eval --print docker-image://alpine:3.19
# Test if policy allows a source
$ docker buildx policy eval .
```
## Policy output with `--progress=plain`
To see policy evaluation during builds, use `--progress=plain`:
```console
$ docker buildx build --progress=plain .
```
This shows all policy checks, decisions, and `print()` output. Without
`--progress=plain`, policy evaluation is silent unless there's an error.
```plaintext
#1 loading policies Dockerfile.rego
#1 0.010 checking policy for source docker-image://alpine:3.19 (linux/arm64)
#1 0.011 Dockerfile.rego:8: image: {"ref":"alpine:3.19","repo":"alpine","tag":"3.19"}
#1 0.012 policy decision for source docker-image://alpine:3.19: ALLOW
```
If a policy denies a source, you'll see:
```text
#1 0.012 policy decision for source docker-image://nginx:latest: DENY
ERROR: source "docker-image://nginx:latest" not allowed by policy
```
## Debug logging
For detailed debugging, add `--policy log-level=debug` to see the full input
JSON, unresolved fields, and policy responses:
```console
$ docker buildx build --progress=plain --policy log-level=debug .
```
This shows significantly more information than the default level, including the
complete input structure for each source without needing `print()` statements
in your policy.
Complete input JSON:
```text
#1 0.007 policy input: {
#1 0.007 "env": {
#1 0.007 "filename": "."
#1 0.007 },
#1 0.007 "image": {
#1 0.007 "ref": "docker.io/library/alpine:3.19",
#1 0.007 "host": "docker.io",
#1 0.007 "repo": "alpine",
#1 0.007 "fullRepo": "docker.io/library/alpine",
#1 0.007 "tag": "3.19",
#1 0.007 "platform": "linux/arm64",
#1 0.007 "os": "linux",
#1 0.007 "arch": "arm64"
#1 0.007 }
#1 0.007 }
```
Unresolved fields:
```text
#1 0.007 unknowns for policy evaluation: [input.image.checksum input.image.labels input.image.user input.image.volumes input.image.workingDir input.image.env input.image.hasProvenance input.image.signatures]
```
Policy response:
```text
#1 0.008 policy response: map[allow:true]
```
This detailed output is invaluable for understanding exactly what data your
policy receives and which fields are not yet resolved. Use debug logging when
developing policies to avoid needing extensive `print()` statements.
## Conditional debugging with print()
While `--policy log-level=debug` shows all input data automatically, the
`print()` function is useful for debugging specific rule logic and conditional
flows:
```rego
allow if {
input.image
print("Checking image:", input.image.repo, "isCanonical:", input.image.isCanonical)
input.image.repo == "alpine"
input.image.isCanonical
}
```
Use `print()` to debug conditional logic within rules or track which rules are
evaluating. For general input inspection during development, use `--policy
log-level=debug` instead - it requires no policy modifications.
> [!NOTE]
> Print statements only execute when their containing rule evaluates. A rule
> like `allow if { input.image; print(...) }` only prints for image inputs,
> not for Git repos, HTTP downloads, or local files.
## Common issues
### Full repository path or repository name
Symptom: Policy checking repository names doesn't match as expected.
Cause: Docker Hub images use `input.image.repo` for the short name
(`"alpine"`) but `input.image.fullRepo` includes the full path
(`"docker.io/library/alpine"`).
Solution:
```rego
# Match just the repo name (works for Docker Hub and other registries)
allow if {
input.image
input.image.repo == "alpine"
}
# Or match the full repository path
allow if {
input.image
input.image.fullRepo == "docker.io/library/alpine"
}
```
### Policy evaluation happens multiple times
Symptom: Build output shows the same source evaluated multiple times.
Cause: BuildKit may evaluate policies at different stages (reference
resolution, actual pull) or for different platforms.
This is normal behavior. Policies should be idempotent (produce same result
each time for the same input).
### Fields missing with `policy eval --print`
Symptom: `docker buildx policy eval --print` doesn't show expected fields
like `hasProvenance`, `labels`, or `checksum`.
Cause: `--print` shows only reference information by default, without
fetching from registries.
Solution: Use `--fields` to fetch specific metadata fields:
```console
$ docker buildx policy eval --print --fields image.labels docker-image://alpine:3.19
```
See [Using build policies](./usage.md#testing-policies-with-policy-eval) for
details.
## Next steps
- See complete field reference: [Input reference](./inputs.md)
- Review example policies: [Examples](./examples.md)
- Learn policy usage patterns: [Using build policies](./usage.md)

View File

@@ -0,0 +1,585 @@
---
title: Policy templates and examples
linkTitle: Templates & examples
description: Browse policy examples from quick-start configs to production-grade security templates
keywords: build policies, policy examples, rego examples, docker security, registry allowlist, policy templates
weight: 50
---
This page provides complete, working policy examples you can copy and adapt.
The examples are organized into two sections: getting started policies for
quick adoption, and production templates for comprehensive security.
If you're new to policies, start with the tutorials:
[Introduction](./intro.md), [Image validation](./validate-images.md), and [Git
validation](./validate-git.md). Those pages teach individual techniques. This
page shows complete policies combining those techniques.
## How to use these examples
1. Copy the policy code into a `Dockerfile.rego` file next to your
Dockerfile
2. Customize any todo comments with your specific values
3. Test by running `docker build .` and verifying the policy works as
expected
4. Refine based on your team's needs
### Using examples with bake
These policies work with both `docker buildx build` and `docker buildx bake`.
For bake, place the policy alongside your Dockerfile and it loads
automatically. To use additional policies:
```hcl
target "default" {
dockerfile = "Dockerfile"
policy = ["extra.rego"]
}
```
See the [Usage guide](./usage.md) for complete bake integration details.
## Getting started
These policies work immediately with minimal or no customization. Use them to
adopt policies quickly and demonstrate value to your team.
### Development-friendly baseline
A permissive policy that allows typical development workflows while blocking
obvious security issues.
```rego
package docker
default allow := false
allow if input.local
allow if input.git
# Allow common public registries
allow if {
input.image.host == "docker.io" # Docker Hub
}
allow if {
input.image.host == "ghcr.io" # GitHub Container Registry
}
allow if {
input.image.host == "dhi.io" # Docker Hardened Images
}
# Require HTTPS for all downloads
allow if {
input.http.schema == "https"
}
decision := {"allow": allow}
```
This policy allows local and Git contexts, images from Docker Hub, GitHub
Container Registry, and [Docker Hardened Images](/dhi/), and `ADD` downloads
over HTTPS. It blocks HTTP downloads and non-standard registries.
When to use: Starting point for teams new to policies. Provides basic security
without disrupting development workflows.
### Registry allowlist
Control which registries your builds can pull images from.
```rego
package docker
default allow := false
allow if input.local
# TODO: Add your internal registry hostname
allowed_registries := ["docker.io", "ghcr.io", "dhi.io", "registry.company.com"]
allow if {
input.image.host in allowed_registries
}
# Allow mirrored DHI images from Docker Hub (DHI Enterprise users)
# TODO: Replace with your organization namespace
allow if {
input.image.host == "docker.io"
startswith(input.image.repo, "myorg/dhi-")
}
deny_msg contains msg if {
not allow
input.image
msg := sprintf("registry %s is not in the allowlist", [input.image.host])
}
decision := {"allow": allow, "deny_msg": deny_msg}
```
This policy restricts image pulls to approved registries. Customize and add
your internal registry to the list. If you have a DHI Enterprise subscription
and have mirrored Docker Hardened Images to Docker Hub, add a rule to allow
images from your organization's namespace.
When to use: Enforce corporate policies about approved image sources. Prevents
developers from using arbitrary public registries.
### Pin base images to digests
Require digest references for reproducible builds.
```rego
package docker
default allow := false
allow if input.local
# Require digest references for all images
allow if {
input.image.isCanonical
}
deny_msg contains msg if {
not allow
input.image
msg := sprintf("image %s must use digest reference (e.g., @sha256:...)", [input.image.ref])
}
decision := {"allow": allow, "deny_msg": deny_msg}
```
This policy requires images use digest references like
`alpine@sha256:abc123...` instead of tags like `alpine:3.19`. Digests are
immutable - the same digest always resolves to the same image content.
When to use: Ensure build reproducibility. Prevents builds from breaking when
upstream tags are updated. Required for compliance in some environments.
### Control external dependencies
Pin specific versions of dependencies downloaded during builds.
```rego
package docker
default allow := false
allow if input.local
# Allow any image (add restrictions as needed)
allow if input.image
# TODO: Add your allowed Git repositories and tags
allowed_repos := {
"https://github.com/moby/buildkit.git": ["v0.26.1", "v0.27.0"],
}
# Only allow Git input from allowed_repos
allow if {
some repo, versions in allowed_repos
input.git.remote == repo
input.git.tagName in versions
}
# TODO: Add your allowed downloads
allow if {
input.http.url == "https://example.com/app-v1.0.tar.gz"
}
decision := {"allow": allow}
```
This policy creates allowlists for external dependencies. Add your Git
repositories with approved version tags, and URLs.
When to use: Control which external dependencies can be used in builds.
Prevents builds from pulling arbitrary versions or unverified downloads.
## Production templates
These templates demonstrate comprehensive security patterns. They require
customization but show best practices for production environments.
### Image attestation and provenance
Require images have provenance attestations from trusted builders.
```rego
package docker
default allow := false
allow if input.local
# TODO: Add your repository names
allowed_repos := ["myorg/backend", "myorg/frontend", "myorg/worker"]
# Production images need full attestations
allow if {
some repo in allowed_repos
input.image.repo == repo
input.image.hasProvenance
some sig in input.image.signatures
trusted_github_builder(sig, repo)
}
# Helper to validate GitHub Actions build from main branch
trusted_github_builder(sig, repo) if {
sig.signer.certificateIssuer == "CN=sigstore-intermediate,O=sigstore.dev"
sig.signer.issuer == "https://token.actions.githubusercontent.com"
startswith(sig.signer.buildSignerURI, sprintf("https://github.com/myorg/%s/.github/workflows/", [repo]))
sig.signer.sourceRepositoryRef == "refs/heads/main"
sig.signer.runnerEnvironment == "github-hosted"
}
# Allow Docker Hardened Images with built-in attestations
allow if {
input.image.host == "dhi.io"
input.image.isCanonical
input.image.hasProvenance
}
# Allow official base images with digests
allow if {
input.image.repo == "alpine"
input.image.host == "docker.io"
input.image.isCanonical
}
decision := {"allow": allow}
```
This template validates that your application images have provenance
attestations, and were built by GitHub Actions from your main branch. Docker
Hardened Images are allowed when using digests since they include comprehensive
attestations by default. Other base images must use digests.
Customize:
- Replace `allowed_repos` with your image names
- Update the organization name in `trusted_github_builder()`
- Add rules for other base images you use
When to use: Enforce supply chain security for production deployments. Ensures
images are built by trusted CI/CD pipelines with auditable provenance.
### Signed Git releases
Enforce signed tags from trusted maintainers for Git dependencies.
```rego
package docker
default allow := false
allow if input.local
allow if input.image
# TODO: Replace with your repository URL
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)
}
# Version tags must be signed
allow if {
is_version_tag
input.git.tagName != ""
verify_git_signature(input.git.tag, "maintainers.asc")
}
# Allow unsigned refs for development
allow if {
is_buildkit
not is_version_tag
}
decision := {"allow": allow}
```
This template requires production release tags to be signed by trusted
maintainers. Development branches and commits can be unsigned.
Setup:
1. Export maintainer PGP public keys to `maintainers.asc`:
```console
$ gpg --export --armor user1@example.com user2@example.com > maintainers.asc
```
2. Place `maintainers.asc` in the same directory as your policy file
Customize:
- Replace the repository URL in `is_buildkit`
- Update the maintainers in the PGP keyring file
- Adjust the version tag regex pattern if needed
When to use: Validate that production dependencies come from signed releases.
Protects against compromised releases or unauthorized updates.
### Multi-registry policy
Apply different validation rules for internal and external registries.
```rego
package docker
default allow := false
allow if input.local
# TODO: Replace with your internal registry hostname
internal_registry := "registry.company.com"
# Internal registry: basic validation
allow if {
input.image.host == internal_registry
}
# External registries: strict validation
allow if {
input.image.host != internal_registry
input.image.host != ""
input.image.isCanonical
input.image.hasProvenance
}
# Docker Hub: allowlist specific images
allow if {
input.image.host == "docker.io"
# TODO: Add your approved base images
input.image.repo in ["alpine", "golang", "node"]
input.image.isCanonical
}
# Docker Hardened Images: trusted by default with built-in attestations
allow if {
input.image.host == "dhi.io"
input.image.isCanonical
}
decision := {"allow": allow}
```
This template defines a trust boundary between internal and external image
sources. Internal images require minimal validation, while external images need
digests and provenance. Docker Hardened Images from `dhi.io` are treated as
trusted since they include comprehensive attestations and security guarantees.
Customize:
- Set your internal registry hostname
- Add your approved Docker Hub base images
- Adjust validation requirements based on your security policies
When to use: Organizations with internal registries that need different rules
for internal and external sources. Balances security with practical workflow
needs.
### Multi-environment policy
Apply different rules based on the build target or stage. For example,
```rego
package docker
default allow := false
allow if input.local
# TODO: Define your environment detection logic
is_production if {
input.env.target == "production"
}
is_development if {
input.env.target == "development"
}
# Production: strict rules - only digest images with provenance
allow if {
is_production
input.image.isCanonical
input.image.hasProvenance
}
# Development: permissive rules - any image
allow if {
is_development
input.image
}
# Staging inherits production rules (default target detection)
allow if {
not is_production
not is_development
input.image.isCanonical
}
decision := {"allow": allow}
```
This template uses build targets to apply different validation levels.
Production requires attestations and digests, development is permissive, and
staging uses moderate rules.
Customize:
- Update environment detection logic (target names, build args, etc.)
- Adjust validation requirements for each environment
- Add more environments as needed
When to use: Teams with separate build configurations for different deployment
stages. Allows flexibility in development while enforcing strict rules for
production.
### Complete dependency pinning
Pin all external dependencies to specific versions across all input types.
```rego
package docker
default allow := false
allow if input.local
# TODO: Add your pinned images with exact digests
# Docker Hub images use docker.io as host
allowed_dockerhub := {
"alpine": "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412",
"golang": "sha256:abc123...",
}
allow if {
input.image.host == "docker.io"
some repo, digest in allowed_dockerhub
input.image.repo == repo
input.image.checksum == digest
}
# TODO: Add your pinned DHI images
allowed_dhi := {
"python": "sha256:def456...",
"node": "sha256:ghi789...",
}
allow if {
input.image.host == "dhi.io"
some repo, digest in allowed_dhi
input.image.repo == repo
input.image.checksum == digest
}
# TODO: Add your pinned Git dependencies
allowed_git := {
"https://github.com/moby/buildkit.git": {
"tag": "v0.26.1",
"commit": "abc123...",
},
}
allow if {
some url, version in allowed_git
input.git.remote == url
input.git.tagName == version.tag
input.git.commitChecksum == version.commit
}
# TODO: Add your pinned HTTP downloads
allowed_downloads := {
"https://releases.example.com/app-v1.0.tar.gz": "sha256:def456...",
}
allow if {
some url, checksum in allowed_downloads
input.http.url == url
input.http.checksum == checksum
}
decision := {"allow": allow}
```
This template pins every external dependency to exact versions with cryptographic
verification. Images use digests, Git repos use commit SHAs, and downloads use
checksums.
Customize:
- Add all your dependencies with exact versions/checksums
- Maintain this file when updating dependencies
- Consider automating updates through CI/CD
When to use: Maximum reproducibility and security. Ensures builds always use
exact versions of all dependencies. Required for high-security or regulated
environments.
### Manual signature verification
Verify image signatures by inspecting signature metadata fields.
```rego
package docker
default allow := false
allow if input.local
# Require valid GitHub Actions signatures
allow if {
input.image
input.image.hasProvenance
some sig in input.image.signatures
valid_github_signature(sig)
}
# Helper function to validate GitHub Actions signature
valid_github_signature(sig) if {
# Sigstore keyless signing
sig.signer.certificateIssuer == "CN=sigstore-intermediate,O=sigstore.dev"
sig.signer.issuer == "https://token.actions.githubusercontent.com"
# TODO: Replace with your organization
startswith(sig.signer.buildSignerURI, "https://github.com/myorg/.github/workflows/")
startswith(sig.signer.sourceRepositoryURI, "https://github.com/myorg/")
# Verify GitHub hosted runner
sig.signer.runnerEnvironment == "github-hosted"
# Require timestamp
count(sig.timestamps) > 0
}
decision := {"allow": allow}
```
This policy validates that images were built by GitHub Actions using Sigstore
keyless signing.
Customize:
- Replace `myorg` with your GitHub organization
- Adjust workflow path restrictions
- Add additional signature field checks as needed
When to use: Enforce that images are built by CI/CD with verifiable signatures,
not manually pushed by developers.
## Next steps
- Write unit tests for your policies: [Test build policies](./testing.md)
- Review [Built-in functions](./built-ins.md) for signature verification and
attestation checking
- Check the [Input reference](./inputs.md) for all available fields you can
validate
- Read the tutorials for detailed explanations:
[Introduction](./intro.md), [Image validation](./validate-images.md), [Git
validation](./validate-git.md)

View File

@@ -0,0 +1,539 @@
---
title: Input reference
linkTitle: Input reference
description: Reference documentation for policy input fields
keywords: build policies, input reference, policy fields, image metadata, git metadata
weight: 80
---
When Buildx evaluates policies, it provides information about build inputs
through the `input` object. The structure of `input` depends on the type of
resource your Dockerfile references.
## Input types
Build inputs correspond to Dockerfile instructions:
| Dockerfile instruction | Input type | Access pattern |
| --------------------------------------- | ---------- | -------------- |
| `FROM alpine:latest` | Image | `input.image` |
| `COPY --from=builder /app /app` | Image | `input.image` |
| `ADD https://example.com/file.tar.gz /` | HTTP | `input.http` |
| `ADD git@github.com:user/repo.git /src` | Git | `input.git` |
| Build context (`.`) | Local | `input.local` |
Each input type has specific fields available for policy evaluation.
## HTTP inputs
HTTP inputs represent files downloaded over HTTP or HTTPS using the `ADD`
instruction.
### Example Dockerfile
```dockerfile
FROM alpine
ADD --checksum=sha256:abc123... https://example.com/app.tar.gz /app.tar.gz
```
### Available fields
#### `input.http.url`
The complete URL of the resource.
```rego
allow if {
input.http.url == "https://example.com/app.tar.gz"
}
```
#### `input.http.schema`
The URL scheme (`http` or `https`).
```rego
# Require HTTPS for all downloads
allow if {
input.http.schema == "https"
}
```
#### `input.http.host`
The hostname from the URL.
```rego
# Allow downloads from approved domains
allow if {
input.http.host == "cdn.example.com"
}
```
#### `input.http.path`
The path component of the URL.
```rego
allow if {
startswith(input.http.path, "/releases/")
}
```
#### `input.http.checksum`
The checksum specified with `ADD --checksum=...`, if present. Empty string if
no checksum was provided.
```rego
# Require checksums for all downloads
allow if {
input.http.checksum != ""
}
```
#### `input.http.hasAuth`
Boolean indicating if the request includes authentication (HTTP basic auth or
bearer token).
```rego
# Require authentication for internal servers
allow if {
input.http.host == "internal.company.com"
input.http.hasAuth
}
```
## Image inputs
Image inputs represent container images from `FROM` instructions or
`COPY --from` references.
### Example Dockerfile
```dockerfile
FROM alpine:3.19@sha256:abc123...
COPY --from=builder:latest /app /app
```
### Available fields
#### `input.image.ref`
The complete image reference as written in the Dockerfile.
```rego
allow if {
input.image.ref == "alpine:3.19@sha256:abc123..."
}
```
#### `input.image.host`
The registry hostname. Docker Hub images use `"docker.io"`.
```rego
# Only allow Docker Hub images
allow if {
input.image.host == "docker.io"
}
# Only allow images from GitHub Container Registry
allow if {
input.image.host == "ghcr.io"
}
```
#### `input.image.repo`
The repository name without the registry host.
```rego
allow if {
input.image.repo == "library/alpine"
}
```
#### `input.image.fullRepo`
The full repository path including registry host.
```rego
allow if {
input.image.fullRepo == "docker.io/library/alpine"
}
```
#### `input.image.tag`
The tag portion of the reference. Empty if using a digest reference.
```rego
# Allow only specific tags
allow if {
input.image.tag == "3.19"
}
```
#### `input.image.isCanonical`
Boolean indicating if the reference uses a digest (`@sha256:...`).
```rego
# Require digest references
allow if {
input.image.isCanonical
}
```
#### `input.image.checksum`
The SHA256 digest of the image manifest.
```rego
allow if {
input.image.checksum == "sha256:abc123..."
}
```
#### `input.image.platform`
The target platform for multi-platform images.
```rego
allow if {
input.image.platform == "linux/amd64"
}
```
#### `input.image.os`
The operating system from the image configuration.
```rego
allow if {
input.image.os == "linux"
}
```
#### `input.image.arch`
The CPU architecture from the image configuration.
```rego
allow if {
input.image.arch == "amd64"
}
```
#### `input.image.hasProvenance`
Boolean indicating if the image has provenance attestations.
```rego
# Require provenance for production images
allow if {
input.image.hasProvenance
}
```
#### `input.image.labels`
A map of image labels from the image configuration.
```rego
# Check for specific labels
allow if {
input.image.labels["org.opencontainers.image.vendor"] == "Example Corp"
}
```
#### `input.image.signatures`
Array of attestation signatures. Each signature in the array has the following
fields:
- `kind`: Signature kind (e.g., `"docker-github-builder"`, `"self-signed"`)
- `type`: Signature type (e.g., `"bundle-v0.3"`, `"simplesigning-v1"`)
- `timestamps`: Trusted timestamps from transparency logs
- `dockerReference`: Docker image reference
- `isDHI`: Boolean indicating if this is a Docker Hardened Image
- `signer`: Sigstore certificate details
```rego
# Require at least one signature
allow if {
count(input.image.signatures) > 0
}
```
For Sigstore signatures, the `signer` object provides detailed certificate
information from the signing workflow:
- `certificateIssuer`: Certificate issuer
- `subjectAlternativeName`: Subject alternative name from certificate
- `buildSignerURI`: URI of the build signer
- `buildSignerDigest`: Digest of the build signer
- `runnerEnvironment`: CI/CD runner environment
- `sourceRepositoryURI`: Source repository URL
- `sourceRepositoryDigest`: Source repository digest
- `sourceRepositoryRef`: Source repository ref (branch/tag)
- `sourceRepositoryIdentifier`: Source repository identifier
- `sourceRepositoryOwnerURI`: Repository owner URI
- `buildConfigURI`: Build configuration URI
- `buildTrigger`: What triggered the build
- `runInvocationURI`: CI/CD run invocation URI
```rego
# Require signatures from GitHub Actions
allow if {
some sig in input.image.signatures
sig.signer.runnerEnvironment == "github-hosted"
startswith(sig.signer.sourceRepositoryURI, "https://github.com/myorg/")
}
```
## Git inputs
Git inputs represent Git repositories referenced in `ADD` instructions or used
as build context.
### Example Dockerfile
```dockerfile
ADD git@github.com:moby/buildkit.git#v0.12.0 /src
```
### Available fields
#### `input.git.schema`
The URL scheme (`https`, `http`, `git`, or `ssh`).
```rego
# Require HTTPS for Git clones
allow if {
input.git.schema == "https"
}
```
#### `input.git.host`
The Git host (e.g., `github.com`, `gitlab.com`).
```rego
allow if {
input.git.host == "github.com"
}
```
#### `input.git.remote`
The complete Git URL.
```rego
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
}
```
#### `input.git.ref`
The Git reference.
```rego
allow if {
input.git.ref == "refs/heads/master"
}
```
#### `input.git.tagName`
The tag name if the reference is a tag.
```rego
# Only allow version tags
allow if {
regex.match(`^v[0-9]+\.[0-9]+\.[0-9]+$`, input.git.tagName)
}
```
#### `input.git.branch`
The branch name if the reference is a branch.
```rego
allow if {
input.git.branch == "main"
}
```
#### `input.git.subDir`
The subdirectory path within the repository, if specified.
```rego
# Ensure clones are from the root
allow if {
input.git.subDir == ""
}
```
#### `input.git.isCommitRef`
Boolean indicating if the reference is a commit SHA (as opposed to a branch or
tag name).
```rego
# Require commit SHAs for production
allow if {
input.env.target == "production"
input.git.isCommitRef
}
```
#### `input.git.checksum`
The checksum of the Git reference. For commit references and branches, this is
the commit hash. For annotated tags, this is the tag object hash.
```rego
allow if {
input.git.checksum == "abc123..."
}
```
#### `input.git.commitChecksum`
The commit hash that the reference points to. For annotated tags, this differs
from `checksum` (which is the tag object hash). For commit references and
branches, this is the same as `checksum`.
```rego
allow if {
input.git.commitChecksum == "abc123..."
}
```
#### `input.git.isAnnotatedTag`
Boolean indicating if the reference is an annotated tag (as opposed to a
lightweight tag).
```rego
# Require annotated tags
allow if {
input.git.tagName != ""
input.git.isAnnotatedTag
}
```
#### `input.git.commit`
Object containing commit metadata:
- `author`: Author name, email, when
- `committer`: Committer name, email, when
- `message`: Commit message
- `pgpSignature`: PGP signature details if signed
- `sshSignature`: SSH signature details if signed
```rego
# Check commit author
allow if {
input.git.commit.author.email == "maintainer@example.com"
}
```
#### `input.git.tag`
Object containing tag metadata for annotated tags:
- `tagger`: Tagger name, email, when
- `message`: Tag message
- `pgpSignature`: PGP signature details if signed
- `sshSignature`: SSH signature details if signed
```rego
# Require signed tags
allow if {
input.git.tag.pgpSignature != null
}
```
## Local inputs
Local inputs represent the build context directory.
### Available fields
#### `input.local.name`
The name or path of the local context.
```rego
allow if {
input.local.name == "."
}
```
Local inputs are typically less restricted than remote inputs, but you can
still write policies to enforce context requirements.
## Environment fields
The `input.env` object provides build configuration information set by user on
invoking the build, not specific to a resource type.
### Available fields
#### `input.env.filename`
The name of the Dockerfile being built.
```rego
# Stricter rules for production Dockerfile
allow if {
input.env.filename == "Dockerfile"
input.image.isCanonical
}
# Relaxed rules for development
allow if {
input.env.filename == "Dockerfile.dev"
}
```
#### `input.env.target`
The build target from multi-stage builds.
```rego
# Require signing only for release builds
allow if {
input.env.target == "release"
input.git.tagName != ""
verify_git_signature(input.git.tag, "maintainer.asc")
}
```
#### `input.env.args`
Build arguments passed with `--build-arg`. Access specific arguments by key.
```rego
# Check build argument values
allow if {
input.env.args.ENVIRONMENT == "production"
input.image.hasProvenance
}
```
## Next steps
- See [Built-in functions](./built-ins.md) for built-in helper functions to
check and validate input properties
- Browse [Example policies](./examples.md) for common patterns
- Read about [Rego](https://www.openpolicyagent.org/docs/latest/policy-language/)
for advanced policy logic

View File

@@ -0,0 +1,334 @@
---
title: Introduction to build policies
linkTitle: Introduction
description: Get started with writing and evaluating build policies
keywords: build policies, opa, rego, policy tutorial, docker build, security
weight: 10
---
Build policies let you validate the inputs to your Docker builds before they
run. This tutorial walks you through creating your first policy, teaching the
Rego basics you need along the way.
## What you'll learn
By the end of this tutorial, you'll understand:
- How to create and organize policy files
- Basic Rego syntax and patterns
- How to write policies that validate URLs, checksums, and images
- How policies evaluate during builds
## Prerequisites
- Buildx version 0.31 or later
- Basic familiarity with Dockerfiles and building images
## How policies work
When you build an image, Buildx resolves all the inputs your
Dockerfile references: base images from `FROM` instructions, files
from `ADD` or `COPY` or build contexts, and Git repositories. Before
running the build, Buildx evaluates your policies against these
inputs. If any input violates a policy, the build fails before any
instructions execute.
Policies are written in Rego, a declarative language designed for expressing
rules and constraints. You don't need to know Rego to get started - this
tutorial teaches you what you need.
## Create your first policy
Create a new directory for this tutorial and add a Dockerfile:
```console
$ mkdir policy-tutorial
$ cd policy-tutorial
```
Create a `Dockerfile` that downloads a file with `ADD`:
```dockerfile
FROM scratch
ADD https://example.com/index.html /index.html
```
Now create a policy file. Policies use the `.rego` extension and live alongside
your Dockerfile. Create `Dockerfile.rego`:
```rego {title="Dockerfile.rego"}
package docker
default allow := false
allow if input.local
allow if {
input.http.host == "example.com"
}
decision := {"allow": allow}
```
Save this file as `Dockerfile.rego` in the same directory as your Dockerfile.
Let's break down what this policy does:
- `package docker` - All build policies must start with this package declaration
- `default allow := false` - This example uses a deny-by-default rule: if inputs do not match an `allow` rule, the policy check fails
- `allow if input.local` - The first rule allows any local files (your build context)
- `allow if { input.http.host == "example.com" }` - The second rule allows HTTP downloads from `example.com`
- `decision := {"allow": allow}` - The final decision object tells Buildx whether to allow or deny the input
This policy says: "Only allow local files and HTTP downloads from
`example.com`". Rego evaluates all the policy rules to figure out the return
value for the `decision` variable, for each build input. The evaluations happen
in parallel and on-demand; the order of the policy rules has no significance.
### About `input.local`
You'll see `allow if input.local` in nearly every policy. This rule allows
local file access, which includes your build context (typically, the `.`
directory) and importantly, the Dockerfile itself. Without this rule, Buildx
can't read your Dockerfile to start the build.
Even builds that don't reference any files from the build context often need
`input.local` because the Dockerfile is a local file. The policy evaluates
before the build starts, and denying local access means denying access to the
Dockerfile.
In rare cases, you might want stricter local file policies - for example, in CI
builds where the build context uses a Git URL as a context directly. In these
cases, you may want to deny local sources to prevent untracked files or changes
from making their way into your build.
## Automatic policy loading
Buildx automatically loads policies that match your Dockerfile name. When you
build with `Dockerfile`, Buildx looks for `Dockerfile.rego` in the same
directory. For a file named `app.Dockerfile`, it looks for
`app.Dockerfile.rego`.
This automatic loading means you don't need any command-line flags in most
cases - create the policy file and build.
The policy file must be in the same directory as the Dockerfile. If Buildx
can't find a matching policy, the build proceeds without policy evaluation
(unless you use `--policy strict=true`).
For more control over policy loading, see the [Usage guide](./usage.md).
## Run a build with your policy
Build the image with policy evaluation enabled:
```console
$ docker build .
```
The build succeeds because the URL in your Dockerfile matches the policy. Now
try changing the URL in your Dockerfile to something else:
```dockerfile
FROM scratch
ADD https://api.github.com/users/octocat /user.json
```
Build again:
```console
$ docker build .
```
This time the build fails with a policy violation. The `api.github.com`
hostname doesn't match the rule in your policy, so Buildx rejects it before
running any build steps.
## Debugging policy failures
If your build fails with a policy violation, use `--progress=plain` to see
exactly what went wrong:
```console
$ docker buildx build --progress=plain .
```
This shows all policy checks, the input data for each source, and allow/deny
decisions. For complete debugging guidance, see [Debugging](./debugging.md).
## Add helpful error messages
When a policy denies an input, users see a generic error message. You can
provide custom messages that explain why the build was denied:
```rego {title="Dockerfile.rego"}
package docker
default allow := false
allow if input.local
allow if {
input.http.host == "example.com"
input.http.schema == "https"
}
deny_msg contains msg if {
not allow
input.http
msg := "only HTTPS downloads from example.com are allowed"
}
decision := {"allow": allow, "deny_msg": deny_msg}
```
Now when a build is denied, users see your custom message explaining what went
wrong:
```console
$ docker buildx build .
Policy: only HTTPS downloads from example.com are allowed
ERROR: failed to build: ... source not allowed by policy
```
The `deny_msg` rule uses `contains` to add messages to a set. You can add
multiple deny messages for different failure conditions to help users understand
exactly what needs to change.
## Understand Rego rules
Rego policies are built from rules. A rule defines when something is allowed.
The basic pattern is:
```rego
allow if {
condition_one
condition_two
condition_three
}
```
All conditions must be true for the rule to match. Think of it as an AND
operation - the URL must match AND the checksum must match AND any other
conditions you specify.
You can have multiple `allow` rules in one policy. If any rule matches, the
input is allowed:
```rego
# Allow downloads from example.com
allow if {
input.http.host == "example.com"
}
# Also allow downloads from api.github.com
allow if {
input.http.host == "api.github.com"
}
```
This works like OR - the input can match the first rule OR the second rule.
## Access input fields
The `input` object gives you access to information about build inputs. The
structure depends on the input type:
- `input.http` - Files downloaded with `ADD https://...`
- `input.image` - Container images from `FROM` or `COPY --from`
- `input.git` - Git repositories from `ADD git://...` or build context
- `input.local` - Local file context
Refer to the [Input reference](./inputs.md) for all available input fields.
For HTTP downloads, you can access:
| Key | Description | Example |
| ------------------- | ---------------------------------- | -------------------------------- |
| `input.http.url` | The full URL | `https://example.com/index.html` |
| `input.http.schema` | The protocol (HTTP/HTTPS) | `https` |
| `input.http.host` | The hostname | `example.com` |
| `input.http.path` | The URL path, including parameters | `/index.html` |
Update your policy to require HTTPS:
```rego
package docker
default allow := false
allow if {
input.http.host == "example.com"
input.http.schema == "https"
}
decision := {"allow": allow}
```
Now the policy requires both the hostname to be `example.com` and the protocol
to be HTTPS. HTTP URLs (without TLS) would fail the policy check.
## Pattern matching and strings
Rego provides [built-in functions] for pattern matching. Use `startswith()` to
match URL prefixes:
[built-in functions]: https://www.openpolicyagent.org/docs/policy-language#built-in-functions
```rego
allow if {
startswith(input.http.url, "https://example.com/")
}
```
This allows any URL that starts with `https://example.com/`.
Use `regex.match()` for complex patterns:
```rego
allow if {
regex.match(`^https://example\.com/.+\.json$`, input.http.url)
}
```
This matches URLs that:
- Start with `https://example.com/`
- End with `.json`
- Have at least one character between the domain and extension
## Policy file location
Policy files live adjacent to the Dockerfile they validate, using the naming
pattern `<dockerfile-name>.rego`:
```text
project/
├── Dockerfile # Main Dockerfile
├── Dockerfile.rego # Policy for Dockerfile
├── lint.Dockerfile # Linting Dockerfile
└── lint.Dockerfile.rego # Policy for lint.Dockerfile
```
When you build, Buildx automatically loads the corresponding policy file:
```console
$ docker buildx build -f Dockerfile . # Loads Dockerfile.rego
$ docker buildx build -f lint.Dockerfile . # Loads lint.Dockerfile.rego
```
## Next steps
You now understand how to write basic build policies for HTTP resources. To
continue learning:
- Apply and test policies: [Using build policies](./usage.md)
- Learn [Image validation](./validate-images.md) to validate container images
from `FROM` instructions
- Learn [Git validation](./validate-git.md) to validate Git repositories used
in builds
- See [Example policies](./examples.md) for copy-paste-ready policies covering
common scenarios
- Write unit tests for your policies: [Test build policies](./testing.md)
- Debug policy failures: [Debugging](./debugging.md)
- Read the [Input reference](./inputs.md) for all available input fields
- Check the [Built-in functions](./built-ins.md) for signature verification,
attestations, and other security checks

View File

@@ -0,0 +1,214 @@
---
title: Test build policies
linkTitle: Testing
description: Write and run unit tests for build policies, similar to the opa test command
keywords: build policies, opa, rego, testing, unit tests, policy validation
weight: 60
---
The [`docker buildx policy test`](/reference/cli/docker/buildx/policy/test/)
command runs unit tests for build policies using OPA's [standard test
framework](https://www.openpolicyagent.org/docs/policy-testing).
```console
$ docker buildx policy test <path>
```
This validates policy logic with mocked inputs.
For testing against real sources (actual image metadata, Git repositories), use
[`docker buildx policy eval`](/reference/cli/docker/buildx/policy/eval/)
instead. You can use the `eval --print` option to resolve input for a specific
source for writing a test case.
## Basic example
Start with a simple policy that only allows `alpine` images:
```rego {title="Dockerfile.rego"}
package docker
default allow = false
allow if {
input.image.repo == "alpine"
}
decision := {"allow": allow}
```
Create a test file with the `*_test.rego` suffix. Test functions must start
with `test_`:
```rego {title="Dockerfile_test.rego"}
package docker
test_alpine_allowed if {
decision.allow with input as {"image": {"repo": "alpine"}}
}
test_ubuntu_denied if {
not decision.allow with input as {"image": {"repo": "ubuntu"}}
}
```
Run the tests:
```console
$ docker buildx policy test .
test_alpine_allowed: PASS (allow=true)
test_ubuntu_denied: PASS (allow=false)
```
`PASS` indicates that the tests defined in `Dockerfile_test.rego` executed
successfully and all assertions were satisfied.
## Command options
Filter tests by name with `--run`:
```console
$ docker buildx policy test --run alpine .
test_alpine_allowed: PASS (allow=true)
```
Test policies with non-default filenames using `--filename`:
```console
$ docker buildx policy test --filename app.Dockerfile .
```
This loads `app.Dockerfile.rego` and runs `*_test.rego` files against it.
## Test output
Passed tests show the allow status and any deny messages:
```console
test_alpine_allowed: PASS (allow=true)
test_ubuntu_denied: PASS (allow=false, deny_msg=only alpine images are allowed)
```
Failed tests show input, decision output, and missing fields:
```console
test_invalid: FAIL (allow=false)
input:
{
"image": {}
}
decision:
{
"allow": false,
"deny_msg": [
"only alpine images are allowed"
]
}
missing_input: input.image.repo
```
## Test deny messages
To test custom error messages, capture the full decision result and assert on
the `deny_msg` field.
For a policy with deny messages:
```rego {title="Dockerfile.rego"}
package docker
default allow = false
allow if {
input.image.repo == "alpine"
}
deny_msg contains msg if {
not allow
msg := "only alpine images are allowed"
}
decision := {"allow": allow, "deny_msg": deny_msg}
```
Test the deny message:
```rego {title="Dockerfile_test.rego"}
test_deny_message if {
result := decision with input as {"image": {"repo": "ubuntu"}}
not result.allow
"only alpine images are allowed" in result.deny_msg
}
```
## Test patterns
**Test environment-specific rules:**
```rego
test_production_requires_digest if {
decision.allow with input as {
"env": {"target": "production"},
"image": {"isCanonical": true}
}
}
test_development_allows_tags if {
decision.allow with input as {
"env": {"target": "development"},
"image": {"isCanonical": false}
}
}
```
**Test multiple registries:**
```rego
test_dockerhub_allowed if {
decision.allow with input as {
"image": {
"ref": "docker.io/library/alpine",
"host": "docker.io",
"repo": "alpine"
}
}
}
test_ghcr_allowed if {
decision.allow with input as {
"image": {
"ref": "ghcr.io/myorg/myapp",
"host": "ghcr.io",
"repo": "myorg/myapp"
}
}
}
```
For available input fields, see the [Input reference](./inputs.md).
## Organize test files
The test runner discovers all `*_test.rego` files recursively:
```plaintext
build-policies/
├── Dockerfile.rego
├── Dockerfile_test.rego
└── tests/
├── registries_test.rego
├── signatures_test.rego
└── environments_test.rego
```
Run all tests:
```console
$ docker buildx policy test .
```
Or test specific files:
```console
$ docker buildx policy test tests/registries_test.rego
```

View File

@@ -0,0 +1,493 @@
---
title: Using build policies
linkTitle: Usage
description: Apply policies to builds and develop policies iteratively
keywords: build policies, policy eval, docker buildx, policy development, debugging
weight: 20
---
Build policies validate inputs before builds execute. This guide covers how to
develop policies iteratively and apply them to real builds with `docker buildx
build` and `docker buildx bake`.
## Prerequisites
- Buildx 0.31.0 or later - Check your version: `docker buildx version`
- BuildKit 0.26.0 or later - Verify with: `docker buildx inspect
--bootstrap`
If you're using Docker Desktop, ensure you're on a version that includes these
updates.
## Policy development workflow
Buildx automatically loads policies that match your Dockerfile name. When you
build with `Dockerfile`, Buildx looks for `Dockerfile.rego` in the same
directory. For a file named `app.Dockerfile`, it looks for
`app.Dockerfile.rego`. See the [Advanced: Policy configuration](#advanced-policy-configuration)
section for configuration options and manual policy loading.
Writing policies is an iterative process:
1. Start with a basic deny-all policy.
2. Build with debug logging to see what inputs your Dockerfile uses.
3. Add rules to allow specific sources based on the debug output.
4. Test and refine.
### Viewing inputs from your Dockerfile
To see the inputs that your Dockerfile references (images, Git repos, HTTP
downloads), build with debug logging:
```console
$ docker buildx build --progress=plain --policy log-level=debug .
```
Example output for an image source:
```text
#1 0.010 checking policy for source docker-image://alpine:3.19 (linux/arm64)
#1 0.011 policy input: {
#1 0.011 "env": {
#1 0.011 "filename": "."
#1 0.011 },
#1 0.011 "image": {
#1 0.011 "ref": "docker.io/library/alpine:3.19",
#1 0.011 "host": "docker.io",
#1 0.011 "repo": "alpine",
#1 0.011 "tag": "3.19",
#1 0.011 "platform": "linux/arm64"
#1 0.011 }
#1 0.011 }
#1 0.011 unknowns for policy evaluation: [input.image.checksum input.image.labels ...]
#1 0.012 policy decision for source docker-image://alpine:3.19: ALLOW
```
This shows the complete input structure, which fields are unresolved, and the
policy decision for each source. See [Input reference](./inputs.md) for all
available fields.
### Testing policies with policy eval
Use [`docker buildx policy eval`](/reference/cli/docker/buildx/policy/eval/) to
test whether your policy allows a specific source without running a full build.
Note: `docker buildx policy eval` tests the source specified as the argument.
It doesn't parse your Dockerfile to evaluate all inputs - for that, [build with
--progress=plain](#viewing-inputs-from-your-dockerfile).
Test if your policy allows the local context:
```console
$ docker buildx policy eval .
```
No output means the policy allowed the source. If denied, you see:
```console
ERROR: policy denied
```
Test other sources:
```console
$ docker buildx policy eval https://example.com # Test HTTP
$ docker buildx policy eval https://github.com/org/repo.git # Test Git
```
By default, `--print` shows reference information parsed from the source string
(like `repo`, `tag`, `host`) without fetching from registries. To inspect
metadata that requires fetching the source (like `labels`, `checksum`, or
`hasProvenance`), specify which fields to fetch with `--fields`:
```console
$ docker buildx policy eval --print --fields image.labels docker-image://alpine:3.19
```
Multiple fields can be specified as a comma-separated list.
### Iterative development example
Here's a practical workflow for developing policies:
1. Start with basic deny-all policy:
```rego {title="Dockerfile.rego"}
package docker
default allow := false
allow if input.local
decision := {"allow": allow}
```
2. Build with debug logging to see what inputs your Dockerfile uses:
```console
$ docker buildx build --progress=plain --policy log-level=debug .
```
The output shows the denied image and its input structure:
```text
#1 0.026 checking policy for source docker-image://docker.io/library/alpine:3.19
#1 0.027 policy input: {
#1 0.027 "image": {
#1 0.027 "repo": "alpine",
#1 0.027 "tag": "3.19",
#1 0.027 ...
#1 0.027 }
#1 0.027 }
#1 0.028 policy decision for source docker-image://alpine:3.19: DENY
#1 ERROR: source "docker-image://alpine:3.19" not allowed by policy
```
3. Add a rule allowing the alpine image:
```rego
allow if {
input.image.repo == "alpine"
}
```
4. Build again to verify the policy works:
```console
$ docker buildx build .
```
If it fails, see [Debugging](./debugging.md) for troubleshooting guidance.
## Using policies with `docker build`
Once you've developed and tested your policy, apply it to real builds.
### Basic usage
Create a policy alongside your Dockerfile:
```dockerfile {title="Dockerfile"}
FROM alpine:3.19
RUN echo "hello"
```
```rego {title="Dockerfile.rego"}
package docker
default allow := false
allow if input.local
allow if {
input.image.repo == "alpine"
}
decision := {"allow": allow}
```
Build normally:
```console
$ docker buildx build .
```
Buildx loads the policy automatically and validates the `alpine:3.19` image
before building.
### Build with different Dockerfile names
Specify the Dockerfile with `-f`:
```console
$ docker buildx build -f app.Dockerfile .
```
Buildx looks for `app.Dockerfile.rego` in the same directory.
### Build with manual policy
Add an extra policy to the automatic one:
```console
$ docker buildx build --policy filename=extra-checks.rego .
```
Both `Dockerfile.rego` (automatic) and `extra-checks.rego` (manual) must pass.
### Build without automatic policy
Use only your specified policy:
```console
$ docker buildx build --policy reset=true,filename=strict.rego .
```
## Using policies with bake
[Bake](/build/bake/) supports automatic policy loading just like `docker buildx
build`. Place `Dockerfile.rego` alongside your Dockerfile and run:
```console
$ docker buildx bake
```
### Manual policy in bake files
Specify additional policies in your `docker-bake.hcl`:
```hcl {title="docker-bake.hcl"}
target "default" {
dockerfile = "Dockerfile"
policy = ["extra.rego"]
}
```
The `policy` attribute takes a list of policy files. Bake loads these in
addition to the automatic `Dockerfile.rego` (if it exists).
### Multiple policies in bake
```hcl {title="docker-bake.hcl"}
target "webapp" {
dockerfile = "Dockerfile"
policy = [
"shared/base-policy.rego",
"security/image-signing.rego"
]
}
```
All policies must pass for the target to build successfully.
### Different policies per target
Apply different validation rules to different targets:
```hcl {title="docker-bake.hcl"}
target "development" {
dockerfile = "dev.Dockerfile"
policy = ["policies/permissive.rego"]
}
target "production" {
dockerfile = "prod.Dockerfile"
policy = ["policies/strict.rego", "policies/signing-required.rego"]
}
```
Build with the appropriate target:
```console
$ docker buildx bake development # Uses permissive policy
$ docker buildx bake production # Uses strict policies
```
### Bake with policy options
Currently, bake doesn't support policy options (reset, strict, disabled) in the
HCL file. Use command-line flags instead:
```console
$ docker buildx bake --policy disabled=true production
```
## Testing in CI/CD
Validate policies in continuous integration by running builds with the `--policy` flag. For unit testing policies before running builds, see [Test build policies](./testing.md).
Test policies during CI builds:
```yaml {title=".github/workflows/test-policies.yml"}
name: Test Build Policies
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Test build with policy
run: docker buildx build --policy strict=true .
```
This ensures policy changes don't break builds and that new rules work as
intended. The `strict=true` flag fails the build if policies aren't loaded (for
example, if the BuildKit instance used by the build is too old and doesn't
support policies).
## Advanced: Policy configuration
This section covers advanced policy loading mechanisms and configuration
options.
### Automatic policy loading
Buildx automatically loads policies that match your Dockerfile name. When you
build with `Dockerfile`, Buildx looks for `Dockerfile.rego` in the same
directory. For a file named `app.Dockerfile`, it looks for
`app.Dockerfile.rego`.
```text
project/
├── Dockerfile
├── Dockerfile.rego # Loaded automatically for Dockerfile
├── app.Dockerfile
├── app.Dockerfile.rego # Loaded automatically for app.Dockerfile
└── src/
```
This automatic loading means you don't need command-line flags in most cases.
Create the policy file alongside your Dockerfile and build:
```console
$ docker buildx build .
```
Buildx detects `Dockerfile.rego` and evaluates it before running the build.
> [!NOTE]
> Policy files must be in the same directory as the Dockerfile they validate.
> Buildx doesn't search parent directories or subdirectories.
### When policies don't load
If buildx can't find a matching `.rego` file, the build proceeds without policy
evaluation. To require policies and fail if none are found, use strict mode:
```console
$ docker buildx build --policy strict=true .
```
This fails the build if no policy loads or if the BuildKit daemon doesn't
support policies.
### Manual policy configuration
The `--policy` flag lets you specify additional policies, override automatic
loading, or control policy behavior.
Basic syntax:
```console
$ docker buildx build --policy filename=custom.rego .
```
This loads `custom.rego` in addition to the automatic `Dockerfile.rego` (if it
exists).
Multiple policies:
```console
$ docker buildx build --policy filename=policy1.rego --policy filename=policy2.rego .
```
All policies must pass for the build to succeed. Use this to enforce layered
requirements (base policy + project-specific rules).
Available options:
| Option | Description | Example |
| ------------------- | ------------------------------------------------------- | ----------------------------- |
| `filename=<path>` | Load policy from specified file | `filename=custom.rego` |
| `reset=true` | Ignore automatic policies, use only specified ones | `reset=true` |
| `disabled=true` | Disable all policy evaluation | `disabled=true` |
| `strict=true` | Fail if BuildKit doesn't support policies | `strict=true` |
| `log-level=<level>` | Control policy logging (error, warn, info, debug, none). Use `debug` to see complete input JSON and unresolved fields | `log-level=debug` |
Combine options with commas:
```console
$ docker buildx build --policy filename=extra.rego,strict=true .
```
### Exploring sources with policy eval
The `docker buildx policy eval` command lets you quickly explore and test
sources without running a build.
#### Inspect input structure with --print
Use `--print` to see the input structure for any source without running policy
evaluation:
```console
$ docker buildx policy eval --print https://github.com/moby/buildkit.git
```
```json
{
"git": {
"schema": "https",
"host": "github.com",
"remote": "https://github.com/moby/buildkit.git"
}
}
```
Test different source types:
```console
# HTTP downloads
$ docker buildx policy eval --print https://releases.hashicorp.com/terraform/1.5.0/terraform.zip
# Images (requires docker-image:// prefix)
$ docker buildx policy eval --print docker-image://alpine:3.19
# Local context
$ docker buildx policy eval --print .
```
Shows information parsed from the source without fetching. Use `--fields` to
fetch specific metadata (see [above](#testing-policies-with-policy-eval)).
#### Test with specific policy files
The `--filename` flag specifies which policy file to load by providing the base
Dockerfile name (without the `.rego` extension). This is useful for testing
sources against policies associated with different Dockerfiles.
For example, to test a source against the policy for `app.Dockerfile`:
```console
$ docker buildx policy eval --filename app.Dockerfile .
```
This loads `app.Dockerfile.rego` and tests whether it allows the source `.`
(the local directory). The flag defaults to `Dockerfile` if not specified.
Test different sources against your policy:
```console
$ docker buildx policy eval --filename app.Dockerfile https://github.com/org/repo.git
$ docker buildx policy eval --filename app.Dockerfile docker-image://alpine:3.19
```
### Reset automatic loading
To use only your specified policies and ignore automatic `.rego` files:
```console
$ docker buildx build --policy reset=true,filename=custom.rego .
```
This skips `Dockerfile.rego` and loads only `custom.rego`.
### Disable policies temporarily
Disable policy evaluation for testing or emergencies:
```console
$ docker buildx build --policy disabled=true .
```
The build proceeds without any policy checks. Use this carefully - you're
bypassing security controls.
## Next steps
- Write unit tests for your policies: [Test build policies](./testing.md)
- Debug policy failures: [Debugging](./debugging.md)
- Browse working examples: [Example policies](./examples.md)
- Reference all input fields: [Input reference](./inputs.md)

View File

@@ -0,0 +1,431 @@
---
title: Validating Git repositories
linkTitle: Git validation
description: Write policies to validate Git repositories used in your builds
keywords: build policies, git validation, git signatures, gpg, signed commits, signed tags
weight: 40
---
Git repositories often appear in Docker builds as source code inputs. The `ADD`
instruction can clone repositories, and build contexts can reference Git URLs.
Validating these inputs ensures you're building from trusted sources with
verified versions.
This guide teaches you to write policies that validate Git inputs, from basic
version pinning to verifying signed commits and tags.
## Prerequisites
You should understand the policy basics from the [Introduction](./intro.md):
creating policy files, basic Rego syntax, and how policies evaluate during
builds.
## What are Git inputs?
Git inputs come from `ADD` instructions that reference Git repositories:
```dockerfile
# Clone a specific tag
ADD https://github.com/moby/buildkit.git#v0.26.1 /buildkit
# Clone a branch
ADD https://github.com/user/repo.git#main /src
# Clone a commit
ADD https://github.com/user/repo.git#abcde123 /src
```
The build context can also be a Git repository when you build with:
```console
$ docker build https://github.com/user/repo.git#main
```
Each Git reference triggers a policy evaluation. Your policy can inspect
repository URLs, validate versions, check commit metadata, and verify
signatures.
## Match specific repositories
The simplest Git policy restricts which repositories can be used:
```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}
```
This policy:
- Denies all inputs by default
- Allows local build context
- Allows only the BuildKit repository from GitHub
The `host` field contains the Git server hostname, and `remote` contains the
full repository URL. Test it:
```dockerfile {title="Dockerfile"}
FROM scratch
ADD https://github.com/moby/buildkit.git#v0.26.1 /
```
```console
$ docker build .
```
The build succeeds. Try a different repository and it fails.
You can match multiple repositories with additional rules:
```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 to specific versions
Tags and branches can change over time. Pin to specific versions to ensure
reproducible builds:
```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}
```
The `tagName` field contains the tag name when the Git reference points to a
tag. Use `branch` for branches:
```rego
allow if {
input.git.remote == "https://github.com/user/repo.git"
input.git.branch == "main"
}
```
Or use `ref` for any type of reference (branch, tag, or commit SHA):
```rego
allow if {
input.git.ref == "v0.26.1"
}
```
## Use version allowlists
For repositories you trust but want to control versions, maintain an allowlist:
```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}
```
This policy:
- Defines an allowlist of approved versions with metadata
- Uses a helper rule (`is_buildkit`) for readability
- Allows all non-BuildKit inputs
- For BuildKit, checks the tag name, whether it's an annotated tag, and the commit SHA against the allowlist
The helper rule makes complex policies more maintainable. You can expand the
allowlist as new versions are approved:
```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"},
]
```
## Validate with regex patterns
Use pattern matching for 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}
```
This allows any BuildKit tag matching the pattern `vX.Y.Z` where X, Y, and Z
are numbers. The regex ensures you're using release versions, not pre-release
tags like `v0.26.0-rc1`.
Match major versions:
```rego
# Only allow v0.x releases
allow if {
input.git.remote == "https://github.com/moby/buildkit.git"
regex.match(`^v0\.[0-9]+\.[0-9]+$`, input.git.tagName)
}
```
## Inspect commit metadata
The `commit` object provides detailed information about commits:
```rego
package docker
default allow := false
allow if input.local
# Check commit author
allow if {
input.git.remote == "https://github.com/user/repo.git"
input.git.commit.author.email == "trusted@example.com"
}
decision := {"allow": allow}
```
The `commit` object includes:
- `author.name`: Author's name
- `author.email`: Author's email
- `author.when`: When the commit was authored
- `committer.name`: Committer's name
- `committer.email`: Committer's email
- `committer.when`: When the commit was committed
- `message`: Commit message
Validate commit messages:
```rego
allow if {
input.git.commit
contains(input.git.commit.message, "Signed-off-by:")
}
```
Pin to specific commit SHA:
```rego
allow if {
input.git.commitChecksum == "abc123def456..."
}
```
## Require signed commits
GPG-signed commits prove authenticity. Check for commit signatures:
```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}
```
The `pgpSignature` field is `null` for unsigned commits. For signed commits, it
contains signature details.
SSH signatures work similarly:
```rego
allow if {
input.git.commit.sshSignature != null
}
```
## Require signed tags
Annotated tags can be signed, providing a cryptographic guarantee of the
release:
```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}
```
The `tag` object is only available for annotated tags. It includes:
- `tagger.name`: Who created the tag
- `tagger.email`: Tagger's email
- `tagger.when`: When the tag was created
- `message`: Tag message
- `pgpSignature`: GPP signature (if signed)
- `sshSignature`: SSH signature (if signed)
Lightweight tags don't have a `tag` object, so this policy effectively requires
annotated, signed tags.
## Verify signatures with public keys
Use the `verify_git_signature()` function to cryptographically verify Git
signatures against trusted public keys:
```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}
```
This verifies that Git tags are signed by keys in the `keys.asc` public
key file. To set this up:
1. Export maintainer public keys:
```console
$ curl https://github.com/user.gpg > keys.asc
```
2. Place `keys.asc` alongside your policy file
The function verifies PGP signatures on commits or tags. See [Built-in
functions](./built-ins.md) for more details.
## Apply conditional rules
Use different rules for different contexts. Allow unsigned refs during
development but require signing for production:
```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)
}
# Version tags must be signed
allow if {
is_version_tag
input.git.tagName != ""
verify_git_signature(input.git.tag, "keys.asc")
}
# Non-version refs allowed in development
allow if {
is_buildkit
not is_version_tag
input.env.target != "release"
}
decision := {"allow": allow}
```
This policy:
- Defines helper rules for readability
- Requires signed version tags from maintainers
- Allows unsigned refs (branches, commits) unless building the release target
- Uses `input.env.target` to detect the build target
Build a development target without signatures:
```console
$ docker buildx build --target=dev .
```
Build the release target, and signing is enforced:
```console
$ docker buildx build --target=release .
```
## Next steps
You now understand how to validate Git repositories in build policies. To
continue learning:
- Browse [Example policies](./examples.md) for complete policy patterns
- Read [Built-in functions](./built-ins.md) for Git signature verification
functions
- Check the [Input reference](./inputs.md) for all available Git fields

View File

@@ -0,0 +1,424 @@
---
title: Validating image inputs
linkTitle: Image validation
description: Write policies to validate container images used in your builds
keywords: build policies, image validation, docker images, provenance, attestations, signatures
weight: 30
---
Container images are the most common build inputs. Every `FROM` instruction
pulls an image, and `COPY --from` references pull additional images. Validating
these images protects your build supply chain from compromised registries,
unexpected updates, and unauthorized base images.
This guide teaches you to write policies that validate image inputs,
progressing from basic allowlisting to advanced attestation checks.
## Prerequisites
You should understand the policy basics from the [Introduction](./intro.md):
creating policy files, basic Rego syntax, and how policies evaluate during
builds.
## What are image inputs?
Image inputs come from two Dockerfile instructions:
```dockerfile
# FROM instructions
FROM alpine:3.22
FROM golang:1.25-alpine AS builder
# COPY --from references
COPY --from=builder /app /app
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
```
Each of these references triggers a policy evaluation. Your policy can inspect
image metadata, verify attestations, and enforce constraints before the build
proceeds.
## Allowlist specific repositories
The simplest image policy restricts which repositories can be used. This
prevents developers from using arbitrary images that haven't been vetted.
Create a policy that only allows Alpine:
```rego {title="Dockerfile.rego"}
package docker
default allow := false
allow if input.local
allow if {
input.image.repo == "alpine"
}
decision := {"allow": allow}
```
This policy:
- Denies all inputs by default
- Allows local build context
- Allows any image from the `alpine` repository (any tag or digest)
Test it with a Dockerfile:
```dockerfile {title="Dockerfile"}
FROM alpine
RUN echo "hello"
```
```console
$ docker build .
```
The build succeeds. Try changing to `FROM ubuntu`:
```console
$ docker build .
```
The build fails because `ubuntu` doesn't match the allowed repository.
## Compare semantic versions
Restrict images to specific version ranges using Rego's `semver` functions:
```rego
package docker
default allow := false
allow if input.local
# Allow Go 1.21 or newer
allow if {
input.image.repo == "golang"
semver.is_valid(input.image.tag)
semver.compare(input.image.tag, "1.21.0") >= 0
}
decision := {"allow": allow}
```
The `semver.compare(a, b)` function compares semantic versions and returns:
- `-1` if version `a` is less than `b`
- `0` if versions are equal
- `1` if version `a` is greater than `b`
Use `semver.is_valid()` to check if a tag is a valid semantic version before
comparing.
Restrict to specific version ranges:
```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 or newer
semver.compare(version, "21.0.0") < 0 # older than 21.0.0
}
```
This allows only Node.js 20.x versions. The pattern works for any image using
semantic versioning.
These `semver` functions are standard Rego built-ins documented in the [OPA
policy
reference](https://www.openpolicyagent.org/docs/latest/policy-reference/#semver).
## Require digest references
Tags like `alpine:3.22` can change - someone could push a new image with the
same tag. Digests like `alpine@sha256:abc123...` are immutable.
### Requiring users to provide digests
You can require that users always specify digests in their Dockerfiles:
```rego
package docker
default allow := false
allow if input.local
allow if {
input.image.isCanonical
}
decision := {"allow": allow}
```
The `isCanonical` field is `true` when the user's reference includes a digest.
This policy would allow:
```dockerfile
FROM alpine@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
```
But reject tag-only references like `FROM alpine:3.22`.
### Pinning to specific digests
Alternatively (or additionally), you can validate that an image's actual digest
matches a specific value, regardless of how the user wrote the reference:
```rego
allow if {
input.image.repo == "alpine"
input.image.checksum == "sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
decision := {"allow": allow}
```
This checks the actual content digest of the pulled image. It would allow both:
```dockerfile
FROM alpine:3.22
FROM alpine@sha256:4b7ce...
```
As long as the resolved image has the specified digest. This is useful for
pinning critical base images to known-good versions.
## Restrict registries
Control which registries your builds can pull from. This helps enforce
corporate policies or restrict to trusted sources.
```rego
package docker
default allow := false
allow if input.local
# Allow Docker Hub images
allow if {
input.image.host == "docker.io" # Docker Hub
input.image.repo == "alpine"
}
# Allow images from internal registry
allow if {
input.image.host == "registry.company.com"
}
decision := {"allow": allow}
```
The `host` field contains the registry hostname. Docker Hub images use
`"docker.io"` as the host value. Test with:
```dockerfile
FROM alpine # Allowed (Docker Hub)
FROM registry.company.com/myapp:latest # Allowed (company registry)
FROM ghcr.io/someorg/image:latest # Denied (wrong registry)
```
Use `fullRepo` when you need the complete path including registry:
```rego
allow if {
input.image.fullRepo == "docker.io/library/alpine"
}
```
## Validate platform constraints
Multi-architecture images support different operating systems and CPU
architectures. You can restrict builds to specific platforms:
```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}
```
This policy:
- Defines supported architectures in a list
- Checks `input.image.os` matches Linux
- Verifies `input.image.arch` is in the supported list
The `os` and `arch` fields come from the image manifest, reflecting the actual
image platform. This works with Docker's automatic platform selection -
policies validate what Buildx resolves, not what you specify.
## Inspect image metadata
Images contain metadata like environment variables, labels, and working
directories. You can validate these to ensure images meet requirements.
Check for specific environment variables:
```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}
```
This policy validates the official Go image by checking:
- The working directory is `/go`
- The environment has `GOLANG_VERSION` set
- The environment includes `GOTOOLCHAIN=local`
The `input.image.env` field is an array of strings in `KEY=VALUE` format.
Use Rego's `some` iteration to search the array.
Check image labels:
```rego
allow if {
input.image.labels["org.opencontainers.image.vendor"] == "Example Corp"
input.image.labels["org.opencontainers.image.version"] != ""
}
```
The `labels` field is a map, so you access values with bracket notation.
## Require attestations and provenance
Modern images include [attestations](/build/metadata/attestations/):
machine-readable metadata about how the image was built.
[Provenance](/build/metadata/attestations/slsa-provenance/) attestations
describe the build process, and [SBOMs](/build/metadata/attestations/sbom/)
list the software inside.
Require provenance:
```rego
package docker
default allow := false
allow if input.local
allow if {
input.image.hasProvenance
}
decision := {"allow": allow}
```
The `hasProvenance` field is `true` when the image has provenance or SBOM
[attestations](../metadata/attestations/_index.md).
## Verify GitHub Actions signatures
For images built with GitHub Actions, verify they came from trusted workflows by
inspecting signature metadata:
```rego
allow if {
input.image.repo == "myapp"
input.image.hasProvenance
some sig in input.image.signatures
valid_github_signature(sig)
}
# Helper to validate GitHub Actions signature
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}
```
This pattern works with any GitHub Actions workflow using Sigstore keyless
signing. The signature metadata provides cryptographic proof of the build's
origin. For complete signature verification examples, see [Example
policies](./examples.md).
## Combine multiple checks
Real policies often combine several checks. Multiple conditions in one `allow`
rule means AND - all must be true:
```rego
package docker
default allow := false
allow if input.local
# Production images need everything
allow if {
input.image.repo == "alpine"
input.image.isCanonical
input.image.hasProvenance
}
decision := {"allow": allow}
```
Multiple `allow` rules means OR - any rule can match:
```rego
package docker
default allow := false
allow if input.local
# Allow Alpine with strict checks
allow if {
input.image.repo == "alpine"
input.image.isCanonical
}
# Allow Go with different checks
allow if {
input.image.repo == "golang"
input.image.workingDir == "/go"
}
decision := {"allow": allow}
```
Use this pattern to apply different requirements to different base images.
## Next steps
You now understand how to validate container images in build policies. To
continue learning:
- Learn [Git repository validation](./validate-git.md) for source code inputs
- Browse [Example policies](./examples.md) for complete policy patterns
- Read [Built-in functions](./built-ins.md) for signature verification and
attestation checking
- Check the [Input reference](./inputs.md) for all available image fields

View File

@@ -0,0 +1,14 @@
---
datafolder: buildx
datafile: docker_buildx_policy
title: docker buildx policy
layout: cli
---
<!--
This page is automatically generated from Docker's source code. If you want to
suggest a change to the text that appears here, open a ticket or pull request
in the source repository on GitHub:
https://github.com/docker/buildx
-->

View File

@@ -0,0 +1,14 @@
---
datafolder: buildx
datafile: docker_buildx_policy_eval
title: docker buildx policy eval
layout: cli
---
<!--
This page is automatically generated from Docker's source code. If you want to
suggest a change to the text that appears here, open a ticket or pull request
in the source repository on GitHub:
https://github.com/docker/buildx
-->

View File

@@ -0,0 +1,14 @@
---
datafolder: buildx
datafile: docker_buildx_policy_test
title: docker buildx policy test
layout: cli
---
<!--
This page is automatically generated from Docker's source code. If you want to
suggest a change to the text that appears here, open a ticket or pull request
in the source repository on GitHub:
https://github.com/docker/buildx
-->