# podshell_setup.ps1 -- give the pod / crash-cart PC a remote shell for BT411 # bring-up. Run ON THE POD PC, in an ADMINISTRATOR PowerShell, ONCE: # # powershell -ExecutionPolicy Bypass -File podshell_setup.ps1 # # What it does (and nothing else): # 1. installs + starts the Windows OpenSSH SERVER (built into Win10/11) # 2. authorizes ONE public key (the laptop's bt411 key, below) # 3. opens SSH on PRIVATE/tailnet profiles only -- never the public internet # 4. prints the Tailscale address to connect to # # It does NOT install Tailscale (do that first, see the header notes) and it # does NOT enable password logins or touch any other service. # # Undo when the pod work is finished: # Remove-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 # Remove-Item C:\ProgramData\ssh\administrators_authorized_keys # (and uninstall / `tailscale logout` if you like) $ErrorActionPreference = 'Stop' # --- the ONE key that may log in (BT411 bring-up, laptop-held, no passphrase) --- $PUBKEY = 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICmGkinDbZMwQ/UhwqU/oECUIeAN+pBjLgsekj41di7W bt411-claude-code@laptop' function Say($m) { Write-Host " $m" } Write-Host "=== BT411 pod shell setup ===" # 0. admin check -- everything below needs it $isAdmin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "!! Run this in an ADMINISTRATOR PowerShell (right-click > Run as administrator)." -ForegroundColor Red exit 1 } $os = [Version](Get-CimInstance Win32_OperatingSystem).Version Say ("windows {0}" -f $os) if ($os.Major -lt 10) { Write-Host "!! Windows 10+ required for the built-in OpenSSH server." -ForegroundColor Red Write-Host " (If this is the period pod PC, stop here -- use the clipboard/probe route instead.)" exit 1 } # 1. OpenSSH server Say "installing OpenSSH server (if missing) ..." $cap = Get-WindowsCapability -Online -Name OpenSSH.Server* | Select-Object -First 1 if ($cap.State -ne 'Installed') { Add-WindowsCapability -Online -Name $cap.Name | Out-Null } Set-Service -Name sshd -StartupType Automatic Start-Service sshd Say ("sshd: {0}" -f (Get-Service sshd).Status) # 2. authorize the key. Windows OpenSSH quirk: for accounts in the # Administrators group the file is administrators_authorized_keys, and sshd # REFUSES it unless the ACL is exactly SYSTEM + Administrators. $me = "$env:USERDOMAIN\$env:USERNAME" $inAdmins = (Get-LocalGroupMember -Group 'Administrators' -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $me }) -ne $null if ($inAdmins) { $akf = 'C:\ProgramData\ssh\administrators_authorized_keys' Say "account is an Administrator -> using administrators_authorized_keys" } else { $akf = Join-Path $env:USERPROFILE '.ssh\authorized_keys' New-Item -ItemType Directory -Force -Path (Split-Path $akf) | Out-Null Say "standard account -> using $akf" } $existing = '' if (Test-Path $akf) { $existing = Get-Content $akf -Raw } if ($existing -notlike "*$PUBKEY*") { Add-Content -Path $akf -Value $PUBKEY -Encoding ASCII Say "key added" } else { Say "key already present" } if ($inAdmins) { icacls $akf /inheritance:r /grant 'SYSTEM:F' /grant 'BUILTIN\Administrators:F' | Out-Null Say "ACL locked to SYSTEM + Administrators (sshd requires this)" } # 3. firewall: private/domain only. Explicitly NOT public -- the tailnet # interface is what we connect over. $rule = Get-NetFirewallRule -Name 'BT411-SSH' -ErrorAction SilentlyContinue if (-not $rule) { New-NetFirewallRule -Name 'BT411-SSH' -DisplayName 'BT411 SSH (private/tailnet only)' ` -Enabled True -Direction Inbound -Protocol TCP -LocalPort 22 ` -Action Allow -Profile Private,Domain | Out-Null Say "firewall rule added (private/domain profiles only)" } else { Say "firewall rule already present" } # 4. how to reach it Write-Host "" Write-Host "=== CONNECT WITH ===" $ts = 'C:\Program Files\Tailscale\tailscale.exe' if (Test-Path $ts) { $ip = (& $ts ip -4 2>$null | Select-Object -First 1) $st = (& $ts status --json 2>$null | ConvertFrom-Json) $dns = $null if ($st -and $st.Self -and $st.Self.DNSName) { $dns = $st.Self.DNSName.TrimEnd('.') } if ($ip) { Write-Host (" ssh -i ~/.ssh/bt411_pod {0}@{1}" -f $env:USERNAME, $ip) } if ($dns) { Write-Host (" ssh -i ~/.ssh/bt411_pod {0}@{1}" -f $env:USERNAME, $dns) } if (-not $ip) { Write-Host " ! Tailscale installed but not connected -- run: tailscale up" } } else { Write-Host " ! Tailscale not installed yet." Write-Host " winget install --id Tailscale.Tailscale -e (then: tailscale up)" Write-Host " Sign in with the SAME account used on the laptop." Write-Host (" LAN fallback: ssh -i ~/.ssh/bt411_pod {0}@{1}" -f $env:USERNAME, ((Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' } | Select-Object -First 1).IPAddress)) } Write-Host "" Say ("user: {0} host: {1}" -f $env:USERNAME, $env:COMPUTERNAME)