Skip to content

Commit d699e0b

Browse files
Use Azure Trusted Signing (#5122)
- Use Azure Trusted Signing to sign the Windows artifacts instead of the existing code-signing certificate. - Refactor workflow so that the Windows binary can be signed before it is added to the Windows installer. --------- Co-authored-by: Copilot <[email protected]>
1 parent db951c2 commit d699e0b

File tree

1 file changed

+195
-58
lines changed

1 file changed

+195
-58
lines changed

.github/workflows/build.yml

Lines changed: 195 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ jobs:
3535
outputs:
3636
k6_version: ${{ steps.get_k6_version.outputs.k6_version }}
3737
go_version: ${{ steps.get_go_version.outputs.go_version }}
38+
sign_windows_artifacts: ${{ steps.determine_windows_signing.outputs.sign_windows_artifacts }}
3839
steps:
3940
- name: Checkout code
4041
uses: actions/checkout@v5
@@ -47,7 +48,7 @@ jobs:
4748
INPUT_K6_VERSION: ${{ github.event.inputs.k6_version }}
4849
run: |
4950
set -x # Show exactly what commands are executed
50-
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ "${INPUT_K6_VERSION}" != "" ]]; then
51+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]] && [[ "${INPUT_K6_VERSION}" != "" ]]; then
5152
VERSION="${INPUT_K6_VERSION}"
5253
echo "Building custom dev build with version '${VERSION}' from manual workflow_dispatch..."
5354
elif [[ "${GITHUB_REF}" =~ ^refs/tags/v.+$ ]]; then
@@ -65,7 +66,7 @@ jobs:
6566
INPUT_GO_VERSION: ${{ github.event.inputs.go_version }}
6667
run: |
6768
set -x # Show exactly what commands are executed
68-
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ "${INPUT_GO_VERSION}" != "" ]]; then
69+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]] && [[ "${INPUT_GO_VERSION}" != "" ]]; then
6970
GO_VERSION="${INPUT_GO_VERSION}"
7071
echo "Using custom Go version '${GO_VERSION}' from manual workflow_dispatch..."
7172
else
@@ -75,6 +76,26 @@ jobs:
7576
echo "GO_VERSION=${GO_VERSION}"
7677
echo "go_version=${GO_VERSION}" >> $GITHUB_OUTPUT
7778
79+
# Secrets are unavailable when building from project forks, so this
80+
# will fail for external PRs, even if we wanted to do it. And we don't.
81+
# We are only going to sign packages that are built from the default branch
82+
# or a version tag, or manually triggered dev builds, so we have enough
83+
# assurance that package signing works, but don't sign every PR build.
84+
- name: Determine whether to sign the Windows artifacts
85+
id: determine_windows_signing
86+
env:
87+
SIGN_FILES: ${{ github.ref_name == github.event.repository.default_branch || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
88+
run: |
89+
set -x # Show exactly what commands are executed
90+
if [[ "${SIGN_FILES}" == "true" ]]; then
91+
echo "Windows artifacts will be signed"
92+
sign_windows_artifacts="true"
93+
else
94+
echo "Windows artifacts will not be signed"
95+
sign_windows_artifacts="false"
96+
fi
97+
echo "sign_windows_artifacts=${sign_windows_artifacts}" >> ${GITHUB_OUTPUT}
98+
7899
build:
79100
runs-on: ubuntu-latest
80101
needs: [configure]
@@ -228,17 +249,111 @@ jobs:
228249
-t $DOCKER_IMAGE_ID:latest-with-browser \
229250
-t ghcr.io/$GHCR_IMAGE_ID:latest-with-browser .
230251
231-
package-windows:
252+
# Forks, PRs etc. won't actually sign the binary, but the workflow will run most of the same steps as
253+
# GitHub Actions workflows don't support conditional `needs` so we have to run the signing step unconditionally.
254+
sign-binaries:
232255
permissions:
233256
contents: read
234257
actions: read
235258
id-token: write # Required for Vault
236259

260+
env:
261+
VERSION: ${{ needs.configure.outputs.k6_version }}
262+
263+
environment:
264+
name: azure-trusted-signing
265+
237266
runs-on: windows-latest
238267
defaults:
239268
run:
240269
shell: pwsh
241270
needs: [configure, build]
271+
outputs:
272+
binary_artifact_name: ${{ steps.assign-artifact-names.outputs.binary-artifact-name }}
273+
windows_binary_artifact_name: ${{ steps.assign-artifact-names.outputs.windows-binary-artifact-name }}
274+
steps:
275+
- name: Download binaries
276+
uses: actions/download-artifact@v4
277+
with:
278+
name: binaries
279+
path: dist
280+
281+
- name: Unzip Windows binary
282+
run: |
283+
Expand-Archive -Path ".\dist\k6-${env:VERSION}-windows-amd64.zip" -DestinationPath .\packaging\
284+
285+
- name: Upload artifact for Windows installer build
286+
uses: actions/upload-artifact@v4
287+
with:
288+
name: windows-binary
289+
path: 'packaging/k6-${{ env.VERSION }}-windows-amd64/k6.exe'
290+
retention-days: 7
291+
if-no-files-found: error
292+
293+
- name: Get secrets for Azure Trusted Signing
294+
uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets/v1.3.0
295+
id: get-signing-secrets
296+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
297+
with:
298+
export_env: false
299+
repo_secrets: |
300+
client-id=azure-trusted-signing:client-id
301+
subscription-id=azure-trusted-signing:subscription-id
302+
tenant-id=azure-trusted-signing:tenant-id
303+
304+
- name: Sign Windows binary
305+
uses: grafana/shared-workflows/actions/azure-trusted-signing@azure-trusted-signing/v1.0.0
306+
id: sign-artifacts
307+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
308+
with:
309+
application-description: 'Grafana k6'
310+
artifact-to-sign: 'windows-binary'
311+
azure-client-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).client-id }}
312+
azure-subscription-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).subscription-id }}
313+
azure-tenant-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).tenant-id }}
314+
signed-artifact-name: 'windows-binary-signed'
315+
316+
- name: Download signed Windows binary
317+
uses: actions/download-artifact@v4
318+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
319+
with:
320+
name: ${{ steps.sign-artifacts.outputs.artifact-name }}
321+
path: 'packaging/k6-${{ env.VERSION }}-windows-amd64'
322+
323+
# Re-zip the signed Windows binary to replace the original unsigned version
324+
- name: Zip signed Windows binary
325+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
326+
run: |
327+
Compress-Archive -Path ".\packaging\*" -DestinationPath ".\dist\k6-${env:VERSION}-windows-amd64.zip" -Force
328+
329+
- name: Upload signed artifacts
330+
uses: actions/upload-artifact@v4
331+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
332+
with:
333+
name: binaries-signed
334+
path: dist/
335+
retention-days: 7
336+
if-no-files-found: error
337+
338+
- name: Assign artifact name for Windows binary for installer build
339+
id: assign-artifact-names
340+
env:
341+
BINARY_ARTIFACT_NAME: ${{ needs.configure.outputs.sign_windows_artifacts == 'true' && 'binaries-signed' || 'binaries' }}
342+
WINDOWS_BINARY_ARTIFACT_NAME: ${{ needs.configure.outputs.sign_windows_artifacts == 'true' && steps.sign-artifacts.outputs.artifact-name || 'windows-binary' }}
343+
run: |
344+
echo "binary-artifact-name=${env:BINARY_ARTIFACT_NAME}" >> ${env:GITHUB_OUTPUT}
345+
echo "windows-binary-artifact-name=${env:WINDOWS_BINARY_ARTIFACT_NAME}" >> ${env:GITHUB_OUTPUT}
346+
347+
package:
348+
permissions:
349+
contents: read
350+
actions: read
351+
352+
runs-on: windows-latest
353+
defaults:
354+
run:
355+
shell: pwsh
356+
needs: [configure, build, sign-binaries]
242357
env:
243358
VERSION: ${{ needs.configure.outputs.k6_version }}
244359
steps:
@@ -255,71 +370,93 @@ jobs:
255370
curl -Lso wix311-binaries.zip https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip
256371
Expand-Archive -Path .\wix311-binaries.zip -DestinationPath .\wix311\
257372
echo "$pwd\wix311" | Out-File -FilePath $env:GITHUB_PATH -Append
258-
- name: Download binaries
373+
- name: Download Windows binary
259374
uses: actions/download-artifact@v5
260375
with:
261-
name: binaries
262-
path: dist
263-
- name: Unzip Windows binary
264-
run: |
265-
Expand-Archive -Path ".\dist\k6-$env:VERSION-windows-amd64.zip" -DestinationPath .\packaging\
266-
move .\packaging\k6-$env:VERSION-windows-amd64\k6.exe .\packaging\
267-
rmdir .\packaging\k6-$env:VERSION-windows-amd64\
376+
name: ${{ needs.sign-binaries.outputs.windows_binary_artifact_name }}
377+
path: packaging
268378

