Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: docker input for Bazel Builder and Rebuilder #2602

Open
wants to merge 56 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
72fba0c
Update builder_bazel_slsa3.yml
enteraga6 Aug 8, 2023
a70c750
Create rebuilder.sh
enteraga6 Aug 8, 2023
bb8b2eb
Update action.yml
enteraga6 Aug 8, 2023
c516876
printf --> echo
enteraga6 Aug 11, 2023
9f679ee
finish echo conversion
enteraga6 Aug 11, 2023
5f64a76
shell check
enteraga6 Aug 11, 2023
20e0b20
source shellcheck fix att #2
enteraga6 Aug 11, 2023
3c26e32
source shellcheck fix att 3
enteraga6 Aug 11, 2023
e6f8366
cat abuse shellcheck
enteraga6 Aug 11, 2023
be49f2e
binaries --> artifacts
enteraga6 Aug 11, 2023
3a5a99f
binaries --> artifacts
enteraga6 Aug 11, 2023
d033ee7
Update action.yml
enteraga6 Aug 11, 2023
ce6082d
add sha256 download output
enteraga6 Aug 11, 2023
849d25b
Update action.yml
enteraga6 Aug 11, 2023
9a27d42
set -u and used
enteraga6 Aug 11, 2023
503ae6a
use offical slsa-verifier repo
enteraga6 Aug 11, 2023
cde809a
Update internal/builders/bazel/rebuilder.sh
enteraga6 Aug 11, 2023
a5936e9
Update internal/builders/bazel/rebuilder.sh
enteraga6 Aug 11, 2023
fe3b22e
nits: indents and consistency
enteraga6 Aug 11, 2023
96f8cfa
Merge remote-tracking branch 'origin/feat-rebuilder' into feat-rebuilder
enteraga6 Aug 11, 2023
46e7587
Update internal/builders/bazel/rebuilder.sh
enteraga6 Aug 11, 2023
872e2a9
nits: consistency
enteraga6 Aug 11, 2023
2eaf9ed
Merge remote-tracking branch 'origin/feat-rebuilder' into feat-rebuilder
enteraga6 Aug 11, 2023
6441936
shellcheck
enteraga6 Aug 11, 2023
4a4769a
double quotes
enteraga6 Aug 11, 2023
62e616c
lint
enteraga6 Aug 11, 2023
cd75049
Preprend UNTRUSTED, GITHUB_ENV --> GITHUB_OUTPUT & no need for caps, …
enteraga6 Aug 11, 2023
dd40f91
UNTRUSTED_ prepend
enteraga6 Aug 11, 2023
b18ec27
prepend
enteraga6 Aug 11, 2023
0c6aaa3
debug
enteraga6 Aug 11, 2023
e34d9ec
debug
enteraga6 Aug 11, 2023
20ec135
debug
enteraga6 Aug 11, 2023
f6349e0
debug
enteraga6 Aug 11, 2023
543d632
debug
enteraga6 Aug 11, 2023
d3e2249
debug
enteraga6 Aug 11, 2023
3abc64a
debug
enteraga6 Aug 11, 2023
529f50f
Update action.yml
enteraga6 Aug 11, 2023
51d14a3
debug
enteraga6 Aug 11, 2023
b2197fb
debug
enteraga6 Aug 11, 2023
3fdb6ba
debug
enteraga6 Aug 11, 2023
89de881
debug
enteraga6 Aug 11, 2023
3e2d291
debug
enteraga6 Aug 11, 2023
a70bcc7
debug
enteraga6 Aug 11, 2023
e69e6cb
debug
enteraga6 Aug 11, 2023
9894083
debug
enteraga6 Aug 11, 2023
78a55c0
debug
enteraga6 Aug 11, 2023
15bba70
remove debug
enteraga6 Aug 11, 2023
8ba63f6
set -euo pipefail
enteraga6 Aug 11, 2023
fd11a62
lowercase typespeed
enteraga6 Aug 11, 2023
ce9dc3b
add digest with todo
enteraga6 Aug 11, 2023
113e734
start rebuilder doc
enteraga6 Aug 11, 2023
8eb66ad
docker --> env && complete rebuilder docs
enteraga6 Aug 11, 2023
773dd37
markdown lint
enteraga6 Aug 11, 2023
e47447c
lint
enteraga6 Aug 11, 2023
c940cfd
added output for actual artifacts dir name
enteraga6 Aug 11, 2023
74d4190
Merge branch 'slsa-framework:main' into feat-rebuilder
enteraga6 Oct 23, 2023
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
38 changes: 38 additions & 0 deletions .github/workflows/builder_bazel_slsa3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ on:
required: false
type: string
default: ""
env-image:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's re-use the naming from the container-based builder for consistency, unless there's a good reason not to

