Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure DevOps Server pipeline build fails when using self-signed SSL certificate with "unable to get local issuer certificate" during NuGet restore

After upgrading to Azure DevOps Server 2019, automated pipeline builds are failing at the NuGet restore step showing:

Error: Error: unable to get local issuer certificate

Packages failed to restore

Microsoft's documentation states that the build agent running on Windows uses the Windows certificate store, so I have checked that the required certificates are installed correctly on the build server, however it is still failing.

There are many questions with similar symptoms but not the same cause. After investigation, I have found the solution to this but I didn't spot anything on this exact issue so I will post an answer that will hopefully save somebody else some time!

like image 356
Andy Hames Avatar asked Jul 02 '19 15:07

Andy Hames


2 Answers

I use a PowerShell agent job with the following script. This effectively gives a "Use the Windows Machine Certificate Store" option to Node.JS for the pipeline.

Some notes:

  • Monitoring node.exe with ProcMon suggests that the file referenced in NODE_EXTRA_CA_CERTS is read every time the pipeline is run. However, others have suggested running Restart-Service vstsagent* -Force is required for the change to be picked up. This isn't my experience but perhaps something different between environments causes this behaviour.

  • This adds an additional ~1s pipeline execution time. Probably an acceptable price for a "set and forget certificate management for Node in Pipelines on Windows" but worth noting nonetheless.

# 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:\LocalMachine\CA | 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 = "$TargetFolder\TrustedRootCAs.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"
like image 77
alifen Avatar answered Jan 04 '23 04:01

alifen


It turns out that the Azure DevOps build agent is using a version of Node.js that doesn't use the Windows Certificate Store.

The solution required is to point Node.js at an exported copy (*.cer file) of your self-signed SSL certificate's root CA certificate, using either a system environment variable called NODE_EXTRA_CA_CERTS or by using a Task Variable called NODE.EXTRA.CA.CERTS, with a value pointing to the certificate.

Developer Community Issue Link

like image 27
Andy Hames Avatar answered Jan 04 '23 03:01

Andy Hames