We now embark on a detailed discussion of SSH server configuration, using both keywords and command-line options. Please keep in mind that modern SSH products are actively developed and their features may change. Be sure to read their documentation for the latest information.
We begin with initial setup decisions, such as: where should important files be kept? What should their permissions be? What TCP/IP settings should be used? Which encryption algorithms should be supported?
sshd expects certain files to exist, containing the server's host key, the random seed, and other data. The server looks for these files in default locations, or you may override them with keywords and command-line options as described later.
Although you may place these files anywhere you like, we strongly recommend keeping them on a local disk on your server machine, not on a remotely mounted disk (e.g., via NFS). This is for security reasons, as NFS will gleefully transmit your sensitive files unencrypted across the network. This would be especially disastrous for the unencrypted private host key!
As a running example, we use an invented directory, /usr/local/ssh, as our preferred (nondefault) location for the SSH server's files.
The host key of sshd uniquely
identifies a server to SSH clients. The host key is stored in a pair
of files, one containing the private key and the other the public
key. OpenSSH has distinct host keys in DSA (/etc/ssh/ssh_host_dsa_key) and RSA
(/etc/ssh/ssh_host_rsa_key)
formats, as well as a legacy SSH-1 protocol key, /etc/ssh/ssh_host_key. These private keys
are readable only by privileged programs such as the SSH server and
clients. Their locations may be changed with the HostKey
keyword:[49]
# OpenSSH HostKey /usr/local/ssh/my_dsa_key HostKey /usr/local/ssh/my_rsa_key HostKey /usr/local/ssh/my_old_ssh1_key
Each private key has a corresponding public key, stored in a second file with the same name but with .pub appended. So, in the above example, the public keys would be /usr/local/ssh/my_dsa_key.pub, /usr/local/ssh/my_rsa_key.pub, and /usr/local/ssh/my_old_ssh1_key.pub.
For Tectia, the default private key file is /etc/ssh2/hostkey if the server is run by
the superuser or ~/.ssh2/hostkey if run by any other user.
To specify a different private key file, use the HostKeyFile
keyword:
# Tectia HostKeyFile /usr/local/ssh/key
The server's public key file, normally /etc/ssh2/hostkey.pub for superusers or
~/.ssh2/hostkey.pub for others,
may be changed independently with the PublicHostKeyFile
keyword:
# Tectia PublicHostKeyFile /usr/local/ssh/pubkey
If you prefer command-line options, sshd supports the -h command-line option to specify the private key file:
$ sshd -h /usr/local/ssh/key
Once again, the public key filename is derived by appending .pub to the private key filename, in this case, /usr/local/ssh/key.pub.
OpenSSH allows each type of host key to be specified with a separate -h option (and detects the type of each key automatically):
# OpenSSH $ sshd -h /usr/local/ssh/my_dsa_key -h /usr/local/ssh/my_rsa_key -h /usr/local/ssh/ my_old_ssh1_key
For Tectia, if the -h
option is repeated,
only the last file is used and all earlier -h
options are ignored. This is consistent with its usual behavior with
command-line options. [5.2.3]
The SSH server generates pseudo-random numbers for cryptographic operations. [3.6.4] It maintains a pool of random data for this purpose, derived either from the operating system if provided (e.g., /dev/random on Linux) or from various bits of changing machine state (e.g., clock time, statistics on resource use by processes, etc.). This pool is called the random seed.
If running on a system with a random-bit source, such as
/dev/urandom, OpenSSH doesn't
create a random seed file. Tectia stores a random seed in /etc/ssh2/random_seed, and the location
may be overridden with the RandomSeedFile
keyword:
# Tectia RandomSeedFile /usr/local/ssh/seed2
The OpenSSH server's pid is stored in /var/run/sshd.pid, and you can override
this location with the PidFile
keyword:
# OpenSSH PidFile /usr/local/ssh/pid
OpenSSH doesn't record the process ID when it runs in debug mode. [5.9]
There is no corresponding keyword for Tectia. Its pid file is
always named /var/run/sshd2_
n .pid, or
if there is no /var/run
directory, /etc/ssh2/sshd2_
n .pid,
where n is the TCP port number of the
server.[50] Since the default port is 22, the default pid file is
sshd2_22.pid. If multiple
sshd2 processes are run simultaneously on
different ports of the same machine, their pid files can be
distinguished by this naming convention. The directory used to store
pid files can be changed by the configure
option --with-piddir
. [4.3.5.1]
The server configuration file is normally /etc/ssh/sshd_config for OpenSSH and /etc/ssh2/sshd2_config for Tectia. An alternative configuration file can be specified with the -f command-line option:
$ sshd -f /usr/local/ssh/config
This is useful when testing a new server configuration: create a new file and instruct sshd to read it. It is also necessary if you are running multiple sshd s on the same machine and want them to operate with different configurations.
Only a single configuration file is read. If you provide multiple -f options, the last one is used and all others are ignored.
Tectia's sshd expects a user's
SSH-related files to be in the directory ~/.ssh2 by default, but this can be
changed with the UserConfigDirectory
keyword. (OpenSSH has
no such capability.) The directory name may be literal, as
in:
# Tectia UserConfigDirectory /usr/local/ssh/my_dir
or it may be specified with printf
-like patterns, as in:
# Tectia UserConfigDirectory %D/.my-ssh
The %D
pattern expands to
the user's home directory. So, the preceding example expands to
~/.my-ssh. The following table
shows the available patterns:
Pattern | Meaning |
---|---|
%D | User's home directory |
%U | User's login name |
%IU | User's uid (Unix user ID) |
%IG | User's gid (Unix group ID) |
If the %
character is
followed by any other characters, it is left unchanged.[51]
For the system administrator, the UserConfigDirectory
keyword provides a
quick way to override all users' Tectia preferences. Specifically,
you can cause sshd to ignore everybody's
~/.ssh2 directories,
substituting your own instead. For instance, the line:
# Tectia UserConfigDirectory /usr/sneaky/ssh/%U
tells sshd to seek the preferences for each user in /usr/sneaky/ssh/ <username> instead of ~/.ssh2. This powerful feature can also be misused if your machine is compromised. If an intruder inserted the following line into sshd2_config:
# Tectia UserConfigDirectory /tmp/hack
and uploaded his own public key file into /tmp/hack, he would gain SSH access to every user's account.
The OpenSSH server expects to find a user's public-key
authorization file in ~/.ssh/authorized_keys. This location can
be changed with the AuthorizedKeysFile
keyword, followed by
the new location:
# OpenSSH AuthorizedKeysFile .ssh/permitted_keys
Filenames can be absolute or are relative to the user's home
directory. Additionally, the location can contain a few special
symbols: %h
to mean the user's
home directory, %u
for the
username, or %%
for a percent
sign. So, when user smith authenticated on a server machine with
this line in /etc/ssh/sshd_config:
# OpenSSH AuthorizedKeysFile /usr/local/access/%u
the authorization filename would expand to /usr/local/access/smith.
The Tectia server uses a different key file layout than
OpenSSH. [6.1.2] Its
authorization file, normally ~/.ssh2/authorization, contains names of
separate public key files, rather than the keys themselves.
sshd can be instructed to find the
authorization file elsewhere via the keyword AuthorizationFile
:
# Tectia AuthorizationFile my_public_keys
Filenames can be absolute or are relative to each user's Tectia configuration (.ssh2) directory. The preceding example specifies the file ~/.ssh2/my_public_keys.
The utmp file (e.g., /var/run/utmp) contains information about users currently logged in, such as their username, tty, and most notably for us, the hostname from which they've logged in (for remote logins). OpenSSH's sshd can limit the length of hostname information written to the utmp file. (It's inspired by a similar feature in the telnet daemon telnetd.)
# OpenSSH
$ sshd -u 25 Limit hostnames to 25 characters or less
If a remote hostname is longer than this limit, the host's IP address will be written instead. Why is this useful? For two reasons:
Hostnames longer than the default length—which may vary on different systems—will normally be truncated in the utmp file. While you cannot increase the utmp length with the -u option, you can notify sshd of the length limitation so that IP addresses get used in place of long hostnames. This way, you'll accurately record the host's identity. See /usr/include/utmp.h to learn the length limit for your system.
If you specify -u0
, IP addresses will
always be used in place of hostnames. This has the side effect
of forcing sshd not to make DNS requests
for these hostname lookups. (It will not entirely suppress DNS,
however, since it might be needed for authentication.)
As security products, OpenSSH and Tectia require certain files and directories on the server machine to be protected from unwanted access. Imagine if your authorized_keys or .rhosts file were world-writable; anyone on that host could modify them and gain convenient access to your account. sshd has several configuration keywords for reducing this risk.
Users aren't always careful to protect important files and directories in their accounts, such as their .rhosts file or personal SSH directory. Such lapses can lead to security holes and compromised accounts. To combat this, you can configure sshd to reject connections to any user account that has unacceptable permissions.
The StrictModes
keyword,
with a value of yes
(the
default), causes sshd to check the permissions
of important files and directories. They must be owned by the
account owner or by root, and group and world write permission must
be disabled. For OpenSSH, StrictModes
checks:
The user's home directory
The user's ~/.rhosts and ~/.shosts file
The user's SSH configuration directory, ~/.ssh
The user's SSH ~/.ssh/authorized_keys file
The user and system "known hosts" files
For Tectia, the list is smaller and is checked only for hostbased authentication:[3.4.3.6]
The user's home directory
The user's ~/.rhosts and ~/.shosts file
If any check fails, the server rejects SSH connection attempts
to the account. If StrictModes
is
given the value no
, these checks
aren't performed:
StrictModes no
However, we strongly suggest you leave these checks enabled.
Tectia recognizes an undocumented keyword, StrictModes.UserDirMaskBits
, to control
the checks more precisely. The value is an octal number representing
the file permission bits that must be disabled. For example, to
require that files grant no group or world access (read, write, or
execute):
# Tectia StrictModes.UserDirMaskBits 077
The default value is 022, indicating that group and world write permission must be disabled.
Even if StrictModes
is
enabled, it can be defeated by using POSIX access control lists
(ACLs), which are supported in Solaris and some other flavors of
Unix, to set file permissions with greater precision.
sshd doesn't check ACLs, so one could argue
that StrictModes
is an incomplete
test.
Since the SSH protocol operates over TCP/IP, sshd permits control over various parameters related to TCP/IP.
By default, sshd listens on TCP
port 22. The port number may be changed with the Port
keyword:
Port 9876
or the -p command-line option:
$ sshd -p 9876
If you repeat the Port
keyword or -p option, OpenSSH listens on all of
the specified ports:
# OpenSSH $ sshd -p 22 -p 9876
Tectia, on the other hand, allows only a single port setting:
if multiple Port
keywords or
-p options are specified, the server uses only
the last one and ignores all earlier instances.[52]
You may also configure sshd to bind its
listening port on a particular network interface. By default, the
port is bound on all active network interfaces on the host. The
ListenAddress
keyword limits
sshd to listen only on specific interfaces; the
default value is 0.0.0.0.
For example, suppose a computer has two Ethernet cards and is attached to two different networks. One interface has the address 192.168.10.23, and the other, 192.168.11.17. By default, sshd listens on both interfaces; therefore, you can reach the server by connecting to port 22 at either address. However, this may not always be what you want; perhaps you want to provide SSH service only to hosts on one network and not the other:
ListenAddress 192.168.10.23
Of course, this represents a real restriction only if the two networks aren't otherwise connected together (say, by a router) so that port 22 on 192.168.10.23 is not reachable from the network 192.168.11.24.
To listen on multiple, specific interfaces, repeat the
ListenAddress
keyword:
ListenAddress 192.168.10.23 ListenAddress 192.168.11.17
For even more precise control, you can also specify the port for listening on a given interface. The syntax differs for OpenSSH and Tectia:
# OpenSSH ListenAddress 192.168.11.17:12345 Port 12345. Notice the colon between the address and the port. # Tectia ListenAddress 192.168.11.17 12345 Port 12345. Notice the space between the address and the port.
The address 0.0.0.0 means to listen on all interfaces:
ListenAddress 0.0.0.0
optionally qualified by a port number:
# OpenSSH ListenAddress 0.0.0.0:9876 # Tectia ListenAddress 0.0.0.0 9876
OpenSSH servers allow the address to be omitted (meaning all interfaces) if the port is specified:
# OpenSSH ListenAddress :9876
For OpenSSH, a ListenAddress
of 0.0.0.0:2222
will listen on port 2222
only on IPv4 interfaces, whereas :2222
means to listen on both IPv4 and
IPv6 addresses. Additionally, you can specify IPv6 addresses with
colons, but to avoid ambiguity between the address and the port
specification, enclose the IPv6 part in square brackets, e.g.,
ListenAddress
[::1]:2222
.
Tectia servers recognize the address any
for all interfaces, with or without a
port:
# Tectia ListenAddress any ListenAddress any 9876
Since Tectia uses only a single Port
value, the only way to configure the
server to listen on multiple ports is to use multiple ListenAddress
keywords.
OpenSSH also permits hostnames in place of numeric addresses:
ListenAddress server.example.com
If the hostname lookup yields multiple addresses, then they are all used.
If a ListenAddress
value
has no port specified, then the value (or possibly multiple
values, for OpenSSH) of the Port
keyword is used for that address.
In such a case, the Port
keyword(s) must precede that ListenAddress
keyword.
Additionally, the -p command-line
option overrides all Port
and
ListenAddress
keywords in the
configuration file. The server listens on all interfaces if any
-p options are used. Use one or several
-o options with the ListenAddress
keyword to indicate
specific interfaces on the command line.
sshd normally runs as a daemon, listening for incoming connection requests, and forking whenever it accepts a connection from a client. This spawns a separate child process (a copy of the parent sshd process) to handle each session. The child process exits when the session ends.
Alternatively, the server may be invoked by inetd or xinetd, like many other network daemons. In this case, the general-purpose network daemon listens for and accepts the SSH connections. It then starts a new instance of sshd for each session with the already-connected socket attached to the standard input, output, and error streams of sshd. Each sshd invocation is responsible for a single session.
If you prefer this behavior, place an appropriate line in the inetd or xinetd configuration file to describe the SSH service, invoking sshd with the -i command-line option. For inetd, add a single line to /etc/inetd.conf:
ssh stream tcp nowait root /usr/local/sbin/sshd sshd -i
Or if you're using xinetd, create a new file /etc/xinetd.d/ssh containing:
service ssh { socket_type = stream protocol = tcp wait = no user = root server = /usr/local/sbin/sshd server_args = -i disable = no }
You will also need an entry for SSH in the server machine's TCP/IP services database, usually /etc/services (or sometimes /etc/inet/services), such as:
ssh 22/tcp # SSH Remote Login Protocol
The -i option causes sshd to:
Ignore all Port
and
ListenAddress
keywords and
the -p command-line option, because
inetd or xinetd itself
is responsible for listening
(OpenSSH only) Ignore all MaxStartups
keywords
(OpenSSH only) Direct debug output to syslog [5.9] instead of the standard error stream, since stderr is attached to the SSH socket by inetd or xinetd, and debug output would confuse the SSH client at the other end of the connection
The inetd/xinetd approach has advantages and disadvantages. On the up side, it allows a wrapper program to invoke sshd, should that be needed, and xinetd particularly supports many options that can complement the SSH server configuration. Also, inetd and xinetd provide a single, centralized point of control for all types of network connections, which simplifies maintenance. If you want to forbid all types of TCP/IP connections, for example, you can simply disable inetd/xinetd instead of running around killing other daemons. On systems where SSH connections are rare, using inetd/xinetd for the SSH service saves resources (memory and a process slot) otherwise consumed by the SSH server as it listens for incoming connections. Finally, starting a new sshd instance for each connection can make attacks more difficult by introducing additional randomness. On the down side, inetd/xinetd-based SSH connections may be slower to start up.[53]
SSH servers use randomness extensively for cryptographic algorithms and protocols, typically relying on the operating system (or other external state) to provide a source of random bits. [3.6.4] Some operating systems also support Address Space Layout Randomization (ASLR), which protects against certain kinds of attacks that require knowledge of predictable memory locations. ASLR causes random offsets to be used when program segments or shared libraries are loaded, memory regions are dynamically allocated, etc.
Most of the randomness introduced by ASLR occurs when a program is initially loaded and starts running. Even on systems without ASLR, dynamic memory allocations that primarily occur in the early stages of program execution can be affected by the global state of the system's virtual memory, which is hard to predict. In contrast, when a long-running program merely forks to create many child processes, all of the children inherit the memory layout (and even contents) from the parent process. Restarting the child processes after each fork mitigates the risks associated with attacks that are based on guessing memory locations.
By default, the OpenSSH server restarts itself after it accepts each connection from a client, and forks to create a separate child process to handle the session.[54] Relative pathnames can't be used for server restarts, since sshd changes its working directory shortly after it begins running:
# OpenSSH $ ./sshd sshd re-exec requires execution with an absolute path
We'll continue to use the relative pathname "sshd" for our examples as an abbreviation, since the full, absolute pathname usually isn't relevant to our discussions about the sshd command line. Nevertheless, an absolute pathname is recommended in practice, and newer versions of OpenSSH now enforce this, as shown in the preceding example.
If the server restart fails for some other reason (e.g., the executable file used originally to start sshd was renamed or removed), then the child process continues to run after forking, but produces a warning (which is usually sent to syslog):
error: rexec of /usr/sbin/sshd failed: No such file or directory
Before it restarts, the child process adds the undocumented -R option at the end of its command line: this is used by the new process to detect that it has been restarted, and should therefore use the already connected socket that it inherits from its parent for communication with the client.[55] The parent process (i.e., the one that listens for incoming connections) sends a copy of its configuration and the SSH-1 server key (if one is used) via another socket to the restarted child process, which knows to read the data because of the same -R option. The child process then proceeds to handle the session normally.
If OpenSSH is started by inetd or xinetd, then there is no need to restart the SSH server, because a new instance of sshd is started by inetd/xinetd for each connection. [5.3.3.2] In fact, the function of the restarted child process is so similar to the operation of the server with inetd/xinetd that the -R option enables the same side effects as the -i option: notably, debug output is forced to syslog instead of the standard error.
The restart mechanism can be disabled by the undocumented, lowercase -r option:
# OpenSSH $ sshd -r
This is useful in conjunction with server debugging features, since restarts are an inconvenient complication, and the side effect of sending debug output to syslog after the child process restarts is undesirable. [5.9] The -r option can also be used to avoid the slight performance cost for server restarts, especially on systems without ASLR, where such restarts provide little or no additional randomness. There is no configuration option to disable the server restart feature at build time.
The keepalive feature (TCPKeepAlive
in OpenSSH, KeepAlive
in Tectia) is concerned with
recognizing when a connection has failed. Suppose a client
establishes an SSH connection, and sometime later, the client host
crashes abruptly. If the SSH server has no reason to send
unsolicited messages to the client, it may never notice the
half-dead TCP connection to its partner, and the
sshd remains around indefinitely, using up
system resources such as memory and a process slot (and making the
sysadmin's ps output messy).
The TCPKeepAlive
or
KeepAlive
keyword instructs
sshd how to proceed if a connection problem
occurs, such as a prolonged network outage or a client machine
crash:
# OpenSSH TCPKeepAlive yes # Tectia KeepAlive yes
The value yes
(the default)
tells the server to set the TCP keepalive option on its connection
to the client. This causes TCP to transmit and expect periodic
keepalive messages. If it doesn't receive responses to these
messages for a while, it returns an error to
sshd, which then shuts down the
connection.
The value no
means not to
use keepalive messages. Note that SSH clients can also enable
keepalive messages from their side of the connections, so it's
important to disable those too if you want to avoid keepalive
traffic completely. [7.4.5.4]
The TCP keepalive feature is intended to prevent half-dead connections from building up over time. The keepalive message interval and timeout period reflect this: they are quite long, typically on the order of hours. This is to minimize the network load imposed by the keepalive messages and also to prevent connections from being unnecessarily torn down because of transient problems, such as a temporary network outage or routing flap. These timers aren't set in SSH; they are properties of the host's TCP stack. They shouldn't be altered lightly, since they affect every TCP connection using keepalives on that host.
This feature isn't intended to prevent lost connections due to firewall, proxying, NAT, or IP masquerading timeouts. For instance, when your SSH connection is going across a firewall but has been idle for a while, the firewall can decide to tear down the connection. Since this is done to conserve shared resources (such as a limited pool of external, routable IP addresses), these timeouts are typically quite short, perhaps a few minutes to an hour or so. The name "keepalive" suggests that it might be the right thing to use, since that's what you want to do—keep your connection alive. But really, "keepalive" is the wrong name for it; it would be better named "detect dead" (but that sounds like a second-level cleric spell to avoid being eaten by zombies). To deal with this problem, you'd have to shorten the TCP keepalive interval dramatically on the SSH host. This is contrary to its purpose and unwise because it affects not only SSH connections, but also every other TCP connection using keepalives, even those that don't need it. Doing this on the server side is an especially bad idea as a general principle, since a busy server may be using lots of TCP connections, and enabling keepalives on many of them since it's supposed to be an inexpensive feature. This can impose an unnecessary and damaging additional network load, especially if it becomes a widespread practice.
It's good to remember that the timeout annoying you so much is
there for a reason. You might like to leave an unused SSH connection
up for a long time, but if it's occupying one of a limited number of
simultaneous outbound Internet TCP connections for your company,
perhaps it's better if you just suck it up for the common good.
Typing ssh
again once in a while
is really not that hard; use your shell's alias feature if you find
the number of keystrokes onerous. If you genuinely think the timeout
is inappropriate or unnecessary, argue the case with the network
administrator, and try to get it changed.
For the occasions when it's really necessary, the right way to
accomplish this sort of keepalive behavior is with an
application-level mechanism implemented in SSH—having it
periodically send SSH protocol messages over the connection to make
it appear nonidle. This is exactly what OpenSSH does with its
ClientAliveInterval
and ClientAliveCountMax
keywords. ClientAliveInterval
controls how the
server sends client-alive messages.[56] Its argument is a length of time in seconds:
# OpenSSH
ClientAliveInterval 300 Send client-alive every 300 seconds, or five minutes
or a time value with optional units:
# OpenSSH
ClientAliveInterval 5m Send client-alive every five minutes
If your server hasn't heard from the client within the given
amount of time, the server will send a client-alive message to the
client. It will continue sending these messages at the given
interval (in this case, every five minutes) until it receives a
response or gives up. You control how it gives up with the third
keyword, ClientAliveCountMax
,
representing the maximum number of consecutive client-alive messages
the server will send:
# OpenSSH
ClientAliveCountMax 8 Try eight times, then give up. The default is three times.
Once this maximum is reached, the server considers the SSH
connection inactive and terminates it. If you don't want the server
to send client-alive messages, set ClientAliveInterval
to zero.
If your SSH implementation has no similar feature (Tectia doesn't), we recommend simply sending characters over your connection once in a while. Run Emacs with a clock in its mode line. Run a program in the background that prints "Boo!" to your terminal if it's been idle for 20 minutes. You get the idea.
Keepalive messages are concerned with recognizing that
a connection has failed. A related feature is recognizing when a
healthy connection is unused and should be terminated. Tectia
supports the IdleTimeout
keyword
for this purpose. If an SSH connection is established between a
server and a client, but no data passes over the connection for a
long time, what should the server do: keep the connection, or
terminate it?
The IdleTimeout
keyword
tells the server what to do if a connection is idle, i.e., if the
user doesn't transmit any data in a given period. If IdleTimeout
is zero (the default), the
server does nothing, leaving idle connections intact:
# Tectia IdleTimeout 0
Otherwise, the server terminates the connection after a specified interval of idleness. The time value can specify units, e.g., three hours:
# Tectia IdleTimeout 3H
See the sidebar "Time Values in Configuration Files" for more syntax details.
The idle timeout can also be set for a given key in a user's
authorized_keys file using the
idle-timeout
option. [8.2.7] Notably, this option
overrides the server's IdleTimeout
value but only for that key.
This is a rare instance of a per-account option overriding a
serverwide option; however, the server will only allow a client to
decrease the timeout.
Suppose a user attempts to log in via SSH but fails to
authenticate. What should the server do? The keywords LoginGraceTime
, MaxAuthTries
(OpenSSH), and PasswordGuesses
(Tectia) control the
server's response.
Users are given a limited time to authenticate successfully.
The default is 120 seconds (2 minutes) for OpenSSH or 600 seconds
(10 minutes) for Tectia. This timeout is controlled by the LoginGraceTime
keyword, given a value in
seconds:
LoginGraceTime 60
or the -g command-line option:
$ sshd -g 60
OpenSSH allows time units to be used in the configuration file or on the command line:
# OpenSSH LoginGraceTime 5m # OpenSSH $ sshd -g 5m
To disable this feature, provide a LoginGraceTime
value of zero:
LoginGraceTime 0
or by command-line option:
$ sshd -g 0
OpenSSH ignores LoginGraceTime
in debug mode. [5.9]
OpenSSH limits the number of times (six by default) that a user can attempt to authenticate in a single SSH connection:
# OpenSSH
MaxAuthTries 4 Permit four attempts, and log the third and fourth failures if they occur
If authentication fails half the number of times specified (in this example, two times, half of four), then failures are logged by sshd. In other words, sshd gives you the benefit of the doubt at first, then considers you suspicious. By default, you have six chances to authenticate in one connection.
If password authentication is used for a connection request,
Tectia's sshd permits a client three tries to
authenticate before dropping the connection. This restriction may be
modified with the PasswordGuesses
keyword for Tectia:
# Tectia PasswordGuesses 5
There are two sorts of requests a client can make in this
regard: a query whether a particular public key is authorized to log
into the target account, and an actual authentication attempt
including a signature by the corresponding private key. As Tectia
does not limit the number of public-key authentication requests,
there's no issue with it. The OpenSSH MaxAuthTries
setting, however, limits the
number of failed authentication requests overall, of any type, and
OpenSSH counts a "no" answer to a public-key query as a failure. A
common side effect is an unexpected limit to the number of keys you
can usefully have in an agent! If you have five keys in your agent,
and it happens to be the fifth one that would let you in, you're out
of luck: the server will disconnect you after the client tries the
fourth key. And that's assuming the client didn't try and fail some
other methods first, e.g., GSSAPI or hostbased; then even fewer keys
could be tried. (See [7.4.2.1] for a
workaround.)
There are various security arguments to made here, of course. The server can't distinguish between a legitimate user trying keys and an attacker knocking on the door, so it measures all attempts against the repeated-authentication limit. In fact, one can argue that the server shouldn't honor public-key queries because they reveal information to an attacker: which key to try to steal, or whether an account can be accessed at all. These are all trade-offs of convenience versus security, and different server implementations take different approaches.
You can work around this issue by listing your most relevant
keys in your client configuration file, ~/.ssh/config, with the IdentityFile
keyword. [7.4.2] Keys that are in
both the agent and the configuration file are tried first by the
client. Therefore, you can associate particular keys with a
particular host so that they're tried first for
authentication.
sshd can handle an arbitrary
number of simultaneous connections by default. Both OpenSSH and
Tectia provide keywords to limit the maximum number, if you want to
conserve resources on your server machine or reduce the risk of
denial-of-service attacks. For OpenSSH it is MaxStartups
, and for Tectia it is MaxConnections
:
# OpenSSH MaxStartups 32 # Tectia MaxConnections 32
To specify an unlimited number of connections, provide a value of zero:
# OpenSSH MaxStartups 0 # Tectia MaxConnections 0
Of course, the number of connections is also limited by available memory or other operating system resources. These keywords have no effect on these other factors. (Sorry, you can't increase your CPU speed by setting a keyword!)
OpenSSH's MaxStartups
keyword has one additional bit of functionality. If you provide a
triple of integers separated by colons, of the form
A:B:C
, this tells the server to refuse
connections based on probabilities. Specifically, if the number of
connections is A
or greater,
sshd will begin rejecting connections. When
there are A
connections, the probability
of rejection is B
%. When there are
C
connections, the probability of
rejection is 100% (every attempt is rejected). Between
A
and C
connections, the probability increases linearly from
B
% to 100%. So, for example, if you
have:
# OpenSSH MaxStartups 10:50:20
then at a load of 10 connections, the probably of rejection is 50%; at 15 connections (halfway between 10 and 20) it's 75% (halfway between 50% and 100%), and at 20 connections it's 100%.
Tectia's behavior is simpler. After the maximum number of
connections have been accepted, new connection attempts are
rejected, and the server sends a "Too many connections" error
message back to the client before it disconnects. Tectia can also
limit the number of connections at compile time via the
--with-ssh-connection-limit
option. [4.3.5.3]
If sshd is launched by xinetd, then you can control server resources much more precisely: the rate of incoming connections, server memory, and more. [5.3.3.2]
The SSH server optionally does a reverse DNS lookup on
a client's IP address. That is, it looks up the name associated with
the address, then looks up the addresses for that name and makes
sure that the client's address is among them. If this check fails,
the server refuses the connection. This feature uses standard system
services like gethostbyname()
and gethostbyaddr()
to perform
these mappings, so the databases that are consulted depend on the
host operating system configuration. It might use the DNS, the
Network Information Service (NIS or YP), static files on a server
machine, or some combination.
To enable this check for OpenSSH, provide the UseDNS
keyword with a value of yes
or no
:[57]
# OpenSSH UseDNS yes
This feature is a bit of security-oriented consistency checking. SSH uses cryptographic signatures to determine a peer's identity, but the list of peer public keys (the known hosts database) is often indexed by hostname, so SSH must translate the address to a name in order to check the peer's identity. Reverse mapping tries to ensure that someone isn't playing games with the naming service in a cracking attempt. There is a trade-off, however, since in today's Internet, the DNS reverse-address mappings aren't always kept up to date. The SSH server might reject legitimate connection attempts because of poorly maintained reverse-address mappings over which you have no control. In general, we recommend turning off this feature; it isn't usually worth the hassle, and you avoid long reverse-lookup delays at times when DNS is down.
TCP/IP has a feature called the Nagle Algorithm, which
is designed to reduce the number of TCP segments sent with very
small amounts of data (e.g., one byte), usually as part of an
interactive terminal session. Over fast links such as Ethernet, the
Nagle Algorithm generally isn't needed. Over a wide-area network,
however, it can cause noticeable delays in the responsiveness of X
clients and character terminal displays, as multibyte terminal
control sequences may be transmitted inconveniently by the
algorithm. In such cases, you should turn off the Nagle Algorithm
using the NoDelay
keyword:
# Tectia NoDelay yes
NoDelay
disables the Nagle
Algorithm by toggling the TCP_NODELAY bit when requesting a TCP connection from the Unix
kernel. Legal values are yes
(to
disable) and no
(to enable; the
default).
NoDelay
can be enabled or
disabled by the Tectia client, rather than serverwide, using the
client configuration keyword NoDelay
. [7.4.5.5] It usually makes
more sense to use NoDelay
for a
single client connection than to control the Nagle Algorithm
globally for all connections on the server side.
Tectia can seek out and discover other Tectia servers
automatically. The keyword MaxBroadcastsPerSecond
, when given an
integer value greater than zero, causes a Tectia server to respond
to UDP broadcasts sent to port 22:
# Tectia MaxBroadcastsPerSecond 10
The server responds to only this many queries per second; any excess broadcasts are silently ignored. All UDP broadcasts received on port 22 apply to this limit, including unrecognized or malformed packets. The rate limiting prevents a denial-of-service attack that floods the server with queries, causing it to spend all its time replying to them.
By default, Tectia servers do not respond to UDP broadcasts.
This behavior can be specified explicitly by setting MaxBroadcastsPerSecond
to zero:
# Tectia MaxBroadcastsPerSecond 0
No mechanism is provided to use a UDP port other than 22, and the UDP port is completely independent of the TCP port(s) used for ordinary SSH connections.
A program supplied with Tectia, ssh-probe, sends queries to one or more specified broadcast addresses. It listens for replies, and prints the locations (IP addresses and ports) along with the versions of any Tectia servers that it finds:
# Tectia $ ssh-probe 10.1.2.255 10.1.2.3:22:SSH Tectia Server 4.1.3.2 10.1.2.5:22:SSH Tectia Server 4.1.3.2 10.1.2.5:2222:SSH Tectia Server 4.1.3.2 10.1.2.5:3333:SSH Tectia Server 4.1.3.2 10.1.2.9:22:SSH Tectia Server 4.1.3.2 ...
Directed broadcasts (i.e., those on different networks) can be used if intervening gateways are willing to forward them. IP addresses of specific hosts (but not hostnames) can also be used.
UDP datagrams received on non-broadcast addresses are usually delivered only to a single process, so if several Tectia servers are running on a target host, then only one will respond. Use broadcast addresses to detect multiple servers.
ssh-probe does not
use the ProtocolVersionString
to
determine the version: this string is part of the initial
negotiation between SSH servers and clients for TCP connections.
[5.3.7] Tectia servers
always supply their actual version string in response to UDP queries
by ssh-probe.
The default output format is intended to be parsed easily by programs. The -r option prints results in a more human-readable format:
# Tectia $ ssh-probe -r 10.1.2.255 Server address = "10.1.2.3" Server port = "22" Server version = "SSH Tectia Server 4.1.3.2" ... 12 servers detected.
The -s option causes ssh-probe to operate silently, returning only an exit value of 0 to indicate that at least one server was found, 1 if no replies were received, or -1 if some other error occurred:
# Tectia $ ssh-probe -s 10.1.2.255 $ case $? in > 0) echo "Tectia found.";; > 1) echo "Tectia missing.";; > *) echo "Something bad happened to ssh-probe!";; > esac Tectia found.
By default, ssh-probe waits one second for replies. The -t option specifies a longer timeout, e.g., for slow or distant servers:
# Tectia $ ssh-probe -t 5 10.1.2.255
ssh-probe supports the
-d option for debug output. [5.9] The program uses the
module names SshProbe
and
SshServerProbe
.
Port-scanning programs such as nmap provide a more general way to locate SSH servers, including other implementations like OpenSSH, even though port scans typically don't provide version information as ssh-probe does for Tectia servers. For example, to use nmap to scan a range of network addresses for any kinds of SSH servers listening on (TCP) port 22:
$ nmap -v -p 22 10.1.2.0/24
The ScanSSH program [58] scans ranges of network addresses, identifying SSH servers (along with open proxies and other interesting servers, such as HTTP and SMTP). It attempts to determine the version for each. For example, to scan the same network address range:
$ scanssh -s ssh 10.1.2.0/24
MaxBroadcastsPerSecond
and
ssh-probe are a rather ad hoc solution for
locating Tectia servers, and port scans are questionable, since
authorized users typically know the identity of specific servers to
which they have been granted access. Probes often don't work across
firewalls, and they might be mistaken for attacks by people and
programs that monitor network activity.
Better techniques are available to enumerate servers for administrative tasks, e.g., maintaining a list of servers in a netgroup or other database. Dynamic DNS and SRV records are alternatives, although this nameserver functionality is still not widely used.
All SSH servers maintain a persistent host key. It is
generated by the system administrator when installing SSH and
identifies the host for authentication purposes. [5.3.1.1] Additionally,
SSH-2 clients and servers exchange keys for data encryption and
integrity. By default, the Tectia client and server perform this key
exchange every hour (3600 seconds) but you can set this with the
RekeyIntervalSeconds
keyword. A
value of zero disables rekeying.
# Tectia RekeyIntervalSeconds 7200
This keyword only controls the automatic, periodic session rekeying that is initiated by the server. An SSH client can still request session rekeying at any time.
You can make the ssh client force rekeying
with the escape sequence ~R
(OpenSSH) or ~r
(for Tectia). [7.4.6.8]
The SSH server can use a number of data-encryption
algorithms for its secure connection; the client chooses a cipher
from the list the server allows. The Ciphers
keyword describes the subset of
allowable ciphers, selected from those the server software supports.
Its value is a comma-separated list of algorithm names (strings),
case-sensitive,[59] indicating which algorithms are permissible. For
example:
Ciphers 3des-cbc Ciphers 3des-cbc,blowfish-cbc,arcfour
The order of the values is not significant, since the client drives the choice of the cipher.
If multiple Ciphers
keywords
are specified, the values are not accumulated into a single list.
Instead, OpenSSH uses the list for the first Ciphers
keyword, and Tectia uses the
last.
OpenSSH treats unrecognized cipher names as fatal errors, but
Tectia silently ignores them, which makes typos hard to detect. For
troubleshooting, use the sshd -d command-line
option [5.9] with the
SshConfigParse
module and a high
debug level:
# Tectia sshd -d SshConfigParse=9
Look for "ssh_config_set_param_algs" in the output to see the actual list of cipher names that were used.
The Ciphers
keyword is useful
for quickly disabling individual encryption algorithms—say, if a
security hole is discovered in one of them. Just omit that algorithm
from the Ciphers
list and restart
the server.
Both OpenSSH and Tectia support the following standard ciphers that are defined by the IETF SECSH draft:
In addition, Tectia implements the following standard ciphers:[60]
none |
twofish-cbc |
twofish128-cbc |
twofish192-cbc |
twofish256-cbc |
The "none" cipher means that no encryption is used. This is unsuitable for production use, but it might occasionally be convenient for testing, e.g., if you are watching SSH traffic using a network sniffer for diagnostic purposes. Subconfiguration files can restrict insecure ciphers like "none" to specific hosts or users. [11.6.2]
OpenSSH also implements a number of nonstandard ciphers:
acss@openssh.org [61] |
aes128-ctr |
aes192-ctr |
aes256-ctr |
rijndael-cbc@lysator.liu.se |
By default, all ciphers supported by the OpenSSH server (both standard and nonstandard) are allowed.
Tectia supports a different set of recommended, nonstandard ciphers:
cast128-12-cbc@ssh.com |
des-cbc@ssh.com |
rc2-cbc@ssh.com |
rc2-128-cbc@ssh.com |
rijndael-cbc@ssh.com |
Tectia also recognizes special values for the Cipher
keyword indicating sets of
algorithms:
AnyStd
Any standard algorithm implemented by Tectia, including none
AnyStdCipher
Same as AnyStd
, but
excluding none
Any
Any standard or recommended, nonstandard algorithm implemented by Tectia, including none
AnyCipher
Same as Any
, but
excluding none
These special values are case-insensitive, in contrast to the other values for cipher names. We recommend using the capitalization shown earlier, but you may see lowercase values in older Tectia configuration files or documentation.
An important and unfortunate restriction is that the special values for cipher sets cannot be mixed with other cipher names:
# Tectia: This is ILLEGAL Ciphers 3des-cbc,AnyStd
The default for Tectia is AnyStdCipher
.
Tectia's extensive but poorly documented cryptographic library actually supports a much wider range of ciphers, including:
These are not included in the sets for Any
or AnyCipher
. In some cases, this is because
the ciphers are considered experimental or inferior. For example, DES
is usually not recommended because of its short key length, and block
ciphers in ECB mode are considered vulnerable to replay attacks. Other
modes such as CFB, OFB, and CTR are plausible alternatives to the
default CBC, however.
Finally, Tectia recognizes a small number of convenient aliases for sets of ciphers:
Value | Meaning |
---|---|
aes-cbc | aes128-cbc, aes192-cbc aes256-cbc |
cast | cast128-cbc |
twofish | twofish-cbc, twofish128-cbc, twofish192-cbc, twofish256-cbc |
In most cases, the names of block ciphers in CBC mode are also recognized by Tectia without "-cbc", since CBC is considered the default mode. There are exceptions, however, that don't follow any obvious pattern:
aes-cbc@ssh.com
cast128-cbc
cast128-12-cbc@ssh.com
rc2-cbc@ssh.com
rc2-128-cbc@ssh.com
twofish-cbc
We therefore recommend explicitly specifying -cbc
in cipher names.
Tectia is rather forgiving (or sloppy, depending on your point of view) about the @ssh.com suffix for cipher names, which is supposed to be used consistently for nonstandard ciphers.
Most standard cipher names are also recognized with this suffix. The exceptions are:
aes128-cbc
aes192-cbc
aes256-cbc
twofish128-cbc
twofish192-cbc
twofish256-cbc
Similarly, the suffix can be omitted from most nonstandard cipher names. The lone exception is aes-cbc@ssh.com, because the name without the suffix is used as an alias for all AES ciphers in CBC mode with any key length, as described earlier.
Misusing the @ssh.com suffix in this way is inadvisable, because it violates the IETF SECSH draft.
The MACs
keyword
selects the allowable integrity-checking algorithms, known as the
message authentication code (MAC), used by sshd.
[3.4.2.3] Except as
described below, the MACs
keyword
behaves exactly like the Ciphers
keyword. [5.3.5] Here
are some examples:
MACs hmac-sha1 MACs hmac-sha1,hmac-md5
Both OpenSSH and Tectia support the following standard MAC algorithms defined by the IETF SECSH draft:
hmac-sha1 |
hmac-sha1-96 |
hmac-md5 |
hmac-md5-96 |
In addition, Tectia implements the standard "none" MAC, meaning that no integrity checking is performed. This is intended only for testing.
OpenSSH also implements a nonstandard MAC algorithm, hmac-ripemd160@openssh.org. The name hmac-ripemd160 is also recognized without the @openssh.org suffix, but this is deprecated, since all nonstandard names are supposed to use a domain suffix. Tectia also supports some nonstandard MAC algorithms:
hmac-ripemd160@ssh.com |
hmac-ripemd160-96@ssh.com |
hmac-sha256@ssh.com |
hmac-sha256-96@ssh.com |
hmac-tiger128@ssh.com |
hmac-tiger128-96@ssh.com |
hmac-tiger160@ssh.com |
hmac-tiger160-96@ssh.com |
hmac-tiger192@ssh.com |
hmac-tiger192-96@ssh.com |
ssl3-md5@ssh.com |
ssl3-sha1@ssh.com |
Tectia recognizes special values for the Macs
keyword to describe sets of
algorithms:
AnyStd
Any standard algorithm implemented by Tectia, including none
AnyStdMac
Same as AnyStd
, but
excluding none
Any
Any standard or nonstandard algorithm implemented by Tectia, including none
AnyMac
Same as Any
, but
excluding none
By default, Tectia allows algorithms in the AnyStdMac
set. (The Any
value includes all
supported MAC algorithms, unlike the Ciphers
keyword.) OpenSSH allows all its
available MACs by default.
OpenSSH lets you limit its protocol support to SSH-1,
SSH-2, or both, using the Protocol
keyword. Permissible values are 1
(for SSH-1), 2
(for SSH-2), or both
1
and 2
separated by a comma (the default):
# OpenSSH Protocol 2,1
If you specify both protocols, the order doesn't matter since the client, not the server, drives the authentication process. And as we've said before, the SSH-1 protocol is less secure and we recommend avoiding it. [3.5]
SSH servers and clients exchange protocol version information as part of their initial negotiations, to agree on a protocol. [3.4.4.2] You can see the protocol version string used by the server by connecting to the SSH port:
$ telnet localhost 22 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. SSH-2.0-4.1.3.2 SSH Secure Shell
By default, Tectia servers use a string like "4.1.3.2 SSH
Secure Shell" for the comment part (after the second hyphen) of the
protocol version. This can be changed using the undocumented
ProtocolVersionString
keyword:
# Tectia ProtocolVersionString Generic SSH Implementation
Port-scanning tools that connect to the SSH port and observe the protocol version string will not see the detailed information about the specific installed version of Tectia if the string is changed:
$ telnet localhost 22 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. SSH-2.0-Generic SSH Implementation
ProtocolVersionString
changes only the comment part of the version string. The initial
parts (e.g., SSH-2.0) always specify the protocol(s) that the
server is willing to use, according to the SSH protocol
standard.
Although an obscured ProtocolVersionString
might thwart very
simplistic port-scanning tools, in practice it doesn't help much,
since many attacks try to exploit bugs regardless of the version
string, and determined attackers can probably figure out the
implementation by noticing specific behavioral quirks of the server
anyway. If the Tectia server is configured to respond to UDP queries
by ssh-probe [5.3.3.10], then it
always will respond to such queries with the actual version
information, not the changed ProtocolVersionString
. Furthermore,
changing ProtocolVersionString
might prevent workarounds by clients for known server
incompatibilities or bugs.
The OpenSSH server always uses a string like "OpenSSH_3.9p1" for its protocol version string. This cannot be changed except by modifying the source code.
The data flowing between the SSH client and server may
optionally be compressed to save bandwidth. Often this option is set
by the client [7.4.14],
but OpenSSH gives the server the ultimate authority on whether data
compression is permitted, using the Compression
keyword:
# OpenSSH Compression no
The default value is yes
.
[49] HostKey
has the aliases
HostRsaKey
and HostDsaKey
, but they are deprecated
and might be removed in a future version of OpenSSH.
[50] More precisely, n is the value for
the Port
keyword, even if
ListenAddress
keywords cause
the server to use different ports. [5.3.3.1]
[51] You need not double the percent sign (%%
) to get a literal percent
character, i.e., as required for the C function printf
.
[52] The port setting (either explicit or the default value,
22) is used in the name of the process ID file. [5.3.1.3] Tectia
servers can listen on multiple ports, but this requires use of
the ListenAddress
keyword.
[53] Only if you use the SSH-1 protocol, where sshd generates a new server key each time it's invoked. But you're not using SSH-1, are you?
[54] This feature is new in OpenSSH 3.9.
[55] Never use the -R option to start sshd; it's really part of the protocol for communication between the parent and the (restarted) child server processes.
[57] Tectia has a similar-sounding keyword, RequireReverseMapping
, but it applies
only to the AllowHosts
and
DenyHosts
features. [5.5.3]
[59] Older versions of OpenSSH treat the algorithm names as case-insensitive.
[60] A few standard ciphers aren't supported by either OpenSSH or Tectia: idea-cbc, serpent128-cbc, serpent192-cbc, and serpent256-cbc. These are all considered optional by the IETF SECSH draft.
[61] Cipher acss@openssh.org is not allowed by default; it must be explicitly enabled.