From e04e7f7b129972d009eb745b77c43de6ec26d8e6 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Thu, 4 Nov 2021 15:58:23 +0100 Subject: [PATCH] Add option to create docker image provenance --- .github/workflows/tests.yml | 6 ++--- build.sh | 26 ++++++++++++++++++++- tests/test-docker-provenance.sh | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100755 tests/test-docker-provenance.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32753dd..576ff03 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,8 +32,8 @@ jobs: - run: ./builder/build.sh -c -B MYCOOLARG=iLikeTests centos-7 # - Second one will use the vendor cache, but the Docker layer cache gets invalidated by the new cache file - run: ./builder/build.sh -c -B MYCOOLARG=iLikeTests centos-7 - # - Third one is very fast due to the Docker layer cache - - run: ./builder/build.sh -c -B MYCOOLARG=iLikeTests centos-7 + # - Third one is very fast due to the Docker layer cache, also generates provenance + - run: ./builder/build.sh -c -B MYCOOLARG=iLikeTests -t docker-images.json centos-7 + - run: ../tests/test-docker-provenance.sh docker-images.json # Do a reproducible centos-8 build (does not work for centos-7) - run: ../tests/test-centos-8-reproducible.sh - diff --git a/build.sh b/build.sh index 72416e7..a26273f 100755 --- a/build.sh +++ b/build.sh @@ -104,6 +104,7 @@ usage() { echo " -C - Run docker build with --no-cache" echo " -L =: - Overrides the default docker daemon ulimits, can be passed more than once" echo " -P - Run docker build with --pull" + echo " -t FILENAME - Generate a JSON file with provenance of the docker images used" echo echo "Kaniko mode options, ignored in docker mode:" echo " -k URL - Use URL as the cache for kaniko layers." @@ -129,6 +130,7 @@ declare -a ulimitargs declare -a buildargs verbose="" quiet="" +docker_provenance="" dockeroutdev=/dev/stdout forcetests= buildmode=docker @@ -146,7 +148,7 @@ BUILDER_MODULES='' package_match="" cache_buster="" -while getopts ":CcKk:V:R:svqm:Pp:b:e:B:L:r:" opt; do +while getopts ":CcKk:V:R:svqm:Pp:b:e:B:L:r:t:" opt; do case $opt in C) dockeropts+=('--no-cache') ;; @@ -201,6 +203,8 @@ while getopts ":CcKk:V:R:svqm:Pp:b:e:B:L:r:" opt; do ;; L) ulimitargs+=("--ulimit" "${OPTARG}") ;; + t) docker_provenance="${OPTARG}" + ;; \?) echo "Invalid option: -$OPTARG" >&2 usage ;; @@ -384,6 +388,26 @@ else fi fi +if [ ! -z "${docker_provenance}" ]; then + echo '[' > ${docker_provenance} + declare -a ignored_images + set_comma='' + while read line; do + line_elems=($line) + if [[ "${line_elems[0]}" =~ [Ff][Rr][Oo][Mm] && ! "${ignored_images[*]}" =~ "${line_elems[1]}" ]]; then + if [ ! -z "${set_comma}" ]; then + echo ',' >> ${docker_provenance} + fi + docker image inspect -f '{"uri": "docker:{{ index .RepoTags 0 }}", "digest": { {{ $s := split .ID ":" }}"{{ index $s 0}}": "{{ index $s 1}}" } }' ${line_elems[1]} >> ${docker_provenance} + set_comma="y" + fi + if [[ "${line_elems[2]}" =~ [Aa][Ss] ]]; then + ignored_images+=("${line_elems[3]}") + fi + done < ${dockerfilepath} + echo ']' >> ${docker_provenance} +fi + ####################################################################### # Copy artifacts out of the image through a container # diff --git a/tests/test-docker-provenance.sh b/tests/test-docker-provenance.sh new file mode 100755 index 0000000..f9ca6c7 --- /dev/null +++ b/tests/test-docker-provenance.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# We mostly test if we can parse the outputfile and if it is not too short + +if [ -z "$1" ]; then + echo "No input file specified" >&2 + exit 1 +fi + +if [ -z $(command -v jq) ]; then + echo "jq not installed" >&2 + exit 1 +fi + +echo -n "+ Checking if json is valid... " +out=$(jq < "${1}" 2>&1) +if [ $? -ne 0 ]; then + echo "failed" + echo "error: $out" + echo "file contents" + echo "===============================" + cat "${1}" + echo "===============================" + exit 1 +fi +echo "ok" + +echo -n "+ Checking if we did output enough image data... " +out=$(jq < "${1}" | wc -l) +if [ $out -le 4 ]; then + echo "failed" + echo "===============================" + echo "file contents: " + echo "===============================" + cat "${1}" + exit 1 +fi +echo "ok" + +exit 0