Setting SSL/TLS protocol versions with PostgreSQL 12

January 17, 2020

PostgreSQL 12 contains two new server settings::

  • ssl_min_protocol_version
  • ssl_max_protocol_version

As the names indicate, these are used to control the oldest (minimum) and newest (maximum) version of the SSL and TLS protocol family that the server will accept.

(For historical reasons, in PostgreSQL, all settings related to SSL and TLS are named ssl_something, even though TLS is the currently correct term. In this article, I will use both terms and use version numbers were more precision is required.)

Right now, the following versions of the SSL/TLS protocol exist:

  • SSL 1.0 (never publicly released)
  • SSL 2.0 (1995)
  • SSL 3.0 (1996)
  • TLS 1.0 (1999)
  • TLS 1.1 (2006)
  • TLS 1.2 (2008)
  • TLS 1.3 (2018)

SSL support in PostgreSQL first appeared in version 7.0, released in 2000 (commit). (Curiously, this was not mentioned in the release notes at all at the time.) It supported SSL 2.0 and up.

In PostgreSQL 7.4, released in 2003, support for SSL 2.0 was removed from the source code (commit). Support for SSL 3.0 was removed in PostgreSQL 9.4, released in 2014 (commit). (If you check the trail of commit messages carefully, there was some confusion in the intermediate versions where apparently the client side (libpq) would insist on using TLS 1.0 exactly but the server would accept any version, including SSL 3.0 and newer TLS versions. There were also a number of interoperability issues to contend with while introducing the various new TLS versions. If you need yet another reason to get rid of your old PostgreSQL versions, there is one.)

Which TLS versions PostgreSQL supports also depends on what the OpenSSL library in use supports. All releases under the “OpenSSL” label (as opposed to the predecessor “SSLeay”) support at least TLS 1.0. For TLS 1.1 and TLS 1.2, OpenSSL 1.0.1 (2012) is required. For TLS 1.3, OpenSSL 1.1.1 (2018) is required. Many vendors ship patched OpenSSL versions, and there are a number of OpenSSL-compatible libraries (such as LibreSSL) that may have other capabilities. So in general this is quite tricky to track.

By default, PostgreSQL server and client leave the negotiation of the TLS version up to the SSL library. They will negotiate a TLS version between 1.0 and 1.3 transparently and use the latest one that both sides support.

The reason new versions of TLS are developed is, among other reasons, that security issues are found in the older versions. So almost universally, using a later version is better than using a previous version. The only issue is that all the software needs to be updated to actually support the newer versions.

How can you check what TLS versions you are currently using?

You can use the system view pg_stat_ssl to see the SSL status of all connections:

SELECT * FROM pg_stat_ssl;

  pid  | ssl | version |         cipher         | bits | compression | client_dn | client_serial | issuer_dn
-------+-----+---------+------------------------+------+-------------+-----------+---------------+-----------
 59609 | t   | TLSv1.3 | TLS_AES_256_GCM_SHA384 |  256 | f           |           |               |

(Join with pg_stat_activity on the pid column to see more details on each connection. Full access to these views may require superuser permissions.)

If you enable log_connections in the server, each connection attempt over TCP/IP will also contain information about the SSL parameters used.

2019-11-23 09:49:23.322 CET [59633] LOG:  connection authorized: user=peter database=postgres application_name=psql SSL enabled (protocol=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384, bits=256, compression=off)

Using either of these tools, perhaps combined with log analysis tools or similar, you can analyze the TLS use in your environment and detect clients using suspiciously old versions.

If you are using psql, it will also print the same information on the client side when you connect:

$ psql -h localhost
psql (12.1)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=#

and you can use the psql command \conninfo to have this information printed at any time.

If you are writing your own client application using libpq, this information can be obtained using the function PQsslAttribute().

For testing, you can also use the openssl command directly, for example

openssl s_client -connect localhost:5432 -starttls postgres

This will print out a lot of low-level protocol information, including the TLS version, cipher, and so on. Note that you must use the option -starttls postgres, or otherwise this command will report that no SSL is in use. This requires at least OpenSSL 1.1.1.

Now, in order to improve security, you may want to disable the use of old versions TLS. That’s what these settings are for. So for example, if you set

ssl_min_protocol_version = 'TLSv1.1'

then connections with TLS 1.0 are rejected. Similarly, you could set it to TLSv1.2 or TLSv1.3. The default right now is TLSv1 (which means TLS 1.0; the naming is inconsistent for historical reasons).

If a client does not support the version of TLS that is the minimum required, they will get a connection error. So this is not a good option to put into production without careful analysis and testing. You should use the above tools to make sure that no client (that you care about) still uses old TLS versions.

So which TLS version is a good one?

As one data point, PCI DSS requires disabling TLS 1.0 as of 30 June 2018. That means, as of this writing, if you are still using, or allowing the use of, TLS 1.0, then you are not satisfying PCI DSS. Of course, PCI DSS technically only applies to applications handling credit card data, but it is a fairly well-regarded security standard that is also followed by many outside of its direct area of applicability. PCI DSS also strongly encourages the use of TLS 1.2, so while TLS 1.1 is not officially disallowed at this point, the writing is on the wall.

So at the very least, I would recommend setting the minimum version to TLSv1.1, as shown above.

Secondly, browser vendors are planning to remove support for TLS 1.0 and TLS 1.1 from browsers in “early 2020”. There is also an IETF draft to officially deprecate these protocol versions.

Note that all OpenSSL versions that support TLS 1.1 also support TLS 1.2, so setting the minimum version to TLS 1.2 instead of TLS 1.1 would have no practical drawback for users of OpenSSL, as far as I can tell. There are PostgreSQL clients that don’t use OpenSSL, such as the JDBC driver, but it seems unlikely that the version of the Java stack you use would not support at least TLS 1.2 at this point.

It is probably too early to require TLS 1.3. The protocol itself and the required OpenSSL release are only about a year old, and so you probably won’t find them in most operating installations that are currently in use.

Perhaps you are wondering, why do we also have a setting for the maximum version? That setting is not required for configuring security, but it serves two other purposes: First, it allows testing the protocol negotiation and SSL configuration settings in PostgreSQL. And second, there are occasionally combinations of software components with broken support for some TLS versions. For example, when TLS 1.3 came onto the scene, some specific combinations of PostgreSQL-related software components failed to support TLS 1.3 and errored out during a connection attempt. Such situations can be worked around by disabling newer protocol versions on the server side. All instances of such situations in the PostgreSQL ecosystem have been fixed, as far as I know, but it’s still good to have this option around.

 

Share this