SSHFP: SSH host key fingerprint
Publishes an SSH host key fingerprint in DNS. With the zone signed under DNSSEC, a client verifies the fingerprint it gets from DNS instead of trusting the key on first connect.
In one line:
bastion.example.com. SSHFP 4 2 9f8b76e2...
└── this host ──────┘ └type┘ └── has this SSH key fingerprint| Type number | 44 |
| RFC | 4255 (base) · 6594 (ECDSA, SHA-256) · 7479 (Ed25519) |
| RDATA | algorithm · fingerprint type · hex fingerprint |
| Required for trust | DNSSEC: without it, SSHFP authenticates nothing (RFC 4255 §2.4) |
What it holds#
| Field | Codes |
|---|---|
| Algorithm | 1 = RSA · 2 = DSA (disabled by default since OpenSSH 7.0, removed in OpenSSH 10.0) · 3 = ECDSA · 4 = Ed25519 · 6 = Ed448 (RFC 8709) |
| Fingerprint type | 1 = SHA-1 (deprecated) · 2 = SHA-256 |
| Fingerprint | Hex string of the hash |
Code assignments: IANA DNS SSHFP registry. A host typically publishes one SSHFP per key type it offers; the client fingerprints the key the server presents and compares it against whichever record matches.
When to use it#
- Servers that need unattended trust (CI runners, ephemeral instances) without an operator confirming an unknown fingerprint by hand.
- Any host in a zone that runs DNSSEC end to end.
- Fleets where distributing
known_hostsby hand does not scale.
When not to use it#
- Without DNSSEC. RFC 4255 §2.4 requires a client that does not validate the signature itself to reach the validator over a secure channel; an unsigned zone gives it neither, so the record authenticates nothing.
- Single-host setups with one operator:
known_hostsis simpler.
dig example#
Asks for the fingerprints of this host's SSH keys.
dig @1.1.1.1 sshfp.example.isitdns.net SSHFPGO NOERROR and one SSHFP record, algorithm 4 fingerprint type 2
NO GO NOERROR with no answer, which means the host publishes no fingerprints
The demo zone publishes a live SSHFP at sshfp.example.isitdns.net (Ed25519, SHA-256).
DoH query (HTTPS)#
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=sshfp.example.isitdns.net&type=SSHFP' \
| jq '{AD: .AD, answer: .Answer}'{
"AD": true,
"answer": [
{
"name": "sshfp.example.isitdns.net",
"type": 44,
"TTL": 300,
"data": "4 2 ( 0766E147A76CEA6A5F9F098742FDF4E6229977DC84291F9D528990E5BE4A3A2D )"
}
]
}AD: true means Cloudflare's resolver validated the chain for this answer (RFC 4035 §4.9.3); you are trusting its validation, not doing your own.
Gotchas#
- SHA-1 (type 1) is deprecated. Publish SHA-256 (type 2) only, unless very old clients need SHA-1.
- ssh trusts the resolver's AD bit, not its own validation. OpenSSH does not validate DNSSEC itself:
verify_host_key_dns()callsgetrrsetbyname(), which marks a record validated only when the resolver's answer carries the AD bit (OpenSSHopenbsd-compat/getrrsetbyname.c). That bit travels unauthenticated from the resolver to the client, so it only means something if the path to the resolver is trusted, in practice a validating resolver on localhost (RFC 4255 §2.4). VerifyHostKeyDNSdefaults tono.yestrusts a validated match and skips the host-key prompt;askshows the match but still prompts (ssh_config(5)).- Host key rotation drives churn. Every host-key change requires updating SSHFP;
ssh-keygen -r hostname -f keyfileregenerates the records for a key file. - Multiple keys per host. Publish SSHFP for each key type the host offers; the client matches on whichever key the server presents.
- Wildcards skip names that exist. A host with its own A/AAAA gets no SSHFP from
*.example.com(RFC 4592 §2.2.1).