-
-
Save emilwojcik93/7eb1e172f8bb038e324c6e4a7f4ccaaa to your computer and use it in GitHub Desktop.
| #Requires -Version 5.1 | |
| <# | |
| .SYNOPSIS | |
| Universal Corporate Certificate Installer for WSL and Development Environments | |
| .DESCRIPTION | |
| A comprehensive, generic script for automatically installing corporate certificates | |
| in WSL distributions and configuring development environments. Designed to work | |
| across different corporate environments without containing sensitive data. | |
| This script: | |
| - Detects corporate certificates using configurable patterns | |
| - Automatically detects WSL distributions from Windows registry | |
| - Installs certificates in WSL using proper path handling | |
| - Optionally extracts intermediate certificates from live connections | |
| - Configures Node.js and other development environment variables | |
| - Tests connectivity with popular domains | |
| - Creates optimized certificate bundles | |
| Can be executed remotely via: | |
| &([ScriptBlock]::Create((irm https://gist.githubusercontent.com/emilwojcik93/7eb1e172f8bb038e324c6e4a7f4ccaaa/raw/Auto-Install-CertificatesInWSL.ps1))) -Verbose | |
| .PARAMETER DescriptionPatterns | |
| Array of patterns to search for in certificate subjects/descriptions. | |
| Default: @("CA", "Zscaler") | |
| Example: @("Acme Corp", "Zscaler", "Corporate CA") | |
| .PARAMETER ExcludeIssuers | |
| Array of issuer names to exclude from certificate search results. | |
| Default includes common public CAs that should not be exported. | |
| .PARAMETER WSLDistro | |
| Specific WSL distribution name to target. If not specified, uses registry-detected default distro. | |
| Example: "Ubuntu-20.04", "Debian", "Ubuntu" | |
| .PARAMETER SetupNodeJS | |
| Configure Windows Node.js environment variables for certificate bundle usage. | |
| This is a switch parameter - include it to enable Windows Node.js configuration. | |
| .PARAMETER ExtractIntermediate | |
| Attempt to extract intermediate certificates from live HTTPS connections. | |
| Default: $true | |
| .PARAMETER TestUrls | |
| Array of URLs to test certificate installation against. | |
| Default: @("https://google.com", "https://github.com", "https://www.microsoft.com", "https://api.github.com") | |
| .PARAMETER CertificateDirectory | |
| Directory to export certificates to. Default: "$env:USERPROFILE\certificates" | |
| .PARAMETER CreateBundle | |
| Create a combined certificate bundle file for Node.js and other applications. | |
| Default: $true | |
| .PARAMETER SetupEnvironmentVars | |
| Configure additional Windows environment variables (SSL_CERT_FILE, HTTPS_CA_BUNDLE, etc.). | |
| This is a switch parameter - include it to enable additional Windows environment variables. | |
| .PARAMETER SkipWSLInstall | |
| Skip WSL certificate installation (export certificates only). | |
| Default: $false | |
| .PARAMETER ShowWSLInfo | |
| Display detailed information about installed WSL distributions and exit. | |
| This is a switch parameter - include it to show WSL information only. | |
| .PARAMETER InstallAllDistros | |
| Install certificates for ALL installed WSL distributions automatically. | |
| This is a switch parameter - include it to target all distributions. | |
| .PARAMETER OverwriteExisting | |
| Overwrite existing certificate files in the output directory. | |
| This is a switch parameter - include it to overwrite existing files. | |
| .PARAMETER SetupWSLEnvironment | |
| Configure comprehensive environment variables inside WSL distributions for development tools. | |
| This is a switch parameter - include it to enable WSL environment variable setup. | |
| .PARAMETER TestBeforeAndAfter | |
| Test HTTPS connectivity before and after certificate installation with improvement metrics. | |
| Default: $true | |
| .EXAMPLE | |
| .\Auto-Install-CertificatesInWSL.ps1 -Verbose | |
| .EXAMPLE | |
| .\Auto-Install-CertificatesInWSL.ps1 -DescriptionPatterns @("Zscaler", "Acme Corp") -WSLDistro "Ubuntu-20.04" -Verbose | |
| .EXAMPLE | |
| # Remote execution from GitHub Gist | |
| &([ScriptBlock]::Create((irm https://gist.githubusercontent.com/emilwojcik93/7eb1e172f8bb038e324c6e4a7f4ccaaa/raw/Auto-Install-CertificatesInWSL.ps1))) -Verbose | |
| .EXAMPLE | |
| # Corporate environment with custom patterns | |
| .\Auto-Install-CertificatesInWSL.ps1 -DescriptionPatterns @("MyCompany", "Proxy CA") -TestUrls @("https://internal.mycompany.com") -Verbose | |
| .NOTES | |
| Version: 3.0 | |
| Author: Corporate Development Community | |
| Requires: PowerShell 5.1+, WSL 2 | |
| Universal script designed to work across different corporate environments. | |
| Based on: https://gist.github.com/emilwojcik93/7eb1e172f8bb038e324c6e4a7f4ccaaa | |
| Features: | |
| - Generic/universal design with no hardcoded sensitive data | |
| - Multi-pattern certificate detection | |
| - Registry-based WSL distribution detection | |
| - Improved WSL command syntax with proper path handling | |
| - Optional intermediate certificate extraction | |
| - Comprehensive environment variable setup | |
| - Enhanced connectivity testing | |
| - Parametrized options with sensible defaults | |
| - Verbose logging and error handling | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [string[]]$DescriptionPatterns = @("CA", "Zscaler"), | |
| [string[]]$ExcludeIssuers = @( | |
| "Microsoft", "DigiCert", "VeriSign", "Thawte", "GeoTrust", "RapidSSL", | |
| "Symantec", "Entrust", "GlobalSign", "Comodo", "StartCom", "GoDaddy", | |
| "Let's Encrypt", "ISRG", "USERTrust", "AddTrust", "Certum", "QuoVadis", | |
| "SSL.com", "Starfield", "IdenTrust", "Amazon", "Google Trust Services", | |
| "Sectigo", "COMODO", "Baltimore", "UTN-USERFirst", "AAA Certificate Services" | |
| ), | |
| [string]$WSLDistro = "", | |
| [switch]$SetupNodeJS, | |
| [bool]$ExtractIntermediate = $true, | |
| [string[]]$TestUrls = @( | |
| "https://google.com", | |
| "https://github.com", | |
| "https://www.microsoft.com", | |
| "https://api.github.com", | |
| "https://stackoverflow.com", | |
| "https://www.npmjs.com" | |
| ), | |
| [string]$CertificateDirectory = (Join-Path $env:USERPROFILE "certificates"), | |
| [hashtable]$CustomWSLPaths = @{}, | |
| [bool]$CreateBundle = $true, | |
| [switch]$SetupEnvironmentVars, | |
| [bool]$SkipWSLInstall = $false, | |
| [switch]$ShowWSLInfo, | |
| [switch]$InstallAllDistros, | |
| [switch]$OverwriteExisting, | |
| [switch]$SetupWSLEnvironment, | |
| [bool]$TestBeforeAndAfter = $true, | |
| [string]$LogFile = "", | |
| [switch]$EnableLogging | |
| ) | |
| # Suppress PowerShell progress bars for cleaner output | |
| $ProgressPreference = 'SilentlyContinue' | |
| $ErrorActionPreference = 'Continue' | |
| # WSL distribution configurations - supports major Linux distributions | |
| # Based on official WSL distribution names from 'wsl --list --online' | |
| $WSLDistroConfigs = @{ | |
| # Ubuntu family (Debian-based) | |
| "Ubuntu" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| "Ubuntu-20.04" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| "Ubuntu-22.04" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| "Ubuntu-24.04" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| "Debian" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| "kali-linux" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/usr/local/share/ca-certificates/"; FileExt = ".crt" } | |
| # Arch Linux family | |
| "archlinux" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/ca-certificates/trust-source/anchors/"; FileExt = ".crt" } | |
| # SUSE family | |
| "openSUSE-Tumbleweed" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| "openSUSE-Leap-15.6" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| "SUSE-Linux-Enterprise-15-SP6" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| "SUSE-Linux-Enterprise-15-SP7" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| # RHEL family (Red Hat-based) | |
| "FedoraLinux-42" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "AlmaLinux-8" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "AlmaLinux-9" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "AlmaLinux-10" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "AlmaLinux-Kitten-10" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "OracleLinux_7_9" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "OracleLinux_8_10" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "OracleLinux_9_5" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| # Legacy/Common aliases (for backward compatibility) | |
| "CentOS" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "RHEL" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "Fedora" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "AlmaLinux" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "Rocky" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/pki/ca-trust/source/anchors/"; FileExt = ".crt" } | |
| "openSUSE" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| "SLES" = @{ UpdateCommand = "update-ca-certificates"; CertPath = "/etc/ssl/certs/"; FileExt = ".pem" } | |
| "Arch" = @{ UpdateCommand = "update-ca-trust"; CertPath = "/etc/ca-certificates/trust-source/anchors/"; FileExt = ".crt" } | |
| } | |
| #region Helper Functions | |
| function Write-Status { | |
| <# | |
| .SYNOPSIS | |
| Writes formatted status messages with timestamps and color coding, optionally to log file | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [AllowEmptyString()] | |
| [string]$Message, | |
| [ValidateSet("INFO", "SUCCESS", "WARNING", "ERROR", "VERBOSE")] | |
| [string]$Type = "INFO" | |
| ) | |
| # Handle empty messages by just writing a blank line | |
| if ([string]::IsNullOrEmpty($Message)) { | |
| Write-Host "" | |
| if ($script:LogFilePath) { | |
| Add-Content -Path $script:LogFilePath -Value "" -Encoding UTF8 | |
| } | |
| return | |
| } | |
| $timestamp = Get-Date -Format "HH:mm:ss" | |
| $fullTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | |
| $prefix = switch ($Type) { | |
| "SUCCESS" { "[SUCCESS]" } | |
| "WARNING" { "[WARNING]" } | |
| "ERROR" { "[ERROR]" } | |
| "VERBOSE" { "[VERBOSE]" } | |
| default { "[INFO]" } | |
| } | |
| $color = switch ($Type) { | |
| "SUCCESS" { "Green" } | |
| "WARNING" { "Yellow" } | |
| "ERROR" { "Red" } | |
| "VERBOSE" { "Gray" } | |
| default { "Cyan" } | |
| } | |
| $consoleMessage = "[$timestamp] $prefix $Message" | |
| $logMessage = "[$fullTimestamp] $prefix $Message" | |
| # Write to console | |
| Write-Host $consoleMessage -ForegroundColor $color | |
| # Write to log file if logging is enabled | |
| if ($script:LogFilePath) { | |
| try { | |
| Add-Content -Path $script:LogFilePath -Value $logMessage -Encoding UTF8 | |
| } | |
| catch { | |
| # Don't fail if logging fails | |
| Write-Host "[ERROR] Failed to write to log file: $($_.Exception.Message)" -ForegroundColor Red | |
| } | |
| } | |
| } | |
| function Get-WSLDistributionsFromRegistry { | |
| <# | |
| .SYNOPSIS | |
| Retrieves all WSL distributions from Windows registry with detailed information | |
| .DESCRIPTION | |
| Queries the Windows registry to get comprehensive WSL distribution information, | |
| including distribution names, states, versions, and package details. | |
| This is more reliable than parsing WSL command output. | |
| #> | |
| Write-Verbose "Detecting WSL distributions from registry..." | |
| try { | |
| $registryPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" | |
| if (-not (Test-Path $registryPath)) { | |
| Write-Status "WSL registry path not found - WSL may not be installed" "WARNING" | |
| return @() | |
| } | |
| $distributions = @() | |
| # Get default distribution GUID | |
| $defaultGuid = Get-ItemProperty -Path $registryPath -Name DefaultDistribution -ErrorAction SilentlyContinue | | |
| Select-Object -ExpandProperty DefaultDistribution | |
| # Get all distribution GUIDs | |
| $distroGuids = Get-ChildItem -Path $registryPath -ErrorAction SilentlyContinue | | |
| Where-Object { $_.PSChildName -match '^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$' } | |
| foreach ($guidEntry in $distroGuids) { | |
| $distroPath = $guidEntry.PSPath | |
| $distroInfo = Get-ItemProperty -Path $distroPath -ErrorAction SilentlyContinue | |
| if ($distroInfo -and $distroInfo.DistributionName) { | |
| $distribution = @{ | |
| Name = $distroInfo.DistributionName | |
| GUID = $guidEntry.PSChildName | |
| IsDefault = ($guidEntry.PSChildName -eq $defaultGuid) | |
| State = $distroInfo.State # 1 = Running, 0 = Stopped | |
| Version = $distroInfo.Version # WSL version (1 or 2) | |
| DefaultUid = $distroInfo.DefaultUid | |
| BasePath = $distroInfo.BasePath | |
| PackageFamilyName = $distroInfo.PackageFamilyName | |
| DistributionFamily = Get-DistributionFamily -DistributionName $distroInfo.DistributionName | |
| } | |
| $distributions += $distribution | |
| Write-Verbose "Found WSL distribution: $($distribution.Name) (Version: $($distribution.Version), State: $($distribution.State))" | |
| } | |
| } | |
| Write-Status "Found $($distributions.Count) WSL distributions in registry" "SUCCESS" | |
| return $distributions | |
| } | |
| catch { | |
| Write-Status "Error detecting WSL distributions from registry: $($_.Exception.Message)" "ERROR" | |
| return @() | |
| } | |
| } | |
| function Get-DistributionFamily { | |
| <# | |
| .SYNOPSIS | |
| Determines the distribution family from the distribution name | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistributionName | |
| ) | |
| # Determine distribution family for certificate management | |
| switch -Regex ($DistributionName) { | |
| '^Ubuntu.*|^Debian$|^kali-linux$' { return "Debian" } | |
| '^AlmaLinux.*|^FedoraLinux.*|^OracleLinux.*|^CentOS.*|^RHEL.*|^Rocky.*|^Fedora$' { return "RHEL" } | |
| '^openSUSE.*|^SUSE.*|^SLES$' { return "SUSE" } | |
| '^archlinux$|^Arch$' { return "Arch" } | |
| default { return "Unknown" } | |
| } | |
| } | |
| function Get-DefaultWSLDistro { | |
| <# | |
| .SYNOPSIS | |
| Gets the default WSL distribution using registry information | |
| #> | |
| Write-Verbose "Getting default WSL distribution from registry..." | |
| $distributions = Get-WSLDistributionsFromRegistry | |
| if ($distributions.Count -eq 0) { | |
| Write-Status "No WSL distributions found" "ERROR" | |
| return $null | |
| } | |
| # Find default distribution | |
| $defaultDistro = $distributions | Where-Object { $_.IsDefault } | |
| if ($defaultDistro) { | |
| Write-Status "Detected default WSL distribution: $($defaultDistro.Name) (Family: $($defaultDistro.DistributionFamily))" "SUCCESS" | |
| return $defaultDistro.Name | |
| } | |
| # If no default set, use first available running distribution | |
| $runningDistro = $distributions | Where-Object { $_.State -eq 1 } | Select-Object -First 1 | |
| if ($runningDistro) { | |
| Write-Status "Using running WSL distribution: $($runningDistro.Name) (Family: $($runningDistro.DistributionFamily))" "SUCCESS" | |
| return $runningDistro.Name | |
| } | |
| # Otherwise use first available distribution | |
| $firstDistro = $distributions | Select-Object -First 1 | |
| Write-Status "Using first available WSL distribution: $($firstDistro.Name) (Family: $($firstDistro.DistributionFamily))" "SUCCESS" | |
| return $firstDistro.Name | |
| } | |
| function Test-WSLAvailability { | |
| <# | |
| .SYNOPSIS | |
| Verifies WSL availability and distribution installation using registry | |
| #> | |
| Write-Verbose "Testing WSL availability..." | |
| try { | |
| # Check if WSL registry path exists | |
| $registryPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss" | |
| if (-not (Test-Path $registryPath)) { | |
| Write-Status "WSL is not installed or not available (registry path missing)" "ERROR" | |
| return $false | |
| } | |
| # Get distributions from registry | |
| $distributions = Get-WSLDistributionsFromRegistry | |
| if ($distributions.Count -eq 0) { | |
| Write-Status "No WSL distributions are installed" "ERROR" | |
| return $false | |
| } | |
| Write-Status "WSL is available with $($distributions.Count) installed distribution(s)" "SUCCESS" | |
| # Show available distributions in verbose mode | |
| if ($VerbosePreference -eq 'Continue') { | |
| Write-Status "Available WSL distributions:" "INFO" | |
| foreach ($distro in $distributions) { | |
| $status = if ($distro.State -eq 1) { "Running" } else { "Stopped" } | |
| $default = if ($distro.IsDefault) { " (Default)" } else { "" } | |
| Write-Host " - $($distro.Name): $status, WSL$($distro.Version)$default" -ForegroundColor Gray | |
| } | |
| } | |
| return $true | |
| } | |
| catch { | |
| Write-Status "Error testing WSL availability: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Show-WSLDistributionInfo { | |
| <# | |
| .SYNOPSIS | |
| Displays detailed information about installed WSL distributions | |
| #> | |
| $distributions = Get-WSLDistributionsFromRegistry | |
| if ($distributions.Count -eq 0) { | |
| Write-Status "No WSL distributions found" "WARNING" | |
| return | |
| } | |
| Write-Status "Installed WSL Distributions:" "INFO" | |
| Write-Status "============================" "INFO" | |
| foreach ($distro in $distributions) { | |
| $status = if ($distro.State -eq 1) { "Running" } else { "Stopped" } | |
| $default = if ($distro.IsDefault) { " (Default)" } else { "" } | |
| Write-Host "Distribution: $($distro.Name)$default" -ForegroundColor Cyan | |
| Write-Host " Family: $($distro.DistributionFamily)" -ForegroundColor Gray | |
| Write-Host " Status: $status" -ForegroundColor Gray | |
| Write-Host " WSL Version: $($distro.Version)" -ForegroundColor Gray | |
| Write-Host " Default UID: $($distro.DefaultUid)" -ForegroundColor Gray | |
| Write-Host " GUID: $($distro.GUID)" -ForegroundColor Gray | |
| # Show certificate configuration if available | |
| if ($WSLDistroConfigs.ContainsKey($distro.Name)) { | |
| $config = $WSLDistroConfigs[$distro.Name] | |
| Write-Host " Certificate Path: $($config.CertPath)" -ForegroundColor Gray | |
| Write-Host " Update Command: $($config.UpdateCommand)" -ForegroundColor Gray | |
| } else { | |
| Write-Host " Certificate Config: Will use $($distro.DistributionFamily) family defaults" -ForegroundColor Yellow | |
| } | |
| Write-Host "" | |
| } | |
| } | |
| function Test-WSLDistroAvailability { | |
| <# | |
| .SYNOPSIS | |
| Tests if a specific WSL distribution is available and responsive | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName | |
| ) | |
| Write-Verbose "Testing WSL distribution availability: $DistroName" | |
| try { | |
| # Test basic command execution | |
| $testResult = wsl -d $DistroName -e bash -c "echo 'test'" 2>$null | |
| if ($LASTEXITCODE -eq 0 -and $testResult -eq "test") { | |
| Write-Verbose "WSL distribution $DistroName is responsive" | |
| return $true | |
| } else { | |
| Write-Status "WSL distribution $DistroName is not responsive" "ERROR" | |
| return $false | |
| } | |
| } | |
| catch { | |
| Write-Status "Error testing WSL distribution $DistroName : $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Install-WSLDependencies { | |
| <# | |
| .SYNOPSIS | |
| Installs required dependencies in WSL distribution (curl, openssl) | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName | |
| ) | |
| Write-Status "Checking and installing dependencies in WSL distribution: $DistroName" "INFO" | |
| try { | |
| # Test curl availability | |
| $curlTest = wsl -d $DistroName -u root -e bash -c "which curl" 2>$null | |
| if ($LASTEXITCODE -ne 0 -or -not $curlTest) { | |
| Write-Status "curl not found, attempting to install..." "WARNING" | |
| # Detect package manager and install curl | |
| $aptResult = wsl -d $DistroName -u root -e bash -c "which apt-get" 2>$null | |
| $yumResult = wsl -d $DistroName -u root -e bash -c "which yum" 2>$null | |
| $dnfResult = wsl -d $DistroName -u root -e bash -c "which dnf" 2>$null | |
| $pacmanResult = wsl -d $DistroName -u root -e bash -c "which pacman" 2>$null | |
| $zypperResult = wsl -d $DistroName -u root -e bash -c "which zypper" 2>$null | |
| if ($LASTEXITCODE -eq 0 -and $aptResult) { | |
| wsl -d $DistroName -u root -e bash -c "apt-get update && apt-get install -y curl openssl ca-certificates" 2>$null | |
| } elseif ($LASTEXITCODE -eq 0 -and $dnfResult) { | |
| wsl -d $DistroName -u root -e bash -c "dnf install -y curl openssl ca-certificates" 2>$null | |
| } elseif ($LASTEXITCODE -eq 0 -and $yumResult) { | |
| wsl -d $DistroName -u root -e bash -c "yum install -y curl openssl ca-certificates" 2>$null | |
| } elseif ($LASTEXITCODE -eq 0 -and $pacmanResult) { | |
| wsl -d $DistroName -u root -e bash -c "pacman -Sy --noconfirm curl openssl ca-certificates" 2>$null | |
| } elseif ($LASTEXITCODE -eq 0 -and $zypperResult) { | |
| wsl -d $DistroName -u root -e bash -c "zypper install -y curl openssl ca-certificates" 2>$null | |
| } else { | |
| Write-Status "Could not detect package manager to install curl" "ERROR" | |
| return $false | |
| } | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Status "Dependencies installed successfully" "SUCCESS" | |
| return $true | |
| } else { | |
| Write-Status "Failed to install dependencies" "ERROR" | |
| return $false | |
| } | |
| } else { | |
| Write-Verbose "curl is available in $DistroName" | |
| return $true | |
| } | |
| } | |
| catch { | |
| Write-Status "Error installing dependencies: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Search-CorporateCertificates { | |
| <# | |
| .SYNOPSIS | |
| Searches for certificates matching specified patterns and exclusions | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string[]]$DescriptionPatterns, | |
| [Parameter(Mandatory)] | |
| [string[]]$ExcludeIssuers | |
| ) | |
| Write-Status "Searching for certificates matching patterns: $($DescriptionPatterns -join ', ')" "INFO" | |
| # Define certificate stores to search | |
| $certificateStores = @( | |
| "Cert:\LocalMachine\Root", | |
| "Cert:\LocalMachine\CA", | |
| "Cert:\LocalMachine\AuthRoot", | |
| "Cert:\LocalMachine\TrustedPublisher", | |
| "Cert:\LocalMachine\TrustedPeople", | |
| "Cert:\LocalMachine\My", | |
| "Cert:\CurrentUser\Root", | |
| "Cert:\CurrentUser\CA", | |
| "Cert:\CurrentUser\AuthRoot", | |
| "Cert:\CurrentUser\TrustedPublisher", | |
| "Cert:\CurrentUser\TrustedPeople", | |
| "Cert:\CurrentUser\My" | |
| ) | |
| $matchedCertificates = @() | |
| $processedThumbprints = @{} | |
| foreach ($store in $certificateStores) { | |
| Write-Verbose "Searching certificates in store: $store" | |
| try { | |
| $certificates = Get-ChildItem -Path $store -ErrorAction SilentlyContinue | |
| foreach ($cert in $certificates) { | |
| # Skip if we've already processed this certificate (by thumbprint) | |
| if ($processedThumbprints.ContainsKey($cert.Thumbprint)) { | |
| continue | |
| } | |
| # Check if certificate matches any of the description patterns | |
| $matchesPattern = $false | |
| $matchedPattern = "" | |
| foreach ($pattern in $DescriptionPatterns) { | |
| if ($cert.Subject -like "*$pattern*" -or $cert.Issuer -like "*$pattern*") { | |
| $matchesPattern = $true | |
| $matchedPattern = $pattern | |
| break | |
| } | |
| } | |
| if ($matchesPattern) { | |
| # Check if certificate should be excluded | |
| $shouldExclude = $false | |
| foreach ($excludeIssuer in $ExcludeIssuers) { | |
| if ($cert.Issuer -like "*$excludeIssuer*" -or $cert.Subject -like "*$excludeIssuer*") { | |
| $shouldExclude = $true | |
| Write-Verbose "Excluding certificate with issuer/subject containing: $excludeIssuer" | |
| break | |
| } | |
| } | |
| if (-not $shouldExclude) { | |
| Write-Verbose "Matched certificate: $($cert.PSPath)" | |
| Write-Verbose " Subject: $($cert.Subject)" | |
| Write-Verbose " Issuer: $($cert.Issuer)" | |
| Write-Verbose " Expires: $($cert.NotAfter)" | |
| Write-Verbose " Store: $store" | |
| Write-Verbose " Matched Pattern: $matchedPattern" | |
| $matchedCertificates += @{ | |
| Certificate = $cert | |
| Store = $store | |
| MatchedPattern = $matchedPattern | |
| } | |
| # Mark as processed | |
| $processedThumbprints[$cert.Thumbprint] = $true | |
| } | |
| } | |
| } | |
| } | |
| catch { | |
| Write-Verbose "Could not access certificate store $store : $($_.Exception.Message)" | |
| } | |
| } | |
| Write-Status "Found $($matchedCertificates.Count) unique matching certificates" "SUCCESS" | |
| return $matchedCertificates | |
| } | |
| function Export-CertificateToFile { | |
| <# | |
| .SYNOPSIS | |
| Exports a certificate to a PEM file with proper formatting and overwrite handling | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, | |
| [Parameter(Mandatory)] | |
| [string]$FilePath | |
| ) | |
| try { | |
| # Check if file exists and handle overwrite | |
| if (Test-Path $FilePath) { | |
| if ($OverwriteExisting) { | |
| Write-Verbose "Overwriting existing certificate file: $FilePath" | |
| } else { | |
| Write-Status "Certificate file already exists: $(Split-Path -Leaf $FilePath) (use -OverwriteExisting to overwrite)" "WARNING" | |
| return $true # Skip but don't fail | |
| } | |
| } | |
| # Export certificate as Base64 encoded bytes | |
| $bytes = $Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) | |
| $base64 = [Convert]::ToBase64String($bytes) | |
| # Wrap base64 to 64 characters per line (PEM standard) | |
| $wrapped = ($base64 -split '(.{64})' | Where-Object { $_ }) -join "`n" | |
| # Create PEM format with proper headers | |
| $pemContent = "-----BEGIN CERTIFICATE-----`n$wrapped`n-----END CERTIFICATE-----" | |
| # Write to file with UTF8 encoding | |
| Set-Content -Path $FilePath -Value $pemContent -Encoding UTF8 | |
| Write-Verbose "Exported certificate to file: $FilePath" | |
| return $true | |
| } | |
| catch { | |
| Write-Status "Failed to export certificate to $FilePath : $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Get-SafeFileName { | |
| <# | |
| .SYNOPSIS | |
| Creates a safe filename from certificate subject | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, | |
| [string]$MatchedPattern = "", | |
| [int]$Index = 0 | |
| ) | |
| try { | |
| # Extract CN from subject | |
| $subject = $Certificate.Subject | |
| $cn = "" | |
| # Try to extract CN (Common Name) | |
| if ($subject -match "CN=([^,]+)") { | |
| $cn = $Matches[1].Trim() | |
| } | |
| # If no CN found, use the matched pattern or generic name | |
| if (-not $cn) { | |
| $cn = if ($MatchedPattern) { $MatchedPattern } else { "Certificate" } | |
| } | |
| # Clean the name for filesystem compatibility | |
| $safeName = $cn -replace '[^\w\s\-\.]', '_' -replace '\s+', '_' -replace '_+', '_' | |
| $safeName = $safeName.Trim('_') | |
| # Add pattern prefix if available | |
| if ($MatchedPattern -and $cn -notlike "*$MatchedPattern*") { | |
| $safeName = "${MatchedPattern}_${safeName}" | |
| } | |
| # Add index if provided (for uniqueness) | |
| if ($Index -gt 0) { | |
| $safeName = "${safeName}_${Index}" | |
| } | |
| # Ensure reasonable length | |
| if ($safeName.Length -gt 100) { | |
| $safeName = $safeName.Substring(0, 100) | |
| } | |
| return "${safeName}.crt" | |
| } | |
| catch { | |
| # Fallback to generic name | |
| $fallbackName = if ($MatchedPattern) { "${MatchedPattern}_Certificate" } else { "Certificate" } | |
| if ($Index -gt 0) { | |
| $fallbackName = "${fallbackName}_${Index}" | |
| } | |
| return "${fallbackName}.crt" | |
| } | |
| } | |
| function Remove-OldCertificatesFromWSL { | |
| <# | |
| .SYNOPSIS | |
| Removes old certificates from WSL certificate store | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [Parameter(Mandatory)] | |
| [string]$CertPath | |
| ) | |
| try { | |
| Write-Verbose "Removing old certificates from WSL distribution: $DistroName" | |
| # Remove old certificates matching common corporate patterns | |
| $removeCommand = "find '$CertPath' -name '*.crt' -exec rm -f {} \; 2>/dev/null || true" | |
| wsl --cd "/" -d $DistroName -u root -e bash -c $removeCommand 2>$null | |
| Write-Verbose "Removed old certificates from WSL" | |
| return $true | |
| } | |
| catch { | |
| Write-Verbose "Could not remove old certificates (this is normal): $($_.Exception.Message)" | |
| return $false | |
| } | |
| } | |
| function Install-CertificateInWSL { | |
| <# | |
| .SYNOPSIS | |
| Installs a certificate in the specified WSL distribution using improved syntax | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$CertFilePath, | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [Parameter(Mandatory)] | |
| [hashtable]$DistroConfig | |
| ) | |
| $certFileName = Split-Path -Leaf $CertFilePath | |
| $certFileNameWithExt = [System.IO.Path]::GetFileNameWithoutExtension($certFileName) + $DistroConfig.FileExt | |
| Write-Verbose "Installing certificate $certFileName in WSL distribution: $DistroName" | |
| try { | |
| # Convert Windows path to WSL path | |
| $wslSourcePath = $CertFilePath -replace '^C:', '/mnt/c' -replace '\\', '/' | |
| $wslDestPath = $DistroConfig.CertPath + $certFileNameWithExt | |
| # Ensure certificate directory exists | |
| $mkdirCommand = "mkdir -p '$(Split-Path -Path $wslDestPath -Parent)'" | |
| wsl --cd "/" -d $DistroName -u root -e bash -c $mkdirCommand 2>$null | |
| # Copy certificate to WSL using improved syntax | |
| $copyCommand = "cp '$wslSourcePath' '$wslDestPath'" | |
| wsl --cd "$(Split-Path -Path $CertFilePath -Parent)" -d $DistroName -u root -e bash -c $copyCommand 2>$null | Out-Null | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Status "Failed to copy certificate $certFileName to WSL" "ERROR" | |
| return $false | |
| } | |
| Write-Verbose "Certificate copied to $wslDestPath" | |
| # Verify certificate was copied successfully | |
| $verifyCommand = "test -f '$wslDestPath' && echo 'EXISTS' || echo 'MISSING'" | |
| $verifyResult = wsl --cd "/" -d $DistroName -u root -e bash -c $verifyCommand 2>$null | |
| if ($verifyResult -like "*EXISTS*") { | |
| Write-Status "Certificate installed: $certFileNameWithExt" "SUCCESS" | |
| return $true | |
| } else { | |
| Write-Status "Certificate copy verification failed for: $certFileName" "ERROR" | |
| return $false | |
| } | |
| } | |
| catch { | |
| Write-Status "Error installing certificate $certFileName in WSL: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Update-WSLCertificateStore { | |
| <# | |
| .SYNOPSIS | |
| Updates the WSL certificate store using the appropriate command for the distribution | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [Parameter(Mandatory)] | |
| [hashtable]$DistroConfig | |
| ) | |
| Write-Status "Updating certificate store in WSL distribution: $DistroName" "INFO" | |
| try { | |
| # Test if update command is available | |
| $commandCheck = switch ($DistroConfig.UpdateCommand) { | |
| "update-ca-certificates" { "which update-ca-certificates >/dev/null 2>&1 && echo 'AVAILABLE' || echo 'MISSING'" } | |
| "update-ca-trust" { "which update-ca-trust >/dev/null 2>&1 && echo 'AVAILABLE' || echo 'MISSING'" } | |
| "trust extract-compat" { "which trust >/dev/null 2>&1 && echo 'AVAILABLE' || echo 'MISSING'" } | |
| default { "echo 'UNKNOWN'" } | |
| } | |
| $commandAvailable = wsl --cd "/" -d $DistroName -u root -e bash -c $commandCheck 2>$null | |
| Write-Verbose "Command availability check: $commandAvailable" | |
| if ($commandAvailable -like "*AVAILABLE*") { | |
| Write-Verbose "Executing certificate update command: $($DistroConfig.UpdateCommand)" | |
| # Execute the appropriate update command | |
| wsl --cd "/" -d $DistroName -u root -e bash -c $DistroConfig.UpdateCommand 2>$null | Out-Null | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Status "Certificate store updated successfully in WSL" "SUCCESS" | |
| return $true | |
| } else { | |
| Write-Status "Certificate store update command failed (exit code: $LASTEXITCODE)" "ERROR" | |
| return $false | |
| } | |
| } else { | |
| Write-Status "$($DistroConfig.UpdateCommand) is not available in WSL distribution $DistroName" "ERROR" | |
| return $false | |
| } | |
| } | |
| catch { | |
| Write-Status "Error updating certificate store in WSL: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Test-HTTPSConnectivity { | |
| <# | |
| .SYNOPSIS | |
| Tests HTTPS connectivity from WSL using curl with enhanced error handling | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [Parameter(Mandatory)] | |
| [string]$Url | |
| ) | |
| try { | |
| Write-Verbose "Testing HTTPS connectivity for $Url in WSL distribution: $DistroName" | |
| # Use curl with comprehensive options for better testing | |
| $curlCommand = "curl -s -L --max-time 15 --connect-timeout 10 --write-out '%{http_code}' --output /dev/null '$Url'" | |
| $response = wsl --cd "/" -d $DistroName -u root -e bash -c $curlCommand 2>$null | |
| Write-Verbose "Response from HTTPS test for ${Url}: $response (Exit code: $LASTEXITCODE)" | |
| if ($LASTEXITCODE -eq 0 -and $response -match '^\d{3}$') { | |
| # Consider SSL handshake successful if we get any HTTP response | |
| # Even 4xx/5xx errors mean SSL worked, just application-level issues | |
| $statusCode = [int]$response | |
| $sslSuccess = ($statusCode -ge 200 -and $statusCode -lt 600) # Any valid HTTP response | |
| return @{ | |
| Success = $sslSuccess | |
| StatusCode = $response | |
| Error = $null | |
| } | |
| } else { | |
| return @{ | |
| Success = $false | |
| StatusCode = $response | |
| Error = "curl exit code: $LASTEXITCODE" | |
| } | |
| } | |
| } | |
| catch { | |
| Write-Verbose "Error testing HTTPS connectivity for ${Url}: $($_.Exception.Message)" | |
| return @{ | |
| Success = $false | |
| StatusCode = "000" | |
| Error = $_.Exception.Message | |
| } | |
| } | |
| } | |
| function Test-ConnectivityForDistribution { | |
| <# | |
| .SYNOPSIS | |
| Tests HTTPS connectivity for a specific distribution with detailed reporting | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [string[]]$TestUrls, | |
| [string]$TestPhase = "Test" | |
| ) | |
| Write-Status "[$TestPhase] Testing HTTPS connectivity for $DistroName..." "INFO" | |
| $results = @{} | |
| $successCount = 0 | |
| foreach ($url in $TestUrls) { | |
| $result = Test-HTTPSConnectivity -DistroName $DistroName -Url $url | |
| $results[$url] = $result | |
| if ($result.Success) { | |
| $successCount++ | |
| $statusCode = $result.StatusCode | |
| # Classify HTTP status codes more intelligently | |
| # The key is that we got ANY HTTP response - this means SSL handshake succeeded | |
| $statusType = switch ($statusCode) { | |
| { $_ -match '^(200|201|202)$' } { "SUCCESS" } # Perfect responses | |
| { $_ -match '^(301|302|303|307|308)$' } { "SUCCESS" } # Redirects (SSL worked) | |
| { $_ -match '^(403|405|429)$' } { "SUCCESS" } # Blocked but SSL worked | |
| { $_ -match '^(404|410)$' } { "SUCCESS" } # Not found but SSL worked | |
| { $_ -match '^(500|502|503|504)$' } { "WARNING" } # Server errors (SSL worked, server down) | |
| default { "WARNING" } # Other codes (SSL worked) | |
| } | |
| $icon = switch ($statusType) { | |
| "SUCCESS" { "[PASS]" } | |
| "WARNING" { "[WARN]" } | |
| default { "[INFO]" } | |
| } | |
| Write-Status "$icon $url (HTTP $statusCode)" $statusType | |
| } else { | |
| Write-Status "[FAIL] $url (Failed: $($result.Error))" "ERROR" | |
| } | |
| } | |
| $successRate = [Math]::Round(($successCount / $TestUrls.Count) * 100, 1) | |
| if ($successRate -eq 100) { | |
| Write-Status "[$TestPhase] All HTTPS tests passed for $DistroName ($successRate%)" "SUCCESS" | |
| } elseif ($successRate -gt 70) { | |
| Write-Status "[$TestPhase] Most HTTPS tests passed for $DistroName ($successRate%)" "WARNING" | |
| } else { | |
| Write-Status "[$TestPhase] HTTPS connectivity issues detected for $DistroName ($successRate%)" "ERROR" | |
| } | |
| return @{ | |
| Results = $results | |
| SuccessCount = $successCount | |
| TotalCount = $TestUrls.Count | |
| SuccessRate = $successRate | |
| } | |
| } | |
| function Get-IntermediateCertificate { | |
| <# | |
| .SYNOPSIS | |
| Extracts intermediate certificates from live HTTPS connections using openssl | |
| #> | |
| param( | |
| [string]$TestUrl = "https://google.com", | |
| [string]$OutputPath, | |
| [string]$DistroName | |
| ) | |
| if (-not $ExtractIntermediate) { | |
| Write-Verbose "Intermediate certificate extraction is disabled" | |
| return $null | |
| } | |
| Write-Status "Attempting to extract intermediate certificate from $TestUrl" "INFO" | |
| try { | |
| # Extract hostname from URL | |
| $hostname = $TestUrl -replace 'https://', '' -replace 'http://', '' -replace '/.*', '' | |
| # Extract certificate chain using openssl in WSL | |
| $opensslCommand = "echo | openssl s_client -connect ${hostname}:443 -servername $hostname -showcerts 2>/dev/null" | |
| $chainOutput = wsl --cd "/" -d $DistroName -u root -e bash -c $opensslCommand 2>$null | |
| if ($LASTEXITCODE -ne 0 -or -not $chainOutput) { | |
| Write-Status "Failed to extract certificate chain from $TestUrl" "WARNING" | |
| return $null | |
| } | |
| # Parse certificate chain to find intermediate certificate (usually 2nd certificate) | |
| $lines = $chainOutput -split "`n" | |
| $certCount = 0 | |
| $inCert = $false | |
| $intermediateCert = @() | |
| foreach ($line in $lines) { | |
| if ($line -match "-----BEGIN CERTIFICATE-----") { | |
| $certCount++ | |
| if ($certCount -eq 2) { # Second certificate is typically the intermediate | |
| $inCert = $true | |
| $intermediateCert = @($line) | |
| } | |
| } | |
| elseif ($line -match "-----END CERTIFICATE-----" -and $inCert) { | |
| $intermediateCert += $line | |
| break | |
| } | |
| elseif ($inCert) { | |
| $intermediateCert += $line | |
| } | |
| } | |
| if ($intermediateCert.Count -gt 0) { | |
| $certContent = $intermediateCert -join "`n" | |
| $intermediateFileName = "Intermediate_CA_$(Get-Date -Format 'yyyyMMdd_HHmmss').crt" | |
| $intermediatePath = Join-Path $OutputPath $intermediateFileName | |
| Set-Content -Path $intermediatePath -Value $certContent -Encoding UTF8 | |
| Write-Status "Extracted intermediate certificate: $intermediateFileName" "SUCCESS" | |
| return $intermediatePath | |
| } else { | |
| Write-Status "Could not extract intermediate certificate from connection to $TestUrl" "WARNING" | |
| return $null | |
| } | |
| } | |
| catch { | |
| Write-Status "Error extracting intermediate certificate: $($_.Exception.Message)" "ERROR" | |
| return $null | |
| } | |
| } | |
| function Set-NodeJSEnvironmentVariables { | |
| <# | |
| .SYNOPSIS | |
| Configures Node.js and other development environment variables for certificate bundle usage | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$BundlePath | |
| ) | |
| if (-not $SetupNodeJS) { | |
| Write-Verbose "Node.js environment setup is disabled" | |
| return | |
| } | |
| Write-Status "Configuring Windows Node.js environment variables" "INFO" | |
| try { | |
| # Core Node.js certificate environment variables | |
| [Environment]::SetEnvironmentVariable("NODE_EXTRA_CA_CERTS", $BundlePath, "User") | |
| [Environment]::SetEnvironmentVariable("NODE_TLS_REJECT_UNAUTHORIZED", "0", "User") | |
| [Environment]::SetEnvironmentVariable("NODE_NO_WARNINGS", "1", "User") | |
| Write-Status "Set NODE_EXTRA_CA_CERTS = $BundlePath" "SUCCESS" | |
| Write-Status "Set NODE_TLS_REJECT_UNAUTHORIZED = 0" "SUCCESS" | |
| Write-Status "Set NODE_NO_WARNINGS = 1" "SUCCESS" | |
| # Additional SSL/TLS environment variables if requested | |
| if ($SetupEnvironmentVars) { | |
| [Environment]::SetEnvironmentVariable("SSL_CERT_FILE", $BundlePath, "User") | |
| [Environment]::SetEnvironmentVariable("HTTPS_CA_BUNDLE", $BundlePath, "User") | |
| [Environment]::SetEnvironmentVariable("REQUESTS_CA_BUNDLE", $BundlePath, "User") | |
| [Environment]::SetEnvironmentVariable("CURL_CA_BUNDLE", $BundlePath, "User") | |
| [Environment]::SetEnvironmentVariable("JFROG_CLI_CERT_FILE", $BundlePath, "User") | |
| Write-Status "Set SSL_CERT_FILE = $BundlePath" "SUCCESS" | |
| Write-Status "Set HTTPS_CA_BUNDLE = $BundlePath" "SUCCESS" | |
| Write-Status "Set REQUESTS_CA_BUNDLE = $BundlePath" "SUCCESS" | |
| Write-Status "Set CURL_CA_BUNDLE = $BundlePath" "SUCCESS" | |
| Write-Status "Set JFROG_CLI_CERT_FILE = $BundlePath" "SUCCESS" | |
| } | |
| Write-Status "Windows environment variables configured successfully" "SUCCESS" | |
| Write-Status "Restart your terminal/IDE to apply environment variable changes" "WARNING" | |
| } | |
| catch { | |
| Write-Status "Error setting Windows environment variables: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function Set-WSLEnvironmentVariables { | |
| <# | |
| .SYNOPSIS | |
| Configures environment variables inside WSL distributions for development tools | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$DistroName, | |
| [Parameter(Mandatory)] | |
| [string]$BundlePath | |
| ) | |
| if (-not $SetupWSLEnvironment) { | |
| Write-Verbose "WSL environment setup is disabled" | |
| return | |
| } | |
| Write-Status "Configuring WSL environment variables for $DistroName" "INFO" | |
| try { | |
| # Convert Windows path to WSL path | |
| $wslBundlePath = $BundlePath -replace '^C:', '/mnt/c' -replace '\\', '/' | |
| # Create environment setup script | |
| $envSetupScript = @" | |
| # Corporate Certificate Environment Variables | |
| export SSL_CERT_FILE='$wslBundlePath' | |
| export HTTPS_CA_BUNDLE='$wslBundlePath' | |
| export REQUESTS_CA_BUNDLE='$wslBundlePath' | |
| export CURL_CA_BUNDLE='$wslBundlePath' | |
| export NODE_EXTRA_CA_CERTS='$wslBundlePath' | |
| export NODE_TLS_REJECT_UNAUTHORIZED='0' | |
| export NODE_NO_WARNINGS='1' | |
| export PYTHONHTTPSVERIFY='0' | |
| export JFROG_CLI_CERT_FILE='$wslBundlePath' | |
| export DOCKER_CERT_PATH='$wslBundlePath' | |
| export PIP_CERT='$wslBundlePath' | |
| export NPM_CONFIG_CAFILE='$wslBundlePath' | |
| export YARN_CAFILE='$wslBundlePath' | |
| "@ | |
| # Write environment setup to WSL | |
| $envFile = "/etc/profile.d/corporate-certs.sh" | |
| $writeEnvCommand = "echo `"$envSetupScript`" | sudo tee `"$envFile`" > /dev/null && sudo chmod +x `"$envFile`"" | |
| wsl --cd "/" -d $DistroName -u root -e bash -c $writeEnvCommand 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Status "WSL environment variables configured in $envFile" "SUCCESS" | |
| # Also add to common shell profiles | |
| $shellProfiles = @(".bashrc", ".zshrc", ".profile") | |
| foreach ($shellProfile in $shellProfiles) { | |
| $profilePath = "/home/`$(whoami)/$shellProfile" | |
| $sourceCommand = "echo 'source $envFile' >> `"$profilePath`" 2>/dev/null || true" | |
| wsl --cd "/" -d $DistroName -e bash -c $sourceCommand 2>$null | |
| } | |
| Write-Status "Environment variables will be available in new WSL sessions" "SUCCESS" | |
| return $true | |
| } else { | |
| Write-Status "Failed to configure WSL environment variables" "ERROR" | |
| return $false | |
| } | |
| } | |
| catch { | |
| Write-Status "Error setting WSL environment variables: $($_.Exception.Message)" "ERROR" | |
| return $false | |
| } | |
| } | |
| function New-CertificateBundle { | |
| <# | |
| .SYNOPSIS | |
| Creates a combined certificate bundle from exported certificates | |
| #> | |
| param( | |
| [Parameter(Mandatory)] | |
| [string]$CertificateDirectory, | |
| [string]$BundleFileName = "Corporate_CA_Bundle.crt", | |
| [string[]]$CertificateFiles = @() | |
| ) | |
| if (-not $CreateBundle) { | |
| Write-Verbose "Certificate bundle creation is disabled" | |
| return $null | |
| } | |
| Write-Status "Creating certificate bundle: $BundleFileName" "INFO" | |
| try { | |
| $bundlePath = Join-Path $CertificateDirectory $BundleFileName | |
| $bundleContent = @() | |
| # Add header comment | |
| $bundleContent += "# Corporate Certificate Bundle" | |
| $bundleContent += "# Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| $bundleContent += "# Contains corporate certificates for development environments" | |
| $bundleContent += "" | |
| # Use provided certificate files or discover them | |
| if ($CertificateFiles.Count -gt 0) { | |
| # Use the specific files provided | |
| $certFilesToBundle = $CertificateFiles | ForEach-Object { | |
| if (Test-Path $_) { | |
| Get-Item $_ | |
| } | |
| } | |
| } else { | |
| # Fallback to discovering certificate files (excluding bundles) | |
| $certFilesToBundle = Get-ChildItem -Path $CertificateDirectory -Filter "*.crt" | | |
| Where-Object { | |
| $_.Name -ne $BundleFileName -and | |
| $_.Name -notlike "*Bundle*" -and | |
| $_.Name -notlike "*Combined*" | |
| } | | |
| Sort-Object Name | |
| } | |
| if ($certFilesToBundle.Count -eq 0) { | |
| Write-Status "No certificate files found to bundle" "WARNING" | |
| return $null | |
| } | |
| foreach ($certFile in $certFilesToBundle) { | |
| Write-Verbose "Adding certificate to bundle: $($certFile.Name)" | |
| $content = Get-Content $certFile.FullName -Raw | |
| # Add certificate name comment | |
| $bundleContent += "# Certificate: $($certFile.Name)" | |
| $bundleContent += $content.Trim() | |
| $bundleContent += "" | |
| } | |
| # Write bundle to file | |
| $bundleContent -join "`n" | Set-Content -Path $bundlePath -Encoding UTF8 | |
| $bundleSize = [Math]::Round((Get-Item $bundlePath).Length / 1KB, 1) | |
| Write-Status "Created certificate bundle: $BundleFileName ($bundleSize KB)" "SUCCESS" | |
| Write-Status "Bundle contains $($certFilesToBundle.Count) certificates" "SUCCESS" | |
| return $bundlePath | |
| } | |
| catch { | |
| Write-Status "Error creating certificate bundle: $($_.Exception.Message)" "ERROR" | |
| return $null | |
| } | |
| } | |
| #endregion | |
| #region Main Function | |
| function Invoke-Main { | |
| <# | |
| .SYNOPSIS | |
| Main execution function that orchestrates the entire certificate installation process | |
| #> | |
| # Initialize logging if requested | |
| $script:LogFilePath = $null | |
| if ($EnableLogging -or $LogFile) { | |
| if (-not $LogFile) { | |
| $LogFile = Join-Path $env:TEMP "Auto-Install-CertificatesInWSL_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" | |
| } | |
| try { | |
| # Create log file directory if it doesn't exist | |
| $logDir = Split-Path -Path $LogFile -Parent | |
| if ($logDir -and -not (Test-Path $logDir)) { | |
| New-Item -ItemType Directory -Path $logDir -Force | Out-Null | |
| } | |
| # Initialize log file | |
| $script:LogFilePath = $LogFile | |
| $logHeader = @" | |
| ================================================================= | |
| Universal Corporate Certificate Installer for WSL v3.1 | |
| Log started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | |
| ================================================================= | |
| "@ | |
| Set-Content -Path $script:LogFilePath -Value $logHeader -Encoding UTF8 | |
| Write-Host "[INFO] Logging enabled: $script:LogFilePath" -ForegroundColor Cyan | |
| } | |
| catch { | |
| Write-Host "[WARNING] Failed to initialize logging: $($_.Exception.Message)" -ForegroundColor Yellow | |
| $script:LogFilePath = $null | |
| } | |
| } | |
| Write-Status "===================================================================" | |
| Write-Status " Universal Corporate Certificate Installer for WSL v3.1" | |
| Write-Status "===================================================================" | |
| Write-Status "Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| Write-Status "" | |
| # Show WSL distribution information if requested | |
| if ($ShowWSLInfo) { | |
| Show-WSLDistributionInfo | |
| return | |
| } | |
| # Create certificate directory if it doesn't exist | |
| if (-not (Test-Path $CertificateDirectory)) { | |
| try { | |
| New-Item -ItemType Directory -Path $CertificateDirectory -Force | Out-Null | |
| Write-Status "Created certificate directory: $CertificateDirectory" "SUCCESS" | |
| } | |
| catch { | |
| Write-Status "Failed to create certificate directory: $($_.Exception.Message)" "ERROR" | |
| return | |
| } | |
| } else { | |
| Write-Status "Using existing certificate directory: $CertificateDirectory" "INFO" | |
| } | |
| # Test WSL availability (skip if only exporting certificates) | |
| if (-not $SkipWSLInstall) { | |
| if (-not (Test-WSLAvailability)) { | |
| Write-Status "WSL is not available. Please install WSL and at least one distribution." "ERROR" | |
| Write-Status "Alternatively, use -SkipWSLInstall to export certificates only." "INFO" | |
| return | |
| } | |
| } | |
| # Search for corporate certificates | |
| Write-Status "Searching for corporate certificates..." "INFO" | |
| $certificateMatches = Search-CorporateCertificates -DescriptionPatterns $DescriptionPatterns -ExcludeIssuers $ExcludeIssuers | |
| if ($certificateMatches.Count -eq 0) { | |
| Write-Status "No matching certificates found for patterns: $($DescriptionPatterns -join ', ')" "WARNING" | |
| Write-Status "Please verify that corporate certificates are installed and patterns are correct" "WARNING" | |
| Write-Status "You may need to adjust the -DescriptionPatterns parameter" "INFO" | |
| return | |
| } | |
| # Export certificates to files | |
| Write-Status "Exporting certificates to files..." "INFO" | |
| $exportedCerts = @() | |
| $exportIndex = 0 | |
| foreach ($certMatch in $certificateMatches) { | |
| $exportIndex++ | |
| $cert = $certMatch.Certificate | |
| $matchedPattern = $certMatch.MatchedPattern | |
| $fileName = Get-SafeFileName -Certificate $cert -MatchedPattern $matchedPattern -Index $exportIndex | |
| $filePath = Join-Path $CertificateDirectory $fileName | |
| if (Export-CertificateToFile -Certificate $cert -FilePath $filePath) { | |
| Write-Status "Exported: $fileName" "SUCCESS" | |
| $exportedCerts += $filePath | |
| Write-Verbose " Subject: $($cert.Subject)" | |
| Write-Verbose " Issuer: $($cert.Issuer)" | |
| Write-Verbose " Store: $($certMatch.Store)" | |
| Write-Verbose " Pattern: $matchedPattern" | |
| } | |
| } | |
| if ($exportedCerts.Count -eq 0) { | |
| Write-Status "No certificates were successfully exported" "ERROR" | |
| return | |
| } | |
| # Extract intermediate certificate if requested | |
| $intermediateCertPath = $null | |
| if ($ExtractIntermediate -and -not $SkipWSLInstall) { | |
| # Use first available distribution for intermediate certificate extraction | |
| $firstDistro = if ($WSLDistro) { $WSLDistro } else { (Get-WSLDistributionsFromRegistry)[0].Name } | |
| if ($firstDistro) { | |
| $intermediateCertPath = Get-IntermediateCertificate -TestUrl $TestUrls[0] -OutputPath $CertificateDirectory -DistroName $firstDistro | |
| if ($intermediateCertPath) { | |
| $exportedCerts += $intermediateCertPath | |
| } | |
| } | |
| } | |
| # Create certificate bundle from the exported certificates | |
| $bundlePath = $null | |
| if ($CreateBundle) { | |
| $bundlePath = New-CertificateBundle -CertificateDirectory $CertificateDirectory -BundleFileName "Corporate_CA_Bundle.crt" -CertificateFiles $exportedCerts | |
| } | |
| # Determine target WSL distributions | |
| $targetDistributions = @() | |
| $installationSummary = @() | |
| if (-not $SkipWSLInstall) { | |
| $allDistributions = Get-WSLDistributionsFromRegistry | |
| if ($InstallAllDistros) { | |
| Write-Status "Installing certificates for all $($allDistributions.Count) WSL distributions" "INFO" | |
| $targetDistributions = $allDistributions | |
| } else { | |
| # Single distribution mode | |
| if (-not $WSLDistro) { | |
| $WSLDistro = Get-DefaultWSLDistro | |
| if (-not $WSLDistro) { | |
| Write-Status "Could not determine WSL distribution. Please specify -WSLDistro parameter." "ERROR" | |
| return | |
| } | |
| } | |
| $targetDistro = $allDistributions | Where-Object { $_.Name -eq $WSLDistro } | |
| if (-not $targetDistro) { | |
| Write-Status "WSL distribution '$WSLDistro' not found" "ERROR" | |
| Write-Status "Available distributions: $($allDistributions.Name -join ', ')" "INFO" | |
| return | |
| } | |
| $targetDistributions = @($targetDistro) | |
| } | |
| Write-Status "Target WSL distributions: $($targetDistributions.Name -join ', ')" "INFO" | |
| # Process each WSL distribution | |
| foreach ($distro in $targetDistributions) { | |
| Write-Status "" | |
| Write-Status "=======================================================" "INFO" | |
| Write-Status "Processing: $($distro.Name) ($($distro.DistributionFamily) family)" "INFO" | |
| Write-Status "=======================================================" "INFO" | |
| # Test distribution availability | |
| if (-not (Test-WSLDistroAvailability -DistroName $distro.Name)) { | |
| Write-Status "Distribution '$($distro.Name)' is not responsive, skipping..." "ERROR" | |
| $installationSummary += @{ | |
| Distribution = $distro.Name | |
| Success = $false | |
| Reason = "Distribution not responsive" | |
| } | |
| continue | |
| } | |
| # Get distribution configuration | |
| $distroConfig = $null | |
| if ($CustomWSLPaths.ContainsKey($distro.Name)) { | |
| $distroConfig = $CustomWSLPaths[$distro.Name] | |
| Write-Status "Using custom configuration for $($distro.Name)" "INFO" | |
| } elseif ($WSLDistroConfigs.ContainsKey($distro.Name)) { | |
| $distroConfig = $WSLDistroConfigs[$distro.Name] | |
| Write-Status "Using predefined configuration for $($distro.Name)" "SUCCESS" | |
| } else { | |
| # Use family-based defaults | |
| $family = $distro.DistributionFamily | |
| $distroConfig = switch ($family) { | |
| "Debian" { $WSLDistroConfigs["Ubuntu"] } | |
| "RHEL" { $WSLDistroConfigs["FedoraLinux-42"] } | |
| "SUSE" { $WSLDistroConfigs["openSUSE-Tumbleweed"] } | |
| "Arch" { $WSLDistroConfigs["archlinux"] } | |
| default { $WSLDistroConfigs["Ubuntu"] } | |
| } | |
| Write-Status "Using $family family defaults for $($distro.Name)" "INFO" | |
| } | |
| # Install dependencies | |
| if (-not (Install-WSLDependencies -DistroName $distro.Name)) { | |
| Write-Status "Failed to install dependencies in $($distro.Name), skipping..." "ERROR" | |
| $installationSummary += @{ | |
| Distribution = $distro.Name | |
| Success = $false | |
| Reason = "Failed to install dependencies" | |
| } | |
| continue | |
| } | |
| # Test connectivity before installation | |
| $connectivityBefore = $null | |
| if ($TestBeforeAndAfter) { | |
| $connectivityBefore = Test-ConnectivityForDistribution -DistroName $distro.Name -TestUrls $TestUrls -TestPhase "BEFORE" | |
| } | |
| # Install certificates | |
| Write-Status "" | |
| Write-Status "Installing certificates in $($distro.Name)..." "INFO" | |
| # Remove old certificates | |
| Remove-OldCertificatesFromWSL -DistroName $distro.Name -CertPath $distroConfig.CertPath | Out-Null | |
| $successfulInstalls = 0 | |
| foreach ($certPath in $exportedCerts) { | |
| if (Install-CertificateInWSL -CertFilePath $certPath -DistroName $distro.Name -DistroConfig $distroConfig) { | |
| $successfulInstalls++ | |
| } | |
| } | |
| # Update certificate store | |
| $storeUpdateSuccess = $false | |
| if ($successfulInstalls -gt 0) { | |
| $storeUpdateSuccess = Update-WSLCertificateStore -DistroName $distro.Name -DistroConfig $distroConfig | |
| } | |
| # Test connectivity after installation | |
| $connectivityAfter = $null | |
| if ($TestBeforeAndAfter -and $storeUpdateSuccess) { | |
| Write-Status "" | |
| $connectivityAfter = Test-ConnectivityForDistribution -DistroName $distro.Name -TestUrls $TestUrls -TestPhase "AFTER" | |
| } | |
| # Setup WSL environment variables | |
| if ($SetupWSLEnvironment -and $bundlePath -and $storeUpdateSuccess) { | |
| Set-WSLEnvironmentVariables -DistroName $distro.Name -BundlePath $bundlePath | Out-Null | |
| } | |
| # Record results | |
| $improvement = if ($connectivityBefore -and $connectivityAfter) { | |
| $connectivityAfter.SuccessRate - $connectivityBefore.SuccessRate | |
| } else { 0 } | |
| $installationSummary += @{ | |
| Distribution = $distro.Name | |
| Family = $distro.DistributionFamily | |
| Success = $storeUpdateSuccess | |
| Reason = if ($storeUpdateSuccess) { "Success" } else { "Certificate store update failed" } | |
| CertificatesInstalled = $successfulInstalls | |
| ConnectivityBefore = $connectivityBefore | |
| ConnectivityAfter = $connectivityAfter | |
| Improvement = $improvement | |
| } | |
| } | |
| } | |
| # Configure environment variables | |
| if ($SetupNodeJS -and $bundlePath) { | |
| Write-Status "" | |
| Set-NodeJSEnvironmentVariables -BundlePath $bundlePath | Out-Null | |
| } | |
| # Final comprehensive summary | |
| Write-Status "" | |
| Write-Status "===================================================================" | |
| Write-Status " INSTALLATION SUMMARY" | |
| Write-Status "===================================================================" | |
| Write-Status "Certificates found: $($certificateMatches.Count)" | |
| Write-Status "Certificates exported: $($exportedCerts.Count)" | |
| Write-Status "Certificate directory: $CertificateDirectory" | |
| if ($bundlePath) { | |
| Write-Status "Certificate bundle: $(Split-Path -Leaf $bundlePath)" | |
| } | |
| # WSL Installation Summary | |
| if (-not $SkipWSLInstall -and $installationSummary.Count -gt 0) { | |
| Write-Status "" | |
| Write-Status "WSL Installation Results:" "INFO" | |
| Write-Status "========================" "INFO" | |
| $successfulDistros = ($installationSummary | Where-Object { $_.Success }).Count | |
| $totalCertsInstalled = 0 | |
| foreach ($summary in $installationSummary) { | |
| if ($summary.CertificatesInstalled) { | |
| $totalCertsInstalled += $summary.CertificatesInstalled | |
| } | |
| } | |
| foreach ($summary in $installationSummary) { | |
| $status = if ($summary.Success) { "[SUCCESS]" } else { "[FAILED]" } | |
| Write-Status "$status $($summary.Distribution) ($($summary.Family))" $(if ($summary.Success) { "SUCCESS" } else { "ERROR" }) | |
| Write-Status " Certificates installed: $($summary.CertificatesInstalled)" "INFO" | |
| if ($summary.ConnectivityBefore -and $summary.ConnectivityAfter) { | |
| $before = $summary.ConnectivityBefore.SuccessRate | |
| $after = $summary.ConnectivityAfter.SuccessRate | |
| $change = $after - $before | |
| $changeStr = if ($change -gt 0) { "+$change" } elseif ($change -lt 0) { "$change" } else { "0" } | |
| Write-Status " Connectivity: $before% -> $after% ($changeStr%)" "INFO" | |
| } | |
| if (-not $summary.Success) { | |
| Write-Status " Reason: $($summary.Reason)" "ERROR" | |
| } | |
| } | |
| Write-Status "" | |
| Write-Status "Overall: $successfulDistros/$($installationSummary.Count) distributions successful" $(if ($successfulDistros -eq $installationSummary.Count) { "SUCCESS" } else { "WARNING" }) | |
| Write-Status "Total certificates installed: $totalCertsInstalled" "INFO" | |
| } | |
| # Environment Variables Summary | |
| if ($SetupNodeJS -or $SetupEnvironmentVars -or $SetupWSLEnvironment) { | |
| Write-Status "" | |
| Write-Status "Environment Configuration:" "INFO" | |
| if ($SetupNodeJS -or $SetupEnvironmentVars) { | |
| Write-Status "Windows environment: Configured" "SUCCESS" | |
| } | |
| $wslEnvDistros = ($installationSummary | Where-Object { $_.Success -and $SetupWSLEnvironment }).Count | |
| if ($wslEnvDistros -gt 0) { | |
| Write-Status "WSL environment: Configured for $wslEnvDistros distribution(s)" "SUCCESS" | |
| } | |
| } | |
| # Certificate Files Created | |
| Write-Status "" | |
| Write-Status "Certificate files created:" "INFO" | |
| Get-ChildItem -Path $CertificateDirectory -Filter "*.crt" | ForEach-Object { | |
| $size = [Math]::Round($_.Length / 1KB, 1) | |
| Write-Host " - $($_.Name) ($size KB)" -ForegroundColor Gray | |
| } | |
| # Final Status | |
| Write-Status "" | |
| $overallSuccess = if ($SkipWSLInstall) { | |
| $exportedCerts.Count -gt 0 | |
| } else { | |
| ($installationSummary | Where-Object { $_.Success }).Count -gt 0 | |
| } | |
| if ($overallSuccess) { | |
| Write-Status "Installation completed successfully!" "SUCCESS" | |
| if (-not $SkipWSLInstall) { | |
| Write-Status "Corporate certificates are now available in WSL distributions" "SUCCESS" | |
| } else { | |
| Write-Status "Certificate export completed successfully!" "SUCCESS" | |
| } | |
| if ($SetupNodeJS -or $SetupEnvironmentVars) { | |
| Write-Status "" | |
| Write-Status "IMPORTANT: Restart your terminal/IDE to apply Windows environment changes" "WARNING" | |
| } | |
| if ($SetupWSLEnvironment) { | |
| Write-Status "IMPORTANT: Start new WSL sessions to apply WSL environment variables" "WARNING" | |
| } | |
| } else { | |
| Write-Status "Installation completed with issues" "WARNING" | |
| if (-not $SkipWSLInstall) { | |
| Write-Status "Some distributions may not have been configured correctly" "WARNING" | |
| } | |
| } | |
| # Manual test commands | |
| Write-Status "" | |
| Write-Status "Manual test commands:" "INFO" | |
| if (-not $SkipWSLInstall -and $installationSummary.Count -gt 0) { | |
| $firstSuccessful = ($installationSummary | Where-Object { $_.Success })[0] | |
| if ($firstSuccessful) { | |
| Write-Host " wsl -d $($firstSuccessful.Distribution) curl -v https://google.com" -ForegroundColor Gray | |
| Write-Host " wsl -d $($firstSuccessful.Distribution) curl -v https://github.com" -ForegroundColor Gray | |
| } | |
| } | |
| # Log file information | |
| if ($script:LogFilePath) { | |
| Write-Status "" | |
| Write-Status "Detailed log saved to: $script:LogFilePath" "INFO" | |
| } | |
| Write-Status "" | |
| Write-Status "Completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | |
| Write-Status "===================================================================" | |
| } | |
| #endregion | |
| # Execute main function if script is run directly (not dot-sourced) | |
| if ($MyInvocation.InvocationName -ne '.') { | |
| Invoke-Main | |
| } |
Absolutely amazing. Thank you! Spent hours trying to get my certs to work, and this worked beautifully.
Hi @emilwojcik93, thank you so so much for this script.
It helped me solve my own problem. Would you be interested in turning this into a small Repo to allow for public contributions and issues?
Otherwise, I could also do it and link here, if you don't mind.
@JustACasual I was able to work on this one. Here is public repo based on this script and a lot of verified features based on my latest experience
https://github.com/emilwojcik93/corporate-ssl-manager
@emilwojcik93 great to hear!
Will have a look and most probably retire my own fork then https://github.com/hackerbande-nbg/WSL-cert-circus
Thanks for your excellent work once more, helped me so much...
Auto-Install-CertificatesInWSL.ps1
This script searches for certificates with a specific description pattern, exports them, installs them in WSL, and checks the response using curl.
Usage
Options
-DescriptionPattern <String>: The pattern to search for in the certificate description. Default is "CA".-ExcludeIssuers <String[]>: An array of issuer names to exclude from the results. Default is an array of common built-in certificate issuers.-WSLDistro <String>: The WSL distribution to install the certificate in. Default is "Ubuntu".-UpdateCommand <String>: The command to update the CA certificates. Default is the value from the$wslDistrosarray.-Verbose: Enables verbose output.Running the Script from the Internet:
Use
Invoke-RestMethodto download and execute the script. Here is how you can do it:Note
If it doesn't work, then try to Set-ExecutionPolicy via PowerShell (Admin)
Note
To execute the script from the Internet with additional parameters, please run
Example of issue
Example of execution
Example of execution for ArchLinux
Functions
Get-DefaultWSLDistro$defaultDistro = Get-DefaultWSLDistroTest-WSL$isWSLAvailable = Test-WSLTest-CurlAvailabilitycurlis available in the target WSL distribution.WSLDistro: The WSL distribution to check.$isCurlAvailable = Test-CurlAvailability -WSLDistro "Ubuntu"Search-CertificatesDescriptionPattern: The pattern to search for in the certificate description.ExcludeIssuers: An array of issuer names to exclude from the results.$certificates = Search-Certificates -DescriptionPattern "CA" -ExcludeIssuers @("DigiCert", "thawte")Export-CertificateToFileCert: The certificate to export.FilePath: The file path to export the certificate to.Export-CertificateToFile -Cert $cert -FilePath "C:\Temp\cert.crt"Remove-OldCertificateFromWSLCertFileName: The name of the certificate file to remove.WSLDistro: The WSL distribution to remove the certificate from.Remove-OldCertificateFromWSL -CertFileName "cert.crt" -WSLDistro "Ubuntu"Install-CertificateInWSLCertFilePath: The file path of the certificate to install.WSLDistro: The WSL distribution to install the certificate in.UpdateCommand: The command to update the CA certificates.CertPath: The path to install the certificate in WSL.Install-CertificateInWSL -CertFilePath "C:\Temp\cert.crt" -WSLDistro "Ubuntu" -UpdateCommand "update-ca-certificates" -CertPath "/usr/local/share/ca-certificates/"Test-CertificateInWSLWSLDistro: The WSL distribution to check the certificate in.Address: The address to test the certificate against.$response = Test-CertificateInWSL -WSLDistro "Ubuntu" -Address "https://google.com"MainDescriptionPattern: The pattern to search for in the certificate description.ExcludeIssuers: An array of issuer names to exclude from the results.WSLDistro: The WSL distribution to install the certificate in.UpdateCommand: The command to update the CA certificates.Verbose: Enables verbose output.Main -DescriptionPattern "CA" -ExcludeIssuers @("DigiCert", "thawte") -WSLDistro "Ubuntu" -VerboseRelated Links
$wslDistrosarray