From ef9f404ebef040ae27fdfd38ac6f008e5e73990e Mon Sep 17 00:00:00 2001 From: Aleksandr Tserepov-Savolainen Date: Mon, 2 Sep 2024 11:14:22 +0300 Subject: [PATCH 1/4] Add signing to utils script - Add sign file function - Add sign relpath function - Add signing to nix build - Add signing to provenance TODO: - Get rid of hardcoded certificate name Signed-off-by: Aleksandr Tserepov-Savolainen --- utils.groovy | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/utils.groovy b/utils.groovy index 1e119ca..1d17d2b 100644 --- a/utils.groovy +++ b/utils.groovy @@ -63,6 +63,8 @@ def nix_build(String flakeref, String subdir=null) { epoch_seconds = (int) (new Date().getTime() / 1000l) env."BEG_${flakeref_trimmed}_${env.BUILD_TAG}" = epoch_seconds sh "nix build ${flakeref} ${opts}" + // Sign the build result + sign_relpath(flakeref, subdir) // Store the build end time to job's environment epoch_seconds = (int) (new Date().getTime() / 1000l) env."END_${flakeref_trimmed}_${env.BUILD_TAG}" = epoch_seconds @@ -106,6 +108,12 @@ def provenance(String flakeref, String outdir, String flakeref_trimmed) { """ opts = "--recursive --out ${outdir}/provenance.json" sh "provenance ${flakeref} ${opts}" + // Sign the provenance + path="${outdir}/provenance.json" + cert="INT-lenovo-x1-carbon-gen11-debug-x86-64-linux" + sigfile="${path}.sig" + sign_file(path, cert, sigfile) + } def sbomnix(String tool, String flakeref) { @@ -146,6 +154,33 @@ def find_img_relpath(String flakeref, String subdir) { return img_relpath } +def sign_file(String path, String cert, String sigfile) { + println "sign_file: ${path} ### ${cert} ### ${sigfile}" + res = sh( + script: """ + nix run github:tiiuae/ci-yubi#sign -- --path=${path} --cert=${cert} --sigfile=${sigfile} + """, returnStdout: true).trim() + return res +} + +def verify_signature(String path, String cert, String sigfile) { + println "verify_signature: ${path} ### ${cert} ### ${sigfile}" + res = sh( + script: """ + nix run github:tiiuae/ci-yubi#verify -- --path=${path} --cert=${cert} --sigfile=${sigfile} + """, returnStdout: true).trim() + return res +} + +def sign_relpath(String flakeref, String subdir) { + relpath = "$subdir/${find_img_relpath(flakeref, subdir)}" + signame = "${subdir}/${flakeref_trim(flakeref)}.sig" + println "sign_relpath: signame: ${signame}" + res = sign_file(relpath, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", signame) + tst = verify_signature(relpath, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", signame) + return res +} + def ghaf_hw_test(String flakeref, String device_config, String jenkins_url, String testset='_boot_') { testagent_nodes = nodesByLabel(label: 'testagent', offline: false) if (!testagent_nodes) { From f5c4d996fdb28957a41f4ef917d40db1a904a4f8 Mon Sep 17 00:00:00 2001 From: Henri Rosten Date: Tue, 3 Sep 2024 14:01:52 +0300 Subject: [PATCH 2/4] Improve signing utils This commit makes the following changes: - `nix_build` only generates signatures for images. It can still be called for targets that don't produce images, in which case the call to `sign_file` is skipped. - Remove `sign_relpath`, instead, directly call the `sign_file` from the `nix_build` for image outputs. - Remove unused return value from the `sign_file` function. - Remove `verify_signature` function which is now unused. - Use the ci-yubi/sign tool version already available in jenkins PATH. Signed-off-by: Henri Rosten --- utils.groovy | 52 +++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/utils.groovy b/utils.groovy index 1d17d2b..f355816 100644 --- a/utils.groovy +++ b/utils.groovy @@ -63,8 +63,17 @@ def nix_build(String flakeref, String subdir=null) { epoch_seconds = (int) (new Date().getTime() / 1000l) env."BEG_${flakeref_trimmed}_${env.BUILD_TAG}" = epoch_seconds sh "nix build ${flakeref} ${opts}" - // Sign the build result - sign_relpath(flakeref, subdir) + // If the build result is an image, produce a signature file + img_relpath = subdir ? find_img_relpath(flakeref, subdir, abort_on_error='false') : "" + if (img_relpath) { + target_path = "${subdir}/${img_relpath}" + sig_path = "sig/${img_relpath}.sig" + sign_file(target_path, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", sig_path) + // Archive signature file alongside the target image + archive_artifacts("sig") + } else { + println "Build result is not an image, skipping image signing" + } // Store the build end time to job's environment epoch_seconds = (int) (new Date().getTime() / 1000l) env."END_${flakeref_trimmed}_${env.BUILD_TAG}" = epoch_seconds @@ -109,11 +118,9 @@ def provenance(String flakeref, String outdir, String flakeref_trimmed) { opts = "--recursive --out ${outdir}/provenance.json" sh "provenance ${flakeref} ${opts}" // Sign the provenance - path="${outdir}/provenance.json" cert="INT-lenovo-x1-carbon-gen11-debug-x86-64-linux" - sigfile="${path}.sig" - sign_file(path, cert, sigfile) - + target_path = "${outdir}/provenance.json" + sign_file(target_path, cert, "${target_path}.sig") } def sbomnix(String tool, String flakeref) { @@ -137,7 +144,7 @@ def sbomnix(String tool, String flakeref) { archive_artifacts("scs") } -def find_img_relpath(String flakeref, String subdir) { +def find_img_relpath(String flakeref, String subdir, String abort_on_error="true") { flakeref_trimmed = "${flakeref_trim(flakeref)}" img_relpath = sh( script: """ @@ -145,9 +152,9 @@ def find_img_relpath(String flakeref, String subdir) { find -L ${flakeref_trimmed} -regex '.*\\.\\(img\\|raw\\|zst\\|iso\\)\$' -print -quit """, returnStdout: true).trim() if (!img_relpath) { - // Error out stopping the pipeline execution if image was not found - println "Error: no image found from '${subdir}/${flakeref_trimmed}'" - sh "exit 1" + println "Warning: no image found from '${subdir}/${flakeref_trimmed}'" + // Error out stopping the pipeline execution if abort_on_error was set + sh "if [ '${abort_on_error}' = 'true' ]; then exit 1; fi" } else { println "Found flakeref '${flakeref}' image '${img_relpath}'" } @@ -156,29 +163,12 @@ def find_img_relpath(String flakeref, String subdir) { def sign_file(String path, String cert, String sigfile) { println "sign_file: ${path} ### ${cert} ### ${sigfile}" - res = sh( + sh( + // 'sign' command from: https://github.com/tiiuae/ci-yubi script: """ - nix run github:tiiuae/ci-yubi#sign -- --path=${path} --cert=${cert} --sigfile=${sigfile} + mkdir -p \$(dirname '${sigfile}') || true + sign --path=${path} --cert=${cert} --sigfile=${sigfile} """, returnStdout: true).trim() - return res -} - -def verify_signature(String path, String cert, String sigfile) { - println "verify_signature: ${path} ### ${cert} ### ${sigfile}" - res = sh( - script: """ - nix run github:tiiuae/ci-yubi#verify -- --path=${path} --cert=${cert} --sigfile=${sigfile} - """, returnStdout: true).trim() - return res -} - -def sign_relpath(String flakeref, String subdir) { - relpath = "$subdir/${find_img_relpath(flakeref, subdir)}" - signame = "${subdir}/${flakeref_trim(flakeref)}.sig" - println "sign_relpath: signame: ${signame}" - res = sign_file(relpath, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", signame) - tst = verify_signature(relpath, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", signame) - return res } def ghaf_hw_test(String flakeref, String device_config, String jenkins_url, String testset='_boot_') { From 3d85d00f633cde217a29972ae975364d9a680fbf Mon Sep 17 00:00:00 2001 From: Henri Rosten Date: Wed, 4 Sep 2024 08:52:57 +0300 Subject: [PATCH 3/4] Sign with INT-Ghaf-Devenv-Common certificate Signed-off-by: Henri Rosten --- utils.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/utils.groovy b/utils.groovy index f355816..2b83646 100644 --- a/utils.groovy +++ b/utils.groovy @@ -68,7 +68,7 @@ def nix_build(String flakeref, String subdir=null) { if (img_relpath) { target_path = "${subdir}/${img_relpath}" sig_path = "sig/${img_relpath}.sig" - sign_file(target_path, "INT-lenovo-x1-carbon-gen11-debug-x86-64-linux", sig_path) + sign_file(target_path, sig_path) // Archive signature file alongside the target image archive_artifacts("sig") } else { @@ -118,9 +118,8 @@ def provenance(String flakeref, String outdir, String flakeref_trimmed) { opts = "--recursive --out ${outdir}/provenance.json" sh "provenance ${flakeref} ${opts}" // Sign the provenance - cert="INT-lenovo-x1-carbon-gen11-debug-x86-64-linux" target_path = "${outdir}/provenance.json" - sign_file(target_path, cert, "${target_path}.sig") + sign_file(target_path, "${target_path}.sig") } def sbomnix(String tool, String flakeref) { @@ -161,10 +160,10 @@ def find_img_relpath(String flakeref, String subdir, String abort_on_error="true return img_relpath } -def sign_file(String path, String cert, String sigfile) { +def sign_file(String path, String sigfile, String cert="INT-Ghaf-Devenv-Common") { println "sign_file: ${path} ### ${cert} ### ${sigfile}" sh( - // 'sign' command from: https://github.com/tiiuae/ci-yubi + // See the 'sign' command at: https://github.com/tiiuae/ci-yubi script: """ mkdir -p \$(dirname '${sigfile}') || true sign --path=${path} --cert=${cert} --sigfile=${sigfile} From c15e01ebd444ed5f39fdf9301ffdd191cfafb6e6 Mon Sep 17 00:00:00 2001 From: Henri Rosten Date: Wed, 4 Sep 2024 09:33:56 +0300 Subject: [PATCH 4/4] Don't fail the build if signing fails Output an error if signing fails, but don't fail the build. This is needed to make it possible to run the builds from private test environments without requiring the signing step to pass. Signed-off-by: Henri Rosten --- utils.groovy | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/utils.groovy b/utils.groovy index 2b83646..6a5f6c9 100644 --- a/utils.groovy +++ b/utils.groovy @@ -162,12 +162,16 @@ def find_img_relpath(String flakeref, String subdir, String abort_on_error="true def sign_file(String path, String sigfile, String cert="INT-Ghaf-Devenv-Common") { println "sign_file: ${path} ### ${cert} ### ${sigfile}" - sh( - // See the 'sign' command at: https://github.com/tiiuae/ci-yubi - script: """ - mkdir -p \$(dirname '${sigfile}') || true - sign --path=${path} --cert=${cert} --sigfile=${sigfile} + try { + sh( + // See the 'sign' command at: https://github.com/tiiuae/ci-yubi + script: """ + mkdir -p \$(dirname '${sigfile}') || true + sign --path=${path} --cert=${cert} --sigfile=${sigfile} """, returnStdout: true).trim() + } catch (Exception e) { + println "Warning: signing failed: sigfile will not be generated for: ${path}" + } } def ghaf_hw_test(String flakeref, String device_config, String jenkins_url, String testset='_boot_') {