>升级到 Azure DevOps Server 2019 后,自动化管道生成在 NuGet 还原步骤中失败,显示:
错误:错误:无法获取本地颁发者证书
包还原失败
Microsoft的文档指出,在 Windows 上运行的生成代理使用 Windows 证书存储,因此我已经检查了所需的证书是否已正确安装在生成服务器上,但它仍然失败。
有许多问题具有相似的症状,但原因不同。经过调查,我找到了解决这个问题的方法,但我没有发现这个确切问题的任何内容,所以我将发布一个答案,希望能为其他人节省一些时间!
事实证明,Azure DevOps 生成代理使用的是不使用 Windows 证书存储的 Node 版本.js该版本。
所需的解决方案是使用名为NODE_EXTRA_CA_CERTS
的系统环境变量或使用名为NODE.EXTRA.CA.CERTS
的任务变量,将 Node.js 指向自签名 SSL 证书的根 CA 证书的导出副本(*.cer 文件),其值指向证书。
开发人员社区问题链接
我使用以下脚本使用PowerShell代理作业。这有效地为管道的 Node.JS 提供了"使用 Windows 计算机证书存储"选项。
一些注意事项:
-
使用 ProcMon 的监视节点.exe建议每次运行管道时读取
NODE_EXTRA_CA_CERTS
中引用的文件。但是,其他人建议需要运行Restart-Service vstsagent* -Force
才能获取更改。这不是我的经验,但也许环境之间的差异会导致这种行为。 -
这增加了额外的 ~1s 流水线执行时间。对于"在Windows上的管道中设置并忘记节点的证书管理"来说,这可能是一个可以接受的价格,但仍然值得注意。
# If running in a pipeline then use the Agent Home directory,
# otherwise use the machine temp folder which is useful for testing
if ($env:AGENT_HOMEDIRECTORY -ne $null) { $TargetFolder = $env:AGENT_HOMEDIRECTORY }
else { $TargetFolder = [System.Environment]::GetEnvironmentVariable('TEMP','Machine') }
# Loop through each CA in the machine store
Get-ChildItem -Path Cert:LocalMachineCA | ForEach-Object {
# Convert cert's bytes to Base64-encoded text and add begin/end markers
$Cert = "-----BEGIN CERTIFICATE-----`n"
$Cert+= $([System.Convert]::ToBase64String($_.export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert),'InsertLineBreaks'))
$Cert+= "`n-----END CERTIFICATE-----`n"
# Append cert to chain
$Chain+= $Cert
}
# Build target path
$CertFile = "$TargetFolderTrustedRootCAs.pem"
# Write to file system
$Chain | Out-File $CertFile -Force -Encoding ASCII
# Clean-up
$Chain = $null
# Let Node (running later in the pipeline) know from where to read certs
Write-Host "##vso[task.setvariable variable=NODE.EXTRA.CA.CERTS]$CertFile"
我从@alifen格式化了PowerShell脚本。下面的脚本可以在生成代理本身上执行。它采用目标路径的参数,并在服务器上设置环境变量。
信用@alifen
[CmdletBinding()]
param (
[Parameter()]
[string]
$TargetFolder = "$env:SystemDriveCerts"
)
If (-not(Test-Path $TargetFolder))
{
$null = New-Item -ItemType Directory -Path $TargetFolder -Force
}
# Loop through each CA in the machine store
Get-ChildItem -Path Cert:LocalMachineCA | ForEach-Object {
# Convert cert's bytes to Base64-encoded text and add begin/end markers
$Cert = "-----BEGIN CERTIFICATE-----`n"
$Cert += $([System.Convert]::ToBase64String($_.export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert), 'InsertLineBreaks'))
$Cert += "`n-----END CERTIFICATE-----`n"
# Append cert to chain
$Chain += $Cert
}
# Build target path
$CertFile = "$TargetFolderTrustedRootCAs.pem"
# Write to file system
Write-Host "[$($MyInvocation.MyCommand.Name)]: Exporting certs to: [$CertFile]"
$Chain | Out-File $CertFile -Force -Encoding ASCII
# Set Environment variable
Write-Host "[$($MyInvocation.MyCommand.Name)]: Setting environment variable [NODE_EXTRA_CA_CERTS] to [$CertFile]"
[Environment]::SetEnvironmentVariable("NODE_EXTRA_CA_CERTS", "$CertFile", "Machine")