From 56505170ab7169c9c42b7f21dfb03aa512226485 Mon Sep 17 00:00:00 2001 From: Szymon Osiecki Date: Fri, 29 Nov 2024 11:57:47 +0100 Subject: [PATCH] refactor(ps): vagrant self-signed certs fix --- .assets/scripts/vg_cacert_fix.ps1 | 88 ++++++++++++++++++++----------- .assets/scripts/vg_certs_add.ps1 | 23 +++++--- .assets/tools/cert_chain_pem.ps1 | 81 ---------------------------- 3 files changed, 75 insertions(+), 117 deletions(-) delete mode 100755 .assets/tools/cert_chain_pem.ps1 diff --git a/.assets/scripts/vg_cacert_fix.ps1 b/.assets/scripts/vg_cacert_fix.ps1 index c4c91ffc..5cd2037f 100644 --- a/.assets/scripts/vg_cacert_fix.ps1 +++ b/.assets/scripts/vg_cacert_fix.ps1 @@ -7,37 +7,65 @@ Fix self signed certificate error in Vagrant by installing certificates from cha .assets/scripts/vg_cacert_fix.ps1 #> -$ErrorActionPreference = 'Stop' - -# get Vagrant\embedded folder -try { - $vgRoot = Split-Path (Split-Path (Get-Command 'vagrant.exe').Source) - $embeddedDir = Join-Path $vgRoot -ChildPath 'embedded' -} catch [System.Management.Automation.CommandNotFoundException] { - $embeddedDir = 'C:\HashiCorp\Vagrant\embedded' - if (-not (Test-Path $embeddedDir -PathType Containe)) { - Write-Warning 'Vagrant path not found.' - break - } -} catch { - Write-Verbose $_.Exception.GetType().FullName - Write-Error $_ +begin { + $ErrorActionPreference = 'Stop' + + # set location to workspace folder + Push-Location "$PSScriptRoot/../.." + + # import SetupUtils for the Set-WslConf function + Import-Module (Convert-Path './modules/SetupUtils') -Force } -# intercept certificates from chain -$chain = .assets/tools/cert_chain_pem.ps1 -Uri 'gems.hashicorp.com' - -# build cacert.pem with all intercepted certificates -$builder = [System.Text.StringBuilder]::new() -foreach ($cert in $chain) { - $builder.AppendLine("# Issuer: $($cert.Issuer)") | Out-Null - $builder.AppendLine("# Subject: $($cert.Subject)") | Out-Null - $builder.AppendLine("# Label: $($cert.Label)") | Out-Null - $builder.AppendLine("# Serial: $($cert.SerialNumber)") | Out-Null - $builder.AppendLine("# SHA1 Fingerprint: $($cert.Thumbprint)") | Out-Null - $builder.AppendLine($cert.PEM) | Out-Null +process { + # determine the Vagrant\embedded folder + try { + $vgRoot = Split-Path (Split-Path (Get-Command 'vagrant.exe').Source) + $embeddedDir = Join-Path $vgRoot -ChildPath 'embedded' + } catch [System.Management.Automation.CommandNotFoundException] { + $embeddedDir = 'C:\HashiCorp\Vagrant\embedded' + if (-not (Test-Path $embeddedDir -PathType Container)) { + Write-Warning 'Vagrant path not found.' + break + } + } catch { + Write-Verbose $_.Exception.GetType().FullName + Write-Error $_ + } + + # get existing certificates in the vagrant cacert.pem file + $cacertPath = [System.IO.Path]::Combine($embeddedDir, 'cacert.pem') + if (Test-Path $cacertPath) { + $cacert = ConvertFrom-PEM -Path $cacertPath + } else { + New-Item -Path $cacertPath -ItemType File -Force | Out-Null + $cacert = [System.Collections.Generic.List[System.Security.Cryptography.X509Certificates.X509Certificate2]]::new() + } + + # intercept certificates from chain and filter out existing ones + $chain = Get-Certificate -Uri 'gems.hashicorp.com' -BuildChain | Select-Object -Skip 1 | Where-Object { + $_.Thumbprint -notin $cacert.Thumbprint + } + + # build cacert.pem with all intercepted certificates + if ($chain) { + $builder = [System.Text.StringBuilder]::new() + foreach ($cert in $chain) { + $pem = $cert | ConvertTo-PEM -AddHeader + $builder.AppendLine($pem) | Out-Null + } + # add intercepted certificates to the cacert.pem file + [System.IO.File]::AppendAllText($cacertPath, $builder.ToString().Trim()) + + # display added certificates + $cnList = $chain.ForEach({ $([regex]::Match($_.Subject, '(?<=CN=)(.)+?(?=,|$)').Value) }) | Join-String -Separator ', ' -DoubleQuote + Write-Host "Added certificates for $cnList to $cacertPath" + } else { + Write-Host 'No new certificates to add.' + } } -# save cacert.pem to the Vagrant\embedded folder -$cacertPath = [System.IO.Path]::Combine($embeddedDir, 'cacert.pem') -[System.IO.File]::WriteAllText($cacertPath, $builder.ToString().Trim()) +end { + # return to the original location + Pop-Location +} diff --git a/.assets/scripts/vg_certs_add.ps1 b/.assets/scripts/vg_certs_add.ps1 index bd9ca512..b33a2fc6 100755 --- a/.assets/scripts/vg_certs_add.ps1 +++ b/.assets/scripts/vg_certs_add.ps1 @@ -2,7 +2,8 @@ #Requires -PSEdition Core <# .SYNOPSIS -Script synopsis. +Creates bash script to install certificates from chain in the vagrant box +and adds the script invocation into the specified Vagrantfile. .PARAMETER Path Path to the Vagrantfile. @@ -11,7 +12,6 @@ Path to the Vagrantfile. $Path = 'vagrant/hyperv/fedora/Vagrantfile' .assets/scripts/vg_certs_add.ps1 -p $Path #> - [CmdletBinding()] [OutputType([System.Void])] param ( @@ -20,6 +20,14 @@ param ( [string]$Path ) +$ErrorActionPreference = 'Stop' + +# set location to workspace folder +Push-Location "$PSScriptRoot/../.." + +# import SetupUtils for the Set-WslConf function +Import-Module (Convert-Path './modules/SetupUtils') -Force + function Get-SshInstallScript ([string]$CertSaveStr) { $script = [string]::Join("`n", "#!/usr/bin/env bash`n", @@ -39,7 +47,7 @@ function Get-SshInstallScript ([string]$CertSaveStr) { "esac`n", '# write certificate in CERT_PATH', "$CertSaveStr", - "# update certificates", + '# update certificates', 'case $SYS_ID in', 'arch)', " trust extract-compat`n ;;", @@ -58,14 +66,17 @@ $scriptInstallRootCA = [IO.Path]::Combine($PWD, '.tmp', 'script_install_crt_chai $Path = Resolve-Path $Path $content = [IO.File]::ReadAllLines($Path) +# intercept certificates from chain and filter out existing ones +$chain = Get-Certificate -Uri 'gems.hashicorp.com' -BuildChain | Select-Object -Skip 1 + # create installation script New-Item (Split-Path $scriptInstallRootCA) -ItemType Directory -ErrorAction SilentlyContinue | Out-Null -$chain = .assets/tools/cert_chain_pem.ps1 - +# instantiate string builder to store the certificates $builder = [System.Text.StringBuilder]::new() foreach ($cert in $chain) { + $pem = $cert | ConvertTo-PEM $builder.AppendLine("cat <`"`$CERT_PATH/$($cert.Thumbprint).crt`"") | Out-Null - $builder.AppendLine($cert.PEM.Trim()) | Out-Null + $builder.AppendLine($pem.Trim()) | Out-Null $builder.AppendLine('EOF') | Out-Null } # save certificate installation file diff --git a/.assets/tools/cert_chain_pem.ps1 b/.assets/tools/cert_chain_pem.ps1 deleted file mode 100755 index 5df1fe5b..00000000 --- a/.assets/tools/cert_chain_pem.ps1 +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/pwsh -nop -#Requires -PSEdition Core -<# -.SYNOPSIS -Get root and intermediate certificates in PEM format from the certificate chain. - -.PARAMETER Uri -Uri to get the certificate chain from. - -.EXAMPLE -.assets/tools/cert_chain_pem.ps1 -# :specify custom Uri -$Uri = 'www.powershellgallery.com' -.assets/tools/cert_chain_pem.ps1 $Uri -#> -[CmdletBinding()] -param ( - [Parameter(Position = 0)] - [ValidateNotNullOrEmpty()] - [string]$Uri = 'www.google.com' -) - -begin { - $ErrorActionPreference = 'Stop' - - $tcpClient = [System.Net.Sockets.TcpClient]::new($Uri, 443) - $chain = [System.Security.Cryptography.X509Certificates.X509Chain]::new() - $sslStream = [System.Net.Security.SslStream]::new($tcpClient.GetStream()) - - # instantiate list for storing PEM encoded certificates - $pems = [System.Collections.Generic.List[PSCustomObject]]::new() -} - -process { - try { - $sslStream.AuthenticateAsClient($Uri) - $certificate = $sslStream.RemoteCertificate - } finally { - $sslStream.Close() - } - # check certificate chain - $isChainValid = $chain.Build($certificate) - if ($isChainValid) { - # build certificate chain - $certificate = $chain.ChainElements.Certificate - for ($i = 1; $i -lt $certificate.Count; $i++) { - # convert certificate to base64 - $base64 = [System.Convert]::ToBase64String($certificate[$i].RawData) - # build PEM encoded X.509 certificate - $builder = [System.Text.StringBuilder]::new() - $builder.AppendLine('-----BEGIN CERTIFICATE-----') | Out-Null - for ($j = 0; $j -lt $base64.Length; $j += 64) { - $length = [System.Math]::Min(64, $base64.Length - $j) - $builder.AppendLine($base64.Substring($j, $length)) | Out-Null - } - $builder.AppendLine('-----END CERTIFICATE-----') | Out-Null - # create pem object with parsed common information and PEM encoded certificate - $pem = @{ - Issuer = $certificate[$i].Issuer - Subject = $certificate[$i].Subject - SerialNumber = $certificate[$i].SerialNumber - Thumbprint = $certificate[$i].Thumbprint - PEM = $builder.ToString().Replace("`r`n", "`n") - } - # check if CN available, otherwise add OU as label - $cn = [regex]::Match($pem.Subject, '(?<=CN=)(.)+?(?=,|$)').Value.Trim().Trim('"') - if ($cn) { - $pem.Label = $cn - } else { - $pem.Label = [regex]::Match($pem.Subject, '(?<=OU=)(.)+?(?=,|$)').Value.Trim().Trim('"') - } - $pems.Add([PSCustomObject]$pem) - } - } else { - Write-Warning 'SSL certificate chain validation failed.' - } -} - -end { - return $pems -}