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  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:
Copy a built-in policy file, make the desired changes, then scan our server(s) with -P path/to/custom/policy just like before.
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 = email@example.com, firstname.lastname@example.org, email@example.com, firstname.lastname@example.org, email@example.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, firstname.lastname@example.org, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha256 # The ciphers that must match exactly (order matters). ciphers = email@example.com, firstname.lastname@example.org, email@example.com, aes256-ctr, aes192-ctr, aes128-ctr # The MACs that must match exactly (order matters). macs = firstname.lastname@example.org, email@example.com, firstname.lastname@example.org
The key exchanges, ciphers, and macs field are self-explanatory. The lists here must match the server’s list exactly. Note that order matters.
For convenience, server policy scans have been implemented in the web front-end as well: https://www.ssh-audit.com/.