Series · The Quiet Machine · Part 3

Passwordless SSH Into Windows, Then Lock It Down

The box sits in the corner, no monitor, no keyboard, set to auto-power-on after an outage. It comes back to the Windows lock screen, waiting for a PIN nobody is going to type. If the only way in is after a human logs in, you don’t have a server. You have an expensive space heater with a wallpaper.

The goal was to make the box answerable over SSH before anyone logs in (full remote control of a locked machine, no Windows password anywhere in the loop), and then to lock that capability down so hard that leaving it open doesn’t cost me anything.

OpenSSH is a Windows feature now

The pleasant surprise: you don’t install a third-party thing anymore. OpenSSH Server ships with Windows 11 as an on-demand capability, and it runs as a real service.

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Set-Service -Name sshd -StartupType Automatic
Start-Service sshd

With StartupType Automatic set, sshd comes up at boot, at the service layer, before the interactive login, so the box can sit at the lock screen with nobody signed in and SSH still answers. I get a shell on a locked machine that hasn’t seen a human since the power came back.

The same split as before: the Mac still talks to me and to Claude, and the Windows box is just the always-on thing I reach into.

The two gotchas that ate an evening

I generated a key on the Mac (ssh-keygen -t ed25519), copied the public half over, and got a password prompt back. The key was ignored, silently: no error, no log line that meant anything. Two traps, both Windows-specific, both undocumented where you’d look.

Gotcha 1: admin accounts don’t use ~/.ssh

If the account is an administrator, OpenSSH on Windows doesn’t read C:\Users\you\.ssh\authorized_keys. It reads one shared file instead:

C:\ProgramData\ssh\administrators_authorized_keys

And it won’t trust that file unless the ACL is locked down: owned by Administrators and SYSTEM, no inheritance:

icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r
icacls C:\ProgramData\ssh\administrators_authorized_keys /grant Administrators:F /grant SYSTEM:F

Miss the path and key auth fails. Get the path right but skip the ACL and it fails just as quietly.

Gotcha 2: the BOM

I wrote the key file from PowerShell and it still wouldn’t authenticate. Looked perfect in every editor. The problem was invisible: PowerShell’s default redirection writes UTF-8 with a byte-order mark, and sshd chokes on those three leading bytes. The fix is forcing plain ASCII:

Set-Content -Path C:\ProgramData\ssh\administrators_authorized_keys `
  -Value $pubkey -Encoding ascii

-Encoding ascii solves it. I lost real time to three bytes I couldn’t see.

The trick that made everything scriptable

Driving PowerShell over an SSH connection is quoting hell: you’re inside a zsh string, inside an ssh argument, inside a PowerShell parser, and every layer wants its own quotes and escapes. One stray " and the command means something else.

The escape hatch is -EncodedCommand: base64-encode the script as UTF-16LE and hand PowerShell the blob. Nothing is left to parse until PowerShell decodes it, so no quoting survives to be misread.

CMD='Get-Service sshd | Select Status,StartType'
ENC=$(printf '%s' "$CMD" | iconv -t UTF-16LE | base64)
ssh box "powershell -EncodedCommand $ENC"

iconv -t UTF-16LE is non-negotiable; -EncodedCommand expects UTF-16, not UTF-8. Once this worked, every remaining step in the build turned into a one-liner run from the Mac.

Locking the door back up

Opening SSH is the easy half. The honest half is admitting I’d widened the attack surface, then narrowing it back down further than it started. Same idea as the BIOS pass: max capability, minimum exposure. In C:\ProgramData\ssh\sshd_config:

PasswordAuthentication no
MaxAuthTries 3
LoginGraceTime 20

PasswordAuthentication no does the most work here: with it gone, LAN brute-force is gone too (there’s no password to guess), and the threat model gets simple: the only way in is the private key, and that key lives in exactly one place, on the Mac. Protect the Mac (FileVault plus a login password) and the box is airtight by transitivity.

Then I refused to let it face the internet at all. The firewall rule scopes sshd to the local subnet:

New-NetFirewallRule -Name sshd -DisplayName "OpenSSH Server" `
  -Enabled True -Direction Inbound -Protocol TCP -Action Allow `
  -LocalPort 22 -RemoteAddress LocalSubnet

RemoteAddress LocalSubnet means answerable from my LAN, invisible to the entire internet: no cloud relay, no bill, and everything stays inside these walls.

Encrypted key, still no typing

A key sitting in plaintext on disk is one stolen laptop away from being someone else’s key. So I put a passphrase on it, then, because I don’t want to type that passphrase forty times a day, handed it to the macOS Keychain:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519

With UseKeychain yes and AddKeysToAgent yes in ~/.ssh/config, the key stays encrypted at rest but unlocks automatically with the login session.

The moment the tooling told me no

I asked the agent doing this work to add those two lines to ~/.ssh/config. It refused. Its own safety hook blocks writes to credential paths, ~/.ssh/ included, so it stopped, told me exactly which line it wouldn’t write, and handed the edit back to me. I typed it in by hand in ten seconds.

That friction was the point. I’ve wired this agent into both machines with a passwordless key to a locked box, so the one thing it should never do quietly is touch how credentials get used. Ten seconds of typing is a fair price for that.

The tradeoff for all of this hardening: the box is airtight, but only from inside my own apartment. I wanted it from a coffee shop too, without punching a single hole in that firewall. That’s what sent me to Tailscale next.