Migrating from nginx to haproxy 2.0.5 SSL issues

Hi,

We are trying to migrate nginx proxy servers to ha proxy but we are experiencing some issues. We get the following error

4431185516:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:s3_pkt.c:1498:SSL alert number 40
4431185516:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177:
global
  daemon
  log stdout format raw local0 debug
  tune.ssl.default-dh-param 2048
  ssl-default-bind-ciphers EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:AES256+EECDH:AES256+EDH:DHE-RSA-AES256-SHA

  ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
  ssl-default-bind-options no-tlsv10 no-tlsv11 no-tls-tickets
#  ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
  ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
#  ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
  ssl-default-server-options no-tlsv10 no-tlsv11 no-tls-tickets
  ssl-default-server-ciphers EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:AES256+EECDH:AES256+EDH:DHE-RSA-AES256-SHA

defaults
  log     global
  timeout connect  4s
  timeout client  60s
  timeout server  60s
  option  httplog
  option  httpchk
  option  dontlognull
  default-server init-addr none

resolvers dnsserver1
  nameserver opendns ${DNS_ADDR}:53
  hold valid 1s

frontend http
  mode http
  bind *:443 ssl crt /etc/ssl/haproxy.pem ca-file /etc/ssl/cacert.pem verify required accept-proxy alpn http/1.0,h2,http/1.1
  option  forwardfor
  timeout client 1m
  log     global
  log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ {%[ssl_c_verify],%[ssl_c_s_dn],%[ssl_c_i_dn]}\ %{+Q}r,\ %{+Q}[ssl_c_der,base64]

  http-request set-header X-Haproxy-Current-Date %T
  http-request set-header X-SSL                  %[ssl_fc]
  http-request set-header X-SSL-Session_ID       %[ssl_fc_session_id,hex]
  http-request set-header X-SSL-Client-Verify    %[ssl_c_verify]
  http-request set-header X-SSL-Client-DN        %{+Q}[ssl_c_s_dn]
  http-request set-header X-SSL-Client-CN        %{+Q}[ssl_c_s_dn(cn)]
  http-request set-header X-SSL-Issuer           %{+Q}[ssl_c_i_dn]
  http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
  http-request set-header X-SSL-Client-NotAfter  %{+Q}[ssl_c_notafter]
  http-request set-header X-SSL-Subject-DN       %{+Q}[ssl_c_s_dn]
  http-request set-header X-SSL-Issuer-DN        %{+Q}[ssl_c_i_dn]
  http-request set-header X-SSL-Client-Subject-DN %{+Q}[ssl_c_s_dn]
  http-request set-header X-SSL-Client-Issuer-DN %{+Q}[ssl_c_i_dn]

  http-request set-header X-SSL-Cipher %[ssl_fc_cipher]
  http-request set-header X-SSL-Protocol %[ssl_fc_protocol]
  http-request set-header X-SSL-Session_ID %[ssl_fc_session_id,hex]
  http-request set-header X-Forwarded-Proto \ https

  default_backend srvs_api

backend srvs_api
  mode    http
  log     global
  option  httpchk HEAD /
  http-check expect status 503
  balance roundrobin
  server api.0 api.core."$DNS_DOMAIN":443 resolvers dnsserver1 check inter 2000 rise 2 fall 5 ssl crt /etc/ssl/haproxy_client.pem ca-file /etc/ssl/service-server-cacert.pem
  server api.1 api.core."$DNS_DOMAIN":443 resolvers dnsserver1 check inter 2000 rise 2 fall 5 ssl crt /etc/ssl/haproxy_client.pem ca-file /etc/ssl/service-server-cacert.pem
  server api.2 api.core."$DNS_DOMAIN":443 resolvers dnsserver1 check inter 2000 rise 2 fall 5 ssl crt /etc/ssl/haproxy_client.pem ca-file /etc/ssl/service-server-cacert.pem
  timeout connect         10s
  timeout server          1m
  http-request set-header X-Forwarded-Port %[dst_port]
  http-request add-header X-Forwarded-Proto https if { ssl_fc }

  http-request set-header X-Forwarded-By %[var(proc.xforwardedby)]
  http-request set-header X-Forwarded-For "${DOCKERIP}:%[dst_port]"
  http-request set-header X-Client-IP %[var(proc.xclientip)]

This is the output of the server startup

HA-Proxy version 2.0.5 2019/08/16 - https://haproxy.org/
Build options :
TARGET = linux-glibc
CPU = generic
CC = gcc
CFLAGS = -O2 -g -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -Wno-format-truncation -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-old-style-declaration -Wno-ignored-qualifiers -Wno-clobbered -Wno-missing-field-initializers -Wno-implicit-fallthrough -Wno-stringop-overflow -Wno-cast-function-type -Wtype-limits -Wshift-negative-value -Wshift-overflow=2 -Wduplicated-cond -Wnull-dereference
OPTIONS = USE_PCRE2=1 USE_PCRE2_JIT=1 USE_GETADDRINFO=1 USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1

