Disable certain TLS 1.3 ciphers in haproxy.cfg

I am unable to disable certain 128 bit TLS 1.3 ciphers in HAProxy. With the OpenSSL command line you have to split the cipher string in two parts for disabling default TLS 1.3 ciphers. One part for TLS 1.3 with paramter -ciphersuites and another part for TLS 1.2 and lower without paramter. Is it possible to do the same with haproxy.cfg ssl-default-bind-ciphers as on the command line with splitted parameter or how is this possible within haproxy.cfg?

Here is my setup:

haproxy -vv
HA-Proxy version 1.8.13 2018/07/30
Copyright 2000-2018 Willy Tarreau <willy@haproxy.org>

Build options :
  TARGET  = linux2628
  CPU     = native
  CC      = gcc
  CFLAGS  = -O2 -march=native -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -fno-strict-overflow -Wno-format-truncation -Wno-null-dereference -Wno-unused-label
  OPTIONS = USE_LINUX_TPROXY=1 USE_CRYPT_H=1 USE_GETADDRINFO=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 USE_SYSTEMD=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_TFO=1

Default settings :
  maxconn = 2000, bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Built with OpenSSL version : OpenSSL 1.1.1-pre8 (beta) FIPS 20 Jun 2018
Running on OpenSSL version : OpenSSL 1.1.1-pre8 (beta) FIPS 20 Jun 2018
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : SSLv3 TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
Encrypted password support via crypt(3): yes
Built with multi-threading support.
Built with PCRE2 version : 10.31 2018-02-12
PCRE2 library supports JIT : yes
Built with zlib version : 1.2.11
Running on zlib version : 1.2.11
Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Built with network namespace support.

Available polling systems :
      epoll : pref=300,  test result OK
       poll : pref=200,  test result OK
     select : pref=150,  test result OK
Total: 3 (3 usable), will use epoll.

Available filters :
        [SPOE] spoe
        [COMP] compression
        [TRACE] trace
openssl version -a
OpenSSL 1.1.1-pre8 (beta) FIPS 20 Jun 2018
built on: Tue Jul 31 14:27:57 2018 UTC
platform: linux-x86_64
options:  bn(64,64) md2(char) rc4(16x,int) des(int) idea(int) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -O3 -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -Wa,--noexecstack -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPADLOCK_ASM -DPOLY1305_ASM -DZLIB -DNDEBUG -DPURIFY -DSYSTEM_CIPHERS_FILE="/etc/crypto-policies/back-ends/openssl.config"
OPENSSLDIR: "/etc/pki/tls"
ENGINESDIR: "/usr/lib64/engines-1.1"
Seeding source: os-specific
engines:  rdrand dynamic
haproxy.cfg excerpt
ssl-default-bind-options        no-sslv3 no-tlsv11 no-tlsv10 no-tls-tickets
ssl-default-bind-ciphers        TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:RSA+AES256:!RSA:!MD5:!AES128:!AESCCM

Testing with OpenSSL command line shows that you have to split the cipher string in two parts, one for TLS 1.3 and another for TLS 1.2. See TLS1.3 - OpenSSLWiki for further details.

Some tests with openssl ciphers command line

openssl ciphers -v ‘’

TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
TLS_AES_128_CCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESCCM(128) Mac=AEAD

openssl ciphers -v -ciphersuites ‘TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256’ ‘’

TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD

openssl ciphers -v -ciphersuites ‘TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256’ ‘EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:RSA+AES256:!RSA:!MD5:!AES128:!AESCCM’

TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
ECDHE-ECDSA-AES256-SHA  TLSv1 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
ECDHE-RSA-AES256-SHA    TLSv1 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1

openssl ciphers -v ‘TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:RSA+AES256:!RSA:!MD5:!AES128:!AESCCM’

TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
TLS_AES_128_CCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESCCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=RSA  Enc=CHACHA20/POLY1305(256) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA384
ECDHE-ECDSA-AES256-SHA  TLSv1 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1
ECDHE-RSA-AES256-SHA    TLSv1 Kx=ECDH     Au=RSA  Enc=AES(256)  Mac=SHA1

openssl ciphers -v -ciphersuites ‘TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:EECDH+AESGCM:EECDH+CHACHA20:EECDH+AES256:RSA+AES256:!RSA:!MD5:!AES128:!AESCCM’

Error setting TLSv1.3 ciphersuites
140422778111808:error:1426E0B9:SSL routines:ciphersuite_cb:no cipher match:ssl/ssl_ciph.c:1302:

No, this API is not currently implemented.

But why would you disable those 128-bit ciphers? The ciphers in TLSv1.3 have been restricted to only a handful of completely secure ciphers by leading crypto experts.

Just because the symmetric encryption is 128-bit doesn’t mean it’s less secure.

If you connect to Google or Amazon, your browser will use 128-bit AES-GCM, because Google and Amazon prefer 128-bit over 256-bit. Brute-forcing the symmetric key doesn’t make a lot of sense.

I do agree we need to be able to configure this though.

You are absolutely right. There is no need to disable them unless you want a 100% score on SSLLabs.

Fun fact: You only get 90% on cipher strength there when 128 bit TLS 1.3 ciphers are enabled.

Yeah, that’s seems like a stupid thing for SSLlabs todo …

Here some open github issues for SSLlabs to fix this:


Not only that you have to enable at least one special AES128 cipher for HTTP/2 support regarding to RFC7540 you also have to allow prime256 elliptic curves which reduces the score for key exchange by another 10% even if a secure server preferred order is set. HAProxy handles this perfectly correct. So this is all SSLlabs fault because disabling TLS 1.2 is not a valid option at all. :wink:

bind :::443 v4v6 ssl alpn h2,http/1.1 curves secp384r1:X25519:P-256 ...

X25519 is the default for OpenSSL implementations so this probably should be enabled too.

RFC7540 says clearly:

The black list includes the cipher suite that TLS 1.2 makes mandatory, which means that TLS 1.2 deployments could have non- intersecting sets of permitted cipher suites. To avoid this problem causing TLS handshake failures, deployments of HTTP/2 that use TLS 1.2 MUST support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 with the P-256 elliptic curve.

Further draft-ietf-tls-tls13-28 RFC says:

9.1. Mandatory-to-Implement Cipher Suites

In the absence of an application profile standard specifying otherwise, a TLS-compliant application MUST implement the TLS_AES_128_GCM_SHA256 cipher suite and SHOULD implement the TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256 cipher suites.
A TLS-compliant application MUST support digital signatures with rsa_pkcs1_sha256 (for certificates), rsa_pss_rsae_sha256 (for CertificateVerify and certificates), and ecdsa_secp256r1_sha256. A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256) and SHOULD support key exchange with X25519.

As far as I know in OpenSSL 1.1.1-pre9 the fourth cipher (which is still present in pre8 beta) is already deactivated and reflects the current draft status. Thanks for your clarification about this matter.