TUN-9863: Introduce Code Signing for Windows Builds

* TUN-9863: Introduce Code Signing for Windows Builds

This commit adds a signing step to the build script for windows binaries.
Since we package the MSI on Linux, this commit adds another CI step that depends on package-windows and signs all of the windows packages.

To do so, we use azuresigntool which relies on a certificate stored in Azure Vault.

Closes TUN-9863
This commit is contained in:
Gonçalo Garcia 2025-11-06 11:41:21 +00:00
parent a8fdbb83d0
commit eedbcf46d4
4 changed files with 77 additions and 8 deletions

View File

@ -77,7 +77,7 @@ release-cloudflared-to-github:
- ci-image-get-image-ref - ci-image-get-image-ref
- linux-packaging - linux-packaging
- linux-packaging-fips - linux-packaging-fips
- package-windows - windows-package-sign
script: script:
- ./.ci/scripts/release-target.sh github-release - ./.ci/scripts/release-target.sh github-release

View File

@ -4,6 +4,7 @@ $ProgressPreference = "SilentlyContinue"
$env:TARGET_OS = "windows" $env:TARGET_OS = "windows"
$env:LOCAL_OS = "windows" $env:LOCAL_OS = "windows"
$TIMESTAMP_RFC3161 = "http://timestamp.digicert.com"
New-Item -Path ".\artifacts" -ItemType Directory New-Item -Path ".\artifacts" -ItemType Directory
@ -13,6 +14,8 @@ $env:LOCAL_ARCH = "amd64"
$env:CGO_ENABLED = 1 $env:CGO_ENABLED = 1
& make cloudflared & make cloudflared
if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for amd64" } if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for amd64" }
# Sign build
azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\cloudflared.exe
copy .\cloudflared.exe .\artifacts\cloudflared-windows-amd64.exe copy .\cloudflared.exe .\artifacts\cloudflared-windows-amd64.exe
Write-Output "Building for 386" Write-Output "Building for 386"
@ -21,4 +24,6 @@ $env:LOCAL_ARCH = "386"
$env:CGO_ENABLED = 0 $env:CGO_ENABLED = 0
& make cloudflared & make cloudflared
if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for 386" } if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for 386" }
## Sign build
azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\cloudflared.exe
copy .\cloudflared.exe .\artifacts\cloudflared-windows-386.exe copy .\cloudflared.exe .\artifacts\cloudflared-windows-386.exe

View File

@ -0,0 +1,26 @@
# Sign Windows artifacts using azuretool
# This script processes MSI files from the artifacts directory
$ErrorActionPreference = "Stop"
# Define paths
$ARTIFACT_DIR = "artifacts"
$TIMESTAMP_RFC3161 = "http://timestamp.digicert.com"
Write-Host "Looking for Windows artifacts to sign in $ARTIFACT_DIR..."
# Find all Windows MSI files
$msiFiles = Get-ChildItem -Path $ARTIFACT_DIR -Filter "cloudflared-windows-*.msi" -ErrorAction SilentlyContinue
if ($msiFiles.Count -eq 0) {
Write-Host "No Windows MSI files found in $ARTIFACT_DIR"
exit 1
}
Write-Host "Found $($msiFiles.Count) file(s) to sign:"
foreach ($file in $msiFiles) {
Write-Host "Running azuretool sign for $($file.Name)"
azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\\$ARTIFACT_DIR\\$($file.Name)
}
Write-Host "Signing process completed"

View File

