Skip to content

SSL deployment FAQ

William Lallemand edited this page Jun 4, 2024 · 11 revisions

SSL deployment FAQ

RSA + ECDSA

configuration

Having an RSA + ECDSA configuration could be tricky to configure, it's not that difficult but one must be aware of limitations.

A standard configuration would have 2 files with a cert+key pairs in foobar.pem.ecdsa and foobar.pem.rsa. Please check that you certificates contains a CN or a DNS entry so you could select them using their SNI.

$ openssl x509 -in foobar.pem.rsa -noout -text  | grep '\(Subject: CN\|DNS\)'
        Subject: CN = foo.bar.com
                DNS:foo.bar.com

configured this way:

haproxy.cfg:

frontend in
     bind *:443 ssl crt foobar.pem.rsa crt foobar.pem.ecdsa

This configuration will allow you to match the RSA certificate when no Server Name indication was specified by the client (the first certificate declared is the fallback one). Or match either an RSA or ECDSA certificate if the right SNI was specified.

Testing

Without SNI this will result in delivering the first certificate, which is the RSA one:

$ openssl s_client -connect foo.bar.com:443 -tls1_2 </dev/null | openssl x509 -noout -text  | grep "Public Key Algorithm"
        Public Key Algorithm: rsaEncryption

Trying to reach haproxy without an SNI and with only an ECDSA cipher will result in an handshake failure.

$ openssl s_client -connect foo.bar.com:443 -cipher 'ECDHE-ECDSA-AES128-GCM-SHA256' -tls1_2 </dev/null  | openssl x509 -noout -text  | grep "Public Key Algorithm"
40D7FF43CD740000:error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1590:SSL alert number 40

Using a SNI allows to chose between the RSA and ECDSA ciphers:

$ openssl s_client -connect foo.bar.com:443 -servername foo.bar.com -cipher 'ECDHE-ECDSA-AES128-GCM-SHA256' -tls1_2 </dev/null  | openssl x509 -noout -text  | grep "Public Key Algorithm"
            Public Key Algorithm: id-ecPublicKey
$ openssl s_client -connect foo.bar.com:443 -servername foo.bar.com -cipher 'ECDHE-RSA-AES128-GCM-SHA256' -tls1_2 </dev/null  | openssl x509 -noout -text  | grep "Public Key Algorithm"
            Public Key Algorithm: rsaEncryption

Notes about testssl.sh and other tools

Some tools like testssl.sh could report that only ECDSA is available on the server, which is wrong. For example:

$ testssl -E foo.bar.com:443
 Testing ciphers per protocol via OpenSSL plus sockets against the server, ordered by encryption strength 

Hexcode  Cipher Suite Name (OpenSSL)       KeyExch.   Encryption  Bits     Cipher Suite Name (IANA/RFC)
-----------------------------------------------------------------------------------------------------------------------------
TLS 1.2
 xc02c   ECDHE-ECDSA-AES256-GCM-SHA384     ECDH 253   AESGCM      256      TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384            
 xc024   ECDHE-ECDSA-AES256-SHA384         ECDH 253   AES         256      TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384            
 xc00a   ECDHE-ECDSA-AES256-SHA            ECDH 253   AES         256      TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA               
 xcca9   ECDHE-ECDSA-CHACHA20-POLY1305     ECDH 253   ChaCha20    256      TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256      
 xc02b   ECDHE-ECDSA-AES128-GCM-SHA256     ECDH 253   AESGCM      128      TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256            
 xc023   ECDHE-ECDSA-AES128-SHA256         ECDH 253   AES         128      TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256            
 xc009   ECDHE-ECDSA-AES128-SHA            ECDH 253   AES         128      TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA               
TLS 1.3
 x1302   TLS_AES_256_GCM_SHA384            ECDH 253   AESGCM      256      TLS_AES_256_GCM_SHA384                             
 x1303   TLS_CHACHA20_POLY1305_SHA256      ECDH 253   ChaCha20    256      TLS_CHACHA20_POLY1305_SHA256                       
 x1301   TLS_AES_128_GCM_SHA256            ECDH 253   AESGCM      128      TLS_AES_128_GCM_SHA256                         

However this is not the case, it only returns this result because HAProxy replies with the ECDSA certificate during the test. To actually check the ciphers you need to test them one by one, which is not done by testssl.

Testing the ciphers

Testing the usable ciphers can be achieved by iterating on a openssl s_client -ciphers command. For example:

supported-ciphers.sh:

