OpenSSH/Cookbook/Load Balancing

From Wikibooks, open books for an open world
Jump to: navigation, search



Random early drop can be enabled by specifying the three colon-separated values start:rate:full. After the number of unauthenticated connections reaches the value specified by start, sshd(8) will begin to refuse new connections at a percentage specified by rate. The proportional rate of refused connections then increases linearly as the limit specified by full is approached until 100% is reached. At that point all new attempts at connection are refused until the backlog goes down.

MaxStartups   10:30:100

For example, if MaxStartups 5:30:90 is given in sshd_config(5), then starting with 5 new connections pending authentication the server will start to drop 30% of the new connections. By the time the backlog increases to 90 pending unauthenticated connections, 100% will be dropped.

In the default settings, the value for full has been increased to 100 pending connections to make it harder to succumb to a denial of service due to attack or heavy load. So the new default is 10:30:100.

Alternately, if the number of incoming connections is not managed at the network level by a packet filter or other tricks like round-robin DNS, it is possible to limit it at the SSH server itself. Setting MaxStartups to an integer sets a hard limit to the maximum number of concurrent unauthenticated connections to the SSH daemon.

MaxStartups   10

Additional connections will be dropped until authentication succeeds or the LoginGraceTime expires for another connection. The old default was 10.

Preventing Timeouts Of A Not So Active Session[edit]

There are two connections that can be tracked during an SSH session, the network's TCP connection and the encrypted SSH session traveling on top of it. Some tunnels and VPNs might not be active all the time, so there is the risk that the session times out or even that the TCP session times out at the router or firewall. The network connection can be be tracked with TCPKeepAlive, but is not an accurate indication of the state of the actual SSH connections. It is, however, a useful indicator of the status of the actual network. Either the client or the server can counter that by keeping either the encrypted connection active using a heartbeat.

On the client side, if the global client configuration is not already set, individuals can use ServerAliveInterval to choose an interval in seconds for server alive heartbeats and use ServerAliveCountMax to set the corresponding maximum number of missed client messages allowed before the encrypted SSH session is considered closed.

ServerAliveInterval  15
ServerAliveCountMax  4

On the server side, ClientAliveInterval sets the interval in seconds between client alive heartbeats. ClientAliveCountMax sets the maximum number of missed client messages allowed before the encrypted SSH session is considered closed. If no other data has been sent or received during that time, sshd(8) will send a message through the encrypted channel to request a response from the client. If sshd_config(5) has ClientAliveInterval set to 15, and ClientAliveCountMax set to 4, unresponsive SSH clients will be disconnected after approximately 60 (= 15 x 4) seconds.

ClientAliveInterval  15
ClientAliveCountMax  4

If a time-based RekeyLimit is also used but the time limit is shorter than the ClientAliveInterval heartbeat, then the shorter re-key limit will be used for the heartbeat interval instead.

This is more or less the same principal as with the server. Again, that is set in ~/.ssh/config and can be applied globally to all connections from that account or selectively to specific connections using a Host or Match block.

TCP Wrappers, Also Called tcpd(8)[edit]

As of 6.7, OpenSSH's sshd(8) no longer supports TCP Wrappers. So this subsection only applies to 6.6 and earlier. Other options that can be used with or without TCP Wrappers include packet filters like PF [1], ipf or IPTables.

TCP Wrappers, also known as tcpd(8), is an access control utility for incoming requests to Internet services. It can be used for services that have a one-to-one mapping to executables, such as sshd(8), and that have been compiled to interact with tcpd(8). It checks first a whitelist (/etc/hosts.allow) and then a blacklist (/etc/hosts.deny) to approve or deny access. The first pattern to match any given connection attempt is used. The default in /etc/hosts.deny is to block access, if no rules match in /etc/hosts.allow:

sshd: ALL

In addition to access control, tcpd(8) can be set to run scripts using twist or spawn when a rule is triggered. spawn launches another program as a child process of tcpd. From /etc/hosts.allow:

sshd: : allow
sshd: : spawn \
        /bin/date -u +"%%F %%T UTC from %h" >> /var/log/sshd.log : allow

The variable %h expands to the connecting clients host name or ip number. The manual page for hosts_access(5) includes a full description of the variables available. Because the program in the example, date, uses the same symbol for variables, the escape character (%) must be escaped (%%) so it will be ignored by tcpd(8) and get passed on to date correctly.

twist replaces the service requested with another program. It is sometimes used for honeypots, but can really be used for anything. From /etc/hosts.deny:

sshd: : deny
sshd: ALL : twist /bin/echo "Sorry, fresh out." : deny

With TCP Wrappers the whitelist /etc/hosts.allow is searched first, then the blacklist /etc/hosts.deny. The first match is applied. If no applicable rules are found, then access is granted by default.

Using TCP Wrappers To Allow Connections From Only A Specific Subnet[edit]

It was possible to use TCP Wrappers just set sshd(8) to listen only to the local address and not accept any external connections. That was one way. To use TCP Wrappers for that, put a line in /etc/hosts.deny blocking everything:

sshd: ALL

And add an exception for the in /etc/hosts.allow by designating the ip range using CIDR notation or by domain name


The same method can be used to limit access to just the localhost ( by adding a line to /etc/hosts.allow:


Again, the best practice is to block from everywhere and then open up exceptions. Keep in mind that if domains are used instead of IP ranges, DNS entries must be in order and DNS itself accessible. However, that is better done by setting sshd_config(5) accordingly.

The Extended Internet Services Daemon (xinetd)[edit]

The Extended Internet Services Daemon, xinetd(8), can provide many kinds of access control. That includes but is not limited to name, address or network of remote host, and time of day. It can place limits on the number of services per each service as well as discontinue services if loads exceed a certain limit.

The super-server listens for incoming requests and launches sshd(8) on demand, so it is necessary to first stop sshd(8) from running as standalone daemon. This may mean modifying System V init scripts or Upstart configuration files. Then make an xinetd(8) configuration file for the service SSH. It will probably go in /etc/xinetd.d/ssh The argument -i is important as it tells sshd(8) that it is being run from xinetd(8).

service ssh
        socket_type     = stream
        protocol        = tcp
        wait            = no
        user            = root
        server          = /usr/sbin/sshd
        server_args     = -i
        per_source      = UNLIMITED
        log_on_failure  = USERID HOST
        access_times    = 08:00-15:25
        banner          = /etc/banner.inetd.connection.txt
        banner_success  = /etc/banner.inetd.welcome.txt 
        banner_fail     = /etc/banner.inetd.takeahike.txt

        # log_on_success  = PID HOST DURATION TRAFFIC EXIT
        # instances       = 10
        # nice            = 10
        # bind            =
        # only_from       =
        # no_access       =
        # no_access       +=

Finally, reload the configuration by sending SIGHUP to xinetd(8).