description: "Image for build environment to run on"
required: false
type: string
default: ""
env-image-digest:
description: >
TODO(#2630): Add verification method for digest.
The image digest of the environment image.
This must be specified in order to verify the image.
required: false
type: string
needs-runfiles:
description: >
A boolean input that if true will package the artifact's runfiles along with the artifact.
Expand Down Expand Up @@ -76,6 +88,32 @@ on:
When run on other triggers, attestations are signed and have an "intoto.sigstore" extension.
value: ${{ jobs.slsa-run.outputs.attestations-download-name }}

provenance-download-sha256:
description: >
The sha256 digest of the attestations.

Users should verify the download against this digest to prevent tampering.
value: ${{ jobs.slsa-run.outputs.attestations-download-sha256 }}

artifacts-download-name:
description: >
The name of the folder containing the built artifacts. There is a random hash at the
enteraga6 marked this conversation as resolved.
Show resolved Hide resolved
beginning of it in form <hash>-binaries to avoid collisions.
value: ${{ fromJSON(jobs.slsa-run.outputs.build-artifacts-outputs).artifacts-download-name }}

artifacts-download-sha256:
description: "SHA256 of the uploaded tarball of built artifacts."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use sha256 lower case like in other description

value: ${{ fromJSON(jobs.slsa-run.outputs.build-artifacts-outputs).artifacts-download-sha256 }}

artifacts-actual-name:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's be consistent with the name used by other builders

description: >
The name of the folder which contains the artifacts.

After downloading artifacts-download-name and extracting
the folder.tgz from inside. A folder with artifacts with
this name will be extracted.
value: ${{ fromJSON(jobs.slsa-run.outputs.build-artifacts-outputs).artifacts-actual-name }}

jobs:
slsa-setup:
permissions:
Expand Down
48 changes: 48 additions & 0 deletions internal/builders/bazel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ workflow the "Bazel builder" from now on.
- [Workflow Outputs](#workflow-outputs)
- [Provenance Format](#provenance-format)
- [Provenance Example](#provenance-example)
- [Verification and Rebuilding](#verification-and-rebuilding)
- [Verification](#verification)
- [Rebuilding](#rebuilding)

<!-- tocstop -->

Expand Down Expand Up @@ -319,3 +322,48 @@ The following is an example of the generated provenance.
}
}
```

## Verification and Rebuilding

### Verification

Verification of the provenance generated for an artifact can be done with one of two ways. Using the [`slsa-verifier`](https://github.com/slsa-framework/slsa-verifier)
as follows on their [instructions](https://github.com/slsa-framework/slsa-verifier#verification-for-github-builders) for verification of Github builder artifacts.

Verification can also be done through the passing the `--verify` flag to the rebuilder.

### Rebuilding

To rebuild your artifacts and check for reproducible builds use the Bazel Rebuilder,
which takes in the following arguments on the command line.

Arguments:

| Argument Name to Rebuilder | Required For Rebuilder | Additionally Required for Verification | Description |
| ------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--artifact_path=<path>` | Yes | | Path to the artifact to rebuild and compares checksums with. |
| `--prov_path=<path>` | Yes | | Path to the provenance of the artifact that is being rebuilt. |
| `--source_uri=<uri>` | Yes | | expected source repository that should have produced the binary, e.g. github.com/some/repo
| `--builder_id=<id>` | No | Yes | The unique builder ID who created the provenance
| `--env_image=<image>` | No | | A published image to be pull to build on top of
| `--verify` | No | Yes | Flag to verify provenance for artifact being rebuilt
| `--verbose` | No | | Flag to include extra output to track progress
| `--cleanup` | No | | Removes cloned repos (`source_uri` and `slsa-verifier`) as well as directory for rebuilt artifacts

The rebuilder does the following:

1. Verifies the provenance for the artifact to rebuild,
2. Parses out the attested build process from the provenance,
3. Clones the repo that produced it,
4. Rebuilds the inputted artifact with the attest build process,
5. Compares checksums for reproducibility

An example usage of the rebuilder is the following command:
`./rebuilder.sh --artifact_path==<path> --prov_path=<path> --source_uri=<uri> --builder_id=<id> --env_image=<image> --verify --verbose`

Using an image that dictates the build environment is needed to make reproducible rebuilding possible. The artifact
must be built on an image from a registry originally during the Github Actions Workflow run to also build on a provided image during the rebuilding process.

If no image was provided or if the artifact was not originally built on an image, the rebuilding process will use
the user's local environment, thus diminishing the chances of a reproducible rebuild due to non-determinism from the
different build environments.
60 changes: 54 additions & 6 deletions internal/builders/bazel/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ inputs:
slsa-workflow-secret14: {}
slsa-workflow-secret15: {}

outputs:
artifacts-download-name:
description: "The name of binaries folder to download"
# NOTE: This is an "untrusted" value returned from the build.
value: "${{ steps.rng.outputs.random }}-artifacts"
artifacts-download-sha256:
description: "SHA256 of the uploaded tarball of artifacts."
value: ${{ steps.generate-artifacts.outputs.sha256 }}
artifacts-actual-name:
description: >
The name of the folder which contains the artifacts.

After downloading artifacts-download-name and extracting
the folder.tgz from inside. A folder with artifacts with
this name will be extracted.
value: "bazel_builder_binaries_to_upload_to_gh_7bc972367cb286b7f36ab4457f06e369"

runs:
using: "composite"
steps:
Expand All @@ -52,20 +69,51 @@ runs:
uses: bazelbuild/setup-bazelisk@95c9bf48d0c570bb3e28e57108f3450cd67c1a44 # v2.0.0

- name: Setup Java
if: ${{ fromJson(inputs.slsa-workflow-inputs).includes-java }} == 'true'
id: java
uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
with:
distribution: "${{ fromJson(inputs.slsa-workflow-inputs).user-java-distribution }}"
java-version: "${{ fromJson(inputs.slsa-workflow-inputs).user-java-version }}"

- name: Check for Environment Image
id: env-image
Copy link
Collaborator

@laurentsimon laurentsimon Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this step? Why can't we simply use if: ${{ fromJson(inputs.slsa-workflow-inputs).env-image == '' }} in the next step?

shell: bash
run: |
if [[ -z "${{ fromJson(inputs.slsa-workflow-inputs).env-image }}" ]]
then
echo "No Environment Image provided. Will build without."
echo "use_env_image=false" >> $GITHUB_OUTPUT
else
echo "Docker image provided. Running build on Docker Image."
echo "use_env_image=true" >> $GITHUB_OUTPUT
fi

- name: Build on Environment Image
if: ${{ steps.env-image.outputs.use_env_image == 'true' }}
env:
UNTRUSTED_TARGETS: ${{ fromJson(inputs.slsa-workflow-inputs).targets }}
UNTRUSTED_FLAGS: ${{ fromJson(inputs.slsa-workflow-inputs).flags }}
UNTRUSTED_NEEDS_RUNFILES: ${{ fromJson(inputs.slsa-workflow-inputs).needs-runfiles }}
UNTRUSTED_INCLUDES_JAVA: ${{ fromJson(inputs.slsa-workflow-inputs).includes-java }}
UNTRUSTED_ENV_IMAGE: ${{ fromJson(inputs.slsa-workflow-inputs).env-image }}
shell: bash
run: |
set -euo pipefail
docker pull $UNTRUSTED_ENV_IMAGE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double quote missing

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the pull required? Will docker pull automatically as part of docker run?

curr_dir=$(basename "$(pwd)")
docker run --rm --env UNTRUSTED_TARGETS=${UNTRUSTED_TARGETS} --env UNTRUSTED_FLAGS=${UNTRUSTED_FLAGS} --env UNTRUSTED_NEEDS_RUNFILES=${UNTRUSTED_NEEDS_RUNFILES} --env UNTRUSTED_INCLUDES_JAVA=${UNTRUSTED_INCLUDES_JAVA} -v $PWD/../:/src -w /src $UNTRUSTED_ENV_IMAGE /bin/sh -c "ls && tree && cd $curr_dir && ls && tree && ./../__TOOL_ACTION_DIR__/build.sh"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double quote missing


- id: build
if: ${{ steps.env-image.outputs.use_env_image == 'false' }}
env:
TARGETS: ${{ fromJson(inputs.slsa-workflow-inputs).targets }}
FLAGS: ${{ fromJson(inputs.slsa-workflow-inputs).flags }}
NEEDS_RUNFILES: ${{ fromJson(inputs.slsa-workflow-inputs).needs-runfiles }}
INCLUDES_JAVA: ${{ fromJson(inputs.slsa-workflow-inputs).includes-java }}
UNTRUSTED_TARGETS: ${{ fromJson(inputs.slsa-workflow-inputs).targets }}
UNTRUSTED_FLAGS: ${{ fromJson(inputs.slsa-workflow-inputs).flags }}
UNTRUSTED_NEEDS_RUNFILES: ${{ fromJson(inputs.slsa-workflow-inputs).needs-runfiles }}
UNTRUSTED_INCLUDES_JAVA: ${{ fromJson(inputs.slsa-workflow-inputs).includes-java }}
shell: bash
run: ./../__TOOL_ACTION_DIR__/build.sh
run: |
./../__TOOL_ACTION_DIR__/build.sh

# rng generates a random number to avoid name collision in artifacts
# when multiple workflows run concurrently.
Expand All @@ -77,7 +125,7 @@ runs:
id: generate-artifacts
uses: slsa-framework/slsa-github-generator/.github/actions/secure-upload-folder@main
with:
name: "${{ steps.rng.outputs.random }}-binaries"
name: "${{ steps.rng.outputs.random }}-artifacts"
path: "./bazel_builder_binaries_to_upload_to_gh_7bc972367cb286b7f36ab4457f06e369" # path-to-artifact(s)

- name: Echo statement
Expand Down
26 changes: 13 additions & 13 deletions internal/builders/bazel/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ binaries_dir="bazel_builder_binaries_to_upload_to_gh_7bc972367cb286b7f36ab4457f0
mkdir ${binaries_dir}

# Transfer flags and targets to their respective arrays
IFS=' ' read -r -a build_flags <<< "${FLAGS}"
IFS=' ' read -r -a build_targets <<< "${TARGETS}"
IFS=' ' read -r -a build_flags <<< "${UNTRUSTED_FLAGS}"
IFS=' ' read -r -a build_targets <<< "${UNTRUSTED_TARGETS}"

# If the targets includes Java targets, include Java build flag
# and add Github Runner Java rule to WORKSPACE
if [[ "${INCLUDES_JAVA}" == "true" ]]
if [[ "${UNTRUSTED_INCLUDES_JAVA}" == "true" ]]
then
build_flags+=("--java_runtime_version=myjdk")

Expand All @@ -52,20 +52,20 @@ declare -A targets_set
################################################

for input in "${build_targets[@]}"; do

# Using bazel query extracts all targets from a glob pattern.
# Thus we can change Java targets to their _deploy.jar target.
for target in $(bazel query "$input"); do

# Check to see if the target is a Java target. If it is the output is a Java target.
# Note: targets that already have the _deploy.jar suffix will have no output from the query
output=$(bazel query "kind(java_binary, $target)" 2>/dev/null)

# If there is a Java target without _deploy.jar suffix, add suffix, build and add to target set.
if [[ -n "$output" ]]
then
bazel build "${build_flags[@]}" "${target}_deploy.jar"
targets_set["${target}_deploy.jar"]="1"
targets_set["${target}_deploy.jar"]="1"
else
# Build target regularly.
bazel build "${build_flags[@]}" "$target"
Expand All @@ -81,7 +81,7 @@ done
################################################

for curr_target in "${!targets_set[@]}"; do

# Removes everything up to and including the first colon
# "//src/internal:fib" --> "fib"
binary_name=${curr_target#*:}
Expand All @@ -107,7 +107,7 @@ for curr_target in "${!targets_set[@]}"; do
# Copy JAR to artifact-specific dir in ./${binaries_dir} and remove symbolic links.
file="$bazel_generated"
cp -Lr "$file" "./${binaries_dir}/$run_script_name"

# Get the path the to run-script associated with the {$curr_target}_deploy.jar
# If the user inputted the path to their local JAVABIN insert that into the run-script to define it.
# Inputting a local path to JAVABIN is needed or else run-script will not work as it points to Github Runner JAVABIN
Expand All @@ -118,19 +118,19 @@ for curr_target in "${!targets_set[@]}"; do
# to run the run-script themselves, which would not be possible as it is either set to the Github Runner VM Java bin path
# if no flag to USER_LOCAL_JAVABIN is passed in their workflow or to the path passed in their flag.
awk -v n=66 -v s=' --local_javabin=*) USER_JAVA_BIN=( "${1#--local_javabin=}" ) ;;' 'NR == n {print s} {print}' "$run_script_path" > temp_file && mv -f temp_file "$run_script_path"

# Updates Java Bin in run-script after the flags get proccessed
awk -v n=127 -v s='' 'NR == n {print s} {print}' "$run_script_path" > temp_file && mv -f temp_file "$run_script_path"
awk -v n=128 -v s='if [[ -n $USER_JAVA_BIN ]]; then JAVABIN=$USER_JAVA_BIN; fi' 'NR == n {print s} {print}' "$run_script_path" > temp_file && mv -f temp_file "$run_script_path"

cp -L "$run_script_path" "./${binaries_dir}/$run_script_name"

################################################
# #
# Logic for Non-Java Targets #
# #
################################################

else

################################################
Expand All @@ -139,7 +139,7 @@ for curr_target in "${!targets_set[@]}"; do
# #
################################################

if [[ "${NEEDS_RUNFILES}" == "true" ]]
if [[ "${UNTRUSTED_NEEDS_RUNFILES}" == "true" ]]
then
# Get file(s) generated from build with respect to the target
bazel_generated=$(bazel cquery --output=starlark --starlark:expr="'\n'.join([f.path for f in target.files.to_list()])" "$curr_target" 2>/dev/null)
Expand Down Expand Up @@ -178,7 +178,7 @@ for curr_target in "${!targets_set[@]}"; do
else
# Get file(s) generated from build with respect to the target
bazel_generated=$(bazel cquery --output=starlark --starlark:expr="'\n'.join([f.path for f in target.files.to_list()])" "$curr_target" 2>/dev/null)

# Uses a Starlark expression to pass new line seperated list of file(s) into the set of files
while read -r file; do
cp -L "$file" ./${binaries_dir}
Expand Down
Loading
Loading