Password-less SSH remote login demystified
This is documented everywhere, and still, I always find myself messing around with this. So once and for all:
The files
In any user’s .ssh/ directory, there should be (among others) two files: id_rsa and id_rsa.pub. Or maybe with dsa instead of rsa. Doesn’t matter too much. These are the keys that are used when you try to login from this account to another host.
id_rsa is the secret key, and is id_rsa.pub is public. The former should be readable only by the user (and root), and the latter by anyone. If anyone has the secret key, he or she may login to whatever host that identifies you with it.
If these files aren’t in .ssh/, they can be generated with ssh-keygen. This should be done once for each new shell account you generate, and maybe even once in a lifetime: It’s much more convenient to copy these files from your old user account, or you’ll have to re-establish the automatic login on each remote server with the new key.
So it goes:
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/eli/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/eli/.ssh/id_rsa. Your public key has been saved in /home/eli/.ssh/id_rsa.pub. The key fingerprint is: 77:7c:bf:4d:3b:a9:8a:e7:56:09:24:03:6f:22:d7:ca eli@myhost.localdomain The key's randomart image is: +--[ RSA 2048]----+ | .. | | oo . | | . o ++ | | + + o | | ES . + o | | . . + . | | . +| | .o ++| | .+o...oo| +-----------------+
The public key file (id_rsa.pub) looks something like this:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAs/ggsf1ZXbvyqQ7NbzIT+UDnGqo1LOgV3PpEUpVt8lw44jDgDCNGXXMZepMVwp3LgcGPKrrZ4n7b9/5zgXVrH86HZVyi+guu0IWLsYA4K+OgQY0m6rmXss/v7lt6ItIZTTJWhgTr4E8DE8+9PibYfBrvdITxdVAVl+FxmDEHhunnMzeqUsTMD7hniEWvlvHE0aE6Gp2rQPMU5sx3+LEGJ4y1BDzChrNa6dc2L7GP1ViGaP9SZBYVFPqbdkdCOOoR6N+FU/VHYIBeK5RdkTkfxGHKHfec1p8sXzveDHT69ouDaw0+c+3j2KlNq4ugnbTGKWrJaQBxQBEzvLgTdePCtQ== eli@myhost.localdomain
Note the eli@myhost.localdomain part at the end. It has no significance crypto-wise. It’s considered a comment. More about it below.
The private (secret) file, id_rsa, looks something like this (I don’t really use it, right? Don’t publish your public key!)
-----BEGIN RSA PRIVATE KEY----- MIIEoQIBAAKCAQEAs/ggsf1ZXbvyqQ7NbzIT+UDnGqo1LOgV3PpEUpVt8lw44jDg DCNGXXMZepMVwp3LgcGPKrrZ4n7b9/5zgXVrH86HZVyi+guu0IWLsYA4K+OgQY0m 6rmXss/v7lt6ItIZTTJWhgTr4E8DE8+9PibYfBrvdITxdVAVl+FxmDEHhunnMzeq UsTMD7hniEWvlvHE0aE6Gp2rQPMU5sx3+LEGJ4y1BDzChrNa6dc2L7GP1ViGaP9S ZBYVFPqbdkdCOOoR6N+FU/VHYIBeK5RdkTkfxGHKHfec1p8sXzveDHT69ouDaw0+ c+3j2KlNq4ugnbTGKWrJaQBxQBEzvLgTdePCtQIBIwKCAQAFJFi0oNaq6BzgQkBi Q0JmNQ3q0am/dFhlZjx3Y1rpqt0NxuHUdglTIIuzC4RHY5gZpnHOBVausyrbMyff IJypIyhwnD8rtzDhYua77bh2SFUJL+srRyGXZQba7Ku32h365C5bmb2YsczjTxQJ Fw1/44MvNv+VozPRI7LJ1YPfSHanPoc77ZvKC/5hsXBgBioIaacO63HNaUeSgIwg WTNo3zjRBGHPsDmNIR0rMT1STlpMQ/2kJ4BzV0HKKc0F6rDazIDTKTXBiziRSKfM ftbayNu0iqCcGJWLvMlTNYB36VXBrb3NcKiFfsx99xIKvtG/UV/Slh7wz/ol2PnP KTmrAoGBAOYpirjibbF2kP2zD6jJi/g6BiKl2xPumzFCEurqgLRWdT5Ns3hbS+F1 c/WhZyCRuYK/ZlQTo7D+FCE9Vft5nsSnZLpOu9kJ2pW4LuAfpNVQCvAcjtRWmMcX dl0pH68/rdfC/oO3oMcUY8tZrJ/4NOD6dUyXZ+Ahjr5lEznFQWhNAoGBAMgsIHQ+ 2s35g6J586msjg1xKUBqkgg88xqdJmSh/kp6krIi7+rGT5so3EOmjw0C6Ks8TVDf C9RR+HuVOj7wNR9XhS4mlxTgnQyWdox8POqK4NBSdNMoqfMs9fqDBLtR9vItTcel 5hKD740ZF4ktaTgG1WMHElYyE0Iq+rJd/3gJAoGAdl6B220ieIYerlwWrpOJ0B3X RQTXEZCnlazzyUVmwyUmWo5cTIa5T2D5zsgJJrFYF1seruWHYlbIhh+LTiFKVoH5 Sd9ZSwxhyVdokIVNdQSX6TNCJA9HQdGNVHuMo0VSFzEVLcwmzMioWfOam2m0y3l+ J2PPBY2Z3kKcLFbRLlMCgYEAvLvkFdTc7hckV10KT4Vv/gub7Ec5OvejYjxmBxxk yeFIfBJP6/zOtt1h9qRa/aOoLGwOYjFi7MJQrwkLCCRPWBCwxR0SGv+qBI3dfSSu dr104azUjJQN8+iQJrYLxo8cCOji73CId9t7dmgdgVazqdqOrdN3sFsZeOax21/w 3uMCgYBa0ZqQiFgL/sYUYysgqCF6N+aL/Nr19tdp/025feZgwG/9Q1196YTUiADn jQzU3vpFpTpMnvTybE/+Zq3nGPXthOnsUBRK0/Lc5I8Ofgc9s9T0YrLwio6FGTAm Hj0oC0CwrDMtSPtm7HOG+wpA4qxO6gf3OkgGzfZccyZjB2NiDQ== -----END RSA PRIVATE KEY-----
How it works
The gory details left aside, the authentication goes like this: When you attempt to log in, your ssh client checks your .ssh/ directory for the key files. If it finds such, it notifies the server that it wants to try these key files, and sends information on the public keys it has.
The server on the remote host looks up the user’s home directory for a .ssh/authorized_keys file. If such exists, it should a line that is identical to the id_rsa.pub file on the client’s side. If such match is found, the server uses the public key to create a challenge for the client. The client, which has the secret key passes this challenge, and the authentication is done.
So .ssh/authorized_keys is just a concatenation of id_rsa.pub files, each line for a different key.
Now to the eli@myhost.localdomain part I mentioned above. It goes into the .ssh/authorized_keys file as well. It’s there to help people, who have several authentication keys for logging in from different computers, to keep track which line in .ssh/authorized_keys belongs to which. Just in case they wanted to delete a line or so.
Important: The home directory’s on the remote host must not be writable by anyone else than the user (and root, of course), or ssh will ignore authorized_keys. In other words, the home directory’s permission can be 0755 for example (viewable by all, but writable by user only) or more restrictive, but if it’s 0775 or 0777, password-less login will not work. Rationale: If someone else can rename and replace your .ssh directory, that someone else can log in to your account.
One can always try
Making the remote host recognize you
There’s a command-line utility for this, namely ssh-copy-id. It merely uses SSH to log into the remote host (this time a password will be required, or why are you doing this?). All it does is to append id_rsa.pub to .ssh/authorized_keys on the remote host. That is, in fact, all that is required.
Alternatively, manually copy the line from id_rsa.pub into .ssh/authorized_keys.
Remember that there is no problem disclosing id_rsa.pub to anyone. It’s really public. It’s the secret file you need to keep to yourself. And it’s quite easy to tell the difference between the two.
Having multiple SSH keys
It’s sometimes required to maintain multiple SSH keys. For example, in order to access Github as multiple users.
First, create a new SSH key pair:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/eli/.ssh/id_rsa): id_rsa_github2
Note that the utility allows choosing a different file name. This is how a new key pair lives side-by-side with the existing one.
The next step is to create (or edit) .ssh/config. This file should have permission mode 0600 (accessible only to user) because it’s sensitive by its nature, but also because ssh may ignore it otherwise. See “man ssh_config”.
Now let’s follow the scenario of multiple keys on Github. Say that .ssh/config reads as follows:
# Github access as second user Host github-amigo HostName github.com User git IdentityFile ~/.ssh/id_rsa_github2
If no entry in the config file matches, ssh uses the default settings. So existing ssh connections remain unaffected. In other words, this impacts only the host name that we’ve just invented. No need to state the default behavior explicitly. No collateral damage.
It’s of course possible to add several entries as shown above.
The setting above means is that ssh now recognizes “github-amigo” as a legit name of a host. If that name is used, ssh will connect with github.com, identify itself as “git” and use the said key.
It’s hence perfectly reasonable to connect with github.com with something like:
$ ssh github-amigo PTY allocation request failed on channel 0 Hi amigouser! You've successfully authenticated, but GitHub does not provide shell access. Connection to github.com closed.
The line in .git/config is accordingly
[remote "github"] url = github-amigo:amigouser/therepository.git fetch = +refs/heads/*:refs/remotes/github/*
In the url, the part before the colon is the name of the host. There is no need to state the user’s name, because ssh fills it in anyhow. After the colon, it’s the name of the repository.
A successful session
If it doesn’t work, the -v flag can be used to get debug info on an ssh session. This is what it looks like when it’s OK. YMMV.
$ ssh -v remotehost.org OpenSSH_5.3p1, OpenSSL 1.0.0b-fips 16 Nov 2010 debug1: Reading configuration data /etc/ssh/ssh_config debug1: Applying options for * debug1: Connecting to remotehost.org [84.200.84.244] port 22. debug1: Connection established. debug1: identity file /home/eli/.ssh/identity type -1 debug1: identity file /home/eli/.ssh/id_rsa type 1 debug1: identity file /home/eli/.ssh/id_dsa type -1 debug1: Remote protocol version 2.0, remote software version OpenSSH_5.3 debug1: match: OpenSSH_5.3 pat OpenSSH* debug1: Enabling compatibility mode for protocol 2.0 debug1: Local version string SSH-2.0-OpenSSH_5.3 debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: server->client aes128-ctr hmac-md5 none debug1: kex: client->server aes128-ctr hmac-md5 none debug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<1024<8192) sent debug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP debug1: SSH2_MSG_KEX_DH_GEX_INIT sent debug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY debug1: checking without port identifier debug1: Host 'remotehost.org' is known and matches the RSA host key. debug1: Found key in /home/eli/.ssh/known_hosts:50 debug1: found matching key w/out port debug1: ssh_rsa_verify: signature correct debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: SSH2_MSG_SERVICE_REQUEST sent debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password debug1: Next authentication method: gssapi-with-mic debug1: Unspecified GSS failure. Minor code may provide more information Credentials cache file '/tmp/krb5cc_1010' not found debug1: Unspecified GSS failure. Minor code may provide more information Credentials cache file '/tmp/krb5cc_1010' not found debug1: Unspecified GSS failure. Minor code may provide more information debug1: Next authentication method: publickey debug1: Offering public key: /home/eli/.ssh/id_rsa debug1: Server accepts key: pkalg ssh-rsa blen 277 debug1: Authentication succeeded (publickey). debug1: channel 0: new [client-session] debug1: Requesting no-more-sessions@openssh.com debug1: Entering interactive session. debug1: Sending environment. debug1: Sending env XMODIFIERS = @im=none debug1: Sending env LANG = en_US.UTF-8
And shell prompt comes next
Reader Comments
Thanks for this post! Two minor additions: I am used to have authorized_keys2 instead of authorized_keys, and it works the same for me. Not sure what is the difference between the two, if any, in terms of functionality. And the second thing is that one can use also ssh -vv and also ssh -vvv to get a higher level of verbosity.
Regards,
Rami Rosen