269379
- name: Create the MSI package
270380
run: |
271381
$env:VERSION = $env:VERSION -replace 'v(\d+\.\d+\.\d+).*','$1'
272382
pandoc -s -f markdown -t rtf -o packaging\LICENSE.rtf LICENSE.md
273383
cd .\packaging
274-
candle.exe -arch x64 "-dVERSION=$env:VERSION" k6.wxs
384+
candle.exe -arch x64 "-dVERSION=${env:VERSION}" k6.wxs
275385
light.exe -ext WixUIExtension k6.wixobj
276-
277-
# GH secrets are unavailable when building from project forks, so this
278-
# will fail for external PRs, even if we wanted to do it. And we don't.
279-
# We are only going to sign packages that are built from master or a
280-
# version tag, or manually triggered dev builds, so we have enough
281-
# assurance that package signing works, but don't sign every PR build.
282-
- if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
283-
uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets/v1.3.0
284-
with:
285-
repo_secrets: |
286-
WIN_SIGN_CERT=winsign:WIN_SIGN_CERT
287-
WIN_SIGN_PASS=winsign:WIN_SIGN_PASS
288-
289-
- name: Sign Windows binary and .msi package
290-
if: ${{ env.WIN_SIGN_CERT != '' && env.WIN_SIGN_PASS != '' }}
291-
run: |
292-
# Convert base64 certificate to PFX
293-
$bytes = [Convert]::FromBase64String("${{ env.WIN_SIGN_CERT }}")
294-
[IO.File]::WriteAllBytes("k6.pfx", $bytes)
295-
296-
# Get the latest signtool executable
297-
$SignTool = Get-ChildItem -Path "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Recurse -Filter signtool.exe | Where-Object { $_.DirectoryName -like "*\x64" } | Sort-Object -Descending | Select-Object -First 1
298-
299-
# Sign the Windows binary
300-
& $SignTool sign /f k6.pfx /p "${{ env.WIN_SIGN_PASS }}" /tr "http://timestamp.digicert.com" /td sha256 /fd sha256 "packaging\k6.exe"
301-
302-
# Sign the MSI package
303-
& $SignTool sign /f k6.pfx /p "${{ env.WIN_SIGN_PASS }}" /tr "http://timestamp.digicert.com" /td sha256 /fd sha256 "packaging\k6.msi"
304-
305-
# Cleanup signing artifacts
306-
del k6.pfx
307386
308387
- name: Rename MSI package
309388
# To keep it consistent with the other artifacts
310-
run: move "packaging\k6.msi" "packaging\k6-$env:VERSION-windows-amd64.msi"
389+
run: move "packaging\k6.msi" "packaging\k6-${env:VERSION}-windows-amd64.msi"
311390

