Skip to content

Commit

Permalink
build-push-to-dockerhub: Generate SBOM & provenance attestations
Browse files Browse the repository at this point in the history
This generates an SBOM and provenance attestations. These are also
stored in one of GitHub's SigStore instances, so the provenance of
images built using this action can be verified.

We delete the `syft-sbom-report` action. This wasn't a usable action (no
`action.yaml`) anyway.

What we do here is based on [a comment][comment] from one of the GitHub
attestation folks. We only generate the provenance attestation with
GitHub directly, and use Docker to generate the SBOM and another
provenance attestation about the Dockerish bits of the build. The GitHub
attestation acts as a signature over all of that, and should handle
multi-arch manifests properly, which isn't possible with the documented
GitHub way that exists currently.

[comment]: actions/attest-sbom#60 (comment)
  • Loading branch information
iainlane committed Jul 26, 2024
1 parent 5d89d95 commit e66e8b8
Show file tree
Hide file tree
Showing 11 changed files with 677 additions and 65 deletions.
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"args": [
"--image-name",
"us.gcr.io/kubernetes-dev/infra-build",
"--dry-run"
]
}
]
}
6 changes: 5 additions & 1 deletion actions/build-push-to-dockerhub/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# build-push-to-dockerhub

This is a composite GitHub Action, used to build Docker images and push them to DockerHub.
This is a composite GitHub Action, used to build Docker images and push them to
DockerHub. A SBOM and provenenace attestation will be generated too, and pushed
to the registry and the GitHub signature store.

It uses `get-vault-secrets` action to get the DockerHub username and password from Vault.

Example of how to use this action in a repository:
Expand All @@ -13,6 +16,7 @@ on:
permissions:
contents: read
id-token: write
attestations: write

jobs:
build:
Expand Down
18 changes: 12 additions & 6 deletions actions/build-push-to-dockerhub/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,21 @@ runs:
tags: ${{ inputs.tags }}

- name: Build and push Docker image
id: build-push
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
with:
attests: type=provenance,mode=max
context: ${{ inputs.context }}
file: ${{ inputs.file }}
labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ inputs.platforms }}
sbom: true
push: ${{ inputs.push }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
file: ${{ inputs.file }}
build-args: ${{ inputs.build-args }}
target: ${{ inputs.target }}
cache-from: ${{ inputs.cache-from }}
cache-to: ${{ inputs.cache-to }}

- name: Attest build provenance
uses: actions/attest-build-provenance@5e9cb68e95676991667494a6a4e59b8a2f13e1d0 # v1.3.3
with:
push-to-registry: ${{ inputs.push }}
subject-digest: ${{ steps.build-push.outputs.digest }}
subject-name: index.docker.io/${{ inputs.repository }}
116 changes: 116 additions & 0 deletions actions/clean-up-container-registry/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Cleanup Docker Registry

description: Clean up Docker images in a registry.

inputs:
image_name:
description: "The name of the Docker image to clean up, e.g., `ghcr.io/myorg/myimage`."
required: true

tag_filter:
description: "Glob pattern to filter tags. Defaults to *."
required: false
default: "*"

exclude_tags:
description: "Comma-separated list of tags to exclude from deletion."
required: false
default: ""

keep_latest:
description: "Number of latest images to keep."
required: true

dry_run:
description: "Run the action in dry-run mode (true/false)."
required: false
default: "true"

crane_version:
description: "The version of crane to use."
required: false
default: "v0.19.2"

runs:
using: "composite"
steps:
- name: Fetch crane
id: fetch-crane
uses: robinraju/release-downloader@c39a3b234af58f0cf85888573d361fb6fa281534 # v1.10
with:
extract: false
fileName: go-containerregistry_Linux_x86_64.tar.gz
releaseId: ${{ inputs.crane_version }}
repository: google/go-containerregistry

- name: Download SLSA Verifier
uses: slsa-framework/slsa-verifier/actions/installer@eb7007070baa04976cb9e25a0d8034f8db030a86 #v2.5.1

- name: Verify Crane SLSA provenance
shell: sh
run: |
slsa-verifier \
verify-artifact \
"${{ fromJSON(steps.fetch-crane.outputs.downloaded_files)[0] }}" \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/google/go-containerregistry \
--source-tag "${{ inputs.crane_version }}"
- name: Extract crane
shell: sh
run: |
tar -xzvf go-containerregistry_Linux_x86_64.tar.gz crane
chmod +x crane
mv crane /usr/local/bin/crane
- name: List images
shell: sh
id: list_images
run: |
image_name="${{ inputs.image_name }}"
tag_filter="${{ inputs.tag_filter }}"
exclude_tags="${{ inputs.exclude_tags }}"
keep_latest="${{ inputs.keep_latest }}"
dry_run="${{ inputs.dry_run }}"
# Convert exclude_tags to an array
IFS=',' read -r -a exclude_array <<< "$exclude_tags"
# List all image tags using crane
tags=$(crane ls $image_name)
# Convert tags to an array
tags_array=($tags)
# Filter tags by the glob pattern
filtered_tags=()
for tag in "${tags_array[@]}"; do
if [[ $tag == $tag_filter ]]; then
filtered_tags+=("$tag")
fi
done
# Exclude specified tags
for exclude in "${exclude_array[@]}"; do
filtered_tags=("${filtered_tags[@]/$exclude}")
done
# Sort tags using version sort
sorted_tags=$(printf "%s\n" "${filtered_tags[@]}" | sort -V)
# Determine tags to remove
tags_to_remove=($(echo "$sorted_tags" | head -n -"$keep_latest"))
if [ "$dry_run" == "true" ]; then
echo "Dry-run mode: the following tags would be removed:"
printf "%s\n" "${tags_to_remove[@]}"
else
echo "Removing the following tags:"
printf "%s\n" "${tags_to_remove[@]}"
for tag in "${tags_to_remove[@]}"; do
crane delete $image_name:$tag
done
fi
# Output removed tags
echo "::set-output name=removed_tags::$(IFS=,; echo "${tags_to_remove[*]}")"
30 changes: 30 additions & 0 deletions actions/clean-up-container-registry/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module github.com/grafana/shared-workflows/actions/clean-up-container-registry

go 1.22.4

require (
github.com/gobwas/glob v0.2.3
github.com/google/go-containerregistry v0.19.2
github.com/urfave/cli/v2 v2.27.2
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
golang.org/x/sync v0.7.0
)

require (
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/docker/cli v24.0.0+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.1 // indirect
github.com/vbatts/tar-split v0.11.3 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
golang.org/x/sys v0.15.0 // indirect
)
70 changes: 70 additions & 0 deletions actions/clean-up-container-registry/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM=
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.0+incompatible h1:z4bf8HvONXX9Tde5lGBMQ7yCJgNahmJumdrStZAbeY4=
github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.19.2 h1:TannFKE1QSajsP6hPWb5oJNgKe1IKjHukIKDUmvsV6w=
github.com/google/go-containerregistry v0.19.2/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ=
github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
Loading

0 comments on commit e66e8b8

Please sign in to comment.