Skip to content
Draft
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
34 changes: 34 additions & 0 deletions .github/workflows/lambda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,37 @@ jobs:
name: coverage-reports
path: ./**/coverage
retention-days: 5

microvm-lifecycle-hooks:
name: Build MicroVM lifecycle hook
runs-on: ubuntu-latest
container:
image: node:24@sha256:aa648b387728c25f81ff811799bbf8de39df66d7e2d9b3ab55cc6300cb9175d9
defaults:
run:
working-directory: ./lambdas

steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run prettier
run: yarn prettier --check "services/microvm-lifecycle-hooks/**/*.{ts,json,md}"

- name: Run linter
run: yarn eslint services/microvm-lifecycle-hooks/src

- name: Run tests
run: yarn nx test @aws-github-runner/microvm-lifecycle-hooks

- name: Build distribution
run: yarn workspace @aws-github-runner/microvm-lifecycle-hooks build
72 changes: 53 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ jobs:
- name: Build dist
working-directory: lambdas
run: yarn install --frozen-lockfile && yarn run test && yarn dist

- name: Build MicroVM lifecycle hook
working-directory: lambdas
run: yarn workspace @aws-github-runner/microvm-lifecycle-hooks build

- name: Package MicroVM lifecycle hook
working-directory: lambdas/services/microvm-lifecycle-hooks
run: (cd dist && zip -r ../microvm-lifecycle-hooks.zip .)
- name: Get installation token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: token
Expand All @@ -61,35 +69,48 @@ jobs:
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: '${{ github.workspace }}/lambdas/functions/**/*.zip'

- name: Attest MicroVM lifecycle hook
if: ${{ steps.release.outputs.releases_created == 'true' }}
id: lifecycle-hook-attest
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: '${{ github.workspace }}/lambdas/services/microvm-lifecycle-hooks/microvm-lifecycle-hooks.zip'

- name: Update release notes with attestation
if: ${{ steps.release.outputs.releases_created == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ github.event.inputs.version }}
TAG_NAME: ${{ steps.release.outputs.tag_name }}
ATTESTATION_URL: ${{ steps.attest.outputs.attestation-url }}
LIFECYCLE_HOOK_ATTESTATION_URL: ${{ steps.lifecycle-hook-attest.outputs.attestation-url }}
REPOSITORY: ${{ github.repository }}
run: |
version="${VERSION}"
tag_name="${TAG_NAME}"
attestation_url="${ATTESTATION_URL}"
lifecycle_hook_attestation_url="${LIFECYCLE_HOOK_ATTESTATION_URL}"
repository="${REPOSITORY}"
gh release view $version --json body -q '.body' > new-release-notes.md
gh release view "$tag_name" --json body -q '.body' > new-release-notes.md
echo "## Attestation" >> new-release-notes.md
echo "Attestation url: $attestation_url" >> new-release-notes.md
echo "Lambda attestation url: $attestation_url" >> new-release-notes.md
echo "MicroVM lifecycle hook attestation url: $lifecycle_hook_attestation_url" >> new-release-notes.md
echo "Verify the artifacts by running \`gh attestation verify <name_of_artifact> --repo ${repository}\`" >> new-release-notes.md
gh release edit $tag_name -F new-release-notes.md -t $tag_name
gh release edit "$tag_name" -F new-release-notes.md -t "$tag_name"

- name: Upload release assets
if: ${{ steps.release.outputs.releases_created == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ steps.release.outputs.tag_name }}
run: |
tag_name="${TAG_NAME}"
for f in $(find . -name '*.zip'); do
gh release upload $tag_name $f
done
- name: Attach attestation
while IFS= read -r -d '' f; do
gh release upload "$tag_name" "$f"
done < <(find lambdas/functions -name '*.zip' -print0)
gh release upload "$tag_name" \
"lambdas/services/microvm-lifecycle-hooks/microvm-lifecycle-hooks.zip"

- name: Attach Lambda attestation
if: ${{ steps.release.outputs.releases_created == 'true' }}
env:
ATTESTATION_BUNDLE: ${{ steps.attest.outputs.bundle-path }}
Expand All @@ -99,13 +120,26 @@ jobs:
run: |
# rename attest bundle to github-aws-runners-terraform-aws-github-runner-attestation-$attestation-id.sigstore
# OpenSSF expects the attestation bundle to be named in this format (*.sigstore)
SIGSTORE_BUNDLE=$RUNNER_TEMP/github-aws-runners-terraform-aws-github-runner-attestation-${ATTESTATION_ID}.sigstore
INTOTO_BUNDLE=$RUNNER_TEMP/github-aws-runners-terraform-aws-github-runner-attestation-${ATTESTATION_ID}.intoto.jsonl
mv ${ATTESTATION_BUNDLE} $SIGSTORE_BUNDLE
if [ -z "$SIGSTORE_BUNDLE" ]; then
echo "No attestation bundle found, skipping attachment."
exit 0
fi
gh release upload $TAG_NAME "$SIGSTORE_BUNDLE"
cat ${SIGSTORE_BUNDLE} | jq -r '.dsseEnvelope | select(.payloadType == "application/vnd.in-toto+json").payload' | base64 -d | jq .> ${INTOTO_BUNDLE}
gh release upload $TAG_NAME "${INTOTO_BUNDLE}"
sigstore_bundle="$RUNNER_TEMP/github-aws-runners-terraform-aws-github-runner-attestation-${ATTESTATION_ID}.sigstore"
intoto_bundle="$RUNNER_TEMP/github-aws-runners-terraform-aws-github-runner-attestation-${ATTESTATION_ID}.intoto.jsonl"
cp "$ATTESTATION_BUNDLE" "$sigstore_bundle"
gh release upload "$TAG_NAME" "$sigstore_bundle"
jq -r '.dsseEnvelope | select(.payloadType == "application/vnd.in-toto+json").payload' "$sigstore_bundle" \
| base64 --decode > "$intoto_bundle"
gh release upload "$TAG_NAME" "$intoto_bundle"