312-
- name: Upload artifacts
391+
- name: Upload Windows installer
313392
uses: actions/upload-artifact@v4
314393
with:
315394
name: binaries-windows
316395
path: |
317396
packaging/k6-*.msi
318397
retention-days: 7
398+
if-no-files-found: error
399+
400+
# Forks, PRs etc. won't actually sign the installer, but the workflow will run most of the same steps as
401+
# GitHub Actions workflows don't support conditional `needs` so we have to run the signing step unconditionally.
402+
sign-packages:
403+
permissions:
404+
actions: read
405+
contents: read
406+
id-token: write # Required for Vault
407+
408+
environment:
409+
name: azure-trusted-signing
410+
411+
outputs:
412+
artifact_name: ${{ steps.assign-artifact-name.outputs.artifact-name }}
413+
414+
runs-on: windows-latest
415+
defaults:
416+
run:
417+
shell: pwsh
418+
needs: [configure, package]
419+
steps:
420+
- name: Download Windows artifacts
421+
uses: actions/download-artifact@v4
422+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
423+
with:
424+
name: binaries-windows
425+
path: packaging
426+
427+
- name: Get secrets for Azure Trusted Signing
428+
uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets/v1.3.0
429+
id: get-signing-secrets
430+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
431+
with:
432+
export_env: false
433+
repo_secrets: |
434+
client-id=azure-trusted-signing:client-id
435+
subscription-id=azure-trusted-signing:subscription-id
436+
tenant-id=azure-trusted-signing:tenant-id
437+
438+
- name: Sign Windows installer
439+
uses: grafana/shared-workflows/actions/azure-trusted-signing@azure-trusted-signing/v1.0.0
440+
id: sign-artifacts
441+
if: needs.configure.outputs.sign_windows_artifacts == 'true'
442+
with:
443+
application-description: 'Grafana k6'
444+
artifact-to-sign: 'binaries-windows'
445+
azure-client-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).client-id }}
446+
azure-subscription-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).subscription-id }}
447+
azure-tenant-id: ${{ fromJSON(steps.get-signing-secrets.outputs.secrets).tenant-id }}
448+
signed-artifact-name: 'binaries-windows-signed'
449+
450+
- name: Assign artifact name for Windows installer
451+
id: assign-artifact-name
452+
env:
453+
ARTIFACT_NAME: ${{ needs.configure.outputs.sign_windows_artifacts == 'true' && steps.sign-artifacts.outputs.artifact-name || 'binaries-windows' }}
454+
run: |
455+
echo "artifact-name=${env:ARTIFACT_NAME}" >> ${env:GITHUB_OUTPUT}
319456
320457
publish-github:
321458
runs-on: ubuntu-latest
322-
needs: [configure, build, package-windows]
459+
needs: [configure, sign-binaries, sign-packages]
323460
if: ${{ startsWith(github.ref, 'refs/tags/v') && github.event_name != 'workflow_dispatch' }}
324461
env:
325462
VERSION: ${{ needs.configure.outputs.k6_version }}
@@ -334,12 +471,12 @@ jobs:
334471
- name: Download binaries
335472
uses: actions/download-artifact@v5
336473
with:
337-
name: binaries
474+
name: ${{ needs.sign-binaries.outputs.binary_artifact_name }}
338475
path: dist
339476
- name: Download Windows binaries
340477
uses: actions/download-artifact@v5
341478
with:
342-
name: binaries-windows
479+
name: ${{ needs.sign-packages.outputs.artifact_name }}
343480
path: dist
344481
- name: Generate checksum file
345482
run: cd dist && sha256sum * > "k6-${VERSION}-checksums.txt"
@@ -363,7 +500,7 @@ jobs:
363500
364501
publish-packages:
365502
runs-on: ubuntu-latest
366-
needs: [configure, build, package-windows]
503+
needs: [configure, sign-binaries, sign-packages]
367504
if: ${{ startsWith(github.ref, 'refs/tags/v') && github.event_name != 'workflow_dispatch' }}
368505
env:
369506
VERSION: ${{ needs.configure.outputs.k6_version }}
@@ -380,12 +517,12 @@ jobs:
380517
- name: Download binaries
381518
uses: actions/download-artifact@v5
382519
with:
383-
name: binaries
520+
name: ${{ needs.sign-binaries.outputs.binary_artifact_name }}
384521
path: dist
385522
- name: Download Windows binaries
386523
uses: actions/download-artifact@v5
387524
with:
388-
name: binaries-windows
525+
name: ${{ needs.sign-packages.outputs.artifact_name }}
389526
path: dist
390527
- name: Rename binaries
391528
# To be consistent with the filenames used in dl.k6.io
@@ -409,15 +546,15 @@ jobs:
409546
- name: Setup docker compose environment
410547
run: |
411548
cat > packaging/.env <<EOF
412-
AWS_ACCESS_KEY_ID=${{ env.AWS_ACCESS_KEY_ID }}
413-
AWS_CF_DISTRIBUTION="${{ env.AWS_CF_DISTRIBUTION }}"
549+
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
550+
AWS_CF_DISTRIBUTION="${AWS_CF_DISTRIBUTION}"
414551
AWS_DEFAULT_REGION=us-east-2
415-
AWS_SECRET_ACCESS_KEY=${{ env.AWS_SECRET_ACCESS_KEY }}
416-
AWS_SESSION_TOKEN=${{ env.AWS_SESSION_TOKEN }}
417-
PGP_SIGN_KEY_PASSPHRASE=${{ env.PGP_SIGN_KEY_PASSPHRASE }}
418-
S3_BUCKET=${{ env.S3_BUCKET }}
552+
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
553+
AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
554+
PGP_SIGN_KEY_PASSPHRASE=${PGP_SIGN_KEY_PASSPHRASE}
555+
S3_BUCKET=${S3_BUCKET}
419556
EOF
420-
echo "${{ env.PGP_SIGN_KEY }}" > packaging/sign-key.gpg
557+
echo "${PGP_SIGN_KEY}" > packaging/sign-key.gpg
421558
- name: Publish packages
422559
env:
423560
GITHUB_ACTOR: ${{ github.actor }}

0 commit comments

Comments
 (0)