将 OpenSSH 服务器部署到 Windows 10
PowerShell 7 提供了许多令人信服的理由来采用它。其中最好的一项是支持 SSH 作为 PowerShell 远程协议。不幸的是,这不是以 Windows 为中心的 IT 专业人员通常会想到的话题。我知道这对我来说绝对是正确的,也是我一直在努力解决的缺陷。从好的方面来说,Windows 10 附带了已安装且可以使用的 OpenSSH 客户端。但是 OpenSSH 服务器呢?在 Windows 平台上,如果要在 PowerShell 中使用 SSH 远程处理,则需要启用并配置此元素。当然,我想使用 PowerShell 远程完成此操作。首先,我想向您展示如何在 Windows 10 上设置 OpenSSH Server 组件。
手动设置
在 Windows 10 中安装 OpenSSH Server 组件的 PowerShell 命令非常简单。首先,我需要能力。我正在以交互方式运行这些命令。
Get-WindowsCapability -online -name openssh.server* | Add-WindowsCapability -online我使用带有通配符的 Get-WindowsCapability,以防版本号在某个时刻发生变化。我可以看到这有效。
这将设置未运行并配置为手动启动的 sshd 服务。我想将服务配置为启动并自动启动。
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
我了解到关于 ssh 的一件事,特别是如果您想将它与 PowerShell 远程处理一起使用,那就是验证 ssh 在 PowerShell 之外是否有效。
ssh localhost系统可能会提示您接受密钥和密码。如果你的设置和我的一样,你最终会看到 CMD 提示符。但也许您在通过 ssh 连接时需要不同的 shell。请注意,我还没有谈论通过 ssh 进行 PowerShell 远程处理。您可以修改计算机上的注册表。
$regPath = "HKLM:\SOFTWARE\OpenSSH\"
$regProperty = "DefaultShell"
$regValue = (Get-Command powershell.exe).source
Set-ItemProperty -Path $regPath -Name $regProperty -Value $regValue更改应该立即生效,并且不需要重新启动 sshd 服务。使用 ssh 连接到本地主机现在可以提供 Windows PowerShell 会话。组装一个简单的 PowerShell 脚本来交互运行这些命令并不需要太多时间。
远程安装
但我想远程执行此操作。我有一些 Windows 10 虚拟机,我想通过 ssh 连接到它们。我需要使用传统的 PowerShell 远程处理。是的,您可以使用 psexec.exe 等第三方工具或 Desired State Configuration 等工具,但我想坚持使用您可能已经拥有的工具和技能。
当我从事这项工作时,我有一个简单的 PowerShell 脚本,它运行我刚才向您展示的步骤。我认为我需要做的就是使用 Invoke-Command 运行脚本。没有。事实证明,Add-WindowsCapability 命令在远程会话中不起作用。这显然是按需安装功能的一个已知限制。但我不会让这阻止我。我最终的解决方法是创建一个临时计划任务,将 OpenSSH.Server 添加为系统。
$Time = New-ScheduledTaskTrigger -At 23:59 -Once
$User = New-ScheduledTaskPrincipal System
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument '-noprofile -command "& { Get-WindowsCapability -online -name openssh.server* | Add-WindowsCapability -online}"'
$tmpTask = Register-ScheduledTask -taskpath \ -TaskName "AddTask" -Trigger $Time -Principal $user -Action $Action
Start-ScheduledTask -TaskName AddTask
#give the task a chance to finish
Start-Sleep -Seconds 20
$tmpTask | Unregister-ScheduledTask -confirm:$false触发时间无关紧要,因为我将手动启动任务,然后将其删除。只要我不在接近午夜的时间运行,就应该没问题。
我还希望我的脚本支持 -WhatIf 并提供详细输出,即使代码是远程运行的。这是我想出的脚本。
#requires -version 5.1
<#
This is used to setup SSHD on a Windows 10 Client through PowerShell remoting
Sample usage
Verbose and Whatif
invoke-command -FilePath .\Setup-SSHServer.ps1 -ComputerName win10 -cred $artd -ArgumentList @("Continue",$True)
Verbose
invoke-command -FilePath .\Setup-SSHServer.ps1 -ComputerName win10 -cred $artd -ArgumentList @("Continue")
#>
[cmdletbinding(SupportsShouldProcess)]
param ([string]$VerboseOption = "SilentlyContinue", [bool]$WhatIfOption = $False)
$VerbosePreference = $VerboseOption
$WhatIfPreference = $WhatIfOption
Write-Verbose "Starting SSHD setup process"
Try {
Write-Verbose "Testing for sshd service"
$svc = Get-Service -Name sshd -ErrorAction Stop
if ($svc.status -eq 'Running') {
Write-Verbose "sshd service appears to be installed and running"
}
else {
Write-Warning "sshd service found but it isn't running"
}
#take no further action
Return "OpenSSH Server installation appears to be complete."
}
Catch {
Write-Verbose "sshd service not found. Continuing with installation"
}
Write-Verbose "Testing OpenSSH.Server Windows capability"
if (-Not $svc) {
Try {
$cap = Get-WindowsCapability -Name OpenSSH.Server* -Online -ErrorAction Stop
$cap | Out-String | Write-Verbose
}
Catch {
Throw $_
}
}
if ($cap.State -eq 'NotPresent' -And $cap.name -match "OpenSSH") {
#install capability
Try {
Write-Verbose "Adding $($cap.name)"
if ($pscmdlet.ShouldProcess($cap.Name)) {
#installing with a Scheduledtask as System to get around the limitation
#with Add-WindowsCapability in a PowerShell remoting session
$Time = New-ScheduledTaskTrigger -At 23:59 -Once
$User = New-ScheduledTaskPrincipal System
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument '-noprofile -command "& { Get-WindowsCapability -online -name openssh.server* | Add-WindowsCapability -online}"'
$tmpTask = Register-ScheduledTask -taskpath \ -TaskName "AddTask" -Trigger $Time -Principal $user -Action $Action
Start-ScheduledTask -TaskName AddTask
#give the task a chance to finish
Start-Sleep -Seconds 20
$tmpTask | Unregister-ScheduledTask -confirm:$false
} #Whatif
}
Catch {
Throw $_
}
}
Write-Verbose "Set sshd to auto start"
Try {
if ($pscmdlet.ShouldProcess("sshd", "Configure service")) {
Set-Service -Name sshd -StartupType Automatic -ErrorAction stop
Write-Verbose "Start the sshd service"
Start-Service -Name sshd -ErrorAction Stop
Get-Service sshd | Select-Object -Property * | Out-String | Write-Verbose
}
}
Catch {
Write-Warning "There was a problem configuring the sshd service."
}
if (-Not $WhatIfOption) {
#only display the summary if not using -WhatIf
$msg = @"
SSH setup complete. Edit $ENV:ProgramData\ssh\sshd_config for additional configurations options
or to enable remoting under PowerShell 7.
You will need to restart the sshd service for changes to take effect.
"@
Write-Host $msg -ForegroundColor green
}
Write-Verbose "Ending SSHD setup process."
该代码与交互式运行的代码有点不同。当您运行 Invoke-Command 并指定文件时,PowerShell 会将文件的内容视为脚本块并在远程运行空间中运行它。这就是为什么这些参数不是典型的函数参数。我将 Verbose 和 WhatIf 首选项从原始命令传递到脚本。
Invoke-Command -FilePath .\Setup-SSHServer.ps1 -ComputerName win10 -cred $artd -ArgumentList @("Continue", $True)这为我提供了来自远程 Windows 10 计算机的 Verbose 和 Whatif 输出。使用默认值运行脚本效果很好。如果需要,您可以在注册表中添加行来设置默认 shell。但最终我想通过 ssh 配置 PowerShell 远程处理,所以我不需要它们。
PowerShell 7 SSH 远程处理
最后一步是将远程处理配置为使用 ssh,这发生在远程计算机上。您需要编辑 $env:ProgramData\ssh 中的 sshd_config 文件。这是我的副本,已根据必要的 PowerShell 要求进行了编辑。
# This is the sshd server system-wide configuration file. See
# sshd_config(5) for more information.
# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented. Uncommented options override the
# default value.
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey __PROGRAMDATA__/ssh/ssh_host_rsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_dsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_ecdsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#ENABLED
PubkeyAuthentication yes
# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys
#AuthorizedPrincipalsFile none
# For this to work you will also need host keys in %programData%/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes
# To disable tunneled clear text passwords, change to no here!
# ENABLED: Set this to YES for PowerShell remoting
PasswordAuthentication yes
#PermitEmptyPasswords no
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#PermitTTY yes
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#PermitUserEnvironment no
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none
# no default banner path
#Banner none
# override default of no subsystems
Subsystem sftp sftp-server.exe
#ADDED
Subsystem powershell c:\progra~1\powershell\7\pwsh.exe -sshs -NoLogo
# Example of overriding settings on a per-user basis
#Match User anoncvs
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server
Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
您可能需要进行其他更改,具体取决于您的组织。假设已安装 OpenSSH.Server 和 PowerShell 7,我可以使用 PowerShell 将此文件复制到远程计算机。
Copy-Item -Path .\sshd_config_default -Destination $env:ProgramData\ssh\sshd_config -ToSession $sess应重新启动 sshd 服务以使更改生效。
部署
最后一步是将所有这些整合到一个主控制脚本中。我决定添加代码以使用 PSReleaseTools 模块安装 PowerShell 7。
#requires -version 5.1
#Deployment control script to setup SSH server on a remote Windows 10 desktop
#It is assumed PowerShell 7 is, or will be, installed.
[cmdletbinding(SupportsShouldProcess)]
Param(
[Parameter(Position = 0, Mandatory)]
[string]$Computername,
[pscredential]$Credential,
[switch]$InstallPowerShell
)
#remove parameters from PSBoundparameter that don't apply to New-PSSession
if ($PSBoundParameters.ContainsKey("InstallPowerShell")) {
[void]($PSBoundParameters.remove("InstallPowerShell"))
}
if ($PSBoundParameters.ContainsKey("WhatIf")) {
[void]($PSBoundParameters.remove("WhatIf"))
}
#parameters for Write-Progress
$prog = @{
Activity = "Deploy SSH Server to Windows 10"
Status = $Computername.toUpper()
CurrentOperation = ""
PercentComplete = 0
}
#create the PSSession
Try {
Write-Verbose "Creating a PSSession to $Computername"
$prog.CurrentOperation = "Creating a temporary PSSession"
$prog.PercentComplete = 10
Write-Progress @prog
$sess = New-PSSession @PSBoundParameters -ErrorAction Stop
}
Catch {
Throw $_
}
if ($sess) {
if ($InstallPowerShell) {
$prog.currentOperation = "Installing PowerShell 7"
$prog.percentComplete = 25
Write-Progress @prog
#install PowerShell
if ($pscmdlet.ShouldProcess($Computername.toUpper(),"Install PowerShell 7")) {
Invoke-Command -ScriptBlock {
[void](Install-PackageProvider -Name nuget -force -forcebootstrap)
Install-Module PSReleaseTools -Force
Install-PowerShell -EnableRemoting -EnableContextMenu -mode Quiet
} -session $sess
} #whatif
}
#setup SSH
$prog.currentOperation = "Installing OpenSSH Server"
$prog.percentComplete = 50
Write-Progress @prog
Invoke-Command -FilePath .\Setup-SSHServer.ps1 -Session $sess -ArgumentList @($VerbosePreference, $WhatIfPreference)
#copy the sshd_config file. This assumes you've installed PowerShell 7 on the remote computer
Write-Verbose "Copying sshd_config to target"
$prog.currentOperation = "Copying default sshd_config to target"
$prog.percentcomplete = 60
Write-Progress @prog
Copy-Item -Path .\sshd_config_default -Destination $env:ProgramData\ssh\sshd_config -ToSession $sess
#restart the service
Write-Verbose "Restarting the sshd service on the target"
$prog.currentOperation = "Restarting the ssh service target"
$prog.percentComplete = 75
Write-Progress @prog
if ($pscmdlet.ShouldProcess("sshd","Restart service")) {
Invoke-Command { Restart-Service -Name sshd } -Session $sess
}
Write-Verbose "Removing the temporary PSSession"
$prog.currentOperation = "Removing temporary PSSession"
$prog.percentComplete = 90
Write-Progress @prog
$sess | Remove-PSSession
Write-Progress @prog -completed
$msg = @"
SSH Server deployment complete for $($Computername.toUpper()).
If PowerShell 7 is installed remotely, you should be able to test
with an expression like:
Enter-PSSession -hostname $Computername -username <user> -sshtransport
"@
Write-Host $msg -ForegroundColor yellow
} #if $sess我可能应该在配置 sshd 服务时添加更好的错误处理,但这应该设置所有内容,以便我可以执行以下操作:
下一步
我使用 ssh 和 PowerShell 进行 Windows 之旅的下一部分是配置我的 Windows 服务器。完成后,我可以通过 ssh 使用 PowerShell 远程处理几乎所有内容。明天我会深入探讨这个问题。