@ -14,7 +14,7 @@ include:
########################################## ##########################################
### Build Cloudflared Windows Binaries ### ### Build Cloudflared Windows Binaries ###
########################################## ##########################################
build-cloudflared-windows: windows-build-cloudflared:
<<: *windows-build-defaults <<: *windows-build-defaults
stage: build stage: build
script: script:
@ -26,7 +26,7 @@ build-cloudflared-windows:
###################################################### ######################################################
### Load Environment Variables for Component Tests ### ### Load Environment Variables for Component Tests ###
###################################################### ######################################################
load-windows-env-variables: windows-load-env-variables:
stage: pre-build stage: pre-build
extends: .component-tests extends: .component-tests
script: script:
@ -35,8 +35,29 @@ load-windows-env-variables:
- echo "DNS_API_TOKEN=$DNS_API_TOKEN" >> windows.env - echo "DNS_API_TOKEN=$DNS_API_TOKEN" >> windows.env
# We have to encode the `COMPONENT_TESTS_ORIGINCERT` secret, because it content is a file, otherwise we can't export it using gitlab # We have to encode the `COMPONENT_TESTS_ORIGINCERT` secret, because it content is a file, otherwise we can't export it using gitlab
- echo "COMPONENT_TESTS_ORIGINCERT=$(echo "$COMPONENT_TESTS_ORIGINCERT" | base64 -w0)" >> windows.env - echo "COMPONENT_TESTS_ORIGINCERT=$(echo "$COMPONENT_TESTS_ORIGINCERT" | base64 -w0)" >> windows.env
- echo "KEY_VAULT_URL=$KEY_VAULT_URL" >> windows.env
- echo "KEY_VAULT_CLIENT_ID=$KEY_VAULT_CLIENT_ID" >> windows.env
- echo "KEY_VAULT_TENANT_ID=$KEY_VAULT_TENANT_ID" >> windows.env
- echo "KEY_VAULT_SECRET=$KEY_VAULT_SECRET" >> windows.env
- echo "KEY_VAULT_CERTIFICATE=$KEY_VAULT_CERTIFICATE" >> windows.env
variables: variables:
COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiAuL2Nsb3VkZmxhcmVkLmV4ZQpjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU= COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiAuL2Nsb3VkZmxhcmVkLmV4ZQpjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU=
secrets:
KEY_VAULT_URL:
vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_url@kv
file: false
KEY_VAULT_CLIENT_ID:
vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_client_id@kv
file: false
KEY_VAULT_TENANT_ID:
vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_tenant_id@kv
file: false
KEY_VAULT_SECRET:
vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/secret/key_vault_secret@kv
file: false
KEY_VAULT_CERTIFICATE:
vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/certificate/key_vault_certificate@kv
file: false
artifacts: artifacts:
access: 'none' access: 'none'
reports: reports:
@ -45,12 +66,12 @@ load-windows-env-variables:
################################### ###################################
### Run Windows Component Tests ### ### Run Windows Component Tests ###
################################### ###################################
component-tests-cloudflared-windows: windows-component-tests-cloudflared:
<<: *windows-build-defaults <<: *windows-build-defaults
stage: test stage: test
needs: ["load-windows-env-variables"] needs: ["windows-load-env-variables"]
script: script:
# We have to decode the secret we encoded on the `load-windows-env-variables` job # We have to decode the secret we encoded on the `windows-load-env-variables` job
- $env:COMPONENT_TESTS_ORIGINCERT = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:COMPONENT_TESTS_ORIGINCERT)) - $env:COMPONENT_TESTS_ORIGINCERT = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:COMPONENT_TESTS_ORIGINCERT))
- powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\go-wrapper.ps1" "${GO_VERSION}" ".\.ci\scripts\windows\component-test.ps1" - powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\go-wrapper.ps1" "${GO_VERSION}" ".\.ci\scripts\windows\component-test.ps1"
artifacts: artifacts:
@ -60,13 +81,13 @@ component-tests-cloudflared-windows:
################################ ################################
### Package Windows Binaries ### ### Package Windows Binaries ###
################################ ################################
package-windows: windows-package:
rules: rules:
- !reference [.default-rules, run-on-master] - !reference [.default-rules, run-on-master]
stage: package stage: package
needs: needs:
- ci-image-get-image-ref - ci-image-get-image-ref
- build-cloudflared-windows - windows-build-cloudflared
image: $BUILD_IMAGE image: $BUILD_IMAGE
script: script:
- .ci/scripts/package-windows.sh - .ci/scripts/package-windows.sh
@ -74,3 +95,20 @@ package-windows:
artifacts: artifacts:
paths: paths:
- artifacts/* - artifacts/*
#############################
### Sign Windows Binaries ###
#############################
windows-package-sign:
<<: *windows-build-defaults
rules:
- !reference [.default-rules, run-on-master]
stage: package
needs:
- windows-package
- windows-load-env-variables
script:
- powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\sign-msi.ps1"
artifacts:
paths:
- artifacts/*