# Moku · turn on SSH so your phone can reach this PC. # https://portholelab.com/ssh · v2 (this file never changes; fixes ship as v3) # # Run it in Terminal (Admin) or Windows PowerShell (Admin): # irm https://portholelab.com/ssh/v2/windows.ps1 | iex # # What this does: # 1. Installs Windows' own "OpenSSH Server" optional feature, if missing. # 2. Starts it now and at every boot. # 3. Lets SSH through Windows Firewall on private (home or work) networks, # from the local network only. An SSH setup you already had is left # as it is. # 4. If this network is marked Public, asks you before marking it Private. # 5. Prints your username and this PC's address, and opens a page with a # QR code for the Moku app to scan. # # What it does not do: edit sshd_config, create users or keys, change any # password, or send anything to anyone. # # Everything runs from Invoke-MokuSshSetup, called on the very last line, # so a download cut short runs nothing. function Invoke-MokuSshSetup { $zh = (Get-UICulture).Name -like 'zh*' function Say([string]$Cn, [string]$En, [string]$Color = 'Gray') { Write-Host $(if ($zh) { $Cn } else { $En }) -ForegroundColor $Color } function Step([string]$Cn, [string]$En) { Write-Host '' Say "==> $Cn" "==> $En" 'White' } $identity = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if (-not $identity.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Say '需要管理员权限:右键「开始」按钮,选「终端(管理员)」或「Windows PowerShell(管理员)」,再粘贴运行一次。' ` 'This needs admin rights: right-click Start, choose "Terminal (Admin)" or "Windows PowerShell (Admin)", then paste and run it again.' 'Yellow' return } if ([Environment]::OSVersion.Version.Build -lt 17763) { Say '这台电脑的 Windows 版本太旧,没有内置 OpenSSH。请先更新到 Windows 10 (1809) 或更新版本。' ` 'This version of Windows is too old for the built-in OpenSSH. Update to Windows 10 (1809) or later first.' 'Yellow' return } Step '检查 OpenSSH 服务器' 'Checking OpenSSH Server' $cap = Get-WindowsCapability -Online -Name 'OpenSSH.Server*' -ErrorAction SilentlyContinue | Select-Object -First 1 $freshInstall = $false if ($null -ne $cap -and $cap.State -eq 'Installed') { Say '已安装。' 'Already installed.' } else { Say '正在安装(Windows 自带的功能,可能要几分钟)……' 'Installing (a Windows feature; this can take a few minutes)...' try { $name = if ($null -ne $cap) { $cap.Name } else { 'OpenSSH.Server~~~~0.0.1.0' } Add-WindowsCapability -Online -Name $name -ErrorAction Stop | Out-Null $freshInstall = $true } catch { Say "安装失败:$($_.Exception.Message)" "Install failed: $($_.Exception.Message)" 'Red' Say '也可以手动装:设置 › 系统 › 可选功能 › 添加功能 › 搜索「OpenSSH 服务器」。装好后再运行一次。' ` 'You can also add it by hand: Settings › System › Optional features › Add a feature › "OpenSSH Server". Then run this again.' 'Yellow' return } } Step '启动 SSH(并设为开机自动启动)' 'Starting SSH (and at every boot)' try { Set-Service -Name sshd -StartupType Automatic -ErrorAction Stop Start-Service -Name sshd -ErrorAction Stop } catch { Say "启动失败:$($_.Exception.Message)" "Couldn't start it: $($_.Exception.Message)" 'Red' return } Step '设置防火墙' 'Setting up the firewall' $ruleName = 'OpenSSH-Server-In-TCP' $rule = Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue if ($null -eq $rule) { New-NetFirewallRule -Name $ruleName -DisplayName 'OpenSSH Server (sshd)' -Enabled True ` -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 ` -Profile Private, Domain -RemoteAddress LocalSubnet | Out-Null Say '已允许:仅限家庭/工作网络、仅限同一局域网的设备。' 'Allowed on home/work networks, for devices on the same local network only.' } elseif ($freshInstall) { # The installer just made this rule open to every network; narrow it. Set-NetFirewallRule -Name $ruleName -Enabled True -Profile Private, Domain -RemoteAddress LocalSubnet Say '已允许:仅限家庭/工作网络、仅限同一局域网的设备。' 'Allowed on home/work networks, for devices on the same local network only.' } elseif ("$($rule.Enabled)" -ne 'True') { # Someone switched it off on purpose; say so rather than undo it. Say '你原有的 SSH 防火墙规则(OpenSSH-Server-In-TCP)是关闭的,手机会连不上。需要的话,在「高级安全 Windows Defender 防火墙」里启用它。' ` 'Your existing SSH firewall rule (OpenSSH-Server-In-TCP) is switched off, so your phone will be blocked. Enable it in "Windows Defender Firewall with Advanced Security" if you want this to work.' 'Yellow' } else { Say '保留你原有的防火墙规则。' 'Keeping your existing firewall rule.' } # The adapter the default route leaves by is the one the phone shares. $route = Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue | Sort-Object { $_.RouteMetric + $_.InterfaceMetric } | Select-Object -First 1 $addr = $null if ($null -ne $route) { $addr = Get-NetIPAddress -AddressFamily IPv4 -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue | Where-Object { $_.IPAddress -notlike '169.254.*' } | Select-Object -First 1 -ExpandProperty IPAddress $net = Get-NetConnectionProfile -InterfaceIndex $route.InterfaceIndex -ErrorAction SilentlyContinue if ($null -ne $net -and $net.NetworkCategory -eq 'Public') { Write-Host '' Say "当前网络「$($net.Name)」被标记为「公用网络」,Windows 会挡住手机的连接。" ` "This network (`"$($net.Name)`") is marked Public, so Windows will block your phone." 'Yellow' Say '如果这是你家里或公司的网络,可以改成「专用网络」。咖啡馆、机场等公共 Wi-Fi 请不要改。' ` "If this is your home or work network, it can be marked Private. Don't do this on cafe or airport Wi-Fi." 'Yellow' $answer = Read-Host $(if ($zh) { '改为专用网络?输入 y 确认,直接回车跳过' } else { 'Mark it Private? Type y to confirm, or press Enter to skip' }) if ($answer -match '^\s*[yY]') { try { Set-NetConnectionProfile -InterfaceIndex $route.InterfaceIndex -NetworkCategory Private -ErrorAction Stop Say '已改为专用网络。' 'Marked Private.' 'Green' } catch { Say "没能改成专用网络:$($_.Exception.Message)" "Couldn't mark it Private: $($_.Exception.Message)" 'Red' Say '可以在「设置 › 网络和 Internet」里把这个网络改成「专用网络」。' ` 'You can change it in Settings › Network & internet: set this network to Private.' 'Yellow' } } else { Say '没有改动。在这个网络上手机暂时连不上这台电脑。' "Left as is. Your phone can't reach this PC on this network for now." 'Yellow' } } } $user = $env:USERNAME if ($env:USERDOMAIN -and $env:USERDOMAIN -ne $env:COMPUTERNAME) { $user = "$env:USERDOMAIN\$env:USERNAME" } Write-Host '' Say '完成。在 Moku 里填这些:' 'Done. In Moku, enter:' 'Green' Say " 用户名:$user" " Username: $user" 'White' Say " 地址: $(if ($addr) { $addr } else { '?' })" " Address: $(if ($addr) { $addr } else { '?' })" 'White' Say ' 密码: 你登录 Windows 的密码。用微软账户登录的,就是微软账户密码(不是开机 PIN 码)。' ` ' Password: your Windows password. With a Microsoft account, that account''s password (not your PIN).' 'White' if (-not $addr) { Say '没找到这台电脑的网络地址。请确认它连着 Wi-Fi 或网线。' "Couldn't find this PC's network address. Check it's on Wi-Fi or Ethernet." 'Yellow' return } $target = 'ssh://' + [uri]::EscapeDataString($user) + '@' + $addr + ':22' # The page reads this after "#", which browsers never send to a server. $url = 'https://portholelab.com/ssh/#show=' + [uri]::EscapeDataString($target) Write-Host '' Say '或者在 Moku 里点「扫码导入」,扫这个页面上的二维码:' 'Or tap "Scan QR code" in Moku and scan the code on this page:' Write-Host " $url" # Through Explorer so the browser opens as you, not as administrator. try { Start-Process -FilePath 'explorer.exe' -ArgumentList "`"$url`"" } catch { } Write-Host '' Say '别让陌生人连你的 Wi-Fi:同一网络里的人都能尝试登录这台电脑。' ` 'Keep strangers off your Wi-Fi: anyone on the same network can try to log in to this PC.' } Invoke-MokuSshSetup