Skip to content
Open
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
2 changes: 1 addition & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<clear />
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
<!-- Begin: Package sources from dotnet-dotnet -->
<add key="darc-pub-dotnet-dotnet-e26618a" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-e26618ae/nuget/v3/index.json" />
<add key="darc-pub-dotnet-dotnet-ca9e780" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-ca9e780d/nuget/v3/index.json" />
<!-- End: Package sources from dotnet-dotnet -->
<!-- Begin: Package sources from dotnet-macios -->
<!-- End: Package sources from dotnet-macios -->
Expand Down
10 changes: 5 additions & 5 deletions eng/Version.Details.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props
<Project>
<PropertyGroup>
<!-- dotnet-dotnet dependencies -->
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26473.106</MicrosoftDotNetArcadeSdkPackageVersion>
<MicrosoftDotNetBuildTasksFeedPackageVersion>10.0.0-beta.26473.106</MicrosoftDotNetBuildTasksFeedPackageVersion>
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26505.123</MicrosoftDotNetArcadeSdkPackageVersion>
<MicrosoftDotNetBuildTasksFeedPackageVersion>10.0.0-beta.26505.123</MicrosoftDotNetBuildTasksFeedPackageVersion>
<MicrosoftDotNetCecilPackageVersion>0.11.5-alpha.26070.104</MicrosoftDotNetCecilPackageVersion>
<MicrosoftDotNetSharedFrameworkSdkPackageVersion>10.0.0-beta.26473.106</MicrosoftDotNetSharedFrameworkSdkPackageVersion>
<MicrosoftDotNetSharedFrameworkSdkPackageVersion>10.0.0-beta.26505.123</MicrosoftDotNetSharedFrameworkSdkPackageVersion>
<MicrosoftNETILLinkPackageVersion>10.0.3-servicing.26070.104</MicrosoftNETILLinkPackageVersion>
<MicrosoftNETILLinkTasksPackageVersion>10.0.3</MicrosoftNETILLinkTasksPackageVersion>
<MicrosoftNETRuntimeMonoTargetsSdkPackageVersion>10.0.3</MicrosoftNETRuntimeMonoTargetsSdkPackageVersion>
<MicrosoftNETSdkPackageVersion>10.0.402-servicing.26471.104</MicrosoftNETSdkPackageVersion>
<MicrosoftNETSdkPackageVersion>10.0.403-servicing.26505.123</MicrosoftNETSdkPackageVersion>
<MicrosoftNETCoreAppRefPackageVersion>10.0.3</MicrosoftNETCoreAppRefPackageVersion>
<MicrosoftTemplateEngineAuthoringTasksPackageVersion>10.0.402</MicrosoftTemplateEngineAuthoringTasksPackageVersion>
<MicrosoftTemplateEngineAuthoringTasksPackageVersion>10.0.403</MicrosoftTemplateEngineAuthoringTasksPackageVersion>
<!-- dotnet-macios dependencies -->
<MicrosoftiOSSdknet100_260PackageVersion>26.0.11017</MicrosoftiOSSdknet100_260PackageVersion>
<MicrosoftiOSSdknet90_185PackageVersion>18.5.9227</MicrosoftiOSSdknet90_185PackageVersion>
Expand Down
20 changes: 10 additions & 10 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Dependencies>
<ProductDependencies>
<Dependency Name="Microsoft.NET.Sdk" Version="10.0.402-servicing.26471.104">
<Dependency Name="Microsoft.NET.Sdk" Version="10.0.403-servicing.26505.123">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>82c20eb47c0af1a101d821399e1a600a58a9d899</Sha>
<Sha>ca9e780d66577531bf5eb8899ec8d7b543ecdc8d</Sha>
</Dependency>
<Dependency Name="Microsoft.NET.ILLink" Version="10.0.3-servicing.26070.104">
<Uri>https://github.com/dotnet/dotnet</Uri>
Expand Down Expand Up @@ -95,25 +95,25 @@
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="10.0.0-beta.26473.106">
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="10.0.0-beta.26505.123">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>e26618ae227ceb29376490c71302a875161acad6</Sha>
<Sha>ca9e780d66577531bf5eb8899ec8d7b543ecdc8d</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.SharedFramework.Sdk" Version="10.0.0-beta.26473.106">
<Dependency Name="Microsoft.DotNet.SharedFramework.Sdk" Version="10.0.0-beta.26505.123">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>e26618ae227ceb29376490c71302a875161acad6</Sha>
<Sha>ca9e780d66577531bf5eb8899ec8d7b543ecdc8d</Sha>
</Dependency>
<Dependency Name="Microsoft.TemplateEngine.Authoring.Tasks" Version="10.0.402">
<Dependency Name="Microsoft.TemplateEngine.Authoring.Tasks" Version="10.0.403">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>82c20eb47c0af1a101d821399e1a600a58a9d899</Sha>
<Sha>ca9e780d66577531bf5eb8899ec8d7b543ecdc8d</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.XHarness.iOS.Shared" Version="11.0.0-prerelease.26431.4">
<Uri>https://github.com/dotnet/xharness</Uri>
<Sha>f40ec1c86c47f721ced71581e8228b55c20b3eba</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26473.106">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26505.123">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>e26618ae227ceb29376490c71302a875161acad6</Sha>
<Sha>ca9e780d66577531bf5eb8899ec8d7b543ecdc8d</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
</ToolsetDependencies>
Expand Down
123 changes: 83 additions & 40 deletions eng/common/Get-GitHubAppToken.ps1
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Mints a short-lived GitHub App installation access token by signing a JWT
# with a private key stored in Azure Key Vault (RSA, RS256). The signed JWT is
# exchanged with the GitHub API for a token scoped to a single installation.
# with an RSA private key (RS256). The signed JWT is exchanged with the GitHub
# API for a token scoped to a single installation.
#
# Requirements:
# - A GitHub App whose private key has been uploaded into Key Vault as an RSA
# key (the PEM converted to a Key Vault *key*, NOT stored as a secret).
# - The caller (the federated Azure service connection used to run this script)
# must have the `Key Vault Crypto User` role (or at minimum the `Sign`
# action) on that key.
# - A GitHub App ID and PEM private key stored as Azure Key Vault secrets.
# - The federated Azure service connection running this script must have
# `Get` access to those two secrets.
# - The App must be installed on the target organization/account
# (`InstallationOwner`) with the permissions/repositories it needs.
#
Expand All @@ -16,17 +14,17 @@

