Files
winutil/pester/ssh-server.Tests.ps1
T
Chris Titus a0d3c719a6 Improve UI startup after runspace overhaul (#5056)
* perf: keep UI startup responsive

* test: make favicon checks runner-safe

* fix: qualify favicon visibility state

* test: isolate favicon visibility fixture

* fix: enforce total favicon deadline

* fix: address favicon review feedback

* fix: recognize disabled SSH firewall rule

* perf: restore favicon download throughput

* docs: allow public PR review uploads

* perf: restore overlapping favicon loading
2026-09-07 11:33:39 -05:00

221 lines
9.0 KiB
PowerShell

#===========================================================================
# Tests - OpenSSH Server Setup
#===========================================================================
BeforeAll {
$script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
function Get-WindowsCapability {
param($Name, [switch]$Online)
[pscustomobject]@{ State = "Installed" }
}
function Add-WindowsCapability {
param($Name, [switch]$Online)
}
function Get-NetFirewallRule {
[CmdletBinding()]
param($Name)
if ($script:firewallRuleMissing) {
Write-Error -Message "No matching firewall rule was found." -Category ObjectNotFound
return
}
[pscustomobject]@{ Enabled = $true }
}
function New-NetFirewallRule {
param($Name, $DisplayName, $Enabled, $Direction, $Protocol, $Action, $LocalPort)
}
function Set-NetFirewallRule {
param($Name, $Enabled)
}
function Set-Service {
param($Name, $StartupType)
}
function Start-Service {
param($Name)
}
function Restart-Service {
param($Name, [switch]$Force)
}
. (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilSSHServer.ps1")
$script:defaultAdministratorsBlock = "Match Group administrators`n AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys`n"
$script:overriddenAdministratorsBlock = "# Match Group administrators`n# AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys`n"
function script:New-SshdConfig {
param([string]$AdministratorsBlock)
$content = "# Default sshd_config`nPort 22`n`n$AdministratorsBlock"
Set-Content -Path $script:sshdConfigPath -Value $content -NoNewline
return $content
}
function script:Set-ProfileKeyFile {
param([string[]]$Keys)
New-Item -Path (Split-Path $script:profileKeysPath) -ItemType Directory -Force | Out-Null
Set-Content -Path $script:profileKeysPath -Value $Keys
}
function script:Get-ExplicitKeyFileAccess {
$acl = Get-Acl -Path $script:authorizedKeysPath
@($acl.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])) |
ForEach-Object { $_.IdentityReference.Value }
}
function script:Get-AuthorizedKeyFileContent {
# The key file ends up readable only by Administrators and SYSTEM, so an
# unelevated run has to grant itself read access back through its
# ownership of the file before it can check the contents.
$currentUserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
icacls $script:authorizedKeysPath /grant "*${currentUserSid}:(R)" | Out-Null
@(Get-Content -Path $script:authorizedKeysPath)
}
}
Describe "Invoke-WinUtilSSHServer" {
BeforeEach {
$script:firewallRuleMissing = $false
$script:testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "winutil-ssh-$([guid]::NewGuid())"
$script:programData = Join-Path $script:testRoot "ProgramData"
$script:userProfile = Join-Path $script:testRoot "Users\tester"
New-Item -Path (Join-Path $script:programData "ssh") -ItemType Directory -Force | Out-Null
New-Item -Path $script:userProfile -ItemType Directory -Force | Out-Null
$script:sshdConfigPath = Join-Path $script:programData "ssh\sshd_config"
$script:authorizedKeysPath = Join-Path $script:programData "ssh\administrators_authorized_keys"
$script:profileKeysPath = Join-Path $script:userProfile ".ssh\authorized_keys"
$script:savedProgramData = $env:ProgramData
$script:savedUserProfile = $env:USERPROFILE
$env:ProgramData = $script:programData
$env:USERPROFILE = $script:userProfile
Mock Write-Host { }
Mock Restart-Service { }
}
AfterEach {
$env:ProgramData = $script:savedProgramData
$env:USERPROFILE = $script:savedUserProfile
Remove-Item -Path $script:testRoot -Recurse -Force -ErrorAction SilentlyContinue
}
It "leaves the administrators block in a default sshd_config alone" {
$original = New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock
Invoke-WinUtilSSHServer
Get-Content -Path $script:sshdConfigPath -Raw | Should -BeExactly $original
Should -Invoke -CommandName Restart-Service -Times 0 -Exactly
}
It "silently creates the firewall rule when it does not exist" {
New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock | Out-Null
$script:firewallRuleMissing = $true
Mock New-NetFirewallRule { }
$output = @(Invoke-WinUtilSSHServer 2>&1)
@($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) | Should -BeNullOrEmpty
Should -Invoke New-NetFirewallRule -Times 1 -Exactly
}
It "enables an existing disabled firewall rule instead of recreating it" {
New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock | Out-Null
Mock Get-NetFirewallRule { [pscustomobject]@{ Enabled = 2 } }
Mock Set-NetFirewallRule { }
Mock New-NetFirewallRule { }
Invoke-WinUtilSSHServer
Should -Invoke Set-NetFirewallRule -Times 1 -Exactly -ParameterFilter {
$Name -eq "sshd" -and $Enabled -eq $true
}
Should -Invoke New-NetFirewallRule -Times 0 -Exactly
}
It "leaves an existing enabled firewall rule unchanged" {
New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock | Out-Null
Mock Get-NetFirewallRule { [pscustomobject]@{ Enabled = 1 } }
Mock Set-NetFirewallRule { }
Mock New-NetFirewallRule { }
Invoke-WinUtilSSHServer
Should -Invoke Set-NetFirewallRule -Times 0 -Exactly
Should -Invoke New-NetFirewallRule -Times 0 -Exactly
}
It "leaves an sshd_config without an administrators block alone" {
$original = New-SshdConfig -AdministratorsBlock ""
Set-ProfileKeyFile -Keys @("ssh-ed25519 AAAAnotanadminkey laptop")
Invoke-WinUtilSSHServer
Get-Content -Path $script:sshdConfigPath -Raw | Should -BeExactly $original
Get-AuthorizedKeyFileContent | Should -Not -Contain "ssh-ed25519 AAAAnotanadminkey laptop"
Should -Invoke -CommandName Restart-Service -Times 0 -Exactly
}
It "creates administrators_authorized_keys limited to Administrators and SYSTEM" {
New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock | Out-Null
Invoke-WinUtilSSHServer
Test-Path -Path $script:authorizedKeysPath | Should -BeTrue
(Get-Acl -Path $script:authorizedKeysPath).AreAccessRulesProtected | Should -BeTrue
$sids = Get-ExplicitKeyFileAccess
$sids | Should -HaveCount 2
$sids | Should -Contain "S-1-5-32-544"
$sids | Should -Contain "S-1-5-18"
}
It "restores the administrators block when an earlier run commented it out" {
New-SshdConfig -AdministratorsBlock $script:overriddenAdministratorsBlock | Out-Null
Invoke-WinUtilSSHServer
$config = Get-Content -Path $script:sshdConfigPath -Raw
$config | Should -Match '(?m)^Match Group administrators$'
$config | Should -Match '(?m)^\s+AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys$'
Should -Invoke -CommandName Restart-Service -Times 1 -Exactly
}
It "moves profile keys into administrators_authorized_keys while restoring the block" {
New-SshdConfig -AdministratorsBlock $script:overriddenAdministratorsBlock | Out-Null
Set-ProfileKeyFile -Keys @("# my laptop", "", "ssh-ed25519 AAAAkeyone laptop", "ssh-ed25519 AAAAkeytwo desktop")
Invoke-WinUtilSSHServer
$keys = Get-AuthorizedKeyFileContent
$keys | Should -Contain "ssh-ed25519 AAAAkeyone laptop"
$keys | Should -Contain "ssh-ed25519 AAAAkeytwo desktop"
$keys | Should -Not -Contain "# my laptop"
}
It "does not copy profile keys when sshd_config is already at its default" {
New-SshdConfig -AdministratorsBlock $script:defaultAdministratorsBlock | Out-Null
Set-ProfileKeyFile -Keys @("ssh-ed25519 AAAAnotanadminkey laptop")
Invoke-WinUtilSSHServer
Get-AuthorizedKeyFileContent | Should -Not -Contain "ssh-ed25519 AAAAnotanadminkey laptop"
}
It "keeps keys that are already in administrators_authorized_keys" {
New-SshdConfig -AdministratorsBlock $script:overriddenAdministratorsBlock | Out-Null
Set-Content -Path $script:authorizedKeysPath -Value "ssh-ed25519 AAAAexisting server"
Set-ProfileKeyFile -Keys @("ssh-ed25519 AAAAexisting server", "ssh-ed25519 AAAAnew laptop")
Invoke-WinUtilSSHServer
$keys = @(Get-AuthorizedKeyFileContent | Where-Object { $_.Trim() })
$keys | Should -HaveCount 2
$keys | Should -Contain "ssh-ed25519 AAAAexisting server"
$keys | Should -Contain "ssh-ed25519 AAAAnew laptop"
}
}