- name: Attach MicroVM lifecycle hook attestation
if: ${{ steps.release.outputs.releases_created == 'true' }}
env:
ATTESTATION_BUNDLE: ${{ steps.lifecycle-hook-attest.outputs.bundle-path }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ steps.release.outputs.tag_name }}
ATTESTATION_ID: ${{ steps.lifecycle-hook-attest.outputs.attestation-id }}
run: |
sigstore_bundle="$RUNNER_TEMP/github-aws-runners-microvm-lifecycle-hooks-attestation-${ATTESTATION_ID}.sigstore"
intoto_bundle="$RUNNER_TEMP/github-aws-runners-microvm-lifecycle-hooks-attestation-${ATTESTATION_ID}.intoto.jsonl"
cp "$ATTESTATION_BUNDLE" "$sigstore_bundle"
gh release upload "$TAG_NAME" "$sigstore_bundle"
jq -r '.dsseEnvelope | select(.payloadType == "application/vnd.in-toto+json").payload' "$sigstore_bundle" \
| base64 --decode > "$intoto_bundle"
gh release upload "$TAG_NAME" "$intoto_bundle"
3 changes: 2 additions & 1 deletion lambdas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"private": true,
"workspaces": [
"functions/*",
"libs/*"
"libs/*",
"services/*"
],
"scripts": {
"build": "nx run-many --target=build --all",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ describe('StorageJitConfigSource', () => {
return 'encoded-jit';
}),
};
const exportEnvironment = vi.fn((context: RunnerConfigStorageContext, target: NodeJS.ProcessEnv) => {
const exportEnvironment = vi.fn((context: RunnerConfigStorageContext) => {
events.push('export');
Object.assign(target, context);
return context;
});
const createConsumer = vi.fn((target: NodeJS.ProcessEnv) => {
events.push('create');
Expand Down Expand Up @@ -61,9 +61,7 @@ describe('StorageJitConfigSource', () => {
it('rejects storage context changes after the one-time environment export', async () => {
const environment: NodeJS.ProcessEnv = {};
const consumer: RunnerConfigConsumer = { consume: vi.fn().mockResolvedValue('encoded-jit') };
const exportEnvironment = vi.fn((context: RunnerConfigStorageContext, target: NodeJS.ProcessEnv) => {
Object.assign(target, context);
});
const exportEnvironment = vi.fn((context: RunnerConfigStorageContext) => context);
const createConsumer = vi.fn().mockReturnValue(consumer);
const source = new StorageJitConfigSource({ createConsumer, environment, exportEnvironment });
const options = { deadlineMs: 123_456, signal: new AbortController().signal };
Expand Down
2 changes: 1 addition & 1 deletion lambdas/services/microvm-lifecycle-hooks/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class StorageJitConfigSource implements JitConfigSource {
public async consume(context: RunContext, options: ConsumeOptions): Promise<RunnerBootstrap> {
const fingerprint = storageContextFingerprint(context.storage);
if (this.exportedStorageFingerprint === undefined) {
this.exportEnvironment(context.storage, this.environment);
Object.assign(this.environment, this.exportEnvironment(context.storage));
this.exportedStorageFingerprint = fingerprint;
} else if (this.exportedStorageFingerprint !== fingerprint) {
throw new Error('runner configuration storage context cannot change after initialization');
Expand Down
10 changes: 10 additions & 0 deletions lambdas/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,16 @@ __metadata:
languageName: unknown
linkType: soft

"@aws-github-runner/microvm-lifecycle-hooks@workspace:services/microvm-lifecycle-hooks":
version: 0.0.0-use.local
resolution: "@aws-github-runner/microvm-lifecycle-hooks@workspace:services/microvm-lifecycle-hooks"
dependencies:
"@aws-github-runner/storage-providers": "npm:*"
"@types/node": "npm:^22.19.3"
"@vercel/ncc": "npm:^0.38.4"
languageName: unknown
linkType: soft

"@aws-github-runner/storage-providers@npm:*, @aws-github-runner/storage-providers@workspace:libs/storage-providers":
version: 0.0.0-use.local
resolution: "@aws-github-runner/storage-providers@workspace:libs/storage-providers"
Expand Down