SSH: DDoS mitigation

One9twO
3 min readOct 5, 2022

In sshd_config, there’s a configuration parameter for MaxStartups.

The man page:

MaxStartups
Specifies the maximum number of concurrent unauthenticated
connections to the SSH daemon. Additional connections will
be dropped until authentication succeeds or the
LoginGraceTime expires for a connection. The default is
10:30:100.
Alternatively, random early drop can be enabled by
specifying the three colon separated values start:rate:full
(e.g. "10:30:60"). sshd(8) will refuse connection attempts
with a probability of rate/100 (30%) if there are currently
start (10) unauthenticated connections. The probability
increases linearly and all connection attempts are refused
if the number of unauthenticated connections reaches full
(60).

Based on the description, it doesn’t look apparent to me that if you set it to a number (e.g. 60), will it be inheriting the default start:rate values (10:30). i.e. are the following settings identical:

MaxStartups 10:30:60

MaxStartups 60

To find out, we can:

  1. read the source code (if you love C)
  2. ask on StackOverflow
  3. test it

The following is my testing (i.e. option 3).

Test MaxStartups

  1. First, have a running SSH server with root access. In my example, the SSH server is 10.10.62.201.

2. In your local client, have pssh installed. Reference.

3. In your local client, write 4 host files containing the SSH server hostname/IP, repeated for 10, 30, 60, 100 times respectively. This will simulate a DoS attack by sending X number of requests to the host.

yes 10.10.62.201 | head -n 10 > hostfile10
yes 10.10.62.201 | head -n 30 > hostfile30
yes 10.10.62.201 | head -n 60 > hostfile60
yes 10.10.62.201 | head -n 100 > hostfile100

4. In the SSH server, have MaxStartups 10:30:60 set in /etc/ssh/sshd_config . Each time after a change, make sure SSH server is restarted.

service sshd restart

5. Get the first set of result based on MaxStartups 10:30:60

pssh -i -h hostfile10 -l root --askpass whoami > output10.1
pssh -i -h hostfile30 -l root --askpass whoami > output30.1
pssh -i -h hostfile60 -l root --askpass whoami > output60.1
pssh -i -h hostfile100 -l root --askpass whoami > output100.1

Looking at the output files:

╔═════════════╦═════════╦═════════╦══════════════╗
║ Connections ║ Success ║ Failure ║ Failure Rate ║
╠═════════════╬═════════╬═════════╬══════════════╣
║ 10 ║ 10 ║ 0 ║ 0% ║
║ 30 ║ 26 ║ 4 ║ 13% ║
║ 60 ║ 48 ║ 12 ║ 20% ║
║ 100 ║ 78 ║ 22 ║ 22% ║
╚═════════════╩═════════╩═════════╩══════════════╝

The failure rate does increase linearly. But it is less than 30%. Perhaps the pssh requests were batched (i.e. some of the early requests succeeded).

6. Generate the second set of data based on MaxStartups 60

╔═════════════╦═════════╦═════════╦══════════════╗
║ Connections ║ Success ║ Failure ║ Failure Rate ║
╠═════════════╬═════════╬═════════╬══════════════╣
║ 10 ║ 10 ║ 0 ║ 0% ║
║ 30 ║ 30 ║ 0 ║ 0% ║
║ 60 ║ 60 ║ 0 ║ 0% ║
║ 100 ║ 100 ║ 0 ║ 0% ║
╚═════════════╩═════════╩═════════╩══════════════╝

The first 3 results show that when MaxStartups is set to a number, SSH server will not reject the authentication requests. However, the last result is a little odd. I would expect it to start failing after 60 but it didn’t.

Hmmmm………..

So it turns out there’s an additional parameter for pssh to parallelize the requests

-p parallelism--par parallelismUse the given number as the maximum number of concurrent connections.

Re Test with Specific Parallel Sessions

Since the maximum number of hosts I specified is 100, I’ll add --par 100 in the pssh test.

With MaxStartups 10:30:60 :

╔═════════════╦═════════╦═════════╦══════════════╗
║ Connections ║ Success ║ Failure ║ Failure Rate ║
╠═════════════╬═════════╬═════════╬══════════════╣
║ 10 ║ 10 ║ 0 ║ 0% ║
║ 30 ║ 23 ║ 7 ║ 23% ║
║ 60 ║ 33 ║ 27 ║ 45% ║
║ 100 ║ 47 ║ 53 ║ 53% ║
╚═════════════╩═════════╩═════════╩══════════════╝

With MaxStartups 60 :

╔═════════════╦═════════╦═════════╦══════════════╗
║ Connections ║ Success ║ Failure ║ Failure Rate ║
╠═════════════╬═════════╬═════════╬══════════════╣
║ 10 ║ 10 ║ 0 ║ 0% ║
║ 30 ║ 30 ║ 0 ║ 0% ║
║ 60 ║ 60 ║ 0 ║ 0% ║
║ 100 ║ 60 ║ 40 ║ 40% ║
╚═════════════╩═════════╩═════════╩══════════════╝

Conclusion

  1. With MaxStartups 10:30:60 , it’ll mitigate some DoS attacks with early rejection.
  2. With MaxStartups 60 , it’ll cap concurrent requests at 60.
  3. pssh has a default batch value. You can define the batch size with--par <number>

--

--