#!/usr/bin/env bash
SERVER=$1
SERVERNAME=${SERVER%%:*}
echo Available ciphers using ciphers list from $(openssl version).
while read -r LINE; do
	cipher=( $LINE )

	if [[ "${cipher[1]}" == "TLSv1.3" ]]; then
		args="-ciphersuites ${cipher[0]} -tls1_3"
	else
		args="-cipher ${cipher[0]} -tls1_2"
	fi
	result=`(printf "GET / HTTP/1.1\r\n\r\n"; sleep 0.1) | openssl s_client ${args[*]} -connect $SERVER -servername $SERVERNAME 2>&1`
	if [[ "$result" =~ "200 OK" ]]; then
		echo "${cipher[1]} ${cipher[0]}"
	fi
done < <(openssl ciphers -v 'ALL')
$ bash supported-ciphers.sh foo.bar.com:443 
Available ciphers using ciphers list from OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024).
TLSv1.3 TLS_AES_256_GCM_SHA384
TLSv1.3 TLS_CHACHA20_POLY1305_SHA256
TLSv1.3 TLS_AES_128_GCM_SHA256
TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384
TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384
TLSv1.2 ECDHE-ECDSA-CHACHA20-POLY1305
TLSv1.2 ECDHE-RSA-CHACHA20-POLY1305
TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256
TLSv1.2 ECDHE-ECDSA-AES256-SHA384
TLSv1.2 ECDHE-RSA-AES256-SHA384
TLSv1.2 ECDHE-ECDSA-AES128-SHA256
TLSv1.2 ECDHE-RSA-AES128-SHA256
TLSv1 ECDHE-ECDSA-AES256-SHA
TLSv1 ECDHE-RSA-AES256-SHA
TLSv1 ECDHE-ECDSA-AES128-SHA
TLSv1 ECDHE-RSA-AES128-SHA
TLSv1.2 AES256-GCM-SHA384
TLSv1.2 AES128-GCM-SHA256
TLSv1.2 AES256-SHA256
TLSv1.2 AES128-SHA256
SSLv3 AES256-SHA
SSLv3 AES128-SHA

You can see that the list of ciphers is correct there.

Common errors

TLS is difficult to configure, before debugging, you must ensure that your configuration is doing what you need and what you want to support. Please use these tools:

SSL handshake failure

The "SSL handshake failure" error in your HAProxy logs is a common error that can appear when a client can't achieve the SSL handshake. Most of the time it's because of an incompatibility between the client and the HAProxy frontend configuration. Poorly written bots can do a lot of mess in your logs because of this.

HAProxy provides multiple tools to debug this issue:

Starting with HAProxy 2.5 an error-log-format can be used, this can help you debug the handshake. The %[ssl_fc_err] fetch contains the OpenSSL error code and %[ssl_fc_err_str] contains the associated string.

Starting with HAProxy 2.8, the "SSL hanshake failure" error is followed by the OpenSSL error string, example:

<134>May 12 17:14:04 haproxy[183151]: 127.0.0.1:49346 [12/May/2023:17:14:04.571] frt2/1: SSL handshake failure (error:0A000418:SSL routines::tlsv1 alert unknown ca)

No shared cipher

SSL handshake failure (error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher)

This is the error you can have when the client does not share any ciphers with the HAProxy frontend, this could be because the client only uses old ciphers that are disabled by default with your SSL library. Depending on your SSL library configuration, distribution and build, some ciphers could be disabled. Modern distributions are disabling ciphers using SHA1 or MD5 signatures, or even RSA, DSA en DH keys shorter than 1024 bits. This behaviour could be restored using by tweaking the OpenSSL security level. With HAProxy 3.0 this can be easily done with the ssl-security-level keyword in the global section.

Note:

DHE ciphers are disabled by default since HAProxy 2.6, you can re-enable them with the tune.ssl.default-dh-param global parameter

Unsupported TLS version

SSL handshake failure (error:14209102:SSL routines:tls_early_post_process_client_hello:unsupported protocol)
SSL handshake failure (error:1408F10B:SSL routines:ssl3_get_record:wrong version number)

You can get these messages when a client is trying to connect with an old version of TLS or SSL which is not supported by your setup.

Most modern distributions are disabling SSLv3, TLS 1.0 and TLS 1.1, if you want to enable them back, you need to change the OpenSSL security level in openssl.cnf or with ssl-security-level (>= haproxy 3.0)

Note:

TLS versions below TLSv1.2 are disabled by default since HAProxy 2.2, to re-enable them, you need to use the ssl-min-ver in either the bind line or on the ssl-default-bind-options keyword of the global section.

Http request

SSL handshake failure (error:1408F09C:SSL routines:ssl3_get_record:http request)

This is Openssl getting an HTTP clear request on an HTTPS bind.

Callback failed

SSL handshake failure (error:142090EA:SSL routines:tls_early_post_process_client_hello:callback failed)

This error happens when the OpenSSL ClienHello callback failed, usually this is because it didn't found a certificate that matches the ServerName provided by the client.