Skip to content

Commit 1c3bc8b

Browse files
authored
Merge pull request #1157 from flatcar/krnowak/slsa
overlay profiles: Fix a couple of issues with SLSA provenance stuff
2 parents d97652f + 0993a9a commit 1c3bc8b

File tree

2 files changed

+197
-121
lines changed

2 files changed

+197
-121
lines changed

changelog/changes/2023-09-20-slsa.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Update generation SLSA provenance info from v0.2 to v1.0.
Lines changed: 196 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,194 +1,269 @@
11
# Build provenance hooks
22
# ======================
33
# The functions below hook into every ebuild's execution and generate provenance files
4-
# to meet the SLSA provenance requirements (https://slsa.dev/spec/v0.1/requirements#available).
4+
# to meet the SLSA provenance requirements (https://slsa.dev/spec/v1.0/requirements#provenance-generation).
55
# All input files (source tarball / git commit hash) plus added patches / files,
66
# and all resulting installation binaries and files are captured.
7-
# The information is emitted in SLSA provenance 0.2 format (see https://slsa.dev/provenance/v0.2)
7+
# The information is emitted in SLSA provenance v1 format (see https://slsa.dev/spec/v1.0/provenance)
88

99

1010
# We only record provenance when a package is actually being built.
1111
# See profiles/coreos/base/profile.bashrc for cros_...
1212
cros_post_src_configure_enable_slsa_provenance_report() {
13-
if [ "${GENERATE_SLSA_PROVENANCE:-}" != "true" ] ; then
14-
einfo "Provenance generation not requested by build; skipping."
13+
if [[ ${GENERATE_SLSA_PROVENANCE:-} != 'true' ]] ; then
14+
einfo 'Provenance generation not requested by build; skipping.'
1515
return 0
1616
fi
17-
export generate_slsa_provenance_report="yes"
17+
export generate_slsa_provenance_report=x
1818
}
1919
# --
2020

