Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions .github/actions/build-linux-image/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ runs:
declare -a outputs=();

if test "${push}" = true; then
outputs+=(--output "type=image,compression=zstd,force-compression=true,oci-mediatypes=true,push=true,push-by-digest=true,name=${repo}");
outputs+=(--output "type=image,compression=zstd,force-compression=true,oci-mediatypes=true,load=true,name=${repo}");
# HACK: remove the `-t` arg from the `docker buildx build` command generated by `devcontainer build`
sed -i 's/,t.map(v=>l.push("-t",v))//g' "$(npm list -g | head -n1)"/node_modules/@devcontainers/cli/dist/spec-node/devContainersSpecCLI.js;
fi
Expand All @@ -66,8 +66,53 @@ runs:
fi
done

echo "base_image=${repo,,}:${tag}" >> "$GITHUB_OUTPUT"

- id: embed-sbom
name: Embed SBOM into ${{ inputs.repo }}:${{ inputs.tag }}-${{ inputs.arch }}
shell: bash
env:
arch: "${{ inputs.arch }}"
base_image: "${{ steps.build.outputs.base_image }}"
push: "${{ inputs.push }}"
repo: "${{ inputs.repo }}"
tag: "${{ inputs.tag }}"
run: |
set -euo pipefail

if test -z "${base_image}"; then
echo "Base image tag missing"
exit 1
fi

action_dir="${GITHUB_ACTION_PATH:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
sbom_dockerfile="${action_dir}/sbom.Dockerfile"
sbom_log="${RUNNER_TEMP}/sbom-${arch}.log"

output="type=image,compression=zstd,force-compression=true,oci-mediatypes=true,name=${repo,,}"
if test "${push}" = true; then
echo "hash_${arch}=$(grep 'exporting manifest sha256:' "${{ runner.temp }}/${arch}.log" | tail -n1 | grep -oP 'sha256:\w+')" >> "$GITHUB_OUTPUT";
output+="\,push=true,push-by-digest=true"
else
echo "hash_${arch}=" >> "$GITHUB_OUTPUT";
output+="\,load=true"
fi

docker buildx build \
--platform "linux/${arch}" \
--tag "${repo,,}:${tag}" \
--build-context base="docker-image://${base_image}" \
--build-arg SYFT_VERSION="1.32.0" \
--build-arg SOURCE_IMAGE_NAME="${base_image}" \
--output "${output}" \
--file "${sbom_dockerfile}" \
"${action_dir}" 2>&1 | tee "${sbom_log}"

digest=""
if test "${push}" = true; then
digest="$(grep 'exporting manifest sha256:' "${sbom_log}" | tail -n1 | grep -oP 'sha256:\w+')"
if test -z "${digest}"; then
echo "Failed to determine pushed digest"
exit 1
fi
fi

echo "hash_${arch}=${digest}" >> "$GITHUB_OUTPUT"
31 changes: 31 additions & 0 deletions .github/actions/build-linux-image/sbom.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# syntax=docker/dockerfile:1.6
ARG SYFT_VERSION
ARG SOURCE_IMAGE_NAME

FROM --platform=$BUILDPLATFORM alpine:3.20 AS syft-base
ARG BUILDPLATFORM
ARG SYFT_VERSION
RUN apk add --no-cache curl tar ca-certificates \
&& case "$BUILDPLATFORM" in \
linux/amd64) SYFT_ARCH="linux_amd64" ;; \
linux/arm64) SYFT_ARCH="linux_arm64" ;; \
*) echo "Unsupported BUILDPLATFORM: ${BUILDPLATFORM}" >&2 && exit 1 ;; \
esac \
&& curl -sSfL "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_${SYFT_ARCH}.tar.gz" \
| tar -xz -C /usr/local/bin syft \
&& chmod +x /usr/local/bin/syft

FROM base AS devcontainer-base

FROM syft-base AS sbom
RUN --mount=type=bind,from=devcontainer-base,source=/,target=/rootfs,ro \
mkdir -p /out && \
syft scan \
--source-name "${SOURCE_IMAGE_NAME}" \
--scope all-layers \
--output [email protected]=/out/sbom.json \
dir:/rootfs

FROM devcontainer-base
RUN mkdir -p /sbom
COPY --from=sbom /out/sbom.json /sbom/sbom.json
47 changes: 47 additions & 0 deletions windows/build-windows-image.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,50 @@ catch {
finally {
Pop-Location
}

$syftVersion = "1.32.0"
$arch = switch ($env:PROCESSOR_ARCHITECTURE.ToLower()) {
"amd64" { "windows_amd64" }
"arm64" { "windows_arm64" }
default { throw "Unsupported PROCESSOR_ARCHITECTURE '$env:PROCESSOR_ARCHITECTURE'" }
}
$syftZipName = "syft_${syftVersion}_${arch}.zip"
$syftDownload = "https://github.com/anchore/syft/releases/download/v$syftVersion/$syftZipName"
$tempRoot = Join-Path $env:TEMP ("sbom-" + [guid]::NewGuid())
$syftArchive = Join-Path $tempRoot $syftZipName
$sbomJson = Join-Path $tempRoot "sbom.json"
$contextDir = Join-Path $tempRoot "context"
New-Item -ItemType Directory -Path $tempRoot, $contextDir | Out-Null

try {
Invoke-WebRequest `
-Uri $syftDownload `
-OutFile $syftArchive `
-UseBasicParsing
Expand-Archive -Path $syftArchive -DestinationPath $tempRoot -Force

$syftExe = Get-ChildItem -Path $tempRoot -Filter syft.exe -Recurse |
Select-Object -First 1 |
ForEach-Object FullName
if (-not $syftExe) {
throw "syft.exe not found after extracting $syftZipName"
}

& $syftExe `
"docker:$ENV:IMAGE_NAME" `
--scope all-layers `
--source-name "$ENV:IMAGE_NAME" `
"--output" "[email protected]=$sbomJson"

Copy-Item -Path $sbomJson -Destination (Join-Path $contextDir "sbom.json")
Copy-Item -Path (Join-Path $PSScriptRoot "sbom.Dockerfile") -Destination (Join-Path $contextDir "Dockerfile")

docker build `
--file (Join-Path $contextDir "Dockerfile") `
--build-arg BASE_IMAGE=$ENV:IMAGE_NAME `
--tag $ENV:IMAGE_NAME `
$contextDir
}
finally {
Remove-Item -Path $tempRoot -Recurse -Force
}
9 changes: 9 additions & 0 deletions windows/sbom.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# escape=`
ARG BASE_IMAGE

FROM ${BASE_IMAGE}

SHELL ["powershell.exe", "-Command"]

RUN New-Item -ItemType Directory -Path 'C:\sbom' -Force | Out-Null
COPY ["sbom.json", "C:\\sbom\\sbom.json"]
Loading