FreeBSD: SSH Server Security Audit&Hardening (maximum score at SSHAudit online test)

Written by: Özgür Konstantin Kazanççı -

Category: My FreeBSD Write-ups

Bonum Diem.
In this article, I will discuss about securing&optimization (auditing and hardening) of the SSH server installed on FreeBSD. Today, many system administrators/organizations overlook SSH hardening methods, the security of SSH configurations. While the security of SSH keys is often overlooked as well, misuse or compromise of those keys could lead to unauthorized access, often with high privileges.

The usual default SSH configuration, which comes with a default installation of server operating systems (such as Linux, FreeBSD, OpenBSD), brings a better compatibility rather than high security measures.

Security vulnerabilities discovered in critical system management services such as SSH allow attackers to infiltrate your systems usually by taking advantage of these default configurations.

Therefore, when I setup a new server, to test the security of the SSH service, there’s a great online tool that I occasionally use, called; SSHAudit by Positron Security.
A sample online report: https://www.sshaudit.com/sample_ssh_audit.html

Adding the hostname of my FreeBSD box into “Target SSH Server:” there, with its port number and picking the “Standard Audit” option, I get a horrible security score like “F”, as seen below;

Before hardening.
SSH server security – before hardening.

By default, my (well, FreeBSD’s) ssh server gets a very low score, usually due to the default Host Key Types, Key Exchange Algorithms, Encryption Ciphers and Message Authentication Codes settings.

These keys and ciphers are vulnerable to attacks. So, let’s tighten&polish them! I begin with, checking the default, base SSH server version:

ozgur@freebsdbox:~ # ssh -V
OpenSSH_7.9p1, OpenSSL 1.1.1k-freebsd 24 Aug 2021

Well, it seems the current SSH server version is: OpenSSH 7.9, which is a pretty old version comes by the default on a FreeBSD 13 system.

Let’s disable it and install the most (almost) recent version through packages (ver. 8.7 as of today);

As root user, always fetch&upgrade FreeBSD packages’ repository before installing anything. It is best practice to ensure your out-of-date packages are updated, and package repository catalogues are up to date before doing any package installation.

Invoking “pkg upgrade” will cause repository catalogues to be updated automatically.

root@freebsdbox:~ # pkg upgrade
Updating FreeBSD repository catalogue...
....
Your packages are up to date.

 
Then install OpenSSH server package (version 8.7):

root@freebsdbox:~ # pkg install openssh-portable

The following 5 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
ldns: 1.7.1_2
libcbor: 0.8.0
libcjson: 1.7.15
libfido2: 1.8.0
openssh-portable: 8.7.p1_2,1

Number of packages to be installed: 5

Proceed with this action? [y/N]: y
[1/5] Fetching openssh-portable-8.7.p1_2,1.pkg: 100% 849 KiB 869.9kB/s 00:01
[2/5] Fetching libfido2-1.8.0.pkg: 100% 190 KiB 194.5kB/s 00:01
[3/5] Fetching libcbor-0.8.0.pkg: 100% 29 KiB 30.1kB/s 00:01
[4/5] Fetching libcjson-1.7.15.pkg: 100% 35 KiB 35.5kB/s 00:01
[5/5] Fetching ldns-1.7.1_2.pkg: 100% 518 KiB 530.0kB/s 00:01
Checking integrity... done (0 conflicting)
[1/5] Installing libcjson-1.7.15...
[1/5] Extracting libcjson-1.7.15: 100%
[2/5] Installing libcbor-0.8.0...
[2/5] Extracting libcbor-0.8.0: 100%
[3/5] Installing libfido2-1.8.0...
[3/5] Extracting libfido2-1.8.0: 100%
[4/5] Installing ldns-1.7.1_2...
[4/5] Extracting ldns-1.7.1_2: 100%
[5/5] Installing openssh-portable-8.7.p1_2,1...
[5/5] Extracting openssh-portable-8.7.p1_2,1: 100%
Done.

To enable this version of SSH server, I add;

