From 5bcd370ae206731dc68b60eb000b335a8d08f3ab Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 23 Jun 2023 12:13:51 +0200 Subject: [PATCH] Validate with cosign / ditch codenotary-v1 (#171) --- build.yaml | 3 ++ builder.sh | 122 +++++++++++++++++++++++++++-------------------------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/build.yaml b/build.yaml index b16534f..594cb11 100644 --- a/build.yaml +++ b/build.yaml @@ -8,6 +8,9 @@ build_from: codenotary: signer: notary@home-assistant.io base_image: notary@home-assistant.io +cosign: + base_identity: https://github.com/home-assistant/docker-base/.* + identity: https://github.com/home-assistant/builder/.* args: CAS_VERSION: "1.0.1" YQ_VERSION: "v4.13.2" diff --git a/builder.sh b/builder.sh index 2b039ab..a745b95 100755 --- a/builder.sh +++ b/builder.sh @@ -215,14 +215,17 @@ function run_build() { local docker_cli=("${!7}") local docker_tags=("${!8}") local shadow_repository=${9} - local codenotary_base=${10} - local codenotary_sign=${11} local push_images=() local cache_tag="latest" local metadata local release="${version}" local dockerfile="${build_dir}/Dockerfile" + local cosign_base_identity= + local cosign_base_issuer= + local cosign_identity= + local cosign_issuer= + local codenotary_sign= # Overwrites if bashio::var.has_value "${DOCKER_HUB}"; then repository="${DOCKER_HUB@L}"; fi @@ -243,6 +246,17 @@ function run_build() { *) bashio::exit.nok "Recived unknown architecture ${build_arch}" ;; esac + # Read build.json / cosign + if bashio::fs.file_exists "/tmp/build_config/build.json"; then + cosign_base_identity="$(jq --raw-output '.cosign.base_identity // empty' "/tmp/build_config/build.json")" + cosign_base_issuer="$(jq --raw-output '.cosign.base_issuer // "https://token.actions.githubusercontent.com"' "/tmp/build_config/build.json")" + cosign_identity="$(jq --raw-output '.cosign.identity // empty' "/tmp/build_config/build.json")" + cosign_issuer="$(jq --raw-output '.cosign.issuer // "https://token.actions.githubusercontent.com"' "/tmp/build_config/build.json")" + + # remove later + codenotary_sign="$(jq --raw-output '.codenotary.signer // empty' "/tmp/build_config/build.json")" + fi + # Adjust Qemu CPU if bashio::var.equals "${build_arch}" armhf; then docker_cli+=("--build-arg" "QEMU_CPU=arm1176") @@ -273,7 +287,7 @@ function run_build() { if \ docker image inspect "${repository}/${image}:${cache_tag}" > /dev/null 2>&1 \ - && codenotary_validate "${codenotary_sign}" "${repository}/${image}:${cache_tag}" "false" "${docker_platform}" \ + && cosign_validate "${cosign_issuer}" "${cosign_identity}" "${repository}/${image}:${cache_tag}" "${docker_platform}" "false" \ ; then docker_cli+=("--cache-from" "${repository}/${image}:${cache_tag}") else @@ -291,7 +305,7 @@ function run_build() { docker_cli+=("--label" "org.opencontainers.image.version=${release}") # Validate the base image - if ! codenotary_validate "${codenotary_base}" "${build_from}" "true" "${docker_platform}"; then + if ! cosign_validate "${cosign_base_issuer}" "${cosign_base_identity}" "${build_from}" "${docker_platform}" "true"; then bashio::exit.nok "Invalid base image ${build_from}" fi @@ -411,8 +425,6 @@ function build_base() { local shadow_repository= local raw_image= local args= - local codenotary_base= - local codenotary_sign= local docker_cli=() local docker_tags=() @@ -423,8 +435,6 @@ function build_base() { labels="$(jq --raw-output '.labels // empty | keys[]' "/tmp/build_config/build.json")" raw_image="$(jq --raw-output '.image // empty' "/tmp/build_config/build.json")" shadow_repository="$(jq --raw-output '.shadow_repository // empty' "/tmp/build_config/build.json")" - codenotary_base="$(jq --raw-output '.codenotary.base_image // empty' "/tmp/build_config/build.json")" - codenotary_sign="$(jq --raw-output '.codenotary.signer // empty' "/tmp/build_config/build.json")" fi # Set defaults build things @@ -480,8 +490,7 @@ function build_base() { # Start build run_build "${TARGET}" "${repository}" "${image}" "${VERSION_BASE}" \ - "${build_from}" "${build_arch}" docker_cli[@] docker_tags[@] "${shadow_repository}" \ - "${codenotary_base}" "${codenotary_sign}" + "${build_from}" "${build_arch}" docker_cli[@] docker_tags[@] "${shadow_repository}" } @@ -498,8 +507,6 @@ function build_addon() { local description= local url= local args= - local codenotary_base= - local codenotary_sign= local docker_cli=() local docker_tags=() @@ -508,8 +515,6 @@ function build_addon() { build_from="$(jq --raw-output ".build_from.$build_arch // empty" "/tmp/build_config/build.json")" args="$(jq --raw-output '.args // empty | keys[]' "/tmp/build_config/build.json")" shadow_repository="$(jq --raw-output '.shadow_repository // empty' "/tmp/build_config/build.json")" - codenotary_base="$(jq --raw-output '.codenotary.base_image // empty' "/tmp/build_config/build.json")" - codenotary_sign="$(jq --raw-output '.codenotary.signer // empty' "/tmp/build_config/build.json")" fi # Set defaults build things @@ -562,8 +567,7 @@ function build_addon() { # Start build run_build "$TARGET" "$repository" "$image" "$version" \ - "$build_from" "$build_arch" docker_cli[@] docker_tags[@] "${shadow_repository}" \ - "${codenotary_base}" "${codenotary_sign}" + "$build_from" "$build_arch" docker_cli[@] docker_tags[@] "${shadow_repository}" } @@ -576,7 +580,6 @@ function build_generic() { local shadow_repository= local raw_image= local args= - local codenotary_base= local codenotary_sign= local docker_cli=() local docker_tags=() @@ -588,7 +591,6 @@ function build_generic() { labels="$(jq --raw-output '.labels // empty | keys[]' "/tmp/build_config/build.json")" raw_image="$(jq --raw-output '.image // empty' "/tmp/build_config/build.json")" shadow_repository="$(jq --raw-output '.shadow_repository // empty' "/tmp/build_config/build.json")" - codenotary_base="$(jq --raw-output '.codenotary.base_image // empty' "/tmp/build_config/build.json")" codenotary_sign="$(jq --raw-output '.codenotary.signer // empty' "/tmp/build_config/build.json")" fi @@ -624,8 +626,7 @@ function build_generic() { # Start build run_build "$TARGET" "$repository" "$image" "$VERSION" \ - "$build_from" "$build_arch" docker_cli[@] docker_tags[@] "${shadow_repository}" \ - "${codenotary_base}" "${codenotary_sign}" + "$build_from" "$build_arch" docker_cli[@] docker_tags[@] "${shadow_repository}" } @@ -639,8 +640,6 @@ function build_machine() { local raw_image= local build_from= local shadow_repository= - local codenotary_base= - local codenotary_sign= local docker_cli=() local docker_tags=() @@ -651,8 +650,6 @@ function build_machine() { labels="$(jq --raw-output '.labels // empty | keys[]' "/tmp/build_config/build.json")" raw_image="$(jq --raw-output '.image // empty' "/tmp/build_config/build.json")" shadow_repository="$(jq --raw-output '.shadow_repository // empty' "/tmp/build_config/build.json")" - codenotary_base="$(jq --raw-output '.codenotary.base_image // empty' "/tmp/build_config/build.json")" - codenotary_sign="$(jq --raw-output '.codenotary.signer // empty' "/tmp/build_config/build.json")" fi # Modify build_from @@ -694,8 +691,7 @@ function build_machine() { # Start build run_build "${TARGET}" "${repository}" "${image}" "${VERSION}" \ - "${build_from}" "${build_arch}" docker_cli[@] docker_tags[@] "${shadow_repository}" \ - "${codenotary_base}" "${codenotary_sign}" + "${build_from}" "${build_arch}" docker_cli[@] docker_tags[@] "${shadow_repository}" } @@ -776,30 +772,19 @@ function codenotary_sign() { bashio::log.info "Signed ${image} with ${trust} (cas)" } -function codenotary_validate() { - local trust=$1 - local image=$2 - local pull=$3 - local platform=$4 - local success=false +#### Security cosign #### - if [ "$image" == "scratch" ]; then - bashio::log.info "Scratch image, skiping CodeNotary validation" - return 0 - fi +function cosign_sign() { + local image=$1 - if ! bashio::var.has_value "${trust}"; then - return 0 - fi + local success=false - if bashio::var.true "${pull}"; then - bashio::log.info "Download image ${image} for CodeNotary validation" - docker pull "${image}" --platform "${platform}" > /dev/null 2>&1 || bashio::exit.nok "Can't pull image ${image}" + if bashio::var.false "${DOCKER_PUSH}" || bashio::var.false "${COSIGN}"; then + return 0 fi - - for j in {1..15}; do - # shellcheck disable=SC1007 - if CAS_API_KEY= cas authenticate --signerID "${trust}" --silent "docker://${image}" ; then + + for j in {1..6}; do + if cosign sign --yes "${image}"; then success=true break fi @@ -807,26 +792,41 @@ function codenotary_validate() { done if bashio::var.false "${success}"; then - bashio::log.warning "Validation of ${image} fails!" - return 1 + bashio::exit.nok "Failed to sign the image (cosign)" fi - bashio::log.info "Image ${image} is trusted" + bashio::log.info "Signed ${image} with ${trust} (cosign)" } - -#### Security cosign #### - -function cosign_sign() { +function cosign_verify() { local image=$1 + local issuer=$2 + local identity=$3 + local image=$4 + local platform=$5 + local pull=$6 local success=false - if bashio::var.false "${DOCKER_PUSH}" || bashio::var.false "${COSIGN}"; then + # Support scratch image + if [ "$image" == "scratch" ]; then + bashio::log.info "Scratch image, skiping validation (cosign)" return 0 fi - - for j in {1..15}; do - if cosign sign --yes "${image}"; then + + # Nothing to validate against + if ! bashio::var.has_value "${issuer}" || ! bashio::var.has_value "${identity}" ; then + return 0 + fi + + # Pull image if needed + if bashio::var.true "${pull}"; then + bashio::log.info "Download image ${image} for cosign validation" + docker pull "${image}" --platform "${platform}" > /dev/null 2>&1 || bashio::exit.nok "Can't pull image ${image}" + fi + + # validate image + for j in {1..6}; do + if cosign verify --certificate-oidc-issuer-regexp "${issuer}" --certificate-identity-regexp "${identity}" "${image}"; then success=true break fi @@ -834,9 +834,13 @@ function cosign_sign() { done if bashio::var.false "${success}"; then - bashio::exit.nok "Failed to sign the image (cosign)" + bashio::log.warning "Validation of ${image} fails (cosign)!" + if bashio::var.true "${pull}"; then + docker rmi "${image}" > /dev/null 2>&1 || true + fi + return 1 fi - bashio::log.info "Signed ${image} with ${trust} (cosign)" + bashio::log.info "Image ${image} is trusted (cosign)" }