majorwiki/05-troubleshooting/networking/ssh-missing-host-block-magicdns-host-key-failure.md
majorlinux 950759da52 wiki: add MagicDNS-names-vs-pinned-IPs Tailscale SSH article
New troubleshooting/networking article covering the three SSH failure modes
after a fleet migration (stale hardcoded IP, Tailscale 1.98.x cold-path
teardown, rebuilt-box host-key mismatch) and the durable fix (MagicDNS names +
known_hosts purge + ConnectTimeout), with the WSL2 no-resolver caveat.
Cross-links the existing host-key article (adds a 'when pinning the IP is
wrong' callout) and adds the SUMMARY nav entry.
2026-06-12 01:33:31 -04:00

4.9 KiB

title domain category tags status created updated
SSH Alias Falls Through to MagicDNS — Host-Key Verification Failure (No `Host` Block) selfhosting troubleshooting
ssh
ssh-config
tailscale
magicdns
known-hosts
host-key
troubleshooting
published 2026-06-11 2026-06-12

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

[!note] When pinning the IP is the wrong call Pinning the IP is right while the host is stable. If the box gets migrated or rebuilt — new Tailscale IP and new host key — the pin rots and known_hosts mismatches. At that point switch to MagicDNS names so the alias self-heals. See MagicDNS Names vs Pinned IPs for Tailscale SSH (After a Fleet Migration).

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

  1. ssh -o BatchMode=yes <alias> true — if it fails with Host key verification failed (not Permission denied), it's a host-key problem, not auth.
  2. ssh -G <alias> | grep -E '^(hostname|user|port) ' — if hostname is just your typed arg and there's no real HostName, there's no Host block.
  3. ssh-keygen -F <name> vs ssh-keygen -F <ip> — find which name actually holds the trusted key. Pin whichever one known_hosts has (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).