openssh_enable="YES"

in /etc/rc.conf file. And in order to prevent conflict with the current SSH server in the base system, I add/edit the line;

sshd_enable="NO"

within the same file.

After a reboot, our newly installed SSH server will be activated. But before doing so, you might wish to review your SSH server settings, please note that, the openssh-portable that I installed, will be using its own configuration file from; /usr/local/etc/ssh/sshd_config instead of your current/default one which is likely; /etc/ssh/sshd_config.

So, compare and make your own changes in case of any need, BEFORE rebooting.

After restarting the server, let’s check the ssh server version again;

root@freebsdbox # /usr/local/bin/ssh -V
OpenSSH_8.7p1, OpenSSL 1.1.1k-freebsd 24 Aug 2021

^^This is the actively running ssh server on our server now. OpenSSH 8.7.

Now, let’s get the maximum SSHAudit score possible; the hardening part begins! Cd into OpenSSH directory and remove the default keys, create new ones then;

# cd /usr/local/etc/ssh/
# rm ssh_host_*
# ssh-keygen -t rsa -b 4096 -f ssh_host_rsa_key -N ""
Generating public/private rsa key pair.
Your identification has been saved in ssh_host_rsa_key.
Your public key has been saved in ssh_host_rsa_key.pub.
The key fingerprint is:
SHA256:.... root@freebsdbox
The key randomart image is:
+---[RSA 4096]----+
|... |
+----[SHA256]-----+

# ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N ""
Generating public/private ed25519 key pair.
Your identification has been saved in ssh_host_ed25519_key.
Your public key has been saved in ssh_host_ed25519_key.pub.
The key fingerprint is:
SHA256:....... root@freebsdbox
The key randomart image is:
+--[ED25519 256]--+
| .o... |
+----[SHA256]-----+

I removed the default keys and have just re-generated the RSA (4096-bit) and ED25519 keys for much secure&better exchange, through the commands above.

And always do generate your own SSH moduli! Why? Because the Operating Systems ship pre-computed moduli located as /etc/ssh/moduli (or /usr/local/etc/ssh/moduli) file.

Most users don’t change these moduli files. This facilitates pre-computation attacks. Additionally, these moduli files are often too short (<2048 bit) and vulnerable to attacks.

Shortly, minimum a 3072-bit modulus is needed to provide 128 bits of security.

# ssh-keygen -G moduli-3072.candidates -b 3072
(Generating new moduli file of 3072-bit)

# ssh-keygen -T moduli-3072 -f moduli-3072.candidates
(Testing process above will take a bit long depending on your CPU)

# mv moduli-3072 /usr/local/etc/ssh/moduli
(Moving the freshly generated secure moduli file&changing the default one)

-Alternatively, if the commands above don’t work (due to openssh version changes) you might consider creating the modulus using the ssh-keygen from OpenSSH package (Thanks to ‘Krond‘ for reporting) like:

(Alternative Way of creating modulus)
# /usr/local/bin/ssh-keygen -M generate -O bits=3072 moduli
Sun Nov 21 18:38:12 2021 Sieve next 150896640 plus 3071-bit
Sun Nov 21 18:41:36 2021 Sieved with 203277289 small primes in 204 seconds
Sun Nov 21 18:41:40 2021 Found 126350 candidates

# /usr/local/bin/ssh-keygen -M screen -f moduli moduli-final
Sun Nov 21 18:50:14 2021 processed 67459 of 126350 (53%) in 0:05, ETA 0:04
Sun Nov 21 18:54:40 2021 Found 39 safe primes of 105305 candidates in 565 seconds

# mv moduli-final /usr/local/etc/ssh/

With those commands above, first; I generated a 3072-bit moduli file, containing groups for the Diffie-Hellman Group Exchange/DH-GEX protocol. This is a memory-intensive process. And then checked/tested for DH group exchange candidate primes for safety and suitability, a CPU-intensive process.

Now, add the following 4 lines to the bottom of OpenSSH configuration file; /usr/local/etc/ssh/sshd_config

