Implementar el servidor OpenSSH en Windows 10
PowerShell 7 ofrece varias razones convincentes para adoptarlo. Uno de los mejores es la compatibilidad con SSH como protocolo de comunicación remota de PowerShell. Desafortunadamente, este no es un tema que surja normalmente entre los profesionales de TI centrados en Windows. Sé que esto es definitivamente cierto en mi caso y es una deficiencia en la que he estado trabajando. En el lado positivo, Windows 10 viene con el cliente OpenSSH instalado y listo para funcionar. Pero ¿qué pasa con el servidor OpenSSH? En las plataformas Windows, debe habilitar y configurar este elemento si desea utilizar la comunicación remota SSH en PowerShell. Naturalmente, me gustaría hacer esto con PowerShell y también de forma remota. Primero, quiero mostrarles cómo configurar el componente OpenSSH Server en Windows 10.
Configuración manual
Los comandos de PowerShell para instalar el componente OpenSSH Server en Windows 10 son bastante simples. Primero, necesito la capacidad. Estoy ejecutando estos comandos de forma interactiva.
Get-WindowsCapability -online -name openssh.server* | Add-WindowsCapability -onlineEstoy usando Get-WindowsCapability con un comodín en caso de que el número de versión cambie en algún momento. Puedo ver que esto funcionó.
Esto configurará el servicio sshd que no se está ejecutando y configurado para iniciarse manualmente. Quiero configurar el servicio para que se inicie y se inicie automáticamente.
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
Una cosa que he aprendido sobre ssh, especialmente si desea usarlo con la comunicación remota de PowerShell, es verificar que ssh funcione fuera de PowerShell.
ssh localhostEs posible que se le solicite aceptar claves y una contraseña. Si tu configuración es como la mía, terminarás en un mensaje de CMD. Pero tal vez quieras un shell diferente cuando te conectes a través de ssh. Tenga en cuenta que todavía no estoy hablando de comunicación remota de PowerShell a través de ssh. Puede modificar el registro en su computadora.
$regPath = "HKLM:\SOFTWARE\OpenSSH\"
$regProperty = "DefaultShell"
$regValue = (Get-Command powershell.exe).source
Set-ItemProperty -Path $regPath -Name $regProperty -Value $regValueEl cambio debe ser inmediato y no requerir reiniciar el servicio sshd. Usar ssh para conectarme a localhost ahora me brinda una sesión de Windows PowerShell. No haría falta mucho para crear un script de PowerShell sencillo para ejecutar estos comandos de forma interactiva.
Instalación remota
Pero quiero hacer esto de forma remota. Tengo algunas máquinas virtuales con Windows 10 a las que me gustaría conectarme mediante ssh. Necesitaré utilizar la comunicación remota tradicional de PowerShell. Sí, podrías usar una herramienta de terceros como psexec.exe o algo como Desired State Configuration, pero quiero seguir con las herramientas y habilidades que probablemente ya tengas.
Mientras trabajaba en esto, tenía un script de PowerShell simple que ejecutaba los pasos que les acabo de mostrar. Pensé que todo lo que tenía que hacer era ejecutar el script usando Invoke-Command. No. Resulta que el comando Add-WindowsCapability no funcionará en una sesión remota. Aparentemente, esta es una limitación conocida con la instalación de funciones bajo demanda. Pero no iba a dejar que eso me detuviera. Mi solución final fue crear una tarea programada temporal que agregaría OpenSSH.Server como SISTEMA.
$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:$falseEl tiempo de activación es irrelevante porque iniciaré la tarea manualmente y luego la eliminaré. Mientras no esté ejecutando esto cerca de la medianoche, debería estar bien.
También quería que mi script admitiera -WhatIf y ofreciera salida detallada, aunque el código se ejecutara de forma remota. Aquí está el guión que se me ocurrió.
#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."
El código es un poco diferente al que ejecutaría de forma interactiva. Cuando ejecuta Invoke-Command y especifica un archivo, PowerShell trata el contenido del archivo como un bloque de script y lo ejecuta en un espacio de ejecución remoto. Por eso los parámetros no son parámetros de función típicos. Estoy pasando las preferencias detalladas y WhatIf del comando original al script.
Invoke-Command -FilePath .\Setup-SSHServer.ps1 -ComputerName win10 -cred $artd -ArgumentList @("Continue", $True)Esto me proporciona resultados detallados y Whatif desde la máquina remota con Windows 10. Ejecutar el script con valores predeterminados funciona bien. Si lo desea, puede agregar líneas de registro para configurar el shell predeterminado. Pero, en última instancia, quiero configurar la comunicación remota de PowerShell a través de ssh, por lo que no los necesito.
Conexión remota SSH de PowerShell 7
Hay un último paso para configurar la comunicación remota para usar ssh y eso sucede en la máquina remota. Debe editar el archivo sshd_config que se encuentra en $env:ProgramData\ssh. Aquí está mi copia que ha sido editada con los requisitos necesarios de 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
Es posible que necesite cambios adicionales según su organización. Puedo usar PowerShell para copiar este archivo a la computadora remota, suponiendo que se hayan instalado OpenSSH.Server y PowerShell 7.
Copy-Item -Path .\sshd_config_default -Destination $env:ProgramData\ssh\sshd_config -ToSession $sessSe debe reiniciar el servicio sshd para que los cambios surtan efecto.
Despliegue
El último paso es reunir todo esto en un script de control maestro. Decidí agregar código para instalar también PowerShell 7 usando mi módulo PSReleaseTools.
#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 $sessProbablemente debería agregar un mejor manejo de errores en la configuración del servicio sshd, pero esto debería configurar todo para que pueda hacer esto:
Próximos pasos
La siguiente parte de mi viaje en Windows con ssh y PowerShell es configurar mis servidores Windows. Una vez hecho esto, puedo usar la comunicación remota de PowerShell a través de ssh para casi todo. Me sumergiré en esto mañana.