21-
# Generate SLSA provenance 0.2 Subject information.
22-
# The information will cover all installation files shipped with a package.
23-
__slsa_provenance_subject() {
24-
local parallel="$(nproc)"
25-
local comma=""
21+
# Prints a minimal SLSA ResourceDescriptor, with uri and digest of a
22+
# specific kind. Optionally prints a leading comma.
23+
#
24+
# 1 - URI
25+
# 2 - Digest kind
26+
# 3 - Checksum
27+
# 4 - Prints a leading comma if not empty. Optional, defaults to non-empty value.
28+
__slsa_rd_printf() {
29+
local uri kind csum leading_comma
30+
uri=${1}; shift
31+
kind=${1}; shift
32+
csum=${1}; shift
33+
leading_comma=${1-x}
2634

27-
einfo " Provenance: recording subject (output)"
35+
printf '%s{ "uri": "%s", "digest": { "%s": "%s" } }\n' \
36+
"${leading_comma:+,}" "${uri}" "${kind}" "${csum}"
37+
}
38+
# --
2839

29-
echo ' "subject": ['
40+
# Generate SLSA provenance 1.0 Subject information.
41+
# The information will cover all installation files shipped with a package.
42+
__slsa_provenance_subject_members() {
43+
local parallel do_comma checksum filepath filepath_d
3044

31-
(
32-
cd "$D"
33-
find . -type f -print | sed 's:^./::' | xargs -P "$parallel" -L 1 sha512sum | sort -k2
34-
) | while read checksum filepath; do
35-
echo -en "${comma} {\"name\":\"/$filepath\", \"digest\":{\"sha512\":\"$checksum\"}}"
36-
if [ -z "$comma" ] ; then
37-
comma=',\n'
38-
fi
39-
done
40-
echo -en "\n ]"
45+
einfo ' Provenance: recording subject (output)'
46+
47+
parallel=$(nproc)
48+
do_comma=''
49+
find "${D}" -type f -print0 | \
50+
xargs -0 -P "${parallel}" -L 1 sha512sum | \
51+
sort -k2 | \
52+
while read -r checksum filepath; do
53+
filepath_d=${filepath#"${D}/"}
54+
__slsa_rd_printf "${filepath_d}" 'sha512' "${checksum}" "${do_comma}"
55+
if [[ -z ${do_comma} ]] ; then
56+
do_comma=x
57+
fi
58+
done
4159
}
4260
# --
4361

44-
__slsa_provenance_materials() {
45-
local csum="" uri="" repo="" ebuild="" ebuildcsum=""
46-
47-
local ebuild="${CATEGORY}/${PN}/${PF}.ebuild"
48-
local repopath="$(portageq get_repo_path ${ROOT:-/} coreos)"
49-
if [ -f "${repopath}/${ebuild}" ] ; then
50-
repo="coreos-overlay"
51-
ebuildcsum=$(sha1sum - < "${repopath}/${ebuild}")
52-
else
53-
repopath="$(portageq get_repo_path ${ROOT:-/} portage-stable)"
54-
if [ -f "${repopath}/${ebuild}" ] ; then
55-
repo="portage-stable"
56-
ebuildcsum=$(sha1sum - < "${repopath}/${ebuild}")
62+
__slsa_current_repo() {
63+
local ebuild=${1}; shift
64+
local -n repo_ref=${1}; shift
65+
local -n ebuild_full_path_ref=${1}; shift
66+
67+
local some_root sr_set v
68+
sr_set=
69+
for v in SYSROOT ROOT BROOT; do
70+
if [[ -n ${!v:-} ]]; then
71+
some_root=${!v%/}
72+
# strip all trailing slashes, could be easier with extglob, but
73+
# this is not guaranteed by PMS.
74+
while [[ ${some_root%/} != "${some_root}" ]]; do
75+
some_root=${some_root%/}
76+
done
77+
sr_set=x
78+
break
5779
fi
80+
done
81+
if [[ -z ${sr_set} ]]; then
82+
die "SLSA provenance: No root directory for portage configuration could be found"
5883
fi
59-
if [ -z "${repo}" ]; then
60-
die "SLSA provenance: Unable to detect ebuild repository for package '${ebuild}'"
84+
85+
local repos_conf
86+
local -a locations
87+
repos_conf="${some_root}/etc/portage/repos.conf"
88+
if [[ ! -e "${repos_conf}" ]]; then
89+
die "SLSA provenance: No repos.conf found in '${some_root}/etc/portage'"
6190
fi
62-
ebuildcsum=${ebuildcsum%% *}
91+
mapfile -t locations < <(
92+
if [[ -f ${repos_conf} ]]; then
93+
cat "${repos_conf}"
94+
else
95+
cat "${repos_conf}/"*'.conf'
96+
fi | grep '^[[:space:]]*location[[:space:]]*=' | sed -e 's/^[^=]*=[[:space:]]*//'
97+
)
98+
local loc ebuild_full
99+
for loc in "${locations[@]}"; do
100+
ebuild_full="${loc}/${ebuild}"
101+
if [[ -f ${ebuild_full} ]]; then
102+
ebuild_full_path_ref=${ebuild_full}
103+
repo_ref=${loc##*/}
104+
return 0
105+
fi
106+
done
107+
die "SLSA provenance: ebuild file not found in any repo (${locations[*]})"
108+
}
109+
# --
63110

64-
einfo " Provenance: recording ebuild material (input) '${repo}/${ebuild}'"
65-
echo ' "materials": ['
111+
__slsa_provenance_resolved_dependencies() {
112+
local scripts_hash
113+
scripts_hash=${1}; shift
66114

67-
# The ebuild. Since "configSource" in "invocation" cannot have more than one (top/level) entry
68-
# we add the ebuild and git repo checksum here, as a material.
69-
csum="$(cat "/mnt/host/source/src/scripts/.git/modules/sdk_container/src/third_party/${repo}/HEAD")"
70-
uri="git+https://github.com/flatcar/${repo}.git@${csum}#${ebuild}"
71-
echo -e " { \"uri\": \"${uri}\","
72-
echo -n " \"digest\": {\"sha1\":\"${ebuildcsum}\"} }"
115+
local ebuild spm_repo spm_ebuild_full_path
116+
ebuild="${CATEGORY}/${PN}/${PF}.ebuild"
117+
__slsa_current_repo "${ebuild}" spm_repo spm_ebuild_full_path
118+
119+
local csum
120+
csum=$(sha1sum - < "${spm_ebuild_full_path}")
121+
csum=${csum%% *}
122+
123+
einfo " Provenance: recording ebuild material (input) '${spm_repo}/${ebuild}'"
124+
125+
local repo_uri uri
126+
repo_uri="https://raw.githubusercontent.com/flatcar/scripts/${scripts_hash}/sdk_container/src/third_party/${spm_repo}"
127+
uri="${repo_uri}/${ebuild}"
128+
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
73129

74130
# The main sources
75-
if [ -n "${A}" ] ; then
131+
if [[ -n ${A} ]] ; then
76132
# Package is built from downloaded source tarball(s)
77133
# There can be multiple, and can be used conditionally based on use flags,
78134
# and even replaced with different local names ("http://... -> othername.tgz"). So
79135
# we go through what's actually used ($A), then find the corresponding source URI.
80-
local src="" prev_uri="" rename="false" orig_name=""
136+
declare -A uri_dict=() uri_orig_names=()
137+
local prev_uri='' rename='' base_name prev_base_name
138+
for uri in ${SRC_URI}; do
139+
if [[ ${uri} = '->' ]] ; then
140+
rename=x
141+
continue
142+
fi
143+
base_name=$(basename "${uri}")
144+
uri_orig_names["${uri}"]=${base_name}
145+
if [[ -n ${rename} ]] ; then
146+
unset "uri_dict[${prev_base_name}]"
147+
uri=${prev_uri}
148+
fi
149+
uri_dict["${base_name}"]=${uri}
150+
rename=
151+
prev_uri=${uri}
152+
prev_base_name=${base_name}
153+
done
154+
local src orig_name
81155
for src in ${A}; do
82-
local found="false"
83-
for uri in ${SRC_URI}; do
84-
if [ "${uri}" = "->" ] ; then
85-
rename="true"
86-
continue
87-
fi
88-
if [ "${src}" = "$(basename "${uri}")" ] ; then
89-
orig_name="${src}"
90-
if [ "${rename}" = "true" ] ; then
91-
uri="${prev_uri}"
92-
orig_name="$(basename "${uri}")"
93-
fi
94-
einfo " Provenance: recording tarball material (input) '${src}' ('${orig_name}')"
95-
csum="$(sha512sum "${DISTDIR}/${src}" | cut -d' ' -f1)"
96-
echo -e ",\n { \"uri\": \"${uri}\","
97-
echo -n " \"digest\": {\"sha512\":\"${csum}\"} }"
98-
found="true"
99-
fi
100-
rename="false"
101-
prev_uri="${uri}"
102-
done
103-
if [ "${found}" != "true" ] ; then
156+
uri=${uri_dict["${src}"]:-}
157+
if [[ -z ${uri} ]] ; then
104158
die "No SRC_URI found for source '${src}', unable to record provenance!"
105159
fi
160+
orig_name=${uri_orig_names["${uri}"]}
161+
einfo " Provenance: recording tarball material (input) '${src}' ('${orig_name}')"
162+
csum=$(sha512sum "${DISTDIR}/${src}")
163+
csum=${csum%% *}
164+
__slsa_rd_printf "${uri}" 'sha512' "${csum}"
106165
done
107-
elif [ -n "${EGIT_REPO_URI:-}" ] ; then
166+
elif [[ -n ${EGIT_REPO_URI:-} ]] ; then
108167
# package is built from repo checkout (git)
109168
einfo " Provenance: recording GIT material (input) '${EGIT_REPO_URI}'"
110-
csum="${EGIT_COMMIT}"
111-
uri="${EGIT_REPO_URI}"
112-
echo -e ",\n { \"uri\": \"${uri}\","
113-
echo -n " \"digest\": {\"sha1\":\"$csum\"} }"
169+
uri=${EGIT_REPO_URI}
170+
csum=${EGIT_COMMIT}
171+
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
114172
fi
115173

116174
# Patches / files shipped with the ebuild (if any)
117-
csum="$(cat "/mnt/host/source/src/scripts/.git/modules/sdk_container/src/third_party/${repo}/HEAD")"
118-
uri="git+https://github.com/flatcar/${repo}.git@${csum}#${CATEGORY}/${PN}/files"
119-
if [ -d "${FILESDIR}" ] ; then
175+
local files_uri
176+
files_uri="${repo_uri}/${CATEGORY}/${PN}/files"
177+
if [[ -d ${FILESDIR} ]] ; then
120178
for file in $(cd "$FILESDIR" && find . -type f | sed 's:^./::') ; do
121-
csum="$(sha1sum - <"${FILESDIR}/${file}")"
122-
csum="${csum%% *}"
179+
uri="${files_uri}/${file}"
180+
csum=$(sha1sum - <"${FILESDIR}/${file}")
181+
csum=${csum%% *}
123182
einfo " Provenance: recording ebuild material (input) '${file}'"
124-
echo -e ",\n { \"uri\": \"${uri}/${file}\","
125-
echo -n " \"digest\": {\"sha1\":\"$csum\"} }"
183+
__slsa_rd_printf "${uri}" 'sha1' "${csum}"
126184
done
127185
fi
128-
129-
echo -ne '\n ]'
130186
}
131187
# --
132188

133189
__slsa_provenance_report() {
134-
local scripts_hash="$(cat "/mnt/host/source/src/scripts/.git/HEAD")"
135-
local buildcmd="emerge"
190+
local scripts_hash buildcmd board sdk_version
191+
192+
scripts_hash=$(cat "/mnt/host/source/src/scripts/.git/HEAD")
193+
if [[ ${scripts_hash} = "ref:"* ]]; then
194+
scripts_hash=$(cat "/mnt/host/source/src/scripts/.git/${scripts_hash#'ref: '}")
195+
fi
196+
197+
buildcmd='emerge'
136198
# extract board from e.g. '/build/amd64-usr/build'. Empty if no board is set (SDK build).
137-
local board="$(echo "${CROS_BUILD_BOARD_TREE:-}" | sed -n 's:^/build/\([^/]\+\)/.*:\1:p')"
138-
if [ -n "$board" ] ; then
199+
board=$(echo "${CROS_BUILD_BOARD_TREE:-}" | sed -n 's:^/build/\([^/]\+\)/.*:\1:p')
200+
if [[ -n ${board} ]] ; then
139201
buildcmd="emerge-${board}"
140202
fi
141-
if [[ "${scripts_hash}" == "ref:"* ]]; then
142-
scripts_hash="$(cat /mnt/host/source/src/scripts/.git/${scripts_hash#ref: })"
143-
fi
144203

145204
# FIXME: Supply SDK image ID and sha256 digest along with the version tag
146-
local sdk_version="$(source /mnt/host/source/.repo/manifests/version.txt; echo ${FLATCAR_SDK_VERSION})"
205+
sdk_version=$(source /mnt/host/source/.repo/manifests/version.txt; echo "${FLATCAR_SDK_VERSION}")
147206

148207
# FIXME: add builder ID
149-
cat <<EOF
208+
#
209+
# FIXME: The buildtype should be an URI pointing to some template
210+
# where external parameters and internal parameters could be
211+
# subsituted to build the package. This probably could be what
212+
# old buildConfig.commands used to be:
213+
#
214+
# git clone "${uri}" scripts
215+
# cd scripts
216+
# git checkout "${gitCommit}"
217+
# ./run_sdk_container "${buildCmd}" "${atom}"
218+
cat <<EOF
150219
{
151-
"_type": "https://in-toto.io/Statement/v0.1",
152-
"predicateType": "https://slsa.dev/provenance/v0.2",
153-
"predicate": {
154-
"buildType": "ghcr.io/flatcar/flatcar-sdk-all:${sdk_version}",
155-
"builder": {"id": "TODO - builder ID" },
156-
"invocation": {
157-
"configSource": {
158-
"uri": "https://github.com/flatcar/scripts",
159-
"digest": {"sha1": "${scripts_hash}"}
160-
}
161-
},
162-
"buildConfig": {
163-
"commands": [
164-
"git checkout ${scripts_hash}",
165-
"git submodule init",
166-
"git submodule update",
167-
"./run_sdk_container ${buildcmd} =${CATEGORY}/${PF}"
168-
]
169-
},
220+
"_type": "https://in-toto.io/Statement/v1",
221+
"subject": [
170222
EOF
171-
__slsa_provenance_materials
172-
echo ","
173-
__slsa_provenance_subject
174-
echo ""
175-
cat <<EOF
223+
__slsa_provenance_subject_members
224+
cat <<EOF
225+
],
226+
"predicateType": "https://slsa.dev/provenance/v1",
227+
"predicate": {
228+
"buildDefinition": {
229+
"buildType": "ghcr.io/flatcar/flatcar-sdk-all:${sdk_version}",
230+
"externalParameters": {
231+
"uri": "https://github.com/flatcar/scripts",
232+
"gitCommit": { "sha1": "${scripts_hash}" },
233+
"buildCmd": "${buildcmd}",
234+
"atom": "=${CATEGORY}/${PF}"
235+
},
236+
"resolvedDependencies": [
237+
EOF
238+
__slsa_rd_printf 'https://github.com/flatcar/scripts' 'sha1' "${scripts_hash}" ''
239+
__slsa_provenance_resolved_dependencies "${scripts_hash}"
240+
cat <<EOF
241+
]
242+
},
243+
"runDetails": {
244+
"builder": {
245+
"id": "TODO - builder ID"
246+
}
247+
}
176248
}
177249
}
178250
EOF
179251
}
180252
# --
181253

182254
cros_post_src_install_generate_slsa_provenance_report() {
183-
if [ "${generate_slsa_provenance_report:-no}" != "yes" ] ; then
255+
if [[ -z ${generate_slsa_provenance_report:-} ]] ; then
184256
return
185257
fi
186258

187-
local report_file="${CATEGORY}_${PF}.json.bz2"
188-
local dest_dir="${D}/usr/share/SLSA/"
259+
local report_file dest_dir
189260

190-
__slsa_provenance_report | jq | lbzip2 -9cz > "${T}/${report_file}"
261+
report_file="${CATEGORY}_${PF}.json.zst"
262+
dest_dir="${D}/usr/share/SLSA/"
263+
264+
__slsa_provenance_report | jq | zstd -19 --stdout --compress > "${T}/${report_file}"
191265

192266
mkdir -p "${dest_dir}"
193267
mv "${T}/${report_file}" "${dest_dir}"
194268
}
269+
# --

0 commit comments

Comments
 (0)