[CmdletBinding()]
param(
# Name of the Key Vault that holds the GitHub App's RSA signing key.
# Name of the Key Vault holding the GitHub App credentials.
[Parameter(Mandatory = $true)]
[string] $KeyVaultName,

# Name of the RSA key inside the Key Vault (the App's private key).
# Secret Manager projection containing the GitHub App ID.
[Parameter(Mandatory = $true)]
[string] $KeyName,
[string] $AppIdSecretName,

# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
# Secret Manager projection containing the PEM private key.
[Parameter(Mandatory = $true)]
[string] $AppClientId,
[string] $AppPrivateKeySecretName,

# Login of the organization or user account whose installation we should
# mint the token for (e.g. `dotnet`, `microsoft`).
Expand All @@ -39,16 +37,69 @@ param(
[Parameter(Mandatory = $false)]
[string] $OutputVariableName
)

$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true

. $PSScriptRoot\pipeline-logging-functions.ps1

if ($KeyVaultName -notmatch '^[A-Za-z][A-Za-z0-9-]{1,22}[A-Za-z0-9]$' -or $KeyVaultName.Contains('--')) {
Write-PipelineTelemetryError -Category 'Build' -Message "KeyVaultName '$KeyVaultName' is not a valid Azure Key Vault name."
exit 1
}

function ConvertTo-Base64Url([byte[]] $bytes) {
return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
}

$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
try {
# Azure CLI can emit non-fatal Python warnings to stderr.
$PSNativeCommandUseErrorActionPreference = $false
$keyVaultAccessToken = az account get-access-token `
--resource https://vault.azure.net `
--query accessToken `
--output tsv `
--only-show-errors
$tokenExitCode = $LASTEXITCODE
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to acquire an Azure Key Vault access token: $_"
exit 1
}
finally {
$PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
}
if ($tokenExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($keyVaultAccessToken)) {
Write-PipelineTelemetryError -Category 'Build' -Message "'az account get-access-token' exited with code $tokenExitCode while acquiring an Azure Key Vault access token."
exit 1
}

function Get-KeyVaultSecret([string] $SecretName) {
# Use the data-plane REST API because `az keyvault secret show` can fail
# with Errno 22 on hosted Windows agents when reading these projections.
$escapedSecretName = [Uri]::EscapeDataString($SecretName)
$secretUri = "https://$KeyVaultName.vault.azure.net/secrets/$escapedSecretName`?api-version=7.4"
try {
$response = Invoke-RestMethod `
-Uri $secretUri `
-Headers @{ Authorization = "Bearer $keyVaultAccessToken" } `
-Method Get
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to read secret '$SecretName' from vault '$KeyVaultName': $_. Verify the secret exists and the service connection has 'Key Vault Secrets User' access to it."
exit 1
}
if ([string]::IsNullOrWhiteSpace($response.value)) {
Write-PipelineTelemetryError -Category 'Build' -Message "Secret '$SecretName' in vault '$KeyVaultName' is empty."
exit 1
}
return [string] $response.value
}

Write-Host "Reading GitHub App credentials from vault '$KeyVaultName'..."
$appId = Get-KeyVaultSecret $AppIdSecretName
$privateKey = Get-KeyVaultSecret $AppPrivateKeySecretName

# Build JWT header and payload. Use [ordered] hashtables so JSON
# serialization is deterministic.
$jwtHeader = [ordered]@{
Expand All @@ -59,46 +110,38 @@ $now = [System.DateTimeOffset]::UtcNow
$jwtPayload = [ordered]@{
iat = $now.AddMinutes(-1).ToUnixTimeSeconds()
exp = $now.AddMinutes(5).ToUnixTimeSeconds()
iss = $AppClientId
iss = $appId
}

$headerEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtHeader | ConvertTo-Json -Compress)))
$payloadEncoded = ConvertTo-Base64Url ([System.Text.Encoding]::UTF8.GetBytes(($jwtPayload | ConvertTo-Json -Compress)))
$signingInput = "$headerEncoded.$payloadEncoded"

# Key Vault `sign` expects the *digest* (base64), not the raw bytes.
$sha256 = [System.Security.Cryptography.SHA256]::Create()
$digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
$digestBase64 = [Convert]::ToBase64String($digestBytes)
$sha256 = [System.Security.Cryptography.SHA256]::Create()
try {
$digestBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signingInput))
}
finally {
$sha256.Dispose()
}

Write-Host "Signing JWT with key '$KeyName' in vault '$KeyVaultName'..."
$previousNativeCommandErrorPreference = $PSNativeCommandUseErrorActionPreference
Write-Host 'Signing JWT with the GitHub App private key...'
$rsa = [System.Security.Cryptography.RSA]::Create()
try {
# Azure CLI can emit non-fatal Python warnings to stderr even when signing succeeds.
# Use the exit code to determine success for this invocation.
$PSNativeCommandUseErrorActionPreference = $false
$signatureBase64 = az keyvault key sign `
--vault-name $KeyVaultName `
--name $KeyName `
--algorithm RS256 `
--digest $digestBase64 `
--query signature `
--output tsv `
--only-show-errors
$signExitCode = $LASTEXITCODE
$rsa.ImportFromPem($privateKey)
$signatureBytes = $rsa.SignHash(
$digestBytes,
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$signatureUrl = ConvertTo-Base64Url $signatureBytes
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to sign the JWT via Key Vault (key '$KeyName', vault '$KeyVaultName'): $_. Verify the service connection identity has the 'Key Vault Crypto User' role (Sign action) on the key."
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to sign the GitHub App JWT with the supplied private key: $_"
exit 1
}
finally {
$PSNativeCommandUseErrorActionPreference = $previousNativeCommandErrorPreference
}
if ($signExitCode -ne 0 -or [string]::IsNullOrWhiteSpace($signatureBase64)) {
Write-PipelineTelemetryError -Category 'Build' -Message "'az keyvault key sign' exited with code $signExitCode for key '$KeyName' in vault '$KeyVaultName'. Verify the service connection identity has the 'Key Vault Crypto User' role (Sign action) on the key."
exit 1
$rsa.Dispose()
}
$signatureUrl = $signatureBase64.Trim().TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$signingInput.$signatureUrl"

$headers = @{
Expand Down Expand Up @@ -126,7 +169,7 @@ try {
} while ($pageInstallationCount -eq 100)
}
catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App's Client ID ('$AppClientId') may be incorrect."
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App ID may be incorrect."
exit 1
}
$matchingInstallations = @($installations | Where-Object { $_.account.login -ieq $InstallationOwner })
Expand Down
9 changes: 4 additions & 5 deletions eng/common/core-templates/job/helix-job-monitor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,12 @@ parameters:
type: boolean
default: false

# When true, test results are reported to Azure DevOps using the fully qualified test name
# (Namespace.Type.Method) as the stable automatedTestName and the visible title is qualified as
# well (--use-fully-qualified-test-name). Opt-in because it changes AzDO test identity and display;
# primarily useful for frameworks like MSTest whose display name is only the method name.
# When true (the default), test results are reported to Azure DevOps using the fully qualified test
# name (Namespace.Type.Method) as the stable automatedTestName and the visible title is qualified as
# well (--use-fully-qualified-test-name). Set to false to preserve framework-provided display names.
- name: useFullyQualifiedTestName
type: boolean
default: false
default: true

# Controls per-test output attachments. Defaults to Failed.
- name: testResultAttachmentMode
Expand Down
8 changes: 4 additions & 4 deletions eng/common/core-templates/job/onelocbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ parameters:

# GitHub App authentication for the OneLoc check-in PR.
GitHubAppServiceConnection: 'dnceng-oneloc-githubapp'
GitHubAppClientId: 'Iv23lijBU8x3gc9lDOc9'
GitHubAppKeyVaultName: 'EngKeyVault'
GitHubAppKeyName: 'oneloc-localization-app-key'
GitHubAppIdSecretName: 'oneloc-localization-app-app-id'
GitHubAppPrivateKeySecretName: 'oneloc-localization-app-app-private-key'

SourcesDirectory: $(System.DefaultWorkingDirectory)
CreatePr: true
Expand Down Expand Up @@ -101,8 +101,8 @@ jobs:
${{ else }}:
azureSubscription: ${{ parameters.GitHubAppServiceConnection }}
keyVaultName: ${{ parameters.GitHubAppKeyVaultName }}
keyName: ${{ parameters.GitHubAppKeyName }}
appClientId: ${{ parameters.GitHubAppClientId }}
appIdSecretName: ${{ parameters.GitHubAppIdSecretName }}
appPrivateKeySecretName: ${{ parameters.GitHubAppPrivateKeySecretName }}
installationOwner: ${{ parameters.GitHubOrg }}
outputVariableName: 'GitHubAppInstallationToken'
condition: ${{ parameters.condition }}
Expand Down
29 changes: 11 additions & 18 deletions eng/common/core-templates/steps/get-github-app-token.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
# Mints a short-lived GitHub App installation access token by signing a JWT
# with a private key stored in Azure Key Vault (RSA, RS256). The JWT is
# exchanged with the GitHub API for a token scoped to a single installation.
# with an RSA private key (RS256). The JWT is exchanged with the GitHub API
# for a token scoped to a single installation.
#
# Requirements (per GitHub App you want to authenticate as):
# - A GitHub App with its private key uploaded into Key Vault as an RSA key
# (PEM converted to a key, NOT stored as a secret).
# - The Azure service connection passed via `azureSubscription` must be
# granted the `Key Vault Crypto User` role (or at minimum `Sign` action)
# on that key.
# - A GitHub App ID and PEM private key stored as Azure Key Vault secrets.
# - The Azure service connection passed via `azureSubscription` must have
# `Get` access to those two secrets.
# - The App must be installed on the target organization/account
# (`installationOwner`) with the permissions/repositories you need.
#
Expand All @@ -17,23 +15,18 @@
# enterprise classic-PAT lifetime policy.

parameters:
# Azure DevOps service connection (federated) that can call
# `az keyvault key sign` on the App's signing key.
# Azure DevOps service connection (federated) that can read the App credentials.
- name: azureSubscription
type: string

# Name of the Key Vault that holds the GitHub App's RSA signing key.
# Name of the Key Vault holding Secret Manager's github-app-secret projections.
- name: keyVaultName
type: string

# Name of the RSA key inside the Key Vault (the App's private key).
- name: keyName
- name: appIdSecretName
type: string

# The GitHub App's Client ID (the value to put in the `iss` JWT claim).
# Prefer this over the numeric App ID; GitHub accepts either, but Client ID
# is the documented form going forward.
- name: appClientId
- name: appPrivateKeySecretName
type: string

# Login of the organization or user account whose installation we should
Expand Down Expand Up @@ -73,7 +66,7 @@ steps:
inlineScript: |
& "$(System.DefaultWorkingDirectory)/eng/common/Get-GitHubAppToken.ps1" `
-KeyVaultName '${{ parameters.keyVaultName }}' `
-KeyName '${{ parameters.keyName }}' `
-AppClientId '${{ parameters.appClientId }}' `
-AppIdSecretName '${{ parameters.appIdSecretName }}' `
-AppPrivateKeySecretName '${{ parameters.appPrivateKeySecretName }}' `
-InstallationOwner '${{ parameters.installationOwner }}' `
-OutputVariableName '${{ parameters.outputVariableName }}'
6 changes: 3 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"sdk": {
"version": "10.0.402-servicing.26471.104",
"version": "10.0.403-servicing.26505.123",
"paths": [
"builds/downloads/dotnet",
"$host$"
],
"errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'."
},
"tools": {
"dotnet": "10.0.402-servicing.26471.104"
"dotnet": "10.0.403-servicing.26505.123"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26473.106"
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26505.123"
}
}
Loading