diff --git a/.tekton/build-dm-verity-image-debug.yaml b/.tekton/build-dm-verity-image-debug.yaml index 5f5db98e..96e3661f 100644 --- a/.tekton/build-dm-verity-image-debug.yaml +++ b/.tekton/build-dm-verity-image-debug.yaml @@ -175,6 +175,9 @@ spec: -v $BUILD_DIR/activation-key/:/activation-key/:Z \ -t builder -f $BUILD_DIR/source/konflux/Dockerfile $BUILD_DIR/source + # Extract osc-podvm-payload digest from Dockerfile for SBOM + grep 'osc-podvm-payload@sha256:' $BUILD_DIR/source/konflux/Dockerfile | head -1 | grep -o 'sha256:[a-f0-9]*' > output/payload-digest.txt || true + time sudo podman run -d --authfile=$BUILD_DIR/.docker/config.json --rm -it --privileged --pull=newer \ -v ${BUILD_DIR}:/workspace \ -v $(pwd)/output:/output \ @@ -221,6 +224,10 @@ spec: "$BUILDAH_IMAGE" \ /script-push.sh $DISK $MEASUREMENTS + # Copy SBOM data to BUILD_DIR for transfer back to Tekton pod + cp -r output/rootfs $BUILD_DIR/ 2>/dev/null || true + cp output/payload-digest.txt $BUILD_DIR/ 2>/dev/null || true + REMOTESSHEOF cat >>scripts/script-disk-mods.sh <>scripts/podvm-setup.sh <<'REMOTESSHEOF' @@ -652,33 +666,56 @@ spec: ssh -v $SSH_ARGS "$SSH_HOST" $BUILD_DIR/scripts/script-build.sh rsync -ra "$SSH_HOST:$BUILD_DIR/tekton-results/*" "/tekton/results/" + + # Retrieve SBOM data from remote host + rsync -ra "$SSH_HOST:$BUILD_DIR/rootfs/" "/var/workdir/rootfs/" 2>/dev/null || true + rsync "$SSH_HOST:$BUILD_DIR/payload-digest.txt" "/var/workdir/" 2>/dev/null || true volumeMounts: - mountPath: /ssh name: ssh readOnly: true - mountPath: /activation-key name: activation-key + - name: sbom-syft-generate + image: ghcr.io/anchore/syft:latest@sha256:484dc49cd5869780d8a8e1b2fa2d9381cc1388fc32caeb69e8be81e3ae08bbb1 + command: + - /syft + args: + - scan + - dir:/var/workdir/rootfs + - -o + - spdx-json=/var/workdir/syft-sbom.json + volumeMounts: + - mountPath: /var/workdir + name: workdir - name: sbom-generate - image: quay.io/konflux-ci/mobster@sha256:45298b363ff4b96a084bf77a627b3e23471dcfb821eab55a3fa49a60f0ac43f3 + image: quay.io/konflux-ci/mobster@sha256:1374bc35c71781987cce00ef5efe2be42923f83def19b60a570e947189b90d15 script: | #!/bin/bash set -euo pipefail - OCI_COPY_FILE_PATH=empty-oci-copy.yaml - - # This generates an empty SBOM. Fix this in the future to generate one with real contents - echo "artifacts: []" > $OCI_COPY_FILE_PATH - IMAGE_URL=$(cat "$(results.IMAGE_URL.path)") IMAGE_DIGEST=$(cat "$(results.IMAGE_DIGEST.path)") - mobster generate \ - --output /var/workdir/sbom.json \ - oci-artifact \ - --oci-copy-yaml "$OCI_COPY_FILE_PATH" \ - --image-pullspec "$IMAGE_URL" \ - --image-digest "$IMAGE_DIGEST" \ - --sbom-type "$SBOM_TYPE" + SYFT_SBOM="/var/workdir/syft-sbom.json" + SBOM_FILE="/var/workdir/sbom.json" + PAYLOAD_DIGEST_FILE="/var/workdir/payload-digest.txt" + + MOBSTER_ARGS=( + generate --output "$SBOM_FILE" oci-image + --from-syft "$SYFT_SBOM" + --image-pullspec "$IMAGE_URL" + --image-digest "$IMAGE_DIGEST" + ) + + if [ -f "$PAYLOAD_DIGEST_FILE" ]; then + PAYLOAD_DIGEST=$(tr -d '[:space:]' < "$PAYLOAD_DIGEST_FILE") + if [ -n "$PAYLOAD_DIGEST" ]; then + MOBSTER_ARGS+=(--additional-base-image "quay.io/redhat-user-workloads/ose-osc-tenant/osc-podvm-payload:latest@${PAYLOAD_DIGEST}") + fi + fi + + mobster "${MOBSTER_ARGS[@]}" - name: upload-sbom image: quay.io/konflux-ci/appstudio-utils:1610c1fc4cfc9c9053dbefc1146904a4df6659ef@sha256:90ac97b811073cb99a23232c15a08082b586c702b85da6200cf54ef505e3c50c diff --git a/task/README.md b/task/README.md index d6000baf..1c2c3af7 100644 --- a/task/README.md +++ b/task/README.md @@ -31,7 +31,7 @@ At a high level, the task is doing the following: and verify checksum. 2. Copy sources and required files to the remote SSH build host and run a script remotely to build the QCOW image. -3. Generate a minimal SBOM for the published image. +3. Generate an SBOM for the published image. Additional steps and code exist in the task to share context with the other tasks in our CI (running before or after it). We won't detail them in this document. @@ -102,10 +102,12 @@ them to build our image. Uses Buildah to package the QCOW2 file into a container image and pushes it to the registry. -### step "sbom-generate" +### steps "sbom-syft-generate" and "sbom-generate" -This step is required for Konflux CI, but we are currently doing a very simple -(empty) SBOM. We need to improve this. +These steps generate an SPDX 2.3 SBOM for the published image. First, syft scans +the RPM database extracted from the QCOW2 guest image to produce an SBOM listing +all installed RPM packages. Then, mobster wraps the syft output with the OCI image +metadata and a reference to the `osc-podvm-payload` container (by digest). ## Build and publish the task diff --git a/task/build-dm-verity-image/0.1/build-dm-verity-image.yaml b/task/build-dm-verity-image/0.1/build-dm-verity-image.yaml index cfa0b728..c76bb708 100644 --- a/task/build-dm-verity-image/0.1/build-dm-verity-image.yaml +++ b/task/build-dm-verity-image/0.1/build-dm-verity-image.yaml @@ -175,6 +175,9 @@ spec: -v $BUILD_DIR/activation-key/:/activation-key/:Z \ -t builder -f $BUILD_DIR/source/konflux/Dockerfile $BUILD_DIR/source + # Extract osc-podvm-payload digest from Dockerfile for SBOM + grep 'osc-podvm-payload@sha256:' $BUILD_DIR/source/konflux/Dockerfile | head -1 | grep -o 'sha256:[a-f0-9]*' > output/payload-digest.txt || true + time sudo podman run -d --authfile=$BUILD_DIR/.docker/config.json --rm -it --privileged --pull=newer \ -v ${BUILD_DIR}:/workspace \ -v $(pwd)/output:/output \ @@ -221,6 +224,10 @@ spec: "$BUILDAH_IMAGE" \ /script-push.sh $DISK $MEASUREMENTS + # Copy SBOM data to BUILD_DIR for transfer back to Tekton pod + cp -r output/rootfs $BUILD_DIR/ 2>/dev/null || true + cp output/payload-digest.txt $BUILD_DIR/ 2>/dev/null || true + REMOTESSHEOF cat >>scripts/script-disk-mods.sh <>scripts/podvm-setup.sh <<'REMOTESSHEOF' @@ -652,33 +666,56 @@ spec: ssh -v $SSH_ARGS "$SSH_HOST" $BUILD_DIR/scripts/script-build.sh rsync -ra "$SSH_HOST:$BUILD_DIR/tekton-results/*" "/tekton/results/" + + # Retrieve SBOM data from remote host + rsync -ra "$SSH_HOST:$BUILD_DIR/rootfs/" "/var/workdir/rootfs/" 2>/dev/null || true + rsync "$SSH_HOST:$BUILD_DIR/payload-digest.txt" "/var/workdir/" 2>/dev/null || true volumeMounts: - mountPath: /ssh name: ssh readOnly: true - mountPath: /activation-key name: activation-key + - name: sbom-syft-generate + image: ghcr.io/anchore/syft:latest@sha256:484dc49cd5869780d8a8e1b2fa2d9381cc1388fc32caeb69e8be81e3ae08bbb1 + command: + - /syft + args: + - scan + - dir:/var/workdir/rootfs + - -o + - spdx-json=/var/workdir/syft-sbom.json + volumeMounts: + - mountPath: /var/workdir + name: workdir - name: sbom-generate - image: quay.io/konflux-ci/mobster@sha256:45298b363ff4b96a084bf77a627b3e23471dcfb821eab55a3fa49a60f0ac43f3 + image: quay.io/konflux-ci/mobster@sha256:1374bc35c71781987cce00ef5efe2be42923f83def19b60a570e947189b90d15 script: | #!/bin/bash set -euo pipefail - OCI_COPY_FILE_PATH=empty-oci-copy.yaml - - # This generates an empty SBOM. Fix this in the future to generate one with real contents - echo "artifacts: []" > $OCI_COPY_FILE_PATH - IMAGE_URL=$(cat "$(results.IMAGE_URL.path)") IMAGE_DIGEST=$(cat "$(results.IMAGE_DIGEST.path)") - mobster generate \ - --output /var/workdir/sbom.json \ - oci-artifact \ - --oci-copy-yaml "$OCI_COPY_FILE_PATH" \ - --image-pullspec "$IMAGE_URL" \ - --image-digest "$IMAGE_DIGEST" \ - --sbom-type "$SBOM_TYPE" + SYFT_SBOM="/var/workdir/syft-sbom.json" + SBOM_FILE="/var/workdir/sbom.json" + PAYLOAD_DIGEST_FILE="/var/workdir/payload-digest.txt" + + MOBSTER_ARGS=( + generate --output "$SBOM_FILE" oci-image + --from-syft "$SYFT_SBOM" + --image-pullspec "$IMAGE_URL" + --image-digest "$IMAGE_DIGEST" + ) + + if [ -f "$PAYLOAD_DIGEST_FILE" ]; then + PAYLOAD_DIGEST=$(tr -d '[:space:]' < "$PAYLOAD_DIGEST_FILE") + if [ -n "$PAYLOAD_DIGEST" ]; then + MOBSTER_ARGS+=(--additional-base-image "quay.io/redhat-user-workloads/ose-osc-tenant/osc-podvm-payload:latest@${PAYLOAD_DIGEST}") + fi + fi + + mobster "${MOBSTER_ARGS[@]}" - name: upload-sbom image: quay.io/konflux-ci/appstudio-utils:1610c1fc4cfc9c9053dbefc1146904a4df6659ef@sha256:90ac97b811073cb99a23232c15a08082b586c702b85da6200cf54ef505e3c50c