You need to use SSH or scp in a script and would like to do so without using a password.
Or you’re using them in a cron job and can’t have a password.*
There are two ways to use SSH without a password, the wrong way and the right
way.
The wrong way is to use a public-key that is not encrypted by a passphrase.
The right way is to use a passphrase protected public-key with ssh-agent or keychain.
We assume you are using OpenSSH; if not, consult your documentation (the commands and files will be similar).
First, you need to create a key pair if you don’t already have one.
Only one key pair is necessary to authenticate you to as many machines as you configure, but you may decide to use more than one key pair, perhaps for personal and work reasons.
The pair consists of a private key that you should protect at all costs, and a public key (*.pub) that you can post on a billboard if you like.
The two are related in a complex mathematical way such that they can identify each other, but you can’t derive one from the other.
Use ssh-keygen (might be ssh-keygen2 if you’re not using OpenSSH) to create a key pair. -t is mandatory and its arguments are rsa or dsa. -b is optional and specifies the number of bits in the new key (1024 is the default at the time of this writing) .
-C allows you to specify a comment, but it defaults to user@hostname if you omit it.
We recommend at least using -t dsa -b 2048 and we recommend strongly against using no passphrase.
ssh-keygen also allows you to change your key file’s passphrase or comment.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
$ ssh-keygen You must specify a key type (-t). Usage: ssh-keygen [options] Options: -b bits Number of bits in the key to create. -c Change comment in private and public key files. -e Convert OpenSSH to IETF SECSH key file. -f filename Filename of the key file. -g Use generic DNS resource record format. -i Convert IETF SECSH to OpenSSH key file. -l Show fingerprint of key file. -p Change passphrase of private key file. -q Quiet. -y Read private key file and print public key. -t type Specify type of key to create. -B Show bubblebabble digest of key file. -H Hash names in known_hosts file -F hostname Find hostname in known hosts file -C comment Provide new comment. -N phrase Provide new passphrase. -P phrase Provide old passphrase. -r hostname Print DNS resource record. -G file Generate candidates for DH-GEX moduli -T file Screen candidates for DH-GEX moduli $ ssh-keygen -t dsa -b 2048 -C 'This is my new key' Generating public/private dsa key pair. Enter file in which to save the key (/home/jp/.ssh/id_dsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/jp/.ssh/id_dsa. Your public key has been saved in /home/jp/.ssh/id_dsa.pub. The key fingerprint is: 84:6f:45:fc:08:3b:ce:b2:4f:2e:f3:5e:b6:9f:65:63 This is my new key $ ls -l id_dsa* -rw------- 1 jp jp 1264 Dec 13 23:39 id_dsa -rw-r--r-- 1 jp jp 1120 Dec 13 23:39 id_dsa.pub $ cat id_dsa.pub ssh-dss AAAAB3NzaC1kc3MAAAEBANpgvvTslst2m0ZJA0ayhh1Mqa3aWwU3kfv0m9+myFZ9veFsxM7IVxIjWfAlQh3jp lY+Q78fMzCTiG+ZrGZYn8adZ9yg5/ wAC03KXm2vKt8LfTx6I+qkMR7v15NI7tZyhxGah5qHNehReFWLuk7JXCtRrzRvWMdsHc/ L2SA1Y4fJ9Y9FfVlBdE1Er+ZIuc5xIlO6D1HFjKjt3wjbAal+oJxwZJaupZ0Q7N47uwMslmc5ELQBRNDsaoqF RKlerZASPQ5P+AH/+Cxa/fCGYwsogXSJJ0H5S7+QJJHFze35YZI/ +A1D3BIa4JBf1KvtoaFr5bMdhVAkChdAdMjo96xhbdEAAAAVAJSKzCEsrUo3KAvyUO8KVD6e0B/NAAAA/3u/ Ax2TIB/M9MmPqjeH67Mh5Y5NaVWuMqwebDIXuvKQQDMUU4EPjRGmS89Hl8UKAN0Cq/C1T+OGzn4zrbE06CO/ Sm3SRMP24HyIbElhlWV49sfLR05Qmh9fRl1s7ZdcUrxkDkr2J6on5cMVB9M2nIl90IhRVLd5RxP01u81yqvhv E61ORdA6IMjzXcQ8ebuD2R733O37oGFD7e2O7DaabKKkHZIduL/zFbQkzMDK6uAMP8ylRJN0fUsqIhHhtc// 16OT2H6nMU09MccxZTFUfqF8xIOndElP6um4jXYk5Q30i/CtU3TZyvNeWVwyGwDi4wg2jeVe0YHU2Rh/ ZcZpwAAAQEAv2O86701U9sIuRijp8sO4h13eZrsE5rdn6aul/mkm+xAlO+WQeDXR/ ONm9BwVSrNEmIJB74tEJL3qQTMEFoCoN9Kp00Ya7Qt8n4gZ0vcZlI5u+cgyd1mKaggS2SnoorsRlb2Lh/ Hpe6mXus8pUTf5QT8apgXM3TgFsLDT+3rCt40IdGCZLaP+UDBuNUSKfFwCru6uGoXEwxaL08Nv1wZOc19qrc0 Yzp7i33m6i3a0Z9Pu+TPHqYC74QmBbWq8U9DAo+7yhRIhq/ fdJzk3vIKSLbCxg4PbMwx2Qfh4dLk+L7wOasKnl5//W+RWBUrOlaZ1ZP1/azsK0Ncygno/0F1ew== This is my new key |
Once you have a key pair, add your public key to the ~/.ssh/authorized_keys file in your home directory on any other machines to which you wish to connect using this key pair.
You can use scp, cp with a floppy or USB key, or simple cut-and-paste from terminal sessions to do that.
The important part is that it all ends up on a single line.
While you can do it all in one command (e.g., scp id_dsa.pub remote_host:.ssh/
authorized_keys), we don’t recommend that even when you’re “absolutely sure” that authorized_keys doesn’t exist.
Instead, you can use a slightly more complicated but much safer command, shown in bold:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ ssh remote_host "echo $(cat ~/.ssh/id_dsa.pub) >> ~/.ssh/authorized_keys" jp@remote_host's password: $ ssh remote_host Last login: Thu Dec 14 00:02:52 2006 from openbsd.jpsdomai NetBSD 2.0.2 (GENERIC) #0: Wed Mar 23 08:53:42 UTC 2005 Welcome to NetBSD! -bash-3.00$ exit logout Connection to remote_host closed. |
As you can see, we were prompted for a password for the initial scp, but after that ssh just worked.
What isn’t shown above is the use of the ssh-agent, which cached the passphrase to the key so that we didn’t have to type it.
The command above also assumes that ~/.ssh exists on both machines.
If not, create it using mkdir -m 0700 -p ~/.ssh.
Your ~/.ssh directory must be mode 0700 or OpenSSH will complain.
It’s not a bad idea to use chmod 0600 ~/.ssh/authorized_keys as well.
It’s also worth noting that we’ve just set up a one-way relationship.
We can SSH from our local host to our remote host with no password, but the same is not true in reverse, due to both lack of the private key and lack of the agent on the remote host.
You can simply copy your private key all over the place to enable a “web of passwordless SSH,” but that complicates matters when you want to change your passphrase and it makes it harder to secure your private key.
If possible, you are better off having one well protected and trusted machine from which you ssh out to remote hosts as needed.
The SSH agent is clever and subtle in its use. We might argue it’s too clever.
The way it is intended to be used in practice is via an eval and command substitution: eval ssh-agent
.
That creates two environment variables so that ssh or scp can find the agent and ask it about your identities.
That’s very slick, and it’s well documented in many places.
The only problem is that this is unlike any other program in common use and
is totally obtuse to a new or uninformed user.
If you just run the agent, it prints out some details and looks like it worked.
And it did, in that it’s now running. But it won’t actually do anything, because the necessary environment variables were never actually set.
We should also mention in passing that the handy -k switch tells the agent to exit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# The Wrong Way to use the Agent # Nothing in the environment $ set | grep SSH $ $ ssh-agent SSH_AUTH_SOCK=/tmp/ssh-bACKp27592/agent.27592; export SSH_AUTH_SOCK; SSH_AGENT_PID=24809; export SSH_AGENT_PID; echo Agent pid 24809; # Still nothing $ set | grep SSH $ # Can't even kill it, because -k needs $SSH_AGENT_PID $ ssh-agent -k SSH_AGENT_PID not set, cannot kill agent # Is it even running? Yes $ ps x PID TT STAT TIME COMMAND 24809 ?? Is 0:00.01 ssh-agent 22903 p0 I 0:03.05 -bash (bash) 11303 p0 R+ 0:00.00 ps -x $ kill 24809 $ ps x PID TT STAT TIME COMMAND 22903 p0 I 0:03.06 -bash (bash) 30542 p0 R+ 0:00.00 ps -x # Still the Wrong Way to use the Agent This is correct $ eval `ssh-agent` Agent pid 21642 # Hey, it worked! $ set | grep SSH SSH_AGENT_PID=21642 SSH_AUTH_SOCK=/tmp/ssh-ZfEsa28724/agent.28724 # Kill it - The wrong way $ ssh-agent -k unset SSH_AUTH_SOCK; unset SSH_AGENT_PID; echo Agent pid 21642 killed; # Oops, the process is dead but it didn't clean up after itself $ set | grep SSH SSH_AGENT_PID=21642 SSH_AUTH_SOCK=/tmp/ssh-ZfEsa28724/agent.28724 # The Right Way to use the Agent $ eval `ssh-agent` Agent pid 19330 $ set | grep SSH SSH_AGENT_PID=19330 SSH_AUTH_SOCK=/tmp/ssh-fwxMfj4987/agent.4987 $ eval `ssh-agent -k` Agent pid 19330 killed $ set | grep SSH $ |
Intuitive isn’t it? Not. Very slick, very efficient, very subtle, yes. User friendly, not so much.
OK, so once we have the agent running as expected we have to load our identities
using the ssh-add command.
That’s very easy, we just run it, optionally with a list of key files to load.
It will prompt for all the passphrases needed.
In this example we did not list any keys, so it just used the default as set in the main SSH configuration file:
1 2 3 |
$ ssh-add Enter passphrase for /home/jp/.ssh/id_dsa: Identity added: /home/jp/.ssh/id_dsa (/home/jp/.ssh/id_dsa) |
So now we can use SSH interactively, in this particular shell session, to log in to any machine we’ve previously configured, without a password or passphrase.
So what about other sessions, scripts, or cron?
Use Daniel Robbins’ keychain (http://www.gentoo.org/proj/en/keychain/) script, which:
1 2 3 4 5 6 7 8 9 |
[acts] as a front-end to ssh-agent, allowing you to easily have one long-running sshagent process per system, rather than per login session. This dramatically reduces the number of times you need to enter your passphrase from once per new login session to once every time your local machine is rebooted. [...] keychain also provides a clean, secure way for cron jobs to take advantage of RSA/DSA keys without having to use insecure unencrypted private keys. |
keychain is a clever, well-written and well-commented shell script that automates and manages the otherwise tedious process of exporting variables
we discussed above into other sessions.
It also makes them available to scripts and cron.
But you’re probably saying to yourself, wait a second here, you want me to leave all my keys in this thing forever, until the machine reboots?
Well, yes, but it’s not as bad as it sounds.
First of all, you can always kill it, though that will also prevent scripts or cron from using it.
Second, there is a –clean option that flushes cached keys when you log in.
Sound backward? It actually makes sense.
Here are the details, from keychain’s author (first published by IBM developerWorks at http://www.ibm.com/ developerworks/, seehttp://www.ibm.com/developerworks/linux/library/l-keyc2/):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
I explained that using unencrypted private keys is a dangerous practice, because it allows someone to steal your private key and use it to log in to your remote accounts from any other system without supplying a password. Well, while keychain isn’t vulnerable to this kind of abuse (as long as you use encrypted private keys, that is), there is a potentially exploitable weakness directly related to the fact that keychain makes it so easy to “hook in” to a long-running ssh-agent process. What would happen, I thought, if some intruder were somehow able to figure out my password or passphrase and log into my local system? If they were somehow able to log in under my username, keychain would grant them instant access to my decrypted private keys, making it a no-brainer for them to access my other accounts. Now, before I continue, let’s put this security threat in perspective. If some malicious user were somehow able to log in as me, keychain would indeed allow them to access my remote accounts. Yet, even so, it would be very difficult for the intruder to steal my decrypted private keys since they are still encrypted on disk. Also, gaining access to my private keys would require a user to actually log in as me, not just read files in my directory. So, abusing ssh-agent would be a much more difficult task than simply stealing an unencrypted private key, which only requires that an intruder somehow gain access to my files in ~/.ssh, whether logged in as me or not. Nevertheless, if an intruder were successfully able to log in as me, they could do quite a bit of additional damage by using my decrypted private keys. So, if you happen to be using keychain on a server that you don’t log into very often or don’t actively monitor for security breaches, then consider using the --clear option to provide an additional layer of security. The --clear option allows you to tell keychain to assume that every new login to your account should be considered a potential security breach until proven otherwise. When you start keychain with the --clear option, keychain immediately flushes all your private keys from ssh-agent’s cache when you log in, before performing its normal duties. Thus, if you’re an intruder, keychain will prompt you for passphrases rather than giving you access to your existing set of cached keys. However, even though this enhances security, it does make things a bit more inconvenient and very similar to running ssh-agent all by itself, without keychain. Here, as is often the case, one can opt for greater security or greater convenience, but not both. Despite this, using keychain with --clear still has advantages over using ssh-agent all by itself; remember, when you use keychain --clear, your cron jobs and scripts will still be able to establish passwordless connections; this is because your private keys are flushed at login, not logout. Since a logout from the system does not constitute a potential security breach, there’s no reason for keychain to respond by flushing sshagent’s keys. Thus, the --clear option is an ideal choice for infrequently accessed servers that need to perform occasional secure copying tasks, such as backup servers, firewalls, and routers. |
To actually use the keychain-wrapped ssh-agent from a script or cron, simply source the file keychain creates from your script.
keychain can also handle GPG keys:
1 2 |
[ -r ~/.ssh-agent ] && source ~/.ssh-agent \ || { echo "keychain not runnin" >&2 ; exit 1; } |
When using SSH in a script, you don’t want to be prompted to authenticate or have
extraneous warnings displayed.
The -q option will turn on quiet mode and suppress warnings, while -o ‘BatchMode yes’ will prevent user prompts.
Obviously if there is no way for SSH to authenticate itself, it will fail, since it can’t even fall back to prompting for a password.
But that shouldn’t be a problem since you’ve made it this far in this recipe.
SSH is an amazing, wonderful tool and there is a lot to it, so much that it fills another book about this size.
We highly recommend SSH, The Secure Shell: The Definitive Guide by Richard E. Silverman and Daniel J. Barrett (O’Reilly) and for everything you ever wanted to know (and more) about SSH.
Using public keys between OpenSSH and SSH2 Server from SSH Communications
Security can be tricky; see Chapter 6 in Linux Security Cookbook by Daniel J. Barrett et al. (O’Reilly).
The IBM developerWorks articles on SSH by keychain author (and Gentoo Chief
Architect)
Daniel Robbins are also a great reference (http://www.ibm.com/ developerworks/linux/library/l keyc.htmhttp://www.ibm.com/developerworks/linux/
library/l-keyc2/, http://www.ibm.com/developerworks/linux/library/l-keyc3/).
If keychain doesn’t seem to be working, or if it works for a while then seems to stop,
you may have another script somewhere else re-running ssh-agent and getting things out of sync.
Check the following and make sure the PIDs and socket all agree.
Depending on your operating system, you may have to adjust your ps command; if
-ef doesn’t work, try -eu.
1 2 3 4 5 6 7 8 9 10 |
$ ps -ef | grep [s]sh-agent jp 17364 0.0 0.0 3312 1132 ? S Dec16 0:00 ssh-agent $ cat ~/.keychain/$HOSTNAME-sh SSH_AUTH_SOCK=/tmp/ssh-UJc17363/agent.17363; export SSH_AUTH_SOCK; SSH_AGENT_PID=17364; export SSH_AGENT_PID; $ set | grep SSH_A SSH_AGENT_PID=17364 SSH_AUTH_SOCK=/tmp/ssh-UJc17363/agent.17363 |