Attestations de provenance
Les attestations de provenance incluent des faits sur le processus de build, incluant des détails tels que :
- Horodatages de build
- Paramètres et environnement de build
- Métadonnées de contrôle de version
- Détails du code source
- Matériaux (fichiers, scripts) consommés pendant le build
Les attestations de provenance suivent le schéma de provenance SLSA, version 0.2.
Pour plus d'informations sur comment BuildKit peuple ces propriétés de provenance, se référer aux définitions SLSA.
Créer des attestations de provenance
Pour créer une attestation de provenance, passez l'option --attest type=provenance
à la commande docker buildx build
:
$ docker buildx build --tag <namespace>/<image>:<version> \
--attest type=provenance,mode=[min,max] .
Alternativement, vous pouvez utiliser l'option raccourcie --provenance=true
au lieu de --attest type=provenance
. Pour spécifier le paramètre mode
en utilisant l'option raccourcie, utilisez : --provenance=mode=max
.
Pour un exemple sur comment ajouter des attestations de provenance avec GitHub Actions, voir Ajouter des attestations avec GitHub Actions.
Mode
Vous pouvez utiliser le paramètre mode
pour définir le niveau de détail à inclure dans l'attestation de provenance. Les valeurs supportées sont mode=min
(par défaut) et mode=max
.
Min
En mode min
, les attestations de provenance incluent un ensemble minimal d'informations, telles que :
- Horodatages de build
- Le frontend utilisé
- Matériaux de build
- Dépôt source et révision
- Plateforme de build
- Reproductibilité
Les valeurs des arguments de build, les identités des secrets, et les métadonnées de couche enrichies ne sont pas incluses dans mode=min
. La provenance de niveau min
est sûre à utiliser pour tous les builds, car elle ne fuit pas d'informations depuis aucune partie de l'environnement de build.
L'exemple JSON suivant montre les informations incluses dans une attestation de provenance créée en utilisant le mode min
:
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": [
{
"name": "pkg:docker/<registry>/<image>@<tag/digest>?platform=<platform>",
"digest": {
"sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862"
}
}
],
"predicate": {
"builder": { "id": "" },
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1",
"digest": {
"sha256": "9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc"
}
},
{
"uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
"digest": {
"sha256": "a9b24b67dc83b3383d22a14941c2b2b2ca6a103d805cac6820fd1355943beaf1"
}
}
],
"invocation": {
"configSource": { "entryPoint": "Dockerfile" },
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1",
"source": "docker/dockerfile:1",
"target": "binaries"
},
"locals": [{ "name": "context" }, { "name": "dockerfile" }]
},
"environment": { "platform": "linux/arm64" }
},
"metadata": {
"buildInvocationID": "c4a87v0sxhliuewig10gnsb6v",
"buildStartedOn": "2022-12-16T08:26:28.651359794Z",
"buildFinishedOn": "2022-12-16T08:26:29.625483253Z",
"reproducible": false,
"completeness": {
"parameters": true,
"environment": true,
"materials": false
},
"https://mobyproject.org/buildkit@v1#metadata": {
"vcs": {
"revision": "a9ba846486420e07d30db1107411ac3697ecab68",
"source": "[email protected]:<org>/<repo>.git"
}
}
}
}
}
Max
Le mode max
inclut toutes les informations incluses dans le mode min
, ainsi que :
- La définition LLB du build. Celles-ci montrent les étapes exactes prises pour produire l'image.
- Informations sur le Dockerfile, incluant une version encodée en base64 complète du fichier.
- Cartes sources décrivant la relation entre les étapes de build et les couches d'image.
Quand possible, vous devriez préférer mode=max
car il contient des informations significativement plus détaillées pour l'analyse.
WarningNotez que
mode=max
expose les valeurs des arguments de build.Si vous utilisez mal les arguments de build pour passer des identifiants, jetons d'authentification, ou autres secrets, vous devriez refactoriser votre build pour passer les secrets en utilisant des montages secrets à la place. Les montages secrets ne fuient pas en dehors du build et ne sont jamais inclus dans les attestations de provenance.
Inspecter la Provenance
Pour explorer la Provenance créée exportée via l'exportateur image
, vous pouvez utiliser
imagetools inspect
.
En utilisant l'option --format
, vous pouvez spécifier un template pour la sortie. Toutes les données liées à la provenance sont disponibles sous l'attribut .Provenance
. Par exemple, pour obtenir le contenu brut de la Provenance au format SLSA :
$ docker buildx imagetools inspect <namespace>/<image>:<version> \
--format "{{ json .Provenance.SLSA }}"
{
"buildType": "https://mobyproject.org/buildkit@v1",
...
}
Vous pouvez aussi construire des expressions plus complexes en utilisant la fonctionnalité complète des templates Go. Par exemple, pour la provenance générée avec mode=max
, vous pouvez extraire le code source complet du Dockerfile utilisé pour construire l'image :
$ docker buildx imagetools inspect <namespace>/<image>:<version> \
--format '{{ range (index .Provenance.SLSA.metadata "https://mobyproject.org/buildkit@v1#metadata").source.infos }}{{ if eq .filename "Dockerfile" }}{{ .data }}{{ end }}{{ end }}' | base64 -d
FROM ubuntu:24.04
RUN apt-get update
...
Exemple d'attestation de provenance
L'exemple suivant montre à quoi une représentation JSON d'une attestation de provenance avec mode=max
ressemble :
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": [
{
"name": "pkg:docker/<registry>/<image>@<tag/digest>?platform=<platform>",
"digest": {
"sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862"
}
}
],
"predicate": {
"builder": { "id": "" },
"buildType": "https://mobyproject.org/buildkit@v1",
"materials": [
{
"uri": "pkg:docker/docker/dockerfile@1",
"digest": {
"sha256": "9ba7531bd80fb0a858632727cf7a112fbfd19b17e94c4e84ced81e24ef1a0dbc"
}
},
{
"uri": "pkg:docker/[email protected]?platform=linux%2Farm64",
"digest": {
"sha256": "a9b24b67dc83b3383d22a14941c2b2b2ca6a103d805cac6820fd1355943beaf1"
}
}
],
"buildConfig": {
"llbDefinition": [
{
"id": "step4",
"op": {
"Op": {
"exec": {
"meta": {
"args": ["/bin/sh", "-c", "go mod download -x"],
"env": [
"PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOLANG_VERSION=1.19.4",
"GOPATH=/go",
"CGO_ENABLED=0"
],
"cwd": "/src"
},
"mounts": [
{ "input": 0, "dest": "/", "output": 0 },
{
"input": -1,
"dest": "/go/pkg/mod",
"output": -1,
"mountType": 3,
"cacheOpt": { "ID": "//go/pkg/mod" }
},
{
"input": 1,
"selector": "/go.mod",
"dest": "/src/go.mod",
"output": -1,
"readonly": true
},
{
"input": 1,
"selector": "/go.sum",
"dest": "/src/go.sum",
"output": -1,
"readonly": true
}
]
}
},
"platform": { "Architecture": "arm64", "OS": "linux" },
"constraints": {}
},
"inputs": ["step3:0", "step1:0"]
}
]
},
"metadata": {
"buildInvocationID": "edf52vxjyf9b6o5qd7vgx0gru",
"buildStartedOn": "2022-12-15T15:38:13.391980297Z",
"buildFinishedOn": "2022-12-15T15:38:14.274565297Z",
"reproducible": false,
"completeness": {
"parameters": true,
"environment": true,
"materials": false
},
"https://mobyproject.org/buildkit@v1#metadata": {
"vcs": {
"revision": "a9ba846486420e07d30db1107411ac3697ecab68-dirty",
"source": "[email protected]:<org>/<repo>.git"
},
"source": {
"locations": {
"step4": {
"locations": [
{
"ranges": [
{ "start": { "line": 5 }, "end": { "line": 5 } },
{ "start": { "line": 6 }, "end": { "line": 6 } },
{ "start": { "line": 7 }, "end": { "line": 7 } },
{ "start": { "line": 8 }, "end": { "line": 8 } }
]
}
]
}
},
"infos": [
{
"filename": "Dockerfile",
"data": "RlJPTSBhbHBpbmU6bGF0ZXN0Cg==",
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "local://dockerfile",
"attrs": {
"local.differ": "none",
"local.followpaths": "[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]",
"local.session": "s4j58ngehdal1b5hn7msiqaqe",
"local.sharedkeyhint": "dockerfile"
}
}
},
"constraints": {}
}
},
{ "id": "step1", "op": { "Op": null }, "inputs": ["step0:0"] }
]
}
]
},
"layers": {
"step2:0": [
[
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:261da4162673b93e5c0e7700a3718d40bcc086dbf24b1ec9b54bca0b82300626",
"size": 3259190
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:bc729abf26b5aade3c4426d388b5ea6907fe357dec915ac323bb2fa592d6288f",
"size": 286218
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:7f1d6579712341e8062db43195deb2d84f63b0f2d1ed7c3d2074891085ea1b56",
"size": 116878653
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:652874aefa1343799c619d092ab9280b25f96d97939d5d796437e7288f5599c9",
"size": 156
}
]
]
}
}
}
}
}