mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-09 22:16:58 +02:00
overlay profiles: Fix a couple of issues with SLSA provenance stuff
- Update to slsa 1.0. This is only partially done, as we still need to provide a proper build type. Maybe we could reuse the Github Actions Workflow (https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1). - Stop using portageq - its use in ebuilds is banned, so eventually it would stop working. Replace it with our hack. - Stop trying to get a commit hash of coreos-overlay or portage-stable as if they were submodules. This setup is long gone, so a commit hash of toplevel scripts repo is enough. - Use zstd for compressing generated JSON files.
This commit is contained in:
parent
b26cbfd807
commit
98fbb068ce
@ -1,178 +1,250 @@
|
|||||||
# Build provenance hooks
|
# Build provenance hooks
|
||||||
# ======================
|
# ======================
|
||||||
# The functions below hook into every ebuild's execution and generate provenance files
|
# The functions below hook into every ebuild's execution and generate provenance files
|
||||||
# to meet the SLSA provenance requirements (https://slsa.dev/spec/v0.1/requirements#available).
|
# to meet the SLSA provenance requirements (https://slsa.dev/spec/v1.0/requirements#provenance-generation).
|
||||||
# All input files (source tarball / git commit hash) plus added patches / files,
|
# All input files (source tarball / git commit hash) plus added patches / files,
|
||||||
# and all resulting installation binaries and files are captured.
|
# and all resulting installation binaries and files are captured.
|
||||||
# The information is emitted in SLSA provenance 0.2 format (see https://slsa.dev/provenance/v0.2)
|
# The information is emitted in SLSA provenance v1 format (see https://slsa.dev/spec/v1.0/provenance)
|
||||||
|
|
||||||
|
|
||||||
# We only record provenance when a package is actually being built.
|
# We only record provenance when a package is actually being built.
|
||||||
# See profiles/coreos/base/profile.bashrc for cros_...
|
# See profiles/coreos/base/profile.bashrc for cros_...
|
||||||
cros_post_src_configure_enable_slsa_provenance_report() {
|
cros_post_src_configure_enable_slsa_provenance_report() {
|
||||||
if [ "${GENERATE_SLSA_PROVENANCE:-}" != "true" ] ; then
|
if [[ ${GENERATE_SLSA_PROVENANCE:-} != 'true' ]] ; then
|
||||||
einfo "Provenance generation not requested by build; skipping."
|
einfo 'Provenance generation not requested by build; skipping.'
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
export generate_slsa_provenance_report="yes"
|
export generate_slsa_provenance_report=x
|
||||||
}
|
}
|
||||||
# --
|
# --
|
||||||
|
|
||||||
# Generate SLSA provenance 0.2 Subject information.
|
# Prints a minimal SLSA ResourceDescriptor, with uri and digest of a
|
||||||
|
# specific kind. Optionally prints a leading comma.
|
||||||
|
#
|
||||||
|
# 1 - URI
|
||||||
|
# 2 - Digest kind
|
||||||
|
# 3 - Checksum
|
||||||
|
# 4 - Prints a leading comma if not empty. Optional, defaults to non-empty value.
|
||||||
|
__slsa_rd_printf() {
|
||||||
|
local uri kind csum leading_comma
|
||||||
|
uri=${1}; shift
|
||||||
|
kind=${1}; shift
|
||||||
|
csum=${1}; shift
|
||||||
|
leading_comma=${1-x}
|
||||||
|
|
||||||
|
printf '%s{ "uri": "%s", "digest": { "%s": "%s" } }\n' \
|
||||||
|
"${leading_comma:+,}" "${uri}" "${kind}" "${csum}"
|
||||||
|
}
|
||||||
|
# --
|
||||||
|
|
||||||
|
# Generate SLSA provenance 1.0 Subject information.
|
||||||
# The information will cover all installation files shipped with a package.
|
# The information will cover all installation files shipped with a package.
|
||||||
__slsa_provenance_subject() {
|
__slsa_provenance_subject_members() {
|
||||||
local parallel="$(nproc)"
|
local parallel do_comma checksum filepath filepath_d
|
||||||
local comma=""
|
|
||||||
|
|
||||||
einfo " Provenance: recording subject (output)"
|
einfo ' Provenance: recording subject (output)'
|
||||||
|
|
||||||
echo ' "subject": ['
|
parallel=$(nproc)
|
||||||
|
do_comma=''
|
||||||
|
find "${D}" -type f -print0 | \
|
||||||
|
xargs -0 -P "${parallel}" -L 1 sha512sum | \
|
||||||
|
sort -k2 | \
|
||||||
|
while read -r checksum filepath; do
|
||||||
|
filepath_d=${filepath#"${D}/"}
|
||||||
|
__slsa_rd_printf "${filepath_d}" 'sha512' "${checksum}" "${do_comma}"
|
||||||
|
if [[ -z ${do_comma} ]] ; then
|
||||||
|
do_comma=x
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
# --
|
||||||
|
|
||||||
(
|
__slsa_current_repo() {
|
||||||
cd "$D"
|
local ebuild=${1}; shift
|
||||||
find . -type f -print | sed 's:^./::' | xargs -P "$parallel" -L 1 sha512sum | sort -k2
|
local -n repo_ref=${1}; shift
|
||||||
) | while read checksum filepath; do
|
local -n ebuild_full_path_ref=${1}; shift
|
||||||
echo -en "${comma} {\"name\":\"/$filepath\", \"digest\":{\"sha512\":\"$checksum\"}}"
|
|
||||||
if [ -z "$comma" ] ; then
|
local some_root sr_set v
|
||||||
comma=',\n'
|
sr_set=
|
||||||
|
for v in SYSROOT ROOT BROOT; do
|
||||||
|
if [[ -n ${!v:-} ]]; then
|
||||||
|
some_root=${!v%/}
|
||||||
|
# strip all trailing slashes, could be easier with extglob, but
|
||||||
|
# this is not guaranteed by PMS.
|
||||||
|
while [[ ${some_root%/} != "${some_root}" ]]; do
|
||||||
|
some_root=${some_root%/}
|
||||||
|
done
|
||||||
|
sr_set=x
|
||||||
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo -en "\n ]"
|
if [[ -z ${sr_set} ]]; then
|
||||||
|
die "SLSA provenance: No root directory for portage configuration could be found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local repos_conf
|
||||||
|
local -a locations
|
||||||
|
repos_conf="${some_root}/etc/portage/repos.conf"
|
||||||
|
if [[ ! -e "${repos_conf}" ]]; then
|
||||||
|
die "SLSA provenance: No repos.conf found in '${some_root}/etc/portage'"
|
||||||
|
fi
|
||||||
|
mapfile -t locations < <(
|
||||||
|
if [[ -f ${repos_conf} ]]; then
|
||||||
|
cat "${repos_conf}"
|
||||||
|
else
|
||||||
|
cat "${repos_conf}/"*'.conf'
|
||||||
|
fi | grep '^[[:space:]]*location[[:space:]]*=' | sed -e 's/^[^=]*=[[:space:]]*//'
|
||||||
|
)
|
||||||
|
local loc ebuild_full
|
||||||
|
for loc in "${locations[@]}"; do
|
||||||
|
ebuild_full="${loc}/${ebuild}"
|
||||||
|
if [[ -f ${ebuild_full} ]]; then
|
||||||
|
ebuild_full_path_ref=${ebuild_full}
|
||||||
|
repo_ref=${loc##*/}
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
die "SLSA provenance: ebuild file not found in any repo (${locations[*]})"
|
||||||
}
|
}
|
||||||
# --
|
# --
|
||||||
|
|
||||||
__slsa_provenance_materials() {
|
__slsa_provenance_resolved_dependencies() {
|
||||||
local csum="" uri="" repo="" ebuild="" ebuildcsum=""
|
local scripts_hash
|
||||||
|
scripts_hash=${1}; shift
|
||||||
|
|
||||||
local ebuild="${CATEGORY}/${PN}/${PF}.ebuild"
|
local ebuild spm_repo spm_ebuild_full_path
|
||||||
local repopath="$(portageq get_repo_path ${ROOT:-/} coreos)"
|
ebuild="${CATEGORY}/${PN}/${PF}.ebuild"
|
||||||
if [ -f "${repopath}/${ebuild}" ] ; then
|
__slsa_current_repo "${ebuild}" spm_repo spm_ebuild_full_path
|
||||||
repo="coreos-overlay"
|
|
||||||
ebuildcsum=$(sha1sum - < "${repopath}/${ebuild}")
|
|
||||||
else
|
|
||||||
repopath="$(portageq get_repo_path ${ROOT:-/} portage-stable)"
|
|
||||||
if [ -f "${repopath}/${ebuild}" ] ; then
|
|
||||||
repo="portage-stable"
|
|
||||||
ebuildcsum=$(sha1sum - < "${repopath}/${ebuild}")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ -z "${repo}" ]; then
|
|
||||||
die "SLSA provenance: Unable to detect ebuild repository for package '${ebuild}'"
|
|
||||||
fi
|
|
||||||
ebuildcsum=${ebuildcsum%% *}
|
|
||||||
|
|
||||||
einfo " Provenance: recording ebuild material (input) '${repo}/${ebuild}'"
|
local csum
|
||||||
echo ' "materials": ['
|
csum=$(sha1sum - < "${spm_ebuild_full_path}")
|
||||||
|
csum=${csum%% *}
|
||||||
|
|
||||||
# The ebuild. Since "configSource" in "invocation" cannot have more than one (top/level) entry
|
einfo " Provenance: recording ebuild material (input) '${spm_repo}/${ebuild}'"
|
||||||
# we add the ebuild and git repo checksum here, as a material.
|
|
||||||
csum="$(cat "/mnt/host/source/src/scripts/.git/modules/sdk_container/src/third_party/${repo}/HEAD")"
|
local repo_uri uri
|
||||||
uri="git+https://github.com/flatcar/${repo}.git@${csum}#${ebuild}"
|
repo_uri="https://raw.githubusercontent.com/flatcar/scripts/${scripts_hash}/sdk_container/src/third_party/${spm_repo}"
|
||||||
echo -e " { \"uri\": \"${uri}\","
|
uri="${repo_uri}/${ebuild}"
|
||||||
echo -n " \"digest\": {\"sha1\":\"${ebuildcsum}\"} }"
|
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
|
||||||
|
|
||||||
# The main sources
|
# The main sources
|
||||||
if [ -n "${A}" ] ; then
|
if [[ -n ${A} ]] ; then
|
||||||
# Package is built from downloaded source tarball(s)
|
# Package is built from downloaded source tarball(s)
|
||||||
# There can be multiple, and can be used conditionally based on use flags,
|
# There can be multiple, and can be used conditionally based on use flags,
|
||||||
# and even replaced with different local names ("http://... -> othername.tgz"). So
|
# and even replaced with different local names ("http://... -> othername.tgz"). So
|
||||||
# we go through what's actually used ($A), then find the corresponding source URI.
|
# we go through what's actually used ($A), then find the corresponding source URI.
|
||||||
local src="" prev_uri="" rename="false" orig_name=""
|
local src prev_uri rename orig_name found
|
||||||
for src in ${A}; do
|
for src in ${A}; do
|
||||||
local found="false"
|
found=
|
||||||
|
rename=
|
||||||
|
prev_uri=''
|
||||||
|
orig_name=''
|
||||||
for uri in ${SRC_URI}; do
|
for uri in ${SRC_URI}; do
|
||||||
if [ "${uri}" = "->" ] ; then
|
if [[ ${uri} = '->' ]] ; then
|
||||||
rename="true"
|
rename=x
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if [ "${src}" = "$(basename "${uri}")" ] ; then
|
if [[ ${src} = "$(basename "${uri}")" ]] ; then
|
||||||
orig_name="${src}"
|
orig_name=${src}
|
||||||
if [ "${rename}" = "true" ] ; then
|
if [[ -n ${rename} ]] ; then
|
||||||
uri="${prev_uri}"
|
uri=${prev_uri}
|
||||||
orig_name="$(basename "${uri}")"
|
orig_name=$(basename "${uri}")
|
||||||
fi
|
fi
|
||||||
einfo " Provenance: recording tarball material (input) '${src}' ('${orig_name}')"
|
einfo " Provenance: recording tarball material (input) '${src}' ('${orig_name}')"
|
||||||
csum="$(sha512sum "${DISTDIR}/${src}" | cut -d' ' -f1)"
|
csum=$(sha512sum "${DISTDIR}/${src}")
|
||||||
echo -e ",\n { \"uri\": \"${uri}\","
|
csum=${csum%% *}
|
||||||
echo -n " \"digest\": {\"sha512\":\"${csum}\"} }"
|
__slsa_rd_printf "${uri}" 'sha512' "${csum}"
|
||||||
found="true"
|
found=x
|
||||||
fi
|
fi
|
||||||
rename="false"
|
rename=
|
||||||
prev_uri="${uri}"
|
prev_uri=${uri}
|
||||||
done
|
done
|
||||||
if [ "${found}" != "true" ] ; then
|
if [[ -z ${found} ]] ; then
|
||||||
die "No SRC_URI found for source '${src}', unable to record provenance!"
|
die "No SRC_URI found for source '${src}', unable to record provenance!"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
elif [ -n "${EGIT_REPO_URI:-}" ] ; then
|
elif [[ -n ${EGIT_REPO_URI:-} ]] ; then
|
||||||
# package is built from repo checkout (git)
|
# package is built from repo checkout (git)
|
||||||
einfo " Provenance: recording GIT material (input) '${EGIT_REPO_URI}'"
|
einfo " Provenance: recording GIT material (input) '${EGIT_REPO_URI}'"
|
||||||
csum="${EGIT_COMMIT}"
|
uri=${EGIT_REPO_URI}
|
||||||
uri="${EGIT_REPO_URI}"
|
csum=${EGIT_COMMIT}
|
||||||
echo -e ",\n { \"uri\": \"${uri}\","
|
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
|
||||||
echo -n " \"digest\": {\"sha1\":\"$csum\"} }"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Patches / files shipped with the ebuild (if any)
|
# Patches / files shipped with the ebuild (if any)
|
||||||
csum="$(cat "/mnt/host/source/src/scripts/.git/modules/sdk_container/src/third_party/${repo}/HEAD")"
|
local files_uri
|
||||||
uri="git+https://github.com/flatcar/${repo}.git@${csum}#${CATEGORY}/${PN}/files"
|
files_uri="${repo_uri}/${CATEGORY}/${PN}/files"
|
||||||
if [ -d "${FILESDIR}" ] ; then
|
if [[ -d ${FILESDIR} ]] ; then
|
||||||
for file in $(cd "$FILESDIR" && find . -type f | sed 's:^./::') ; do
|
for file in $(cd "$FILESDIR" && find . -type f | sed 's:^./::') ; do
|
||||||
csum="$(sha1sum - <"${FILESDIR}/${file}")"
|
uri="${files_uri}/${file}"
|
||||||
csum="${csum%% *}"
|
csum=$(sha1sum - <"${FILESDIR}/${file}")
|
||||||
|
csum=${csum%% *}
|
||||||
einfo " Provenance: recording ebuild material (input) '${file}'"
|
einfo " Provenance: recording ebuild material (input) '${file}'"
|
||||||
echo -e ",\n { \"uri\": \"${uri}/${file}\","
|
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
|
||||||
echo -n " \"digest\": {\"sha1\":\"$csum\"} }"
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -ne '\n ]'
|
|
||||||
}
|
}
|
||||||
# --
|
# --
|
||||||
|
|
||||||
__slsa_provenance_report() {
|
__slsa_provenance_report() {
|
||||||
local scripts_hash="$(cat "/mnt/host/source/src/scripts/.git/HEAD")"
|
local scripts_hash buildcmd board sdk_version
|
||||||
local buildcmd="emerge"
|
|
||||||
# extract board from e.g. '/build/amd64-usr/build'. Empty if no board is set (SDK build).
|
scripts_hash=$(cat "/mnt/host/source/src/scripts/.git/HEAD")
|
||||||
local board="$(echo "${CROS_BUILD_BOARD_TREE:-}" | sed -n 's:^/build/\([^/]\+\)/.*:\1:p')"
|
if [[ ${scripts_hash} = "ref:"* ]]; then
|
||||||
if [ -n "$board" ] ; then
|
scripts_hash=$(cat "/mnt/host/source/src/scripts/.git/${scripts_hash#'ref: '}")
|
||||||
buildcmd="emerge-${board}"
|
|
||||||
fi
|
fi
|
||||||
if [[ "${scripts_hash}" == "ref:"* ]]; then
|
|
||||||
scripts_hash="$(cat /mnt/host/source/src/scripts/.git/${scripts_hash#ref: })"
|
buildcmd='emerge'
|
||||||
|
# extract board from e.g. '/build/amd64-usr/build'. Empty if no board is set (SDK build).
|
||||||
|
board=$(echo "${CROS_BUILD_BOARD_TREE:-}" | sed -n 's:^/build/\([^/]\+\)/.*:\1:p')
|
||||||
|
if [[ -n ${board} ]] ; then
|
||||||
|
buildcmd="emerge-${board}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# FIXME: Supply SDK image ID and sha256 digest along with the version tag
|
# FIXME: Supply SDK image ID and sha256 digest along with the version tag
|
||||||
local sdk_version="$(source /mnt/host/source/.repo/manifests/version.txt; echo ${FLATCAR_SDK_VERSION})"
|
sdk_version=$(source /mnt/host/source/.repo/manifests/version.txt; echo "${FLATCAR_SDK_VERSION}")
|
||||||
|
|
||||||
# FIXME: add builder ID
|
# FIXME: add builder ID
|
||||||
cat <<EOF
|
#
|
||||||
|
# FIXME: The buildtype should be an URI pointing to some template
|
||||||
|
# where external parameters and internal parameters could be
|
||||||
|
# subsituted to build the package. This probably could be what
|
||||||
|
# old buildConfig.commands used to be:
|
||||||
|
#
|
||||||
|
# git clone "${uri}" scripts
|
||||||
|
# cd scripts
|
||||||
|
# git checkout "${gitCommit}"
|
||||||
|
# ./run_sdk_container "${buildCmd}" "${atom}"
|
||||||
|
cat <<EOF
|
||||||
{
|
{
|
||||||
"_type": "https://in-toto.io/Statement/v0.1",
|
"_type": "https://in-toto.io/Statement/v1",
|
||||||
"predicateType": "https://slsa.dev/provenance/v0.2",
|
"subject": [
|
||||||
"predicate": {
|
|
||||||
"buildType": "ghcr.io/flatcar/flatcar-sdk-all:${sdk_version}",
|
|
||||||
"builder": {"id": "TODO - builder ID" },
|
|
||||||
"invocation": {
|
|
||||||
"configSource": {
|
|
||||||
"uri": "https://github.com/flatcar/scripts",
|
|
||||||
"digest": {"sha1": "${scripts_hash}"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"buildConfig": {
|
|
||||||
"commands": [
|
|
||||||
"git checkout ${scripts_hash}",
|
|
||||||
"git submodule init",
|
|
||||||
"git submodule update",
|
|
||||||
"./run_sdk_container ${buildcmd} =${CATEGORY}/${PF}"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
EOF
|
EOF
|
||||||
__slsa_provenance_materials
|
__slsa_provenance_subject_members
|
||||||
echo ","
|
cat <<EOF
|
||||||
__slsa_provenance_subject
|
],
|
||||||
echo ""
|
"predicateType": "https://slsa.dev/provenance/v1",
|
||||||
cat <<EOF
|
"predicate": {
|
||||||
|
"buildDefinition": {
|
||||||
|
"buildType": "ghcr.io/flatcar/flatcar-sdk-all:${sdk_version}",
|
||||||
|
"externalParameters": {
|
||||||
|
"uri": "https://github.com/flatcar/scripts",
|
||||||
|
"gitCommit": { "sha1": "${scripts_hash}" },
|
||||||
|
"buildCmd": "${buildcmd}",
|
||||||
|
"atom": "=${CATEGORY}/${PF}"
|
||||||
|
},
|
||||||
|
"resolvedDependencies": [
|
||||||
|
EOF
|
||||||
|
__slsa_rd_printf 'https://github.com/flatcar/scripts' 'sha1' "${scripts_hash}" ''
|
||||||
|
__slsa_provenance_resolved_dependencies "${scripts_hash}"
|
||||||
|
cat <<EOF
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"runDetails": {
|
||||||
|
"builder": {
|
||||||
|
"id": "TODO - builder ID"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
@ -180,15 +252,18 @@ EOF
|
|||||||
# --
|
# --
|
||||||
|
|
||||||
cros_post_src_install_generate_slsa_provenance_report() {
|
cros_post_src_install_generate_slsa_provenance_report() {
|
||||||
if [ "${generate_slsa_provenance_report:-no}" != "yes" ] ; then
|
if [[ -z ${generate_slsa_provenance_report:-} ]] ; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local report_file="${CATEGORY}_${PF}.json.bz2"
|
local report_file dest_dir
|
||||||
local dest_dir="${D}/usr/share/SLSA/"
|
|
||||||
|
|
||||||
__slsa_provenance_report | jq | lbzip2 -9cz > "${T}/${report_file}"
|
report_file="${CATEGORY}_${PF}.json.zst"
|
||||||
|
dest_dir="${D}/usr/share/SLSA/"
|
||||||
|
|
||||||
|
__slsa_provenance_report | jq | zstd -19 --stdout --compress > "${T}/${report_file}"
|
||||||
|
|
||||||
mkdir -p "${dest_dir}"
|
mkdir -p "${dest_dir}"
|
||||||
mv "${T}/${report_file}" "${dest_dir}"
|
mv "${T}/${report_file}" "${dest_dir}"
|
||||||
}
|
}
|
||||||
|
# --
|
||||||
|
Loading…
Reference in New Issue
Block a user