CI: Compare released package hashes (#101)

This commit is contained in:
Michał Leszczyński
2023-03-07 03:37:42 +01:00
committed by GitHub
parent e4114e54bd
commit 0cc6ad243f
3 changed files with 138 additions and 5 deletions

View File

@@ -30,7 +30,7 @@ jobs:
Release contents:
* `halocli-linux-x64.zip` - Linux x64 build (elf; zipped)
* `halocli-win-x64.zip` - Windows 64 build (exe; zipped)
* `halocli-macos-x64.pkg` - MacOS x64 build (installer; signed)
* `halocli-macos-x64.pkg` - Mac OS x64 build (installer; signed)
**Note:** The files `*-keyless.sig` and `*-keyless.pem` constitute a part of [build audit trail](https://github.com/arx-research/libhalo/blob/master/docs/build-audit-trail.md).
- name: Store release upload URL

View File

@@ -30,7 +30,7 @@ jobs:
Release contents:
* `libhalo.js` - standalone JavaScript library for inclusion in classic HTML applications;
* `libhalo.js.LICENSE` - license information;
* `libhalo.cosign.zip` - build audit trail;
* `libhalo-npm-hash.txt` - integrity hash of the package released to npmjs.com and GitHub Packages;
**Note:** The files `*-keyless.sig` and `*-keyless.pem` constitute a part of [build audit trail](https://github.com/arx-research/libhalo/blob/master/docs/build-audit-trail.md).
- name: Store release upload URL
@@ -149,13 +149,13 @@ jobs:
run: npm ci
- name: Get package integrity hash
run: |
PKG_HASH=$(npm publish --dry-run --json 2>/dev/null | jq --raw-output '.integrity')
PKG_HASH=$(npm publish --dry-run --json 2>/dev/null | jq --raw-output '.integrity' | tr -d '\n')
echo "Package hash: ${PKG_HASH}"
echo -n "${PKG_HASH}" > "${RUNNER_TEMP}/libhalo-npm-hash.txt"
echo y | cosign sign-blob "${RUNNER_TEMP}/libhalo-npm-hash.txt" --output-certificate "${RUNNER_TEMP}/libhalo-npm-hash.txt-keyless.pem" --output-signature "${RUNNER_TEMP}/libhalo-npm-hash.txt-keyless.sig"
cosign verify-blob --cert "${RUNNER_TEMP}/libhalo-npm-hash.txt-keyless.pem" --signature "${RUNNER_TEMP}/libhalo-npm-hash.txt-keyless.sig" --certificate-identity "https://github.com/arx-research/libhalo/.github/workflows/prod_build_lib.yml@${GITHUB_REF}" --certificate-oidc-issuer https://token.actions.githubusercontent.com "${RUNNER_TEMP}/libhalo-npm-hash.txt"
- name: Publish package to npmjs
run: npm publish --json
run: npm publish --json | tee "${RUNNER_TEMP}/npmjs-publish.json"
env:
NODE_AUTH_TOKEN: ${{ secrets.RELEASE_NPM_TOKEN }}
- name: Re-setup Node.JS with GitHub pkg
@@ -164,9 +164,15 @@ jobs:
node-version: '16.x'
registry-url: https://npm.pkg.github.com/
- name: Publish package to GitHub
run: npm publish --json
run: npm publish --json | tee "${RUNNER_TEMP}/gh-publish.json"
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Compare released hashes
run: |
( cat "${RUNNER_TEMP}/npmjs-publish.json" | jq --raw-output '.integrity' | tr -d '\n' ) > "${RUNNER_TEMP}/npmjs-hash.txt"
( cat "${RUNNER_TEMP}/gh-publish.json" | jq --raw-output '.integrity' | tr -d '\n' ) > "${RUNNER_TEMP}/gh-hash.txt"
cmp -s "${RUNNER_TEMP}/libhalo-npm-hash.txt" "${RUNNER_TEMP}/npmjs-hash.txt"
cmp -s "${RUNNER_TEMP}/libhalo-npm-hash.txt" "${RUNNER_TEMP}/gh-hash.txt"
- name: Upload release asset (npm hash)
id: upload-release-asset-license
uses: actions/upload-release-asset@v1

View File

@@ -1 +1,128 @@
# LibHaLo Build Audit Trail
> **Note:** This article is solely about verifying additional build signatures. If you are not concerned that much about the build process security, please feel free to skip that read.
Each workflow that gets executed on a GitHub-hosted runner has a short-lived [OpenID Connect (OIDC) token](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) assigned by GitHub.
This token contains essential information about the exact job that was run (commit identifier, git tag, name of the workflow, and other).
During the build process, we are using the tool which validates that OIDC token against the [Fulcio CA](https://www.chainguard.dev/unchained/a-fulcio-deep-dive).
In turn, the CA is issuing an ephemeral X.509 certificate. Practically, the CA is just rewriting the information from the original OIDC token into the newly issued certificate.
Finally, we use that ephemeral certificates in order to additionally sign all release assets that are contained in the [arx-research/libhalo](https://github.com/arx-research/libhalo) repository. This technique is so-called as ["keyless signing"](https://www.chainguard.dev/unchained/zero-friction-keyless-signing-with-github-actions), and the exact tool that we are using is `cosign` (a part of "sigstore" project by The Linux Foundation).
## What does ephemeral certificate contain?
The ephemeral X.509 certificate has the following information asserted by the Fulcio CA:
* The base URL of the OIDC token, so that you can verify that the original OIDC token really came from GitHub-hosted runner (OID: `1.3.6.1.4.1.57264.1.1`);
* Name of the GitHub repository (OID: `1.3.6.1.4.1.57264.1.5`);
* The URL of the exact GitHub Actions workflow file that produced the artifact, together with the target tag name (X509v3 Subject Alternative Name);
* Exact git commit hash for which the build was triggered (OID: `1.3.6.1.4.1.57264.1.3`);
Please note that the above list is not exhaustive, but we have listed the most essential components.
## Why does it matter?
Signatures made with ephemeral certificates consistute a proof of trust, which relies on GitHub and Fulcio CA (run by The Linux Foundation)
that the build was produced by the automated process. Moreover, the real commit ID is cryptographically associated with
the produced release assets, so you are able to review the accurate code snapshot and all the pipeline scripts as they were
at the moment when the build was created. Thus, the builds are more reliable.
## File asset signatures
For each version, the build process will produce the following release assets, which could be downloaded
using the "Releases" sub-page of this repository:
* `halocli-x64-win.zip` - HaLo Command Line Interface Tool (Windows x64)
* `halocli-x64-linux.zip` - HaLo Command Line Interface Tool (Linux x64)
* `halocli-x64-macos.pkg` - HaLo Command Line Interface Tool (Mac OS x64)
* `libhalo.js` - standalone JavaScript library for usage with classic HTML applications;
Each of these assets is accompanied by the signature and certificate files in the format:
* `<asset name>-keyless.pem` - the ephemeral certificate produced by `cosign` tool;
* `<asset name>-keyless.sig` - the signature made with that ephemeral certificate;
### Script: Verifying `halocli` build
```bash
cosign verify-blob \
--signature "${BIN_NAME}-keyless.sig" \
--certificate "${BIN_NAME}-keyless.sig" \
--certificate-identity "https://github.com/arx-research/libhalo/.github/workflows/prod_build_cli.yml@refs/tags/${TAG_NAME}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
"${BIN_NAME}"
```
### Script: Verifying `libhalo.js` build
```
prod_build_lib
```
### Example
Verifying `halocli-x64-win.zip` against automated build of version `libhalo-v1.1.1`:
```bash
cosign verify-blob \
--signature halocli-x64-win.zip-keyless.sig \
--certificate halocli-x64-win.zip-keyless.sig \
--certificate-identity "https://github.com/arx-research/libhalo/.github/workflows/prod_build_cli.yml@refs/tags/libhalo-v1.1.1" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
halocli-x64-win.zip
```
## NPM package signatures
* `@arx-research/libhalo` package on npmjs.com
* `@arx-research/libhalo` package on GitHub Packages
The published package semantic versions correspond to the releases marked `libhalo-v<semver>` on the "Releases" sub-page of this GitHub repository.
Each corresponding release contains `libhalo-npm-hash.txt` file, which is an integrity hash (SHA512) of the published NPM package. This file also contains corresponding
signature made with `cosign`:
* `libhalo-npm-hash.txt-keyless.pem` - the ephemeral certificate for the hash file;
* `libhalo-npm-hash.txt-keyless.sig` - the signature made with that ephemeral certificate;
## Verification script
In order to verify the keyless signatures, you will need to [install cosign tool](https://docs.sigstore.dev/cosign/installation/).
Please store the following example bash script:
**verify.sh**
```bash
#!/usr/bin/env bash
set -e
WORKFLOW_NAME="$1"
BIN_NAME="$2"
TAG_NAME="$3"
cosign verify-blob \
--signature "${BIN_NAME}-keyless.sig" \
--certificate "${BIN_NAME}-keyless.sig" \
--certificate-identity "https://github.com/arx-research/libhalo/.github/workflows/${WORKFLOW_NAME}.yml@refs/tags/${TAG_NAME}" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
"${BIN_NAME}"
```
### Usage examples
Verify `halocli-x64-win.zip` build against the automated build of version `libhalo-v1.1.1`:
```
./verify.sh prod_build_cli halocli-x64-win.zip libhalo-v1.1.1
```
Verify `libhalo.js` build against the automated build of version `libhalo-v1.1.1`:
```
./verify.sh prod_build_lib libhalo.js libhalo-v1.1.1
```
Verify the published NPM package hash against the automated build of version `libhalo-v1.1.1`:
```
# (1) get the integrity hash of your installed version
npm view @arx-research/libhalo | grep integrity
# (2) verify the hash file from "Releases" sub-page
./verify.sh prod_build_lib libhalo-npm-hash.txt libhalo-v1.1.1
# (3) compare if `libhalo-npm-hash.txt` is the same as (1)
cat libhalo-npm-hash.txt
```