SSH Policy Configuration Checks With ssh-audit

Recently I released ssh-audit v2.3.0 with new policy configuration checking support. This makes IT admins' lives easier, since it boils down the output of ssh-audit to a simple pass/fail answer.

Standard Scans vs Policy Scans

Normally, ssh-audit goes through the list of cryptographic algorithms supported by the server, and rates each one. Here’s example output against an unhardened Ubuntu Server 20.04 system:

After applying the right hardening guide for this OS, ssh-audit’s output looks like this:

Wouldn’t it be nice, though, if ssh-audit simply told us if the server is correctly hardened or not? This is where policy checks come into play.

Let’s take a look at what built-in policies we have:

$ ./ssh-audit.py -L

Server policies:

Name:        Hardened OpenSSH v7.7 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_7_7.txt

Name:        Hardened OpenSSH v7.8 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_7_8.txt

Name:        Hardened OpenSSH v7.9 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_7_9.txt

Name:        Hardened OpenSSH v8.0 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_8_0.txt

Name:        Hardened OpenSSH v8.1 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_8_1.txt

Name:        Hardened OpenSSH v8.2 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_8_2.txt

Name:        Hardened OpenSSH v8.3 (version 1)
Policy path: /home/jdog/ssh-audit/policies/openssh_8_3.txt

Name:        Hardened Ubuntu Server 16.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_server_16_04.txt

Name:        Hardened Ubuntu Server 18.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_server_18_04.txt

Name:        Hardened Ubuntu Server 20.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_server_20_04.txt


Client policies:

Name:        Hardened Ubuntu Client 16.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_client_16_04.txt

Name:        Hardened Ubuntu Client 18.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_client_18_04.txt

Name:        Hardened Ubuntu Client 20.04 LTS (version 1)
Policy path: /home/jdog/ssh-audit/policies/ubuntu_client_20_04.txt

Great, there’s one for Ubuntu Server 20.04 already! Let’s use it on an un-hardened machine:

And here’s the result on a hardened machine:

Now we have easy-to-understand output that everything is in order!

Automating Policy Scans

ssh-audit now returns exit code 0 to signify success to any calling program. This lets us automate it to ensure that a server(s) stays hardened over a period of time.

And speaking of servers (plural), there’s the new -T option to specify multiple targets. Here’s an example of scanning three servers against the hardened Ubuntu Server 20.04 policy:

The calling program can simply check that the exit code is 0 to know that all servers are properly hardened. (Or in this case, the calling program gets a non-zero exit code [3] and knows that at least one failure occurred.)

If the calling program wants to parse the error messages, then it should also add -j to ssh-audit’s arguments to enable JSON output.

Creating Custom Policies

As we saw above, the -L option shows all built-in policies, which include hardened configurations for Ubuntu 16.04, 18.04, 20.04, as well as generic OpenSSH v7.7 - v8.4.

But what if we have a different concept of “hardened”? Or what if we’re stuck with an out-dated server that can’t be properly configured (but can still be somewhat improved, like a network appliance)? In that case, ssh-audit supports custom policies.

There are two ways to create a custom policy:

  1. Copy a built-in policy file, make the desired changes, then scan our server(s) with -P path/to/custom/policy just like before.

  2. Set up a server that is configured to what is considered “compliant” (according to your definition), then scan it with -M new_policy.txt. This tells ssh-audit to create a policy in new_policy.txt using the server’s exact configuration as a template. Now you can scan other servers with -P new_policy.txt and see if they match the exact configuration of the example server.

If you have only 1 or 2 small changes to make to an existing policy, then use method #1. Otherwise, for more extensive changes, it’ll be easier to use method #2.

Policy File Syntax

The policy file syntax that ssh-audit understands is pretty straight-forward. Let’s look at the built-in policy for Ubuntu Server 20.04 (though if you’re going to make changes, you should make your own copy):

name = "Hardened Ubuntu Server 20.04 LTS"
version = 1

The name field is any string of characters you’d like. version is any integer you’d like. These are displayed in the output to the user with the pass/fail message.

# RSA host key sizes.
hostkey_size_rsa-sha2-256 = 4096
hostkey_size_rsa-sha2-512 = 4096

Like the comment says, these are the sizes (in bits) that the RSA host keys must be. The target must match them exactly.

# Group exchange DH modulus sizes.
dh_modulus_size_diffie-hellman-group-exchange-sha256 = 2048

This is the Diffie-Hellman modulus size (in bits) for the diffie-hellman-group-exchange-sha256 key exchange. The target must match this exactly.

# The host key types that must match exactly (order matters).
host keys = rsa-sha2-512, rsa-sha2-256, ssh-ed25519

This is a list of all the host key types that the server must exactly match.

# Host key types that may optionally appear.
optional host keys = sk-ssh-ed25519@openssh.com, ssh-ed25519-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com

This is a list of optional host key types that the server may advertise. Not all servers are configured to use smart cards or certificates, hence these are optional.

# The key exchange algorithms that must match exactly (order matters).
key exchanges = curve25519-sha256, curve25519-sha256@libssh.org, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha256

# The ciphers that must match exactly (order matters).
ciphers = chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com, aes256-ctr, aes192-ctr, aes128-ctr

# The MACs that must match exactly (order matters).
macs = hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, umac-128-etm@openssh.com

The key exchanges, ciphers, and macs field are self-explanatory. The lists here must match the server’s list exactly. Note that order matters.

Web Front-End

For convenience, server policy scans have been implemented in the web front-end as well: https://www.ssh-audit.com/.

Enjoy!