In-toto attestation formatter
Overview
The in-toto attestation spec is defined here.
In-toto attestations can be generated for TaskRuns
or PipelineRuns
.
Tekton Chains generates in-toto attestations with the slsa-provenance
predicate format.
Standard in-toto predicate
The in-toto format can be enabled by running:
# For TaskRuns
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.taskrun.format": "in-toto"}}'
# For PipelineRuns
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.pipelinerun.format": "in-toto"}}'
To provide a git URL/commit as material, add a parameter named
CHAINS-GIT_COMMIT
and CHAINS-GIT_URL
. The value of these
parameters should be fed by some VCS task (e.g like this
task).
A PipeLine
example where another task checkout
has URL/commit as
task results:
- name: build
params:
- name: CHAINS-GIT_COMMIT
value: "$(tasks.checkout.results.commit)"
- name: CHAINS-GIT_URL
value: "$(tasks.checkout.results.url)"
Alternatively, CHAINS-GIT_COMMIT
and CHAINS-GIT_URL
can be results instead.
Another Pipeline
example where results are used:
spec:
results:
- description: Repository URL used for buiding the image.
name: CHAINS-GIT_URL
value: $(tasks.checkout.results.url)
- description: Repository commit used for building the image.
name: CHAINS-GIT_COMMIT
value: $(tasks.checkout.results.commit)
tasks:
- name: checkout
Type Hinting
To capture artifacts created by a task, Chains will scan the TaskRun
and PipelineRun
result for a result name *_DIGEST
. The result shall
be a string on the format alg:digest
, alg is common sha256
. If the
result is named Foo_DIGEST
, Chains will try to find a parameter or
result (in that order) with the name Foo
. The parameter or result named
Foo
shall contain the name (reference) to the built artifact.
An example (a Task):
params:
- name: IMAGE
description: Name (reference) of the image to build.
...
results:
- name: IMAGE_DIGEST
description: Digest of the image just built.
So if the IMAGE
parameter have the value gcr.io/test/foo
and the
result IMAGE_DIGEST
is sha256:abcd
then an attestation for the
subject pkg:/docker/test/foo@sha256:abcd?repository_url=gcr.io
is created. Note that image references are represented using Package
URL format.
Limitations
This is an MVP implementation of the in-toto attestation
format. More work would be required to properly capture the
Entrypoint
field in the provenance predicate, now the TaskRef
’s name
is used. Also metadata related to hermeticity/reproducibility are
currently not populated.
Examples
Example TaskRun
attestation:
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://in-toto.io/Provenance/v0.1",
"subject": [
{
"name": "file-SNAPSHOT.jar",
"digest": {
"sha256": "3f1017b520fe358d7b3796879232cd36259066ccd5bab5466cbedb444064dfed"
}
}
],
"predicate": {
"builder": {
"id": "https://configured.builder@v1"
},
"recipe": {
"type": "https://tekton.dev/attestations/chains@v1",
"definedInMaterial": 0,
"entryPoint": "maven"
},
"metadata": {
"buildStartedOn": "2021-05-11T11:05:50Z",
"buildFinishedOn": "2021-05-11T11:15:42Z",
"completeness": {
"arguments": false,
"environment": false,
"materials": false
},
"reproducible": false
},
"materials": [
{
"uri": "git+https://github.com/org/repo",
"digest": {
"git_commit": "c4b75d454655c1755ab116947e88a59ac03e28a9"
}
},
{
"uri": "pkg:docker/alpine@sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f",
"digest": {
"sha256": "69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f"
}
},
{
"uri": "pkg:docker/alpine@sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f",
"digest": {
"sha256": "69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f"
}
},
{
"uri": "pkg:docker/org/build/openjdk-11@sha256:51aa63475b5e1e2e22d1dc416556a14658a7e03a0d3c88bb9dd7b6e3411ae34a?repository_url=gcr.io",
"digest": {
"sha256": "51aa63475b5e1e2e22d1dc416556a14658a7e03a0d3c88bb9dd7b6e3411ae34a"
}
}
]
}
}
Example PipelineRun
attestation:
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": [
{
"name": "registry.example.com/minimal-container/min",
"digest": {
"sha256": "41a8ace7b880ae40708daa60387d2f181c41ecec667c93010294d1529d58c27e"
}
}
],
"predicate": {
"builder": {
"id": "https://tekton.dev/chains/v2"
},
"buildType": "https://tekton.dev/attestations/chains/pipelinerun@v2",
"invocation": {
"configSource": {},
"parameters": {
"git-repo": "https://github.com/lcarva/minimal-container",
"git-revision": "main",
"output-image": "registry.example.com/minimal-container/min:latest"
}
},
"buildConfig": {
"tasks": [
{
"name": "git-clone",
"ref": {
"name": "git-clone",
"kind": "Task"
},
"startedOn": "2022-08-29T18:42:04Z",
"finishedOn": "2022-08-29T18:42:23Z",
"status": "Succeeded",
"steps": [
{
"entryPoint": "#!/usr/bin/env sh\nset -eu\n\nif [ \"${PARAM_VERBOSE}\" = \"true\" ] ; then\n set -x\nfi\n\n\nif [ \"${WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND}\" = \"true\" ] ; then\n cp \"${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.git-credentials\" \"${PARAM_USER_HOME}/.git-credentials\"\n cp \"${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.gitconfig\" \"${PARAM_USER_HOME}/.gitconfig\"\n chmod 400 \"${PARAM_USER_HOME}/.git-credentials\"\n chmod 400 \"${PARAM_USER_HOME}/.gitconfig\"\nfi\n\nif [ \"${WORKSPACE_SSH_DIRECTORY_BOUND}\" = \"true\" ] ; then\n cp -R \"${WORKSPACE_SSH_DIRECTORY_PATH}\" \"${PARAM_USER_HOME}\"/.ssh\n chmod 700 \"${PARAM_USER_HOME}\"/.ssh\n chmod -R 400 \"${PARAM_USER_HOME}\"/.ssh/*\nfi\n\nif [ \"${WORKSPACE_SSL_CA_DIRECTORY_BOUND}\" = \"true\" ] ; then\n export GIT_SSL_CAPATH=\"${WORKSPACE_SSL_CA_DIRECTORY_PATH}\"\nfi\nCHECKOUT_DIR=\"${WORKSPACE_OUTPUT_PATH}/${PARAM_SUBDIRECTORY}\"\n\ncleandir() {\n # Delete any existing contents of the repo directory if it exists.\n #\n # We don't just \"rm -rf ${CHECKOUT_DIR}\" because ${CHECKOUT_DIR} might be \"/\"\n # or the root of a mounted volume.\n if [ -d \"${CHECKOUT_DIR}\" ] ; then\n # Delete non-hidden files and directories\n rm -rf \"${CHECKOUT_DIR:?}\"/*\n # Delete files and directories starting with . but excluding ..\n rm -rf \"${CHECKOUT_DIR}\"/.[!.]*\n # Delete files and directories starting with .. plus any other character\n rm -rf \"${CHECKOUT_DIR}\"/..?*\n fi\n}\n\nif [ \"${PARAM_DELETE_EXISTING}\" = \"true\" ] ; then\n cleandir\nfi\n\ntest -z \"${PARAM_HTTP_PROXY}\" || export HTTP_PROXY=\"${PARAM_HTTP_PROXY}\"\ntest -z \"${PARAM_HTTPS_PROXY}\" || export HTTPS_PROXY=\"${PARAM_HTTPS_PROXY}\"\ntest -z \"${PARAM_NO_PROXY}\" || export NO_PROXY=\"${PARAM_NO_PROXY}\"\n\n/ko-app/git-init \\\n -url=\"${PARAM_URL}\" \\\n -revision=\"${PARAM_REVISION}\" \\\n -refspec=\"${PARAM_REFSPEC}\" \\\n -path=\"${CHECKOUT_DIR}\" \\\n -sslVerify=\"${PARAM_SSL_VERIFY}\" \\\n -submodules=\"${PARAM_SUBMODULES}\" \\\n -depth=\"${PARAM_DEPTH}\" \\\n -sparseCheckoutDirectories=\"${PARAM_SPARSE_CHECKOUT_DIRECTORIES}\"\ncd \"${CHECKOUT_DIR}\"\nRESULT_SHA=\"$(git rev-parse HEAD)\"\nEXIT_CODE=\"$?\"\nif [ \"${EXIT_CODE}\" != 0 ] ; then\n exit \"${EXIT_CODE}\"\nfi\nprintf \"%s\" \"${RESULT_SHA}\" > \"$(results.commit.path)\"\nprintf \"%s\" \"${PARAM_URL}\" > \"$(results.url.path)\"\n",
"arguments": null,
"environment": {
"container": "clone",
"image": "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init@sha256:45dca0972541546d3625d99ee8a8fbcc768b01fc9c199d1251ebd7dfd1b8874c"
},
"annotations": null
}
],
"invocation": {
"configSource": {},
"parameters": {
"deleteExisting": "true",
"depth": "1",
"gitInitImage": "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.29.0",
"httpProxy": "",
"httpsProxy": "",
"noProxy": "",
"refspec": "",
"revision": "main",
"sparseCheckoutDirectories": "",
"sslVerify": "true",
"subdirectory": "",
"submodules": "true",
"url": "https://github.com/lcarva/minimal-container",
"userHome": "/tekton/home",
"verbose": "true"
}
},
"results": [
{
"name": "commit",
"value": "89dedecaca1b85346600c7db9939a4fe090a42ef"
},
{
"name": "url",
"value": "https://github.com/lcarva/minimal-container"
}
]
},
{
"name": "source-security-scan",
"after": [
"git-clone"
],
"ref": {
"name": "trivy-scanner",
"kind": "Task",
"bundle": "gcr.io/tekton-releases/catalog/upstream/trivy-scanner:0.1"
},
"startedOn": "2022-08-29T18:42:24Z",
"finishedOn": "2022-08-29T18:42:40Z",
"status": "Succeeded",
"steps": [
{
"entryPoint": "#!/usr/bin/env sh\n cmd=\"trivy $* $(params.IMAGE_PATH)\"\n echo \"Running trivy task with command below\"\n echo \"$cmd\"\n eval \"$cmd\"\n",
"arguments": [
"$(params.ARGS)"
],
"environment": {
"container": "trivy-scan",
"image": "docker.io/aquasec/trivy@sha256:dea76d4b50c75125cada676a87ac23de2b7ba4374752c6f908253c3b839201d9"
},
"annotations": null
}
],
"invocation": {
"configSource": {},
"parameters": {
"ARGS": [
"filesystem"
],
"IMAGE_PATH": ".",
"TRIVY_IMAGE": "docker.io/aquasec/trivy@sha256:dea76d4b50c75125cada676a87ac23de2b7ba4374752c6f908253c3b839201d9"
}
}
},
{
"name": "image-build",
"after": [
"source-security-scan"
],
"ref": {
"name": "buildah",
"kind": "ClusterTask"
},
"startedOn": "2022-08-29T18:42:41Z",
"finishedOn": "2022-08-29T18:43:03Z",
"status": "Succeeded",
"steps": [
{
"entryPoint": "[[ \"$(workspaces.sslcertdir.bound)\" == \"true\" ]] && CERT_DIR_FLAG=\"--cert-dir $(workspaces.sslcertdir.path)\"\nbuildah ${CERT_DIR_FLAG} --storage-driver=$(params.STORAGE_DRIVER) bud \\\n $(params.BUILD_EXTRA_ARGS) --format=$(params.FORMAT) \\\n --tls-verify=$(params.TLSVERIFY) --no-cache \\\n -f $(params.DOCKERFILE) -t $(params.IMAGE) $(params.CONTEXT)\n",
"arguments": null,
"environment": {
"container": "build",
"image": "quay.io/buildah/stable@sha256:0ceadda5ead6601f347a801c935e668888a72ff858ef0c7b826aca10273f9a77"
},
"annotations": null
},
{
"entryPoint": "[[ \"$(params.SKIP_PUSH)\" == \"true\" ]] && echo \"Push skipped\" && exit 0\n[[ \"$(workspaces.sslcertdir.bound)\" == \"true\" ]] && CERT_DIR_FLAG=\"--cert-dir $(workspaces.sslcertdir.path)\"\nbuildah ${CERT_DIR_FLAG} --storage-driver=$(params.STORAGE_DRIVER) push \\\n $(params.PUSH_EXTRA_ARGS) --tls-verify=$(params.TLSVERIFY) \\\n --digestfile $(workspaces.source.path)/image-digest $(params.IMAGE) \\\n docker://$(params.IMAGE)\n",
"arguments": null,
"environment": {
"container": "push",
"image": "quay.io/buildah/stable@sha256:0ceadda5ead6601f347a801c935e668888a72ff858ef0c7b826aca10273f9a77"
},
"annotations": null
},
{
"entryPoint": "cat \"$(workspaces.source.path)\"/image-digest | tee $(results.IMAGE_DIGEST.path)\necho \"$(params.IMAGE)\" | tee $(results.IMAGE_URL.path)\n",
"arguments": null,
"environment": {
"container": "digest-to-results",
"image": "quay.io/buildah/stable@sha256:0ceadda5ead6601f347a801c935e668888a72ff858ef0c7b826aca10273f9a77"
},
"annotations": null
}
],
"invocation": {
"configSource": {},
"parameters": {
"BUILDER_IMAGE": "quay.io/buildah/stable:v1.18.0",
"BUILD_EXTRA_ARGS": "",
"CONTEXT": ".",
"DOCKERFILE": "./Dockerfile",
"FORMAT": "oci",
"IMAGE": "registry.example.com/minimal-container/min:latest",
"PUSH_EXTRA_ARGS": "",
"SKIP_PUSH": "false",
"STORAGE_DRIVER": "vfs",
"TLSVERIFY": "true"
}
},
"results": [
{
"name": "IMAGE_DIGEST",
"value": "sha256:41a8ace7b880ae40708daa60387d2f181c41ecec667c93010294d1529d58c27e"
},
{
"name": "IMAGE_URL",
"value": "registry.example.com/minimal-container/min:latest\n"
}
]
},
{
"name": "image-security-scan",
"after": [
"image-build"
],
"ref": {
"name": "trivy-scanner",
"kind": "Task",
"bundle": "gcr.io/tekton-releases/catalog/upstream/trivy-scanner@sha256:e4c2916f25ce2d42ec7016c3dc3392e527442c307f43aae3ea63f4622ee5cfe4"
},
"startedOn": "2022-08-29T18:43:03Z",
"finishedOn": "2022-08-29T18:43:14Z",
"status": "Succeeded",
"steps": [
{
"entryPoint": "#!/usr/bin/env sh\n cmd=\"trivy $* $(params.IMAGE_PATH)\"\n echo \"Running trivy task with command below\"\n echo \"$cmd\"\n eval \"$cmd\"\n",
"arguments": [
"$(params.ARGS)"
],
"environment": {
"container": "trivy-scan",
"image": "docker.io/aquasec/trivy@sha256:dea76d4b50c75125cada676a87ac23de2b7ba4374752c6f908253c3b839201d9"
},
"annotations": null
}
],
"invocation": {
"configSource": {},
"parameters": {
"ARGS": [
"image"
],
"IMAGE_PATH": "registry.example.com/minimal-container/min:latest\n",
"TRIVY_IMAGE": "docker.io/aquasec/trivy@sha256:dea76d4b50c75125cada676a87ac23de2b7ba4374752c6f908253c3b839201d9"
}
}
}
]
},
"metadata": {
"buildStartedOn": "2022-08-29T18:42:04Z",
"buildFinishedOn": "2022-08-29T18:43:14Z",
"completeness": {
"parameters": false,
"environment": false,
"materials": false
},
"reproducible": false
},
"materials": [
{
"uri": "git+https://github.com/lcarva/minimal-container.git",
"digest": {
"sha1": "89dedecaca1b85346600c7db9939a4fe090a42ef"
}
}
]
}
}
Feedback
Was this page helpful?
Thanks! Tell us how we can further improve.
Sorry about that. Tell us how we can further improve.