New 05-troubleshooting/networking article covering the case where ssh <alias> fails host-key verification because no Host block exists and the alias resolves via Tailscale MagicDNS to a name with no known_hosts entry (key stored under the IP). Registered in SUMMARY.md and the troubleshooting index.
4.4 KiB
| title | domain | category | tags | status | created | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| SSH Alias Falls Through to MagicDNS — Host-Key Verification Failure (No `Host` Block) | selfhosting | troubleshooting |
|
published | 2026-06-11 | 2026-06-11 |
SSH Alias Falls Through to MagicDNS — Host-Key Verification Failure (No Host Block)
The Problem
You ssh to a host you've reached many times before, but now it dies before any
auth happens:
$ ssh MyMac
ssh_askpass: exec(/usr/libexec/openssh/ssh-askpass): No such file or directory
Host key verification failed.
On a headless box (WSL, a server, a CI runner) there's no askpass binary, so the prompt can't even be shown — SSH just aborts. Connecting by Tailscale IP works fine:
$ ssh user@100.74.124.81 # works
$ ssh MyMac # Host key verification failed
Why It Happens
There is no Host MyMac block in ~/.ssh/config at all — and there never was.
The connection only ever worked by IP, or interactively (where you clicked through
the first-connect yes prompt without noticing).
When no Host block matches, SSH uses the literal argument as the hostname. With
Tailscale MagicDNS, MyMac (or mymac) resolves to the node — so the connection
succeeds — but the host key it presents is checked against known_hosts under the
name mymac, which has no entry. Meanwhile the key you actually trust is stored
under the IP:
$ ssh-keygen -F 100.74.124.81 # found — line 67
$ ssh-keygen -F mymac # nothing
So strict host-key checking has nothing to match, tries to prompt to accept the
"new" key, and on a headless host that prompt fails → Host key verification failed.
Confirm there's no block (and that ssh -G is just echoing defaults):
$ ssh -G MyMac | grep -E '^(hostname|user|port) '
hostname mymac # lowercased literal — NOT an explicit HostName
user youruser # your local username default — not from a block
port 22 # default
If hostname equals the arg you typed (just lowercased) and user is your local
login name, there is no matching Host block.
The Fix
Add an explicit Host block that pins the IP that known_hosts already trusts.
This matches the convention every other host in a Tailscale fleet should follow —
pin the 100.x address, not the MagicDNS name:
Host MyMac mymac
HostName 100.74.124.81
User youruser
IdentityFile ~/.ssh/id_ed25519
Now ssh MyMac resolves to 100.74.124.81, whose key is in known_hosts, and the
check passes with no prompt. Verify non-interactively:
$ ssh -o BatchMode=yes MyMac 'hostname'
mymac.majorlan
BatchMode=yes disables every prompt — if it returns the hostname cleanly, the key
is trusted and a real key authenticated.
Don't over-pin the identity. Run ssh -v user@<IP> true and check the
Will attempt key / accepted-key lines first. A workstation often authenticates
with the default id_ed25519, not a fleet key — if id_ed25519_fleet isn't even
offered, don't put it in the block.
Cleanup: Stale known_hosts Cruft
Drive-by ssh attempts leave junk entries like mymac-2 (auto-suffixed names from
old keys). They never match anything once you pin the IP. Purge them:
$ ssh-keygen -R mymac-2
How to Diagnose This
ssh -o BatchMode=yes <alias> true— if it fails withHost key verification failed(notPermission denied), it's a host-key problem, not auth.ssh -G <alias> | grep -E '^(hostname|user|port) '— ifhostnameis just your typed arg and there's no realHostName, there's noHostblock.ssh-keygen -F <name>vsssh-keygen -F <ip>— find which name actually holds the trusted key. Pin whichever oneknown_hostshas (usually the IP).
Why This Gotcha Is Invisible
It only surfaces on a host with no askpass (headless / WSL / cron). On a desktop,
the first-connect prompt appears, you hit yes, an entry gets written under the
MagicDNS name, and it "just works" — masking the fact that no Host block exists and
the IP-keyed entry is the only durable trust. Move the same config to a headless box
and the missing block becomes a hard failure. Related: SSH only applies Host blocks
by literal pattern match, so connecting by IP also skips them — see Ansible Fails
with Permission Denied While ssh <alias> Works (Host Alias Bypass).