Skip to content

Commit 2162e1d

Browse files
committed
Sign binaries on release
1 parent 92cd71b commit 2162e1d

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

‎.github/workflows/LANCommander.Interposer.Release.yml‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ jobs:
1111
runs-on: windows-latest
1212
permissions:
1313
contents: write
14+
id-token: write # required for the OIDC federated credential used by azure/login
15+
16+
env:
17+
SIGNING_ENABLED: ${{ vars.AZURE_SIGNING_ENDPOINT != '' && 'true' || 'false' }}
1418

1519
steps:
1620
- name: Checkout
@@ -89,6 +93,139 @@ jobs:
8993
/t:LANCommander_Interposer_Plugin_CDKey
9094
/p:Configuration=Release /p:Platform=x86
9195
96+
# ── Authenticode signing (Azure Artifact Signing) ────────────────────────
97+
#
98+
# Signs every PE output under Release\ in place, before any packaging step
99+
# runs. The redistributable payload ZIP and the LCX are both assembled by
100+
# copying out of Release\ further down, so signing here also covers the
101+
# copies embedded in those packages. The ZIP and LCX containers themselves
102+
# are not PE files and cannot be Authenticode signed.
103+
#
104+
# Required repository secrets (OIDC federated credential):
105+
# AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID
106+
#
107+
# Required repository variables:
108+
# AZURE_SIGNING_ENDPOINT e.g. https://eus.codesigning.azure.net
109+
# AZURE_SIGNING_ACCOUNT_NAME
110+
# AZURE_SIGNING_CERT_PROFILE_NAME
111+
#
112+
# Leaving AZURE_SIGNING_ENDPOINT unset disables signing: every step below is
113+
# skipped and the job still produces a complete (unsigned) release, so forks
114+
# without the Azure setup keep working.
115+
#
116+
# NOTE: if these secrets and variables are scoped to a GitHub *environment*
117+
# rather than the repository, they resolve empty here and signing is silently
118+
# skipped. Add a matching `environment:` key to this job in that case — but be
119+
# aware that gates the entire release job, not just signing.
120+
- name: Report signing configuration
121+
shell: pwsh
122+
run: |
123+
if ($env:SIGNING_ENABLED -eq 'true') {
124+
Write-Host "Signing release binaries"
125+
} else {
126+
Write-Host "::warning::AZURE_SIGNING_ENDPOINT is not set. Release binaries will be unsigned."
127+
}
128+
129+
- name: Collect Files to Sign
130+
id: targets
131+
if: env.SIGNING_ENABLED == 'true'
132+
shell: pwsh
133+
run: |
134+
$ErrorActionPreference = 'Stop'
135+
136+
# Every PE the release ships. LANCommander.Interposer.asi is a renamed
137+
# DLL — signtool dispatches on PE format, not file extension.
138+
$patterns = @(
139+
'LANCommander.Interposer.dll'
140+
'LANCommander.Interposer.Injector.exe'
141+
'LANCommander.Interposer.Plugin.CDKey.dll'
142+
'LANCommander.Interposer.asi'
143+
'version.dll'
144+
'dinput8.dll'
145+
)
146+
147+
# The signing action expects absolute paths. Each build configuration
148+
# writes to its own subdirectory (Injector\, Proxy\, ProxyDInput8\,
149+
# ASI\, CDKey\), so the search has to recurse.
150+
$files = Get-ChildItem -Path 'Release' -Recurse -File -Include $patterns |
151+
Select-Object -ExpandProperty FullName |
152+
Sort-Object -Unique
153+
154+
if (-not $files) {
155+
throw "No files under Release\ matched the signing patterns. Refusing to sign nothing."
156+
}
157+
158+
Write-Host "Signing $($files.Count) file(s):"
159+
$files | ForEach-Object { Write-Host " $($_.Substring($PWD.Path.Length + 1))" }
160+
161+
# Persist the exact list so the verification step checks what was signed
162+
# rather than re-deriving it.
163+
$manifest = Join-Path $env:RUNNER_TEMP 'sign-targets.txt'
164+
$files | Set-Content -Path $manifest -Encoding UTF8
165+
166+
"files<<SIGNTARGETS" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
167+
$files | Out-File -FilePath $env:GITHUB_OUTPUT -Append
168+
"SIGNTARGETS" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
169+
170+
# The signing action documents a .NET 8 runtime prerequisite, which is not
171+
# guaranteed to be present on every windows-latest image revision.
172+
- name: Setup .NET
173+
if: env.SIGNING_ENABLED == 'true'
174+
uses: actions/setup-dotnet@v4
175+
with:
176+
dotnet-version: '8.0.x'
177+
178+
- name: Azure Login
179+
if: env.SIGNING_ENABLED == 'true'
180+
uses: azure/login@v3
181+
with:
182+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
183+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
184+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
185+
186+
- name: Sign Files
187+
if: env.SIGNING_ENABLED == 'true'
188+
uses: azure/artifact-signing-action@v2
189+
with:
190+
endpoint: ${{ vars.AZURE_SIGNING_ENDPOINT }}
191+
signing-account-name: ${{ vars.AZURE_SIGNING_ACCOUNT_NAME }}
192+
certificate-profile-name: ${{ vars.AZURE_SIGNING_CERT_PROFILE_NAME }}
193+
files: ${{ steps.targets.outputs.files }}
194+
file-digest: SHA256
195+
timestamp-rfc3161: http://timestamp.acs.microsoft.com
196+
timestamp-digest: SHA256
197+
# Pin authentication to the session established by azure/login rather than
198+
# letting DefaultAzureCredential fall through a chain of unconfigured options.
199+
exclude-environment-credential: true
200+
exclude-azure-cli-credential: false
201+
202+
- name: Verify Signatures
203+
if: env.SIGNING_ENABLED == 'true'
204+
shell: pwsh
205+
run: |
206+
$ErrorActionPreference = 'Stop'
207+
208+
$manifest = Join-Path $env:RUNNER_TEMP 'sign-targets.txt'
209+
$failed = @()
210+
211+
foreach ($path in Get-Content -Path $manifest) {
212+
$signature = Get-AuthenticodeSignature -FilePath $path
213+
$relative = $path.Substring($PWD.Path.Length + 1)
214+
215+
if ($signature.Status -ne 'Valid') {
216+
$failed += "$relative : $($signature.Status) - $($signature.StatusMessage)"
217+
} elseif (-not $signature.TimeStamperCertificate) {
218+
$failed += "$relative : signed but not timestamped"
219+
} else {
220+
Write-Host "$relative : Valid ($($signature.SignerCertificate.Subject))"
221+
}
222+
}
223+
224+
if ($failed) {
225+
$failed | ForEach-Object { Write-Host "::error::$_" }
226+
throw "$($failed.Count) file(s) failed signature verification."
227+
}
228+
92229
- name: Create release ZIPs
93230
shell: pwsh
94231
run: |

0 commit comments

Comments
 (0)