MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3 4##5## WinRM Remoting helper functions for writing remoting tests6##7 8$Script:CIRemoteCred = $null9 10if ($IsWindows) {11 try { $Script:CIRemoteCred = Import-Clixml -Path "$env:TEMP\CIRemoteCred.xml" } catch { }12}13 14function Get-DefaultEndPointName15{16 $endPointName = "PowerShell.$(${PSVersionTable}.GitCommitId)"17 $endPoint = Get-PSSessionConfiguration -Name $endPointName -ErrorAction SilentlyContinue18 19 if ($endPoint -eq $null)20 {21 $null = Enable-PSRemoting -SkipNetworkProfileCheck22 $endPoint = Get-PSSessionConfiguration -Name $endPointName -ErrorAction SilentlyContinue23 24 if ($endPoint -eq $null)25 {26 Write-Warning "Unable to create the remoting configuration endpoint for this PowerShell version: $(${PSVersionTable}.PSVersion)"27 return $endPointName28 }29 }30 31 if ($endPoint.Permission -like "*NT AUTHORITY\NETWORK AccessDenied*")32 {33 $null = Enable-PSRemoting -SkipNetworkProfileCheck34 $endPoint = Get-PSSessionConfiguration -Name $endPointName -ErrorAction SilentlyContinue35 36 if ($endPoint.Permission -like "*NT AUTHORITY\NETWORK AccessDenied*")37 {38 Write-Warning "Unable to enable the remoting configuration endpoint: $(${PSVersionTable}.PSVersion)"39 }40 }41 42 return $endPointName43}44 45function New-RemoteRunspace46{47 param (48 [string] $ConfigurationName49 )50 51 # For PSCore6, we want to always test against a remoting endpoint running PSCore6 (not Windows PowerShell)52 if ([string]::IsNullOrEmpty($ConfigurationName))53 {54 $ConfigurationName = Get-DefaultEndPointName55 }56 57 $wsmanConInfo = [System.Management.Automation.Runspaces.WSManConnectionInfo]::new()58 59 $wsmanConInfo.ShellUri = 'http://schemas.microsoft.com/powershell/' + $ConfigurationName60 61 if ($Script:CIRemoteCred)62 {63 Write-Verbose "Using Global CI Credential" -Verbose64 $wsmanConInfo.Credential = $Script:CIRemoteCred65 }66 else67 {68 Write-Verbose "Using Implicit Credential" -Verbose69 }70 71 $remoteRunspace = [runspacefactory]::CreateRunspace($Host, $wsmanConInfo)72 $remoteRunspace.Open()73 74 Write-Verbose "Successfully created remote runspace on endpoint: $ConfigurationName"75 76 return $remoteRunspace77}78 79function New-RemoteRunspacePool80{81 param (82 [int] $MinRunspace = 1,83 84 [int] $MaxRunspace = 6,85 86 [string] $ConfigurationName87 )88 89 $wsmanConnection = [System.Management.Automation.Runspaces.WSManConnectionInfo]::new()90 91 if ($ConfigurationName -ne $null)92 {93 $wsmanConnection.ShellUri = "http://schemas.microsoft.com/powershell/$ConfigurationName"94 }95 96 if ($Script:CIRemoteCred)97 {98 Write-Verbose "Using Global CI Credential" -Verbose99 $wsmanConnection.Credential = $Script:CIRemoteCred100 }101 else102 {103 Write-Verbose "Using Implicit Credential" -Verbose104 }105 106 [System.Management.Automation.Runspaces.RunspacePool] $remoteRunspacePool = [runspacefactory]::CreateRunspacePool($MinRunspace, $MaxRunspace, $wsmanConnection)107 $remoteRunspacePool.Open()108 109 return $remoteRunspacePool110}111 112function CreateParameters113{114 param (115 [string] $ComputerName,116 [string[]] $Name,117 [string] $ConfigurationName,118 [switch] $CimSession,119 [System.Management.Automation.Remoting.PSSessionOption] $SessionOption,120 [System.Management.Automation.Runspaces.PSSession[]] $Session)121 122 if($ComputerName)123 {124 $parameters = @{ComputerName = $ComputerName}125 }126 else127 {128 if($Session)129 {130 $parameters = @{Session = $Session}131 }132 else133 {134 $parameters = @{ComputerName = '.'}135 }136 }137 138 if ($Name) {139 if($CimSession.IsPresent)140 {141 $parameters["Name"] = [String] $Name142 }143 else144 {145 $parameters["Name"] = $Name146 }147 }148 149 if ($ConfigurationName) {150 $parameters["ConfigurationName"] = $ConfigurationName151 }152 153 if ($SessionOption) {154 $parameters["SessionOption"] = $SessionOption155 }156 157 ## If a PSSession is provided, do not add credentials.158 if ($Script:CIRemoteCred -and (-not $Session))159 {160 Write-Verbose "Using Global CI Credential" -Verbose161 $parameters["Credential"] = $Script:CIRemoteCred162 }163 else164 {165 Write-Verbose "Using Implicit Credential" -Verbose166 }167 168 return $parameters169}170 171function New-RemoteSession172{173 param (174 [string] $Name,175 [string] $ConfigurationName,176 [switch] $CimSession,177 [System.Management.Automation.Remoting.PSSessionOption] $SessionOption178 )179 180 # For PSCore6, we want to always test against a remoting endpoint running PSCore6 (not Windows PowerShell)181 if ([string]::IsNullOrEmpty($ConfigurationName))182 {183 $ConfigurationName = Get-DefaultEndPointName184 }185 186 $parameters = CreateParameters -Name $Name -ConfigurationName $ConfigurationName -SessionOption $SessionOption -CimSession:$CimSession.IsPresent187 188 if ($CimSession) {189 $session = New-CimSession @parameters190 } else {191 $session = New-PSSession @parameters192 }193 194 Write-Verbose "Successfully created remote PSSession on endpoint: $ConfigurationName"195 196 return $session197}198 199function Invoke-RemoteCommand200{201 param (202 [string] $ComputerName,203 [scriptblock] $ScriptBlock,204 [string] $ConfigurationName,205 [switch] $InDisconnectedSession206 )207 208 # For PSCore6, we want to always test against a remoting endpoint running PSCore6 (not Windows PowerShell)209 if ([string]::IsNullOrEmpty($ConfigurationName))210 {211 $ConfigurationName = Get-DefaultEndPointName212 }213 214 $parameters = CreateParameters -ComputerName $ComputerName -ConfigurationName $ConfigurationName215 216 if($ScriptBlock)217 {218 $parameters.Add('ScriptBlock', $ScriptBlock)219 }220 221 if($InDisconnectedSession)222 {223 $parameters.Add('InDisconnectedSession', $InDisconnectedSession.IsPresent)224 }225 226 Invoke-Command @parameters227}228 229function Enter-RemoteSession230{231 param(232 [string] $Name,233 [string] $ConfigurationName,234 [System.Management.Automation.Remoting.PSSessionOption] $SessionOption)235 236 $parameters = CreateParameters -Name $Name -ConfigurationName $ConfigurationName -SessionOption $SessionOption237 Enter-PSSession @parameters238}239 240function Connect-RemoteSession241{242 param(243 [string] $ComputerName,244 [string[]] $Name,245 [System.Management.Automation.Runspaces.PSSession[]] $Session,246 [string] $ConfigurationName247 )248 249 $parameters = CreateParameters -ComputerName $ComputerName -Name $Name -Session $Session -ConfigurationName $ConfigurationName250 Connect-PSSession @parameters251}252 253function Get-PipePath {254 param (255 $PipeName256 )257 if ($IsWindows) {258 return "\\.\pipe\$PipeName"259 }260 "$([System.IO.Path]::GetTempPath())CoreFxPipe_$PipeName"261}262 263##264## SSH Remoting helper functions for writing remoting tests265##266 267function Get-WindowsOpenSSHLink268{269 # From the Win OpenSSH Wiki page (https://github.com/PowerShell/Win32-OpenSSH/wiki/How-to-retrieve-links-to-latest-packages)270 $origSecurityProtocol = [Net.ServicePointManager]::SecurityProtocol271 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12272 try273 {274 $url = 'https://github.com/PowerShell/Win32-OpenSSH/releases/latest/'275 $request = [System.Net.WebRequest]::Create($url)276 $request.AllowAutoRedirect = $false277 $response = & { $request.GetResponse() } 2>$null278 279 if ($null -ne $response)280 {281 $location = [string] $response.GetResponseHeader("Location")282 if (! [string]::IsNullOrEmpty($location))283 {284 return $location.Replace('tag', 'download') + '/OpenSSH-Win64.zip'285 }286 }287 }288 finally289 {290 [Net.ServicePointManager]::SecurityProtocol = $origSecurityProtocol291 }292 293 # Default to last known latest release294 Write-Warning "Unable to get latest OpenSSH release link. Using default release link."295 return 'https://github.com/PowerShell/Win32-OpenSSH/releases/download/v8.1.0.0p1-Beta/OpenSSH-Win64.zip'296}297 298function Install-WindowsOpenSSH299{300 param (301 [switch] $Force302 )303 304 $destPath = Join-Path -Path $env:ProgramFiles -ChildPath 'OpenSSH-Win64'305 if (Test-Path -Path $destPath)306 {307 if (! $Force)308 {309 Write-Verbose -Verbose "OpenSSH-Win64 already exists, skipping install step"310 return311 }312 313 Write-Verbose -Verbose "Force re-install OpenSSH-Win64 ..."314 Stop-Service -Name sshd -ErrorAction SilentlyContinue315 Remove-Item -Path $destPath -Recurse -Force316 }317 318 # Get link to latest OpenSSH release319 Write-Verbose -Verbose "Downloading latest OpenSSH-Win64 package link ..."320 $downLoadLink = Get-WindowsOpenSSHLink321 322 # Download and extract OpenSSH package323 Write-Verbose -Verbose "Downloading OpenSSH-Win64 zip package ..."324 $packageFilePath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath 'OpenSSH-Win64.zip'325 $oldProgressPreference = $ProgressPreference326 $ProgressPreference = 'SilentlyContinue'327 try328 {329 Invoke-WebRequest -Uri $downLoadLink -OutFile $packageFilePath330 Expand-Archive -Path $packageFilePath -DestinationPath $env:ProgramFiles331 }332 finally333 {334 $ProgressPreference = $oldProgressPreference335 }336 337 # Install and start SSHD service338 Push-Location $destPath339 try340 {341 Write-Verbose -Verbose "Running install-sshd.ps1 ..."342 .\install-sshd.ps1343 344 $netRule = Get-NetFirewallRule -Name sshd -ErrorAction SilentlyContinue345 if ($null -eq $netRule)346 {347 Write-Verbose -Verbose "Creating firewall rule for SSHD ..."348 New-NetFirewallRule -Name sshd -DisplayName "OpenSSH Server (sshd)" -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22349 }350 351 Write-Verbose -Verbose "Starting SSHD service ..."352 Restart-Service -Name sshd353 }354 finally355 {356 Pop-Location357 }358 359 # Current release of Windows OpenSSH configures SSHD to change AuthorizedKeyFiles for administrators360 # Comment it out so that normal key based authentication works per user as with Linux platforms.361 # Match Group administrators362 # AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys363 $sshdFilePath = "$env:ProgramData\ssh\sshd_config"364 $sshdContent = Get-Content $sshdFilePath365 $sshdNewContent = [string[]] @()366 $modified = $false367 foreach ($item in $sshdContent)368 {369 if ($item.TrimStart().StartsWith('Match Group administrators') -or370 $item.TrimStart().StartsWith('AuthorizedKeysFile __PROGRAMDATA'))371 {372 if (!$modified) { $modified = $true }373 $sshdNewContent += "#" + $item374 }375 else376 {377 $sshdNewContent += $item378 }379 }380 if ($modified)381 {382 $sshdNewContent | Set-Content -Path $sshdFilePath -Force383 }384}385 386function Install-SSHRemotingOnWindows387{388 param (389 [Parameter(Mandatory=$true)]390 [string] $PowerShellPath391 )392 393 # Install sshd service394 if ($null -eq (Get-Command -Name sshd -ErrorAction SilentlyContinue))395 {396 Write-Verbose -Verbose "Installing SSHD service ..."397 Install-WindowsOpenSSH -Force398 }399 400 if (! (Test-Path -Path "$env:ProgramData\ssh\sshd_config"))401 {402 throw "Unable to install SSH service. Config file $env:ProgramData\ssh\sshd_config does not exist."403 }404 405 # Configure SSH to authenticate with keys for this user.406 # PubkeyAuthentication should be enabled by default.407 # RSA keys should be enabled by default.408 409 # Create user .ssh directory.410 if (! (Test-Path -Path "$HOME\.ssh"))411 {412 Write-Verbose -Verbose "Creating $HOME\.ssh directory ..."413 New-Item -Path "$HOME\.ssh" -ItemType Directory -Force414 }415 416 # Create new rsa keys for current user.417 if ( !(Test-Path "$HOME\.ssh\id_rsa"))418 {419 Write-Verbose -Verbose "Creating rsa keys ..."420 cmd /c "ssh-keygen -t rsa -f $HOME\.ssh\id_rsa -q -N `"`""421 }422 if (! (Test-Path "$HOME\.ssh\id_rsa"))423 {424 throw "id_rsa private key file was not created."425 }426 if (! (Test-Path "$HOME\.ssh\id_rsa.pub"))427 {428 throw "id_rsa.pub public key file was not created."429 }430 431 # Create authorized keys file.432 Write-Verbose -Verbose "Creating authorized_keys ..."433 Get-Content -Path "$HOME\.ssh\id_rsa.pub" | Set-Content -Path "$HOME\.ssh\authorized_keys" -Force434 435 # Create known_hosts file for 'localhost' connection.436 Write-Verbose -Verbose "Creating known_hosts ..."437 ssh-keyscan -H localhost | Set-Content -Path "$HOME\.ssh\known_hosts" -Force438 439 # Install Microsoft.PowerShell.RemotingTools module.440 if ($null -eq (Get-Module -Name Microsoft.PowerShell.RemotingTools -ListAvailable))441 {442 Write-Verbose -Verbose "Installing Microsoft.PowerShell.RemotingTools ..."443 Install-Module -Name Microsoft.PowerShell.RemotingTools -Force -SkipPublisherCheck444 }445 446 # Add PowerShell endpoint to SSHD.447 Write-Verbose -Verbose "Running Enable-SSHRemoting ..."448 Enable-SSHRemoting -SSHDConfigFilePath "$env:ProgramData\ssh\sshd_config" -PowerShellFilePath $PowerShellPath -Force449 450 Write-Verbose -Verbose "Restarting sshd service ..."451 Restart-Service -Name sshd452 453 # Test SSH remoting.454 Write-Verbose -Verbose "Testing SSH remote connection ..."455 $session = New-PSSession -HostName localhost456 try457 {458 if ($null -eq $session)459 {460 throw "Could not successfully create SSH remoting connection."461 }462 }463 finally464 {465 Remove-PSSession $session466 }467}468 469function WriteVerboseSSHDStatus470{471 param (472 [string] $Msg = 'SSHD service status'473 )474 475 $sshdStatus = sudo service ssh status476 Write-Verbose -Verbose "${Msg}: $sshdStatus"477}478 479function DumpTextFile480{481 param (482 [string] $FilePath = '/etc/ssh/sshd_config'483 )484 485 $content = Get-Content -Path $FilePath -Raw486 Write-Verbose -Verbose $content487}488 489function Install-SSHRemotingOnLinux490{491 param (492 [Parameter(Mandatory=$true)]493 [string] $PowerShellPath494 )495 496 # Install ssh daemon.497 if (! (Test-Path -Path /etc/ssh/sshd_config))498 {499 Write-Verbose -Verbose "Installing openssh-server ..."500 sudo apt-get install --yes openssh-server501 502 Write-Verbose -Verbose "Restarting sshd service after install ..."503 WriteVerboseSSHDStatus "SSHD service status before restart"504 sudo service ssh restart505 WriteVerboseSSHDStatus "SSHD service status after restart"506 }507 if (! (Test-Path -Path /etc/ssh/sshd_config))508 {509 throw "Unable to install SSH daemon. Config file /etc/ssh/sshd_config does not exist."510 }511 512 # Configure SSH to authenticate with keys for this user.513 # PubkeyAuthentication should be enabled by default.514 # RSA keys should be enabled by default.515 516 # Create user .ssh directory.517 if (! (Test-Path -Path "$HOME/.ssh"))518 {519 Write-Verbose -Verbose "Creating $HOME/.ssh directory ..."520 New-Item -Path "$HOME/.ssh" -ItemType Directory -Force521 }522 523 # Create new rsa keys for current user.524 if ( !(Test-Path "$HOME/.ssh/id_rsa"))525 {526 Write-Verbose -Verbose "Creating rsa keys ..."527 bash -c "ssh-keygen -t rsa -f $HOME/.ssh/id_rsa -q -N ''"528 }529 if (! (Test-Path "$HOME/.ssh/id_rsa"))530 {531 throw "id_rsa private key file was not created."532 }533 if (! (Test-Path "$HOME/.ssh/id_rsa.pub"))534 {535 throw "id_rsa.pub public key file was not created."536 }537 538 # Create authorized keys file.539 Write-Verbose -Verbose "Creating authorized_keys ..."540 Get-Content -Path "$HOME/.ssh/id_rsa.pub" | Set-Content -Path "$HOME/.ssh/authorized_keys" -Force541 542 # Create known_hosts file for 'localhost' connection.543 Write-Verbose -Verbose "Updating known_hosts ..."544 ssh-keyscan -H localhost | Set-Content -Path "$HOME/.ssh/known_hosts" -Force545 546 <#547 # Install Microsoft.PowerShell.RemotingTools module.548 if ($null -eq (Get-Module -Name Microsoft.PowerShell.RemotingTools -ListAvailable))549 {550 Write-Verbose -Verbose "Installing Microsoft.PowerShell.RemotingTools ..."551 Install-Module -Name Microsoft.PowerShell.RemotingTools -Force -SkipPublisherCheck552 }553 #>554 555 # Add PowerShell endpoint to SSHD.556 Write-Verbose -Verbose "Running Enable-SSHRemoting ..."557 Write-Verbose -Verbose "PSScriptRoot: $PSScriptRoot"558 $modulePath = "${PSScriptRoot}\..\Microsoft.PowerShell.RemotingTools\Microsoft.PowerShell.RemotingTools.psd1"559 $sshdFilePath = '/etc/ssh/sshd_config'560 561 # First create a default 'powershell' named endpoint.562 $cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -Force"563 Write-Verbose -Verbose "CmdLine: $cmdLine"564 sudo pwsh -c $cmdLine565 566 # Next create a 'pwshconfig' named configured endpoint.567 # Configuration file:568 $configFilePath = Join-Path -Path "$env:HOME" -ChildPath 'PSTestConfig.pssc'569 '@{570 GUID = "4d667b90-25f8-47d5-9c90-619b27954748"571 Author = "Microsoft"572 Description = "Test local PowerShell session configuration"573 LanguageMode = "ConstrainedLanguage"574 }' | Out-File -FilePath $configFilePath575 $cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -ConfigFilePath $configFilePath -SubsystemName 'pwshconfig' -Force"576 Write-Verbose -Verbose "CmdLine: $cmdLine"577 sudo pwsh -c $cmdLine578 579 # Finally create a 'pwshbroken' named configured endpoint.580 $cmdLine = "Import-Module ${modulePath}; Enable-SSHRemoting -SSHDConfigFilePath $sshdFilePath -PowerShellFilePath $PowerShellPath -ConfigFilePath '$HOME/NoSuch.pssc' -SubsystemName 'pwshbroken' -Force"581 Write-Verbose -Verbose "CmdLine: $cmdLine"582 sudo pwsh -c $cmdLine583 584 # Restart SSHD service for changes to take effect.585 Start-Sleep -Seconds 1586 WriteVerboseSSHDStatus "SSHD service status before restart"587 Write-Verbose -Verbose "Restarting sshd ..."588 sudo service ssh restart589 WriteVerboseSSHDStatus "SSHD service status after restart"590 591 # Try starting again if needed.592 $status = sudo service ssh status593 $result = $status | Where-Object { ($_ -like '*not running*') -or ($_ -like '*stopped*') }594 if ($null -ne $result)595 {596 Start-Sleep -Seconds 1597 Write-Verbose -Verbose "Starting sshd again ..."598 sudo service ssh start599 WriteVerboseSSHDStatus "SSHD service status after second start attempt"600 }601 602 # Test SSH remoting.603 Write-Verbose -Verbose "Testing SSH remote connection ..."604 $session = New-PSSession -HostName localhost605 try606 {607 if ($null -eq $session)608 {609 throw "Could not successfully create SSH remoting connection."610 }611 else612 {613 Write-Verbose -Verbose "SUCCESS: SSH remote connection"614 }615 }616 finally617 {618 Remove-PSSession $session619 }620}621 622<#623.Synopsis624 Installs and configures SSH components, and creates an SSH PowerShell remoting endpoint.625.Description626 This cmdlet assumes SSH client is installed on the machine, but will check for SSHD service and627 install it if needed.628 Next, it will configure SSHD for key based user authentication, for the current user context.629 Then it configures SSHD for a PowerShell endpoint based on the provided PowerShell file path.630 If no PowerShell file path is provided, the current PowerShell instance ($PSHOME) is used.631 Finally, it will test the new SSH remoting endpoint connection to ensure it works.632 Currently, only Ubuntu and Windows platforms are supported.633.Parameter PowerShellPath634 Specifies a PowerShell, pwsh(.exe), executable path that will be used for the remoting endpoint.635#>636function Install-SSHRemoting637{638 param (639 [string] $PowerShellFilePath640 )641 642 Write-Verbose -Verbose "Install-SSHRemoting called with PowerShell file path: $PowerShellFilePath"643 644 if ($IsWindows)645 {646 if ([string]::IsNullOrEmpty($PowerShellFilePath)) { $PowerShellFilePath = "$PSHOME/pwsh.exe" }647 Install-SSHRemotingOnWindows -PowerShellPath $PowerShellFilePath648 return649 }650 elseif ($IsLinux)651 {652 $LinuxInfo = Get-Content /etc/os-release -Raw | ConvertFrom-StringData653 if ($LinuxInfo.ID -match 'ubuntu')654 {655 if ([string]::IsNullOrEmpty($PowerShellFilePath)) { $PowerShellFilePath = "$PSHOME/pwsh" }656 Install-SSHRemotingOnLinux -PowerShellPath $PowerShellFilePath657 return658 }659 }660 661 Write-Error "Platform not supported. Only Windows and Ubuntu plaforms are currently supported."662}663 