301 lines
11 KiB
PowerShell
301 lines
11 KiB
PowerShell
<#
|
|
LXS - Remote Access (Windows)
|
|
Description: Remote Desktop and the OpenSSH server — status, enable,
|
|
disable. The Windows counterpart of root-ssh-login.sh.
|
|
Repo: https://git.hyko.cx/hykocx/lxs
|
|
#>
|
|
|
|
# Load LXS common library (colors, UI helpers, spinner, loggers, guards).
|
|
# Prefers the sibling ..\lib\common.ps1 (repo checkout or installed layout) and
|
|
# only hits the network when this script is run standalone.
|
|
$LxsRawBase = if ($env:LXS_RAW_BASE) { $env:LXS_RAW_BASE } else { 'https://git.hyko.cx/hykocx/lxs/raw/branch/main' }
|
|
if (-not $env:LXS_RAW_PLATFORM_BASE) { $env:LXS_RAW_PLATFORM_BASE = "$LxsRawBase/windows" }
|
|
$LxsLibPath = if ($PSScriptRoot) { Join-Path $PSScriptRoot '..\lib\common.ps1' } else { $null }
|
|
if ($LxsLibPath -and (Test-Path $LxsLibPath)) {
|
|
. $LxsLibPath
|
|
} else {
|
|
try {
|
|
$LxsLibSource = Invoke-RestMethod -Uri "$env:LXS_RAW_PLATFORM_BASE/lib/common.ps1" -UseBasicParsing -ErrorAction Stop
|
|
} catch {
|
|
Write-Error 'Failed to fetch lib/common.ps1'
|
|
exit 1
|
|
}
|
|
. ([scriptblock]::Create($LxsLibSource))
|
|
}
|
|
|
|
$env:LXS_LOG_FILE = Join-Path (Get-LxsTempDir) 'lxs_remote_access.log'
|
|
|
|
if (-not (Assert-LxsWindows)) { exit 1 }
|
|
Assert-LxsAdmin -ScriptPath $PSCommandPath -Arguments $args
|
|
|
|
$LxsRdpKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server'
|
|
$LxsRdpTcpKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp'
|
|
|
|
function Get-LxsRdpState {
|
|
try {
|
|
# fDenyTSConnections 0 = RDP allowed, 1 = denied.
|
|
$deny = (Get-ItemProperty -Path $LxsRdpKey -Name 'fDenyTSConnections' -ErrorAction Stop).fDenyTSConnections
|
|
return ($deny -eq 0)
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Get-LxsNlaState {
|
|
try {
|
|
return ((Get-ItemProperty -Path $LxsRdpTcpKey -Name 'UserAuthentication' -ErrorAction Stop).UserAuthentication -eq 1)
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Show-LxsRemoteStatus {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'REMOTE ACCESS STATUS'
|
|
Write-Host ''
|
|
|
|
Write-Host "$($script:Cyan)$($script:Bold)Remote Desktop$($script:NC)"
|
|
if (Get-LxsRdpState) {
|
|
Write-LxsOk 'RDP is ENABLED'
|
|
if (Get-LxsNlaState) {
|
|
Write-LxsOk 'Network Level Authentication is on'
|
|
} else {
|
|
Write-LxsWarn 'Network Level Authentication is OFF — turn it on (option 2).'
|
|
}
|
|
try {
|
|
$port = (Get-ItemProperty -Path $LxsRdpTcpKey -Name 'PortNumber' -ErrorAction Stop).PortNumber
|
|
Write-Host " Listening port: $port"
|
|
} catch {
|
|
Write-Host ' Listening port: 3389 (default)'
|
|
}
|
|
} else {
|
|
Write-Host " $($script:Gray)RDP is disabled$($script:NC)"
|
|
}
|
|
|
|
try {
|
|
$rules = @(Get-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction Stop | Where-Object { $_.Enabled -eq 'True' })
|
|
Write-Host " Firewall rules enabled: $($rules.Count)"
|
|
} catch {
|
|
# Group may not exist on Server Core images.
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host "$($script:Cyan)$($script:Bold)OpenSSH Server$($script:NC)"
|
|
$svc = Get-Service -Name sshd -ErrorAction SilentlyContinue
|
|
if (-not $svc) {
|
|
Write-Host " $($script:Gray)Not installed$($script:NC)"
|
|
} else {
|
|
if ($svc.Status -eq 'Running') { Write-LxsOk "sshd is running (startup: $($svc.StartType))" }
|
|
else { Write-LxsWarn "sshd is installed but $($svc.Status) (startup: $($svc.StartType))" }
|
|
try {
|
|
$shell = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name 'DefaultShell' -ErrorAction Stop).DefaultShell
|
|
Write-Host " Default shell : $shell"
|
|
} catch {
|
|
Write-Host " Default shell : cmd.exe (Windows default)"
|
|
}
|
|
}
|
|
Write-Host ''
|
|
}
|
|
|
|
function Enable-LxsRdp {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'ENABLE REMOTE DESKTOP'
|
|
Write-Host ''
|
|
Write-Host 'This allows inbound RDP connections and opens the matching'
|
|
Write-Host 'firewall rules. Network Level Authentication will be required,'
|
|
Write-Host 'so clients must authenticate before a session is created.'
|
|
Write-Host ''
|
|
if (-not (Confirm-LxsAction -Question 'Enable Remote Desktop?')) {
|
|
Write-LxsInfo 'Cancelled.'
|
|
return
|
|
}
|
|
Write-Host ''
|
|
if (Set-LxsRegistryValue -Path $LxsRdpKey -Name 'fDenyTSConnections' -Value 0) {
|
|
Write-LxsOk 'RDP connections allowed'
|
|
}
|
|
if (Set-LxsRegistryValue -Path $LxsRdpTcpKey -Name 'UserAuthentication' -Value 1) {
|
|
Write-LxsOk 'Network Level Authentication required'
|
|
}
|
|
try {
|
|
Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction Stop
|
|
Write-LxsOk 'Firewall rules for Remote Desktop enabled'
|
|
} catch {
|
|
Write-LxsWarn "Could not enable the firewall rules: $($_.Exception.Message)"
|
|
}
|
|
Write-Host ''
|
|
Write-LxsWarn 'Only expose RDP to the internet behind a VPN — it is a constant brute-force target.'
|
|
}
|
|
|
|
function Disable-LxsRdp {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'DISABLE REMOTE DESKTOP'
|
|
Write-Host ''
|
|
if (-not (Confirm-LxsAction -Question 'Disable Remote Desktop and its firewall rules?')) {
|
|
Write-LxsInfo 'Cancelled.'
|
|
return
|
|
}
|
|
Write-Host ''
|
|
if (Set-LxsRegistryValue -Path $LxsRdpKey -Name 'fDenyTSConnections' -Value 1) {
|
|
Write-LxsOk 'RDP connections denied'
|
|
}
|
|
try {
|
|
Disable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction Stop
|
|
Write-LxsOk 'Firewall rules for Remote Desktop disabled'
|
|
} catch {
|
|
Write-LxsWarn "Could not disable the firewall rules: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
function Install-LxsSshServer {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'OPENSSH SERVER'
|
|
Write-Host ''
|
|
Write-Host 'Installs the OpenSSH Server capability, starts sshd, sets it to'
|
|
Write-Host 'start automatically and opens TCP/22 in the firewall.'
|
|
Write-Host ''
|
|
if (-not (Confirm-LxsAction -Question 'Install and enable the OpenSSH server?')) {
|
|
Write-LxsInfo 'Cancelled.'
|
|
return
|
|
}
|
|
Write-Host ''
|
|
|
|
try {
|
|
$cap = Get-WindowsCapability -Online -Name 'OpenSSH.Server*' -ErrorAction Stop |
|
|
Select-Object -First 1
|
|
} catch {
|
|
Write-LxsErr "Could not query Windows capabilities: $($_.Exception.Message)"
|
|
return
|
|
}
|
|
|
|
if (-not $cap) {
|
|
Write-LxsErr 'The OpenSSH Server capability is not available on this edition.'
|
|
return
|
|
}
|
|
|
|
if ($cap.State -ne 'Installed') {
|
|
Write-LxsInfo "Installing $($cap.Name) (this can take a few minutes)..."
|
|
try {
|
|
Add-WindowsCapability -Online -Name $cap.Name -ErrorAction Stop | Out-Null
|
|
Write-LxsOk 'OpenSSH Server installed'
|
|
} catch {
|
|
Write-LxsErr "Installation failed: $($_.Exception.Message)"
|
|
return
|
|
}
|
|
} else {
|
|
Write-LxsOk 'OpenSSH Server was already installed'
|
|
}
|
|
|
|
try {
|
|
Set-Service -Name sshd -StartupType Automatic -ErrorAction Stop
|
|
Start-Service -Name sshd -ErrorAction Stop
|
|
Write-LxsOk 'sshd is running and set to start automatically'
|
|
} catch {
|
|
Write-LxsErr "Could not start sshd: $($_.Exception.Message)"
|
|
return
|
|
}
|
|
|
|
# The capability normally creates this rule; create it when it is missing.
|
|
try {
|
|
if (-not (Get-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -ErrorAction SilentlyContinue)) {
|
|
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' `
|
|
-Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 -ErrorAction Stop | Out-Null
|
|
Write-LxsOk 'Firewall rule created for TCP/22'
|
|
} else {
|
|
Write-LxsOk 'Firewall rule for TCP/22 already present'
|
|
}
|
|
} catch {
|
|
Write-LxsWarn "Could not create the firewall rule: $($_.Exception.Message)"
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host "$($script:Gray)Connect with: ssh $env:USERNAME@$(Get-LxsPublicIP)$($script:NC)"
|
|
Write-Host "$($script:Gray)Key-based auth: put your public key in %ProgramData%\ssh\administrators_authorized_keys$($script:NC)"
|
|
Write-Host "$($script:Gray)for admin accounts, or in %USERPROFILE%\.ssh\authorized_keys otherwise.$($script:NC)"
|
|
}
|
|
|
|
function Disable-LxsSshServer {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'DISABLE OPENSSH SERVER'
|
|
Write-Host ''
|
|
$svc = Get-Service -Name sshd -ErrorAction SilentlyContinue
|
|
if (-not $svc) {
|
|
Write-LxsWarn 'The OpenSSH server is not installed.'
|
|
return
|
|
}
|
|
if (-not (Confirm-LxsAction -Question 'Stop sshd and set it to Disabled?')) {
|
|
Write-LxsInfo 'Cancelled.'
|
|
return
|
|
}
|
|
try {
|
|
Stop-Service -Name sshd -Force -ErrorAction Stop
|
|
Set-Service -Name sshd -StartupType Disabled -ErrorAction Stop
|
|
Write-LxsOk 'sshd stopped and disabled'
|
|
} catch {
|
|
Write-LxsErr "Failed: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
function Set-LxsSshDefaultShell {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'SSH DEFAULT SHELL'
|
|
Write-Host ''
|
|
Write-Host 'Which shell should SSH sessions land in?'
|
|
Write-Host ''
|
|
Show-LxsMenuItem '1' 'PowerShell' 'powershell.exe'
|
|
Show-LxsMenuItem '2' 'PowerShell 7' 'pwsh.exe (must be installed)'
|
|
Show-LxsMenuItem '3' 'Command Prompt' 'cmd.exe (Windows default)'
|
|
Show-LxsMenuItem '0' 'Cancel' '' -Exit
|
|
Write-Host ''
|
|
$choice = Read-LxsChoice
|
|
|
|
$shell = switch ($choice) {
|
|
'1' { "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" }
|
|
'2' { (Get-Command pwsh -ErrorAction SilentlyContinue).Source }
|
|
'3' { "$env:SystemRoot\System32\cmd.exe" }
|
|
default { $null }
|
|
}
|
|
if (-not $shell) {
|
|
if ($choice -eq '2') { Write-LxsErr 'pwsh.exe was not found — install PowerShell 7 first.' }
|
|
else { Write-LxsInfo 'Cancelled.' }
|
|
return
|
|
}
|
|
Write-Host ''
|
|
if (Set-LxsRegistryValue -Path 'HKLM:\SOFTWARE\OpenSSH' -Name 'DefaultShell' -Value $shell -Type String) {
|
|
Write-LxsOk "SSH sessions will start: $shell"
|
|
}
|
|
}
|
|
|
|
function Show-LxsRemoteMenu {
|
|
while ($true) {
|
|
Clear-Host
|
|
Show-LxsBoxTop -Title 'REMOTE ACCESS' -Right 'ADMIN'
|
|
Write-Host ''
|
|
Show-LxsMenuItem '1' 'Show status'
|
|
Show-LxsMenuItem '2' 'Enable Remote Desktop' 'with NLA + firewall'
|
|
Show-LxsMenuItem '3' 'Disable Remote Desktop'
|
|
Show-LxsMenuItem '4' 'Install OpenSSH Server' 'service + firewall'
|
|
Show-LxsMenuItem '5' 'Disable OpenSSH Server'
|
|
Show-LxsMenuItem '6' 'Set SSH default shell'
|
|
Show-LxsMenuItem '0' 'Back' '' -Exit
|
|
Write-Host ''
|
|
Show-LxsBoxBottom
|
|
Write-Host ''
|
|
$choice = Read-LxsChoice
|
|
Write-Host ''
|
|
|
|
switch ($choice) {
|
|
'1' { Show-LxsRemoteStatus; Read-LxsEnter }
|
|
'2' { Enable-LxsRdp; Read-LxsEnter }
|
|
'3' { Disable-LxsRdp; Read-LxsEnter }
|
|
'4' { Install-LxsSshServer; Read-LxsEnter }
|
|
'5' { Disable-LxsSshServer; Read-LxsEnter }
|
|
'6' { Set-LxsSshDefaultShell; Read-LxsEnter }
|
|
'0' { return }
|
|
default { Write-LxsErr 'Invalid protocol. Select 0-6.'; Start-Sleep -Seconds 1 }
|
|
}
|
|
}
|
|
}
|
|
|
|
Show-LxsRemoteMenu
|
|
exit 75
|