--- title: "SSH Alias Falls Through to MagicDNS — Host-Key Verification Failure (No `Host` Block)" domain: selfhosting category: troubleshooting tags: - ssh - ssh-config - tailscale - magicdns - known-hosts - host-key - troubleshooting status: published created: 2026-06-11 updated: 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: ```sshconfig 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)](tailscale-ssh-magicdns-vs-pinned-ip-after-migration.md)*. 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@ 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 true` — if it fails with `Host key verification failed` (not `Permission denied`), it's a host-key problem, not auth. 2. `ssh -G | 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 ` vs `ssh-keygen -F ` — 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 ` Works (Host Alias Bypass)*.