<# .SYNOPSIS Instala e habilita OpenSSH Server em Windows (compatível com Server 2012 R2 ~ 2022). .DESCRIPTION - Compatível com PowerShell 3+ (não exige Expand-Archive) - Força TLS 1.2 para download via HTTPS (Server 2012 R2 default = TLS 1.0) - Faz backup de instalação anterior antes de sobrescrever - Idempotente: pode ser executado várias vezes sem quebrar - Permite definir porta SSH e injetar chave pública autorizada - Cria regra do Windows Firewall (mesmo desabilitado) .PARAMETER Port Porta TCP do sshd. Default: 22 .PARAMETER PublicKey String com chave pública (ed25519/rsa) a adicionar em administrators_authorized_keys .PARAMETER Version Versão do Win32-OpenSSH a instalar. Default: v9.5.0.0p1-Beta .EXAMPLE powershell -ExecutionPolicy Bypass -File .\install-openssh.ps1 .EXAMPLE .\install-openssh.ps1 -Port 2222 -PublicKey "ssh-ed25519 AAAA... user@host" .NOTES Executar como Administrador. #> [CmdletBinding()] param( [int]$Port = 22, [string]$PublicKey = "", [string]$Version = "v9.5.0.0p1-Beta" ) $ErrorActionPreference = "Stop" function Write-Step($msg) { Write-Host "[+] $msg" -ForegroundColor Cyan } function Write-Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } function Write-Warn2($msg) { Write-Host "[!] $msg" -ForegroundColor Yellow } function Write-Err2($msg) { Write-Host "[X] $msg" -ForegroundColor Red } # --- 1. Admin check --- $current = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($current) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Err2 "Execute este script como Administrador." exit 1 } Write-Ok "Administrador confirmado." # --- 2. TLS 1.2 --- Write-Step "Habilitando TLS 1.2 (necessário em Server 2012 R2)." [Net.ServicePointManager]::SecurityProtocol = ` [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 # --- 3. Variáveis --- $installDir = "C:\Program Files\OpenSSH" $zipUrl = "https://github.com/PowerShell/Win32-OpenSSH/releases/download/$Version/OpenSSH-Win64.zip" $tmpZip = Join-Path $env:TEMP "OpenSSH-Win64.zip" $tmpExtract = Join-Path $env:TEMP "OpenSSH-Win64-extract" $backupRoot = "C:\Backups\OpenSSH" $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" # --- 4. Backup instalação anterior --- if (Test-Path $installDir) { Write-Step "Instalação prévia detectada em $installDir. Fazendo backup." New-Item -ItemType Directory -Force -Path $backupRoot | Out-Null $backupPath = Join-Path $backupRoot "OpenSSH-$timestamp" Copy-Item -Path $installDir -Destination $backupPath -Recurse -Force Write-Ok "Backup salvo em $backupPath" # Backup das chaves/host config if (Test-Path "C:\ProgramData\ssh") { $cfgBackup = Join-Path $backupRoot "ProgramData-ssh-$timestamp" Copy-Item -Path "C:\ProgramData\ssh" -Destination $cfgBackup -Recurse -Force Write-Ok "Backup config sshd em $cfgBackup" } # Parar serviço se existir Get-Service sshd -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Running' } | Stop-Service -Force } # --- 5. Download --- Write-Step "Baixando OpenSSH $Version..." if (Test-Path $tmpZip) { Remove-Item $tmpZip -Force } try { Invoke-WebRequest -Uri $zipUrl -OutFile $tmpZip -UseBasicParsing } catch { Write-Err2 "Falha no download: $_" exit 1 } Write-Ok "Download concluído ($([math]::Round((Get-Item $tmpZip).Length/1MB,2)) MB)." # --- 6. Extrair (compatível PS3+ via .NET) --- Write-Step "Extraindo zip..." if (Test-Path $tmpExtract) { Remove-Item $tmpExtract -Recurse -Force } Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($tmpZip, $tmpExtract) $extracted = Join-Path $tmpExtract "OpenSSH-Win64" if (-not (Test-Path $extracted)) { Write-Err2 "Pasta esperada não encontrada após extração: $extracted" exit 1 } if (Test-Path $installDir) { Remove-Item $installDir -Recurse -Force } Move-Item -Path $extracted -Destination $installDir -Force Write-Ok "Instalado em $installDir" # --- 7. Instalar serviço sshd --- Write-Step "Registrando serviço sshd..." $installScript = Join-Path $installDir "install-sshd.ps1" if (-not (Test-Path $installScript)) { Write-Err2 "Script install-sshd.ps1 não encontrado." exit 1 } & powershell.exe -ExecutionPolicy Bypass -File $installScript # --- 8. Configurar porta personalizada --- if ($Port -ne 22) { Write-Step "Configurando porta $Port em sshd_config..." $sshdConfig = "C:\ProgramData\ssh\sshd_config" if (-not (Test-Path "C:\ProgramData\ssh")) { New-Item -ItemType Directory -Path "C:\ProgramData\ssh" -Force | Out-Null } if (-not (Test-Path $sshdConfig)) { Copy-Item (Join-Path $installDir "sshd_config_default") $sshdConfig -Force } # Backup config antes de alterar Copy-Item $sshdConfig "$sshdConfig.bak-$timestamp" -Force $content = Get-Content $sshdConfig $content = $content -replace '^#?Port\s+\d+', "Port $Port" if (-not ($content -match "^Port\s+$Port")) { $content += "Port $Port" } $content | Set-Content $sshdConfig -Encoding ASCII Write-Ok "Porta configurada: $Port" } # --- 9. Regra do firewall Windows --- Write-Step "Configurando regra do Windows Firewall (TCP $Port)..." $ruleName = "OpenSSH-Server-In-TCP-$Port" # Remove regras antigas conflitantes netsh advfirewall firewall delete rule name=$ruleName 2>$null | Out-Null netsh advfirewall firewall add rule name=$ruleName dir=in action=allow protocol=TCP localport=$Port 2>&1 | Out-Null Write-Ok "Regra firewall: $ruleName" # --- 10. Iniciar serviços + autostart --- Write-Step "Iniciando sshd e ssh-agent..." Set-Service -Name sshd -StartupType Automatic Start-Service sshd Set-Service -Name ssh-agent -StartupType Automatic -ErrorAction SilentlyContinue Start-Service ssh-agent -ErrorAction SilentlyContinue Write-Ok "Serviços iniciados." # --- 11. Default shell PowerShell (opcional, melhora UX) --- Write-Step "Definindo PowerShell como shell padrão do SSH..." New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell ` -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" ` -PropertyType String -Force | Out-Null Write-Ok "Shell padrão: PowerShell" # --- 12. Chave pública (opcional) --- if ($PublicKey -ne "") { Write-Step "Instalando chave pública para administradores..." $authKeys = "C:\ProgramData\ssh\administrators_authorized_keys" if (Test-Path $authKeys) { Copy-Item $authKeys "$authKeys.bak-$timestamp" -Force } if (-not (Get-Content $authKeys -ErrorAction SilentlyContinue | Select-String -SimpleMatch $PublicKey)) { Add-Content -Path $authKeys -Value $PublicKey } # ACL correta exigida pelo sshd icacls.exe $authKeys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" | Out-Null Write-Ok "Chave pública adicionada em $authKeys" } # --- 13. Status final --- Write-Host "" Write-Host "===== STATUS =====" -ForegroundColor Cyan Get-Service sshd, ssh-agent | Format-Table -AutoSize $listening = netstat -anob 2>$null | Select-String ":$Port\s.*LISTENING" if ($listening) { Write-Ok "sshd ouvindo na porta $Port" $listening | ForEach-Object { Write-Host " $_" } } else { Write-Warn2 ("sshd nao detectado na porta " + $Port + ". Verifique logs em Event Viewer / Applications / OpenSSH.") } Write-Host "" Write-Ok ("Concluido. Acesse: ssh USUARIO@IP -p " + $Port) if (Test-Path $backupRoot) { Write-Warn2 "Backups disponíveis em: $backupRoot" }