Support cosign container signing
The official Home Assistant builder supports both singing the images it generates, as well as checking the signature of base images.
This is configured in the build configuration file using:
| Key | Description |
|---|---|
| codenotary | Enable container signature with codenotary CAS. |
| codenotary.signer | Owner signer E-Mail address for this image. |
| codenotary.base_image | Verify the base container image. If you use our official images, use notary@home-assistant.io |
From: https://developers.home-assistant.io/docs/add-ons/configuration/#add-on-extended-build
It seems the official documentation is outdated or wrong, as the build.sh script looks for information under the cosign key, not the codenotary key:
# 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")"
fi
...
cosign_verify "${cosign_issuer}" "${cosign_identity}" "${repository}/${image}:${cache_tag}" "${docker_platform}" "false"
...
cosign_verify "${cosign_base_issuer}" "${cosign_base_identity}" "${build_from}" "${docker_platform}" "true"
...
image_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${repository}/${image}:${version}")
cosign_sign "${image_digest}"
...
function cosign_sign() {
local image=$1
local success=false
for j in {1..6}; do
if cosign sign --yes "${image}"; then
success=true
break
fi
sleep $((5 * j))
done
if bashio::var.false "${success}"; then
bashio::exit.nok "Failed to sign the image with cosign"
fi
bashio::log.info "Signed ${image} with cosign"
}
function cosign_verify() {
local issuer=$1
local identity=$2
local image=$3
local platform=$4
local pull=$5
local success=false
if bashio::var.false "${COSIGN_VERIFY}"; then
bashio::log.warning "Validation of ${image} signature is disabled"
return 0
fi
# Support scratch image
if [ "$image" == "scratch" ]; then
bashio::log.info "Scratch image, skiping validation with cosign"
return 0
fi
# 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
sleep $((5 * j))
done
if bashio::var.false "${success}"; then
bashio::log.warning "Validation of ${image} fails with cosign!"
if bashio::var.true "${pull}"; then
docker rmi "${image}" > /dev/null 2>&1 || true
fi
return 1
fi
bashio::log.info "Image ${image} is trusted by cosign"
}
The singing and validation is done using cosign. This is done using "keyless" singing somehow. Documentation about that can be found here.
Edited by Kevin Joris Holm