In this chapter we'll delve deeply into some advanced topics:
complex port forwarding, integration of SSH with other applications,
and more. Some interesting features of SSH don't come to the
surface unless examined closely, so we hope you get a lot out of
these case studies. Roll up your sleeves, dive in, and have fun.
11.1. Unattended SSH: Batch or cron Jobs
SSH
isn't only a great interactive tool but also a resource for
automation. Batch scripts,
cron jobs, and other
automated tasks can benefit from the security provided by SSH, but
only if implemented properly. The major challenge is authentication:
how can a client prove its identity when no human is available to
type a password or passphrase? (We'll just write
"password" from now on to mean both.) You must carefully
select an authentication method, and then equally carefully make it
work. Once this infrastructure is established, you must invoke
ssh properly to avoid prompting the user. In this
case study, we discuss the pros and cons of different authentication
methods for operating an SSH client unattended.
Note that any kind of unattended authentication presents a security
problem and requires compromise, and SSH is no exception. Without a
human present when needed to provide credentials (type a password,
provide a thumbprint, etc.), those credentials must be stored
persistently somewhere on the host system. Therefore, an attacker who
compromises the system badly enough can use those credentials to
impersonate the program and gain whatever access it has. Selecting a
technique is a matter of understanding the pros and cons of the
available methods, and picking your preferred poison. If you
can't live with this fact, you shouldn't expect strong
security of unattended remote jobs.
11.1.1. Password Authentication
Rule
number 1: forget
password authentication if you care about the security of your batch
jobs. In order to use password authentication, you must embed the
password within the batch script or put it in a file which the script
reads, etc. Whatever you do, the location of the password will be
obvious to anyone reading the script. We don't recommend this
technique; the public-key methods coming up are more secure.
11.1.2. Public-Key Authentication
In
public-key authentication, a private key is the client's
credentials. Therefore the batch job needs access to the key, which
must be stored where the job can access it. You have three choices of
location for the key, which we discuss separately:
- Store the encrypted key and its passphrase in the filesystem.
- Store a plaintext (unencrypted) private key in the filesystem, so it
doesn't require a passphrase.
- Store the key in an agent, which keeps secrets out of the filesystem
but requires a human to decrypt the key at system boot time.
11.1.2.1. Storing the passphrase in the filesystem
In this technique, you store an
encrypted key and its passphrase in the filesystem so a script can
access them. We don't recommend this method, since you can
store an unencrypted key in the filesystem with the same level of
security (and considerably less complication). In either case, you
rely solely on the filesystem's protections to keep the key
secure. This observation is the rationale for the next technique.
11.1.2.2. Using a plaintext key
A
plaintext or unencrypted key requires no
passphrase. To create one, run
ssh-keygen and
simply press the Return key when prompted for a passphrase (or
similarly, remove the passphrase from an existing key using
ssh-keygen
-p). You can then
supply the key filename on the
ssh command line
using the
-i option, or in the client configuration
file with the
IdentityFile keyword. [
Section 7.4.2, "User Identity"]
Usually plaintext keys are undesirable, equivalent to leaving your
password in a file in your account. They are never a good idea for
interactive logins, since the SSH agent provides the same benefits in
a much more secure fashion. But a plaintext key is a viable option
for automation, since the unattended aspect forces us to rely on some
kind of persistent state in the machine. The filesystem is one
possibility.
Given that the situations of a plaintext key, encrypted key with
stored passphrase, and stored password are in a sense all equivalent,
there are still three reasons to prefer the plaintext key method:
- SSH provides much better control over account use on the server side
with public-key authentication than with password; this is critical
when setting up batch jobs, as we'll discuss shortly.
- All other things being equal, public-key authentication is more
secure than password authentication, since it doesn't expose
the authentication secret to theft by a malicious server.
- It is awkward to supply a password to SSH from another program. SSH
is designed to take passwords from a user only: it doesn't read
them from standard input but directly opens its controlling terminal
to interact with the user. If there is no terminal, it fails with an
error. In order to make this work from a program, you need to use a
pseudo-terminal to interact with SSH (e.g., use a tool like Expect).
Plaintext keys are frightening, though. To steal the key, an attacker
needs to override filesystem protections only once, and this
doesn't necessarily require any fancy hacking: stealing a
single backup tape will do. That's why for most cases, we
recommend the next method.
11.1.2.3. Using an agent
The
ssh-agent
provides another, somewhat less vulnerable method of key storage for
batch jobs. A human invokes an agent and loads the needed keys from
passphrase-protected key files, just once. Thereafter, unattended
jobs use this long-running agent for authentication.
In this case, the keys are still in plaintext but within the memory
space of the running agent rather than in a file on disk. As a matter
of practical cracking, it is more difficult to extract a data
structure from the address space of a running process than to gain
illicit access to a file. Also, this solution avoids the problem of
an intruder's walking off with a backup tape containing the
plaintext key.
Security can still be compromised by overriding filesystem
permissions, though. The agent provides access to its services via a
Unix-domain socket, which appears as a node in the filesystem. Anyone
who can read and write that socket can instruct the agent to sign
authentication requests and thus gain use of the keys. But this
compromise isn't quite so devastating since the attacker
can't get the keys themselves through the agent socket. She
merely gains use of the keys for as long as the agent is running and
as long as she can maintain her compromise of the host.
The agent method does have a down side: the system can't
continue unattended after a reboot. When the host comes up again
automatically, the batch jobs won't have their keys until
someone shows up to restart the agent and provide the passphrases to
load the keys. This is just a cost of the improved security, and you
have a pager, right?
Another bit of complication with the agent method is that you must
arrange for the batch jobs to find the agent. SSH clients locate an
agent via an environment variable pointing to the agent socket, such
as
SSH_AUTH_SOCK
for the SSH1 and OpenSSH agents. [
Section 6.3.2.1, "Single-shell method"] When
you start the agent for batch jobs, you need to record its output
where the jobs can find it. For instance, if the job is a shell
script, you can store the environment values in a file:
$ ssh-agent | head -2 > ~/agent-info
$ cat ~/agent-info
setenv SSH_AUTH_SOCK /tmp/ssh-res/ssh-12327-agent;
setenv SSH_AGENT_PID 12328;
You can add keys to the agent (assuming C shell syntax here):
$ source ~/agent-info
$ ssh-add batch-key
Need passphrase for batch-key (batch job SSH key).
Enter passphrase: **************
then instrument any scripts to set the same values for the
environment variables:
#!/bin/csh
# Source the agent-info file to get access to our ssh-agent.
set agent = ~/agent-info
if (-r $agent) then
source $agent
else
echo "Can't find or read agent file; exiting."
exit 1
endif
# Now use SSH for something...
ssh -q -o 'BatchMode yes' user@remote-server my-job-command
You also need to ensure that the batch jobs (and nobody else!) can
read and write the socket. If there's only one uid using the
agent, the simplest thing to do
is start the agent under that uid (e.g., as root, do
su
<batch_account>
ssh-agent ...). If multiple uids are using the agent, you
must adjust the permissions on the socket and its containing
directory so that these uids can all access it, perhaps using group
permissions.
TIP:
Some operating systems behave oddly with respect to permissions on
Unix-domain sockets. Some versions of Solaris, for example,
completely ignore the modes on a socket, allowing any process at all
full access to it. To protect a socket in such situations, set the
containing directory to forbid access. For example, if the containing
directory is mode 700, only the directory owner may access the
socket. (This assumes there's no other shortcut to the socket
located elsewhere, such as a hard link.)
Using an agent for automation is more complicated and restrictive
than using a plaintext key; however, it is more resistant to attack
and doesn't leave the key on disk and tape where it can be
stolen. Considering that the agent is still vulnerable to being
misused via the filesystem, and that it is intended to run
indefinitely, the advantages of this method are debatable. Still, we
recommend the agent method as the most secure and flexible strategy
for automated SSH usage in a security-conscious environment.
11.1.3. Trusted-Host Authentication
If
security concerns are
relatively light, consider trusted-host authentication for batch
jobs. In this case, the "credentials" are the operating
system's notion of a process's uid: the identity under
which a process is running, which determines what rights it has over
protected objects. An attacker need only manage to get control of a
process running under your uid, to impersonate you to a remote SSH
server. If he breaks root on the client, this is particularly simple,
since root may create processes under any uid. The real crux, though,
is the client host key: if the attacker gets that, he can sign bogus
authentication requests presenting himself as any user at all, and
sshd will believe them.
Trusted-host authentication is in many ways the least secure SSH
authentication method. [
Section 3.4.2.3, "Trusted-host authentication (Rhosts and RhostsRSA)"] It leaves systems
vulnerable to transitive compromise: if an attacker gains access to
an account on host H, she immediately has access to the same account
on all machines that trust H, with no further effort. Also,
trusted-host configuration is limited, fragile, and easy to get
wrong. Public-key authentication affords both greater security and
flexibility, particularly since you can restrict the commands that
may be invoked and the client hosts that may connect, using its
forced commands and other options in the authorization file.
11.1.4. Kerberos
Kerberos-5 [
Section 11.4, "Kerberos and SSH"] contains support for long-running jobs in the
form of
renewable tickets.
While there's no explicit support for these in SSH, a batch job
can be designed to use them. As with agent usage, a human performs an
initial
kinit
to get a TGT for the batch account, using the
-r
switch to request a renewable ticket. Periodically, the batch job
uses
kinit -R
to renew the TGT before it expires. This can be repeated up to the
maximum renewable lifetime of the ticket, typically a few days.
Like trusted-host authentication, however, SSH Kerberos support lacks
the close authorization controls provided by the public-key options.
Even in an installation using Kerberos for user authentication,
it's probably best to use some form of public-key
authentication for unattended jobs instead. For more information on
renewable tickets, see the Kerberos-5 documentation.
11.1.5. General Precautions for Batch Jobs
Regardless of the method you
choose, some extra precautions will help secure your environment.
11.1.5.1. Least-privilege accounts
The account under which the
automated job runs should have only those privileges needed to run
the job, and no more. Don't run every batch job as root just
because it's convenient. Arrange your filesystem and other
protections so the job can run as a less privileged user. Remember
that unattended remote jobs increase the risk of account compromise,
so take the extra trouble to avoid the root account whenever
possible.
11.1.5.2. Separate, locked-down automation accounts
Create accounts that are used solely for automation. Try not to run
system batch jobs in a user account, since you might not be able to
reduce its privileges to the small set necessary to support the job.
In many cases, an automation account doesn't even need to admit
interactive logins. If jobs running under its uid are created
directly by the batch job manager (e.g.,
cron),
the account doesn't need a password and should be locked.
11.1.5.3. Restricted-use keys
As much as possible, restrict the target account to perform only the
work needed for the job. With public-key authentication, automated
jobs should use keys that aren't shared by interactive logins.
Imagine that someday you might need to eliminate the key for security
reasons, and you don't want to affect other users or jobs by
this change. For maximum control, use a separate key for each
automated task. Additionally, place all possible restrictions on the
key by setting options in the authorization file. [
Section 8.2, "Public Key-Based Configuration "] The
command option
restricts the key to running only the needed remote command, and the
from option restricts usage to appropriate client
hosts. Consider always adding the following
options as well, if they don't
interfere with the job:
no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
These make it harder to misuse the key should it be stolen.
If you're using trusted-host authentication, these restrictions
aren't available. In this case, it's best to use a
special shell for the account, which limits the commands that may be
executed. Since
sshd uses the target
account's shell to run any commands on the user's behalf,
this is an effective restriction. One standard tool is the Unix
"
restricted shell." Confusingly, the
restricted shell is usually named "rsh", but has nothing
to do with the Berkeley r-command for opening a remote shell,
rsh.
11.1.5.4. Useful ssh options
When running SSH
commands in a batch job, it's a good idea to use these options:
ssh -q -o 'BatchMode yes'
The
-q option is for
quiet mode, preventing SSH from printing
a variety of warnings. This is sometimes necessary if you're
using SSH as a pipe from one program to another. Otherwise, the SSH
warnings may be interpreted as remote program output and confuse the
local program. [
Section 7.4.15, "Logging and Debugging"]
The
BatchMode
keyword tells SSH not to prompt the user, who in this case
doesn't exist. This makes error reporting more straightforward,
eliminating some confusing SSH messages about failing to access a
tty. [
Section 7.4.5.4, "Batch mode: suppressing prompts"]
11.1.6. Recommendations
Our recommended method for best
security with unattended SSH operation is public-key authentication
with keys stored in an agent. If that isn't feasible,
trusted-host or plaintext-key authentication may be used instead;
your local security concerns and needs will determine which is
preferable, using the foregoing discussion as a guideline.
To the extent possible, use separate accounts and keys for each job.
By doing so, you limit the damage caused by compromising any one
account, or stealing any one key. But of course, there is a
complexity trade-off here; if you have a hundred batch jobs, separate
accounts or keys for each one may be too much to deal with. In that
case, partition the jobs into categories according to the privileges
they need, and use a separate account and/or key for each category of
job.
You can ease the burden of multiple keys by applying a little
automation to the business of loading them. The keys can all be
stored under the same passphrase: a script prompts for the
passphrase, then runs
ssh-add multiple times to
add the various keys. Or they have different passphrases, and the
human inserts a diskette containing the passphrases when loading
them. Perhaps the passphrase list itself is encrypted under a single
password provided by the human. For that matter, the keys themselves
can be kept on the key diskette and not stored on the filesystem at
all: whatever fits your needs and paranoia level.