A user on my Linux User Group's mailing list recently asked how to use scp to securely copy files from one machine to another, without having to type their password.
I can think of two reasons you would want to do this, and each reason involves its own solution to the problem. However, I'm a firm believer in the idea the people should know how things work. I would highly recommend O'Reilly's SSH book, especially since that's how I learned a lot of this stuff myself.
There are currently two versions of the ssh protocol in use, called (surprisingly enough) ssh1 and ssh2. The ssh1 protocol can be successfully attacked through its connection setup protocol and is therefore not commonly used anymore. The ssh2 protocol has a more robust connection-setup protocol, and is also more flexible.
There are two commonly used packages for the ssh2 protocol- the commercial version, from ssh.com, and OpenSSH, which is more commonly used (probably because it's free.) OpenSSH is included with most Linux distributions.
The rest of this document will assume that you are using OpenSSH.
The ssh2 protocol is built on a two-layer model. A "connection" or "tunnel" is a TCP/IP socket between an ssh client program and an ssh server program. The packets being sent through the connection are encrypted with a form of shared-key cryptography, using a random key which is generated for each new connection and thrown away when that connection is over. The client and the server use public-key cryptography to agree on the session key, and either party may request a re-keying of the session at any time.
Each tunnel contains one or more "channels". A "channel" is a connection (between programs) within the connection (between machines). For example, a simple "remote login" ssh connection will have one channel inside of the connection. If you are using X11 forwarding, a second channel within the same connection is used to carry the X11 packets, separate from your remote login session.
When an ssh client connects to an ssh server, the client requests one or more sessions, each of which uses a channel. The server uses the type code to determine what program(s) to run in order to provide the services which the client has requested. For example, the "server-session" type will cause sshd to run your login shell over the conenction.
A channel can also be used for "port forwarding", where packets sent to a given port number on the local machine are transferred through the tunnel and appear on the server as if they had originated on the server itself.
The whole reason for running a server is to provide access to one or more services. However, you obviously don't want to allow just anybody to access it. When a server accepts an incoming connection, the first thing it does (after setting up the encryption) is verifies that the client is authorized to access the service(s) they're asking for.
There are several ways that a client can prove they are who they say they are. The simplest is by using a password, but other methods exist. The most common method (other than passwords) is by using keys to authenticate.
The idea behind public key cryptography is that you generate a pair of keys, which are matched in such a way that a message encrypted with one key can only be decrypted with the other key. You can give the "public key" to anybody in the world, and this enables them to encrypt a message so that only you can read it. By using other peoples' public keys, you can send messages that only they can read.
By generating a key pair and installing the public key on the server, you can use the secret key to prove that you are who you claim to be. It works like this:
The ssh-keygen program is used to create key pairs. SSH key pairs are stored in two separate files, usually in the .ssh directory in your home directory.
To generate a key pair, run this command on the machine you will be using as the client (i.e. your workstation):
(user@client) > ssh-keygen -t dsa -b 1024 -f id_dsa_something -C 'Some comment'
This creates a DSA key with 1024 bits of random key material, stores the secret key in a file called "id_dsa_something", and stores the public key in a file called "id_dsa_something.pub" with the indicated comment. The key length, 1024 bits, is currently the "standard" length. I have used keys with 2048 bits, and the ssh-keygen program will accept anything as high as 32768 bits. 4096 bits is considered "military grade", anything above that is probably overkill, since the key isn't protecting the session traffic, only the negotiations for the session key.
After the program has generated the keys it will ask for a passphrase. This passphrase encrypts the contents of the secret key file, so that if somebody else managed to get a copy of your key file, they can't use it unless they know your passphrase.
You notice that it's called a passphrase instead of a password. The reason is that if you use a passphrase, it should be longer and harder to guess than a standard password.
If you are generating a key pair which will be used by a program, you should not put a passphrase on the key (i.e. when it asks, just press ENTER.). However, if you do this, you should generate and install new key pairs periodically, especially if other people have access to the system where the secret key is stored, just on the off chance somebody manages to get a copy of the secret key file.
In order to use the key to access a server, a copy of the public key must
be installed on the server, in the
.ssh/authorized_keys2
file in the home directory of
the userid you will be accessing on the server. This is required no matter
what kind of sesion you will be using.
Of course, you must have some method of putting the file there in the first place. This example assumes that you also have password access to the account:
(user@client) > scp id_dsa_something.pub user@server:
user@server's password:
id_dsa_something.pub |********************| 0 0:00
(user@client) > ssh user@server:
user@server's password:
(user@server) > cat id_dsa_something.pub >>
.ssh/authorized_keys2
(user@server) > chmod 600 .ssh/authorized_keys2
(user@server) > exit
This is not the only way to get the public key file into place. You could also copy it on a floppy disk, or email it to the system administrator and have them install it for you (remember this is the PUBLIC key, there is no security risk in sending it via normal email.)
Note that you can have multiple keys listed in the
.ssh/authorized_hosts2
file- this is why the public
keys have comments at the end, so you can easily tell which line in the
file corresponds to which key.
You can test it with a command like this:
(user@client) > ssh -o 'IdentityFile2 id_dsa_something'
user@server
Enter passphrase for key 'id_dsa_something':
(user@server) >
There may be times when programs (such as backup scripts) need to securely copy files from one machine to another. The scp and sftp programs were designed for copying files from one machine to another. They work by opening an ssh connection and requesting the sftp service instead of the normal server-session service used for a remote login shell session.
To use these programs without having to enter a password, you can use a key to authenticate. Of course you have to install the public key on the server first (see above.)
This is an example of how to specify a key to use with scp.
(user@client) > scp -o 'IdentityFile2 id_dsa_something' files-to-copy user@server:
This is an example of how to specify a key to use with sftp.
(user@client) > sftp -o 'IdentityFile2 id_dsa_something'
user@server
sftp>
You may wish to set up a certain key to be able to log into the server, but only for certain things- a backup script, an rdist server, whatever. It's possible to add a "forced command" to the authorized_keys file, so that whenever somebody authenticates with a given key, it runs the forced command no matter what- even if the client asked it to run something else.
To set this up, add command="....."
at the beginning
of the line containing the public key.
An agent is a program which stays in memory on your workstation, holds copies of one or more secret keys, and uses those keys to sign the random numbers sent by the ssh server. SSH client programs (ssh, scp, sftp) are written to use the keys stored in an agent, if one is present, and if one of the keys stored in the agent is able to satisfy the authentication request.
These programs find the key by looking for the environment variable
SSH_AUTH_SOCK
. If present, this environment variable
will contain the filename of a unix socket which is used to request
authentication.
There is a feature of ssh called agent forwarding, where you can run an agent program on your workstation, and all of your ssh connections have access to the same agent.
The process works like this:
You start up an agent program on your workstation.
(user@client) > eval `ssh-agent`
The syntax is a little strange: the ssh-agent program sends commands as output. The backticks cause the "ssh-agent" command to run first, and the output from that command (which is a a series of commands) is added to the "real" command line (after the "eval" command.) the "eval" command causes these commands to be run as if they had been typed at the current command prompt.
The output from this command is a set of commands to set the
SSH_AUTH_SOCK
and SSH_AGENT_PID
environment variables.
Once the agent is running you add your key to the agent.
(user@client) > ssh-add id_dsa_something
Enter passphrase for key 'id_dsa_something':
(user@client) > ssh-add -l
1024 18:f5:a0:41:ea:5a:c2:40:56:59:13:95:f5:4b:60:24 .ssh/id_dsa_test (DSA)
(user@client) >
You then connect to server A, which has your key set up in the authorized_keys2 file. During the authentication process, the server chooses a random number.
An ssh client normally asks for your passphrase, uses that to decrypt the secret key, and uses that key to sign the random number. Since you have an agent, the ssh client passes this random number to the agent. The agent, which already has the decrypted key in memory, uses the key to sign the number and passes the result back to the client, who then passes that result along to the server.
(user@client) > ssh servera
(user@servera) >
Because we're doing agent forwarding, the ssh client requests a second
session within the connection and uses this to pass authentication
requests back to the agent running on your workstation. The ssh server
creates a unix socket and connects it to its end of that channel, and
sets up an SSH_AUTH_SOCK
environment variable for
your shell.
From server A, you then ssh to server B, which also has your key set
up in the authorized_keys2 file. Because the shell on server A has an
SSH_AUTH_SOCK
environment variable, the ssh client on
server A uses this socket to pass server B's random number along to the
agent running on your workstation, and when that answer comes back from
the agent it passes it back along to server B.
(user@servera) > ssh serverb
(user@serverb) >
As long as the various clients and servers are all configured to allow agent forwarding, this "chain" can go on as long as you need. At the ISPs where I used to work, I had one key on my workstation and all of the servers had this key in my userid's .ssh/authorized_keys2 file. I was able to run the agent on my workstation, and didn't need to enter passwords or passphrases to get into any of the servers, or to copy files between the various servers when needed.
While this sounds like a good thing, when writing programs that manipulate
or use keys you have to remember that the agent connection is there. I
wrote several programs which copied files between machines (backup
scripts, automated dns propogation scripts, and so forth) and while
testing these scripts, the agent interfered with the keys I was specifying
on the command lines. I ended up having to add an instruction into these
programs to delete the SSH_AUTH_SOCK
environment
variable before running any external commands (such as ssh or scp.)
On many systems, the process of starting up the agent is already done for you when you log into the system. You can check whether or not this is the case by checking for the existence of an environment variable called SSH_AUTH_SOCK. For example, this session has an agent available...
(user@client) > echo $SSH_AUTH_SOCK
/tmp/ssh-ArQve20135/agent.20135
(user@client) >
and this session does not.
(user@client) > echo $SSH_AUTH_SOCK
(user@client) >
If you don't already have an agent running, you can use the commands listed in the section above to start one up. Coming soon... a script to automate this for systems which don't automatically do it for you.
It is possible to keep a transcript of the contents of an SSH session, by combining ssh or sshd with other programs which most *nix-like systems have already. http://www.jms1.net/ssh-record.shtml explains how to do it, both from the client end and from the server end, depending on your needs.