HostKeyAlgorithms rsa-sha2-512,rsa-sha2-256,ssh-ed25519

KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256

Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com

And with those lines; first, I disable the DSA and ECDSA host keys, restrict host keys to ED25519 and RSA only. Then restrict supported key exchange, cipher, and MAC algorithms, setting better&safer ones.

Finally make a configuration file health check and then, if there isn’t any error, restart the OpenSSH server;

root@freebsdbox:~ # service openssh configtest
Performing sanity check on openssh configuration.

(Seems good)

root@freebsdbox:~ # service openssh restart

Generating public/private dsa key pair.
Your identification has been saved in /usr/local/etc/ssh/ssh_host_dsa_key
Your public key has been saved in /usr/local/etc/ssh/ssh_host_dsa_key.pub
The key fingerprint is:
SHA256:........ root@freebsdbox
The key randomart image is:
+---[DSA 1024]----+
|.E. . |
+----[SHA256]-----+
You already have a RSA host key in /usr/local/etc/ssh/ssh_host_rsa_key
Skipping protocol version 2 RSA Key Generation

Generating public/private ecdsa key pair.
Your identification has been saved in /usr/local/etc/ssh/ssh_host_ecdsa_key
Your public key has been saved in /usr/local/etc/ssh/ssh_host_ecdsa_key.pub
The key fingerprint is:
SHA256:.........root@freebsdbox
The key randomart image is:
+---[ECDSA 256]---+
| .o. .. |
+----[SHA256]-----+
You already have a Elliptic Curve ED25519 host key in /usr/local/etc/ssh/ssh_host_ed25519_key
Skipping protocol version 2 Elliptic Curve ED25519 Key Generation

Performing sanity check on openssh configuration.
Stopping openssh.
Waiting for PIDS: 1012.

Performing sanity check on openssh configuration.
Starting openssh.

(ONE MORE RESTART:)

root@freebsdbox:~ # service openssh restart
Performing sanity check on openssh configuration.
Stopping openssh.
Waiting for PIDS: 5703.
Performing sanity check on openssh configuration.
Starting openssh.

We’re done now. Let’s check our SSH security score once again through SSHAudit;

SSH server security - after hardening.
SSH server security – after hardening.

Not bad, is it? Regarding the “Key Exchanges” score, having 4 of 5 passing (80%), SSHAudit states:

Note: Because of a bug in OpenSSH, 2048-bit DH moduli will still be used in some limited circumstances. Only a maximum score of 95% is possible.

By the way, if you’re getting “Invalid CSRF token!” on SSHAudit website, clear your browser’s cache&refresh page.

Well, that’s all from me today.

Ah, of course, all these security measures are not enough in a world that is open to attacks like the Internet. In the SSH server configuration file, there are a series of lines and options that you can tighten your security. There are also various security tightening methods such as;

1- Port-knocking. See: https://zeroflux.org/projects/knock

2- Tarsnap’s spiped SSH: https://www.tarsnap.com/spiped.html

3- Even “Tor Hidden Service” can be used for SSH, and this has multiple advantages; it provides an additional layer of encryption and server authentication. People looking at your traffic will not know your IP, so they will be unable to scan and target other services running on the same server and client: https://community.torproject.org/onion-services/setup/

I intend to cover all these other security methods in my next articles.

Thank you for reading,
Özgür Kazanççı
Twitter: @ozgurkazancci


4 Comments

Richard 22/11/2021 Reply

There’s a typo in the paragraph talking about copying your current sshd_config settings to the new file. It’s missing a directory: the new file is in `/usr/local/etc/ssh/sshd_config`

Özgür Kazanççı 22/11/2021 Reply

Ouch, my bad! Just corrected it, many thanks Richard!

Gerd 22/11/2021 Reply

That was interesting and very useful! Thanks for this.

dsl 17/08/2022 Reply

Looks detailed. I’ll give it a try. Thanks! 🙂

Leave a Reply