Skip to content

Commit 9fd4ab5

Browse files
authored
ci: implement immutable releases support with actions/attest (#413)
1 parent 8116d20 commit 9fd4ab5

File tree

5 files changed

+42
-65
lines changed

5 files changed

+42
-65
lines changed

.github/actions/build/action.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
name: Build distribution files
22
description: 'Build distribution files'
3-
outputs:
4-
package-hashes:
5-
description: "base64-encoded sha256 hashes of distribution files"
6-
value: ${{ steps.package-hashes.outputs.package-hashes }}
73

84
runs:
95
using: composite
106
steps:
117
- name: Build distribution files
128
shell: bash
139
run: poetry build
14-
- name: Hash build files for provenance
15-
id: package-hashes
16-
shell: bash
17-
working-directory: ./dist
18-
run: |
19-
echo "package-hashes=$(sha256sum * | base64 -w0)" >> "$GITHUB_OUTPUT"

.github/workflows/manual-publish.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ jobs:
1414
permissions:
1515
id-token: write
1616
contents: read
17-
outputs:
18-
package-hashes: ${{ steps.build.outputs.package-hashes}}
17+
attestations: write # Needed for artifact attestations
1918
steps:
2019
- uses: actions/checkout@v4
2120

@@ -33,21 +32,15 @@ jobs:
3332
ssm_parameter_pairs: "/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN"
3433

3534
- uses: ./.github/actions/build
36-
id: build
3735

3836
- name: Publish package distributions to PyPI
39-
if: ${{ inputs.dry_run == false }}
37+
if: ${{ format('{0}', inputs.dry_run) == 'false' }}
4038
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
4139
with:
4240
password: ${{env.PYPI_AUTH_TOKEN}}
4341

44-
release-provenance:
45-
needs: ["build-publish"]
46-
permissions:
47-
actions: read
48-
id-token: write
49-
contents: write
50-
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@5a775b367a56d5bd118a224a811bba288150a563 # v2.0.0
51-
with:
52-
base64-subjects: "${{ needs.build-publish.outputs.package-hashes }}"
53-
upload-assets: ${{ !inputs.dry_run }}
42+
- name: Attest build provenance
43+
if: ${{ format('{0}', inputs.dry_run) == 'false' }}
44+
uses: actions/attest@v4
45+
with:
46+
subject-path: 'dist/*'

.github/workflows/release-please.yml

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@ jobs:
99
runs-on: ubuntu-latest
1010
permissions:
1111
id-token: write # Needed if using OIDC to get release secrets.
12-
contents: write # Contents and pull-requests are for release-please to make releases.
12+
contents: write # Needed for release-please to create releases.
1313
pull-requests: write
14-
outputs:
15-
release-created: ${{ steps.release.outputs.release_created }}
16-
upload-tag-name: ${{ steps.release.outputs.tag_name }}
17-
package-hashes: ${{ steps.build.outputs.package-hashes}}
14+
attestations: write # Needed for artifact attestations
1815
steps:
1916
- uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0
2017
id: release
2118

2219
- uses: actions/checkout@v4
2320
if: ${{ steps.release.outputs.releases_created == 'true' }}
2421
with:
25-
fetch-depth: 0 # Full history is required for proper changelog generation
22+
fetch-depth: 0
2623

2724
- uses: actions/setup-python@v5
2825
if: ${{ steps.release.outputs.releases_created == 'true' }}
@@ -41,7 +38,6 @@ jobs:
4138
ssm_parameter_pairs: "/production/common/releasing/pypi/token = PYPI_AUTH_TOKEN"
4239

4340
- uses: ./.github/actions/build
44-
id: build
4541
if: ${{ steps.release.outputs.releases_created == 'true' }}
4642

4743
- uses: ./.github/actions/build-docs
@@ -53,15 +49,8 @@ jobs:
5349
with:
5450
password: ${{env.PYPI_AUTH_TOKEN}}
5551

56-
release-provenance:
57-
needs: ["release-package"]
58-
if: ${{ needs.release-package.outputs.release-created == 'true' }}
59-
permissions:
60-
actions: read
61-
id-token: write
62-
contents: write
63-
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@5a775b367a56d5bd118a224a811bba288150a563 # v2.0.0
64-
with:
65-
base64-subjects: "${{ needs.release-package.outputs.package-hashes }}"
66-
upload-assets: true
67-
upload-tag-name: ${{ needs.release-package.outputs.upload-tag-name }}
52+
- name: Attest build provenance
53+
if: ${{ steps.release.outputs.releases_created == 'true' }}
54+
uses: actions/attest@v4
55+
with:
56+
subject-path: 'dist/*'

PROVENANCE.md

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
## Verifying SDK build provenance with the SLSA framework
1+
## Verifying SDK build provenance with GitHub artifact attestations
22

3-
LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages.
3+
LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages.
44

5-
As part of [SLSA requirements for level 3 compliance](https://slsa.dev/spec/v1.0/requirements), LaunchDarkly publishes provenance about our SDK package builds using [GitHub's generic SLSA3 provenance generator](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/generic/README.md#generation-of-slsa3-provenance-for-arbitrary-projects) for distribution alongside our packages. These attestations are available for download from the GitHub release page for the release version under Assets > `multiple.intoto.jsonl`.
5+
LaunchDarkly publishes provenance about our SDK package builds using [GitHub's `actions/attest` action](https://github.com/actions/attest). These attestations are stored in GitHub's attestation API and can be verified using the [GitHub CLI](https://cli.github.com/).
66

7-
To verify SLSA provenance attestations, we recommend using [slsa-verifier](https://github.com/slsa-framework/slsa-verifier). Example usage for verifying SDK packages is included below:
7+
To verify build provenance attestations, we recommend using the [GitHub CLI `attestation verify` command](https://cli.github.com/manual/gh_attestation_verify). Example usage for verifying SDK packages is included below:
88

99
<!-- x-release-please-start-version -->
1010
```
@@ -13,32 +13,37 @@ SDK_VERSION=9.15.0
1313
```
1414
<!-- x-release-please-end -->
1515

16-
1716
```
18-
# Download package from PyPi
17+
# Download package from PyPI
1918
$ pip download --only-binary=:all: launchdarkly-server-sdk==${SDK_VERSION}
2019
21-
# Download provenance from Github release into same directory
22-
$ curl --location -O \
23-
https://github.com/launchdarkly/python-server-sdk/releases/download/${SDK_VERSION}/multiple.intoto.jsonl
24-
25-
# Run slsa-verifier to verify provenance against package artifacts
26-
$ slsa-verifier verify-artifact \
27-
--provenance-path multiple.intoto.jsonl \
28-
--source-uri github.com/launchdarkly/python-server-sdk \
29-
launchdarkly_server_sdk-${SDK_VERSION}-py3-none-any.whl
20+
# Verify provenance using the GitHub CLI
21+
$ gh attestation verify launchdarkly_server_sdk-${SDK_VERSION}-py3-none-any.whl --owner launchdarkly
3022
```
3123

3224
Below is a sample of expected output.
3325

3426
```
35-
Verified signature against tlog entry index 76390194 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77ac42700bfad5eb5597ea8bda92acb470aade248c01ccfc44047c0cd5b4433021a
36-
Verified build using builder "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.7.0" at commit c7b42a2e7486904978d77cf05a07280e7e1bbec2
37-
Verifying artifact launchdarkly_server_sdk-9.2.2-py3-none-any.whl: PASSED
27+
Loaded digest sha256:... for file://launchdarkly_server_sdk-9.15.0-py3-none-any.whl
28+
Loaded 1 attestation from GitHub API
29+
30+
The following policy criteria will be enforced:
31+
- Predicate type must match:................ https://slsa.dev/provenance/v1
32+
- Source Repository Owner URI must match:... https://github.com/launchdarkly
33+
- Subject Alternative Name must match regex: (?i)^https://github.com/launchdarkly/
34+
- OIDC Issuer must match:................... https://token.actions.githubusercontent.com
35+
36+
✓ Verification succeeded!
37+
38+
The following 1 attestation matched the policy criteria
3839
39-
PASSED: Verified SLSA provenance
40+
- Attestation #1
41+
- Build repo:..... launchdarkly/python-server-sdk
42+
- Build workflow:. .github/workflows/release-please.yml
43+
- Signer repo:.... launchdarkly/python-server-sdk
44+
- Signer workflow: .github/workflows/release-please.yml
4045
```
4146

42-
Alternatively, to verify the provenance manually, the SLSA framework specifies [recommendations for verifying build artifacts](https://slsa.dev/spec/v1.0/verifying-artifacts) in their documentation.
47+
For more information, see [GitHub's documentation on verifying artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds#verifying-artifact-attestations-with-the-github-cli).
4348

44-
**Note:** These instructions do not apply when building our SDKs from source.
49+
**Note:** These instructions do not apply when building our SDKs from source.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ We encourage pull requests and other contributions from the community. Check out
3636

3737
## Verifying SDK build provenance with the SLSA framework
3838

39-
LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. To learn more, see the [provenance guide](PROVENANCE.md).
39+
LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. To learn more, see the [provenance guide](PROVENANCE.md).
4040

4141
## About LaunchDarkly
4242

0 commit comments

Comments
 (0)