2016-01-29
What SSH identities will be offered to a remote server and when
I've already written an entry on what SSH keys in your .ssh/config will be offered to servers, but it wasn't quite complete and I still managed to confuse myself about this recently. So today I'm going to try to write down in one place more or less everything I know about this.
Assuming that you're using ssh-agent
and you don't have
IdentitiesOnly
set anywhere, the following is what keys
will be offered to the remote server:
- All keys from
ssh-agent
, in the order they were loaded intossh-agent
. - The key from a
-i
argument, if any. - Any key(s) from matching
Host
orMatch
stanzas in.ssh/config
, in the order they are listed (and matched) in the file. Yes, all keys from all matching stanzas;IdentityFile
directives are cumulative, which can be a bit surprising.(If there are no
IdentityFile
matches in.ssh/config
, OpenSSH will fall back to the default.ssh/id_*
keys if they exist.)
(If you aren't using ssh-agent
, only #2 and #3 are applicable
and you can pretty much ignore the rest of this entry.)
If there is a 'IdentitiesOnly yes
' directive in any matching
.ssh/config
stanza (whether it is in a 'Host *
' or a specific
'Host <whatever>
'), the only keys from ssh-agent
that will be
offered in #1 are the keys that would otherwise be offered in both
#2 and #3. Unfortunately IdentitiesOnly
doesn't change the order
that keys are offered in; keys in ssh-agent
are still offered
first (in #1) and in the order they were loaded into ssh-agent
,
not the order that they would be offered in if ssh-agent
wasn't
running.
Where the 'IdentitiesOnly yes
' directive comes from makes no
difference, as you'd pretty much expect. The only difference between
having it in eg 'Hosts *
' versus only (some) specific 'Host
<whatever>
' entries is how many connections it applies to. This
leads to an important observation:
The main effect of a universal
IdentitiesOnly
directive is to make it harmless to load a ton of keys into yourssh-agent
.
OpenSSH servers have a relatively low limit on how many public keys
they will let you offer to them; usually it's six or less (technically
it's a limit on total authentication 'attempts', which can wind up
including eg a regular password). Since OpenSSH normally offers all
keys from your ssh-agent
, loading too many keys into it can cause
authentication problems (how many problems you have depends on how
many places you can authenticate to with the first five or six keys
loaded). Setting a universal 'IdentitiesOnly yes
' means that you
can safely load even host-specific keys into ssh-agent
and still
have everything usable.
(This is the sshd
configuration directive MaxAuthTries
.)
Note that specifying -i
does not help if you're offering too many
keys through ssh-agent
, because the ssh-agent
keys are offered
first. You must enable IdentitiesOnly
as well, either in .ssh/config
or as a command line option. Even this may not be a complete cure
if your .ssh/config
enables too many IdentityFile
directives
and those keys are loaded into ssh-agent
so that they get offered
first.
If the key for -i
is loaded into your ssh-agent
, OpenSSH will
use the ssh-agent
version for authentication. This will cause a
confirmation check if the key was loaded with 'ssh-add -c
' (and
yes, this still happens even if the -i
key is unencrypted).
(ssh-agent
confirmation checks only happen when the key is
about to be used to authenticate you, not when it is initially
offered to the server.)
PS: you can see what keys you're going to be offering in what order
with 'ssh -v -v ...
'. Look for the 'debug2: key: ...' lines, and
also 'debug1: Offering ...' lines. Note that keys with paths and
marked 'explicit
' may still come from ssh-agent
; that explicit
path just means that they're known through an IdentityFile
directive.
Sidebar: the drawback of a universal IdentitiesOnly
The short version is 'agent forwarding from elsewhere'. Suppose
that you are on machine A, with a ssh-agent
collection of keys,
and you log into machine B with agent forwarding (for whatever
reason). If machine B is set up with
up with universal IdentitiesOnly
, you will be totally unable to
use any ssh-agent
keys that machine B doesn't know about. This
can sort of defeat the purpose of agent forwarding.
There is a potential half way around this, which is that IdentityFile
can be used without the private key file. Given a stanza:
Host * IdentitiesOnly yes IdentityFile /u/cks/.ssh/ids/key-ed2
If you have a key-ed2.pub
file but no key-ed2
private key file,
this key will still be offered to servers. If you have key-ed2
loaded into your ssh-agent
through some alternate path, SSH
can authenticate you to the remote server; otherwise ssh
will
offer the key, have it accepted by the server, and then discover
that it can't authenticate with it because there is no private key.
SSH will continue to try any remaining authentication methods,
including more identities.
(This is the inverse of how SSH only needs you to decrypt private keys when it's about to use them.)
However, this causes SSH to offer the key all the time, using up
some of your MaxAuthTries
even in situations where the key is not
usable. Unfortunately, as far as I can tell there is no way to tell
SSH 'offer this key only if ssh-agent
supports it', which is what
we really want here.