Feature list : +EPOLL -KQUEUE -MY_EPOLL -MY_SPLICE +NETFILTER -PCRE -PCRE_JIT +PCRE2 +PCRE2_JIT +POLL -PRIVATE_CACHE +THREAD -PTHREAD_PSHARED -REGPARM -STATIC_PCRE -STATIC_PCRE2 +TPROXY +LINUX_TPROXY +LINUX_SPLICE +LIBCRYPT +CRYPT_H -VSYSCALL +GETADDRINFO +OPENSSL +LUA +FUTEX +ACCEPT4 -MY_ACCEPT4 +ZLIB -SLZ +CPU_AFFINITY +TFO +NS +DL +RT -DEVICEATLAS -51DEGREES -WURFL -SYSTEMD -OBSOLETE_LINKER +PRCTL +THREAD_DUMP -EVPORTS

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

Built with multi-threading support (MAX_THREADS=64, default=1).
Built with OpenSSL version : OpenSSL 1.1.1c 28 May 2019
Running on OpenSSL version : OpenSSL 1.1.1c 28 May 2019
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
Built with Lua version : Lua 5.3.5
Built with network namespace support.
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
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 PCRE2 version : 10.33 2019-04-16
PCRE2 library supports JIT : yes
Encrypted password support via crypt(3): yes
Built with the Prometheus exporter as a service

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 multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
h2 : mode=HTX side=FE|BE mux=H2
h2 : mode=HTTP side=FE mux=H2
<default> : mode=HTX side=FE|BE mux=H1
<default> : mode=TCP|HTTP side=FE|BE mux=PASS

Available services :
prometheus-exporter

Available filters :
[SPOE] spoe
[COMP] compression
[CACHE] cache
[TRACE] trace

Proxy http started.
Proxy srvs_api started.
[NOTICE] 252/055404 (1) : New worker #1 (33) forked
[WARNING] 252/055404 (33) : srvs_api/api.0 changed its IP from to 172.28.4.75 by dnsserver1/opendns.
[WARNING] 252/055404 (33) : Server srvs_api/api.0 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
[WARNING] 252/055404 (33) : Server srvs_api/api.0 administratively READY thanks to valid DNS answer.
[WARNING] 252/055404 (33) : srvs_api/api.1 changed its IP from to 172.28.5.93 by DNS cache.
[WARNING] 252/055404 (33) : Server srvs_api/api.1 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
[WARNING] 252/055404 (33) : Server srvs_api/api.1 administratively READY thanks to valid DNS answer.
[WARNING] 252/055404 (33) : srvs_api/api.2 changed its IP from to 172.28.6.224 by DNS cache.
[WARNING] 252/055404 (33) : Server srvs_api/api.2 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
[WARNING] 252/055404 (33) : Server srvs_api/api.2 administratively READY thanks to valid DNS answer.
srvs_api/api.0 changed its IP from to 172.28.4.75 by dnsserver1/opendns.
Server srvs_api/api.0 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
Server srvs_api/api.0 administratively READY thanks to valid DNS answer.
srvs_api/api.1 changed its IP from to 172.28.5.93 by DNS cache.
Server srvs_api/api.1 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
Server srvs_api/api.1 administratively READY thanks to valid DNS answer.
srvs_api/api.2 changed its IP from to 172.28.6.224 by DNS cache.
Server srvs_api/api.2 ('api.core.qa.au.littlepay.com') is UP/READY (resolves again).
Server srvs_api/api.2 administratively READY thanks to valid DNS answer.

The log is shown as follows:

XX.XX.XX.XX:3524 [10/Sep/2019:07:19:16.571] http/1: SSL handshake failure

It would be really appreciated any help :slight_smile:
Nestor

SSL alert number 40 really just means handshake failure, which is not very informative.

Do you have any additional logs from your backend server? Could it be that it just needs SNI or perhaps there is a ciphers mismatch?

Did you enfore the same TLS settings with nginx? What did that configuration look like?

Hi Lukas,

Thanks for your quick response. This is my Nginx xonfiguration.

server {

listen       443 ssl;
server_name  ALIAS_NAME;

ssl_certificate           /etc/nginx/certs/nginx.bundle.cer;
ssl_certificate_key       /etc/nginx/certs/nginx.key;
ssl_client_certificate    /etc/nginx/certs/cacert.pem;
ssl_verify_client         on;
ssl_prefer_server_ciphers on;
ssl_protocols             TLSv1.2;
ssl_ciphers               'EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:AES256+EECDH:AES256+EDH:DHE-RSA-AES256-SHA';
ssl_dhparam               /etc/nginx/dhparam.pem;
ssl_session_cache         shared:SSL:10m;
ssl_session_tickets       on;
ssl_session_timeout       1h;
ssl_verify_depth          2;

ssl_stapling              on;
ssl_stapling_verify       on;
resolver                  127.0.0.11 valid=60s;
resolver_timeout          2s;

proxy_ssl_trusted_certificate /etc/nginx/certs/service-server-cacert.pem;
proxy_ssl_verify              on;
proxy_ssl_verify_depth        2;
proxy_ssl_session_reuse       on;

proxy_connect_timeout         4s;
proxy_send_timeout            60s;
proxy_read_timeout            60s;
send_timeout                  60s;

error_page   500 502 503 504  /50x.html;
location = /50x.html {
    root   /usr/share/nginx/html;
}

include /etc/nginx/location.d/*.conf;

}

I am trying to get more logsā€¦!

Thanks once again