"SSL handshake failure" , How to add the client ip address to stick-table?


When haproxy logs the error, “SSL handshake failure”, I would like to add that client ip address to a stick-table. I wanted to know if it is possible to define an ACL that triggers the addition of the client ip to the stick-table even because TLS negotiation fails.

Our test server forces TLSv1.3 using “ssl-default-bind-options force-tlsv13” . The result is TLSv1.2 and earlier clients fail to connect logging “SSL handshake failure”, as is expected. Since the TLSv1.2 and earlier clients never negotiate the TLS connection, the standard ACL methods to look for TLS details like client ciphers do not work as far as I understand.

For example, the following “SSL handshake failure” log line was triggered by a TLSv1.2 cURL client connecting to a TLSv1.3 only server. The cURL client is immediately dropped and the server side TCP connection goes to a TIME_WAIT state for 60 seconds (Linux).

May  10 10:00:00 local0.info haproxy[35]: :12345 [10/May/2022:10:00:00.000] fe_https/1: SSL handshake failure

The problem we are seeing is the client(s) can connect repeatedly putting each TCP connection into a TIME_WAIT state. If the ip address was in a stick-table we could rate limit or even silent-drop to avoid TIME_WAIT states.

haproxy.conf simplified test configuration

   ssl-default-bind-ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256
   ssl-default-bind-options force-tlsv13 prefer-client-ciphers no-tls-tickets

backend gatekeeper
   stick-table type ipv6 size 1m expire 75m store gpc0,conn_cnt,conn_rate(75m),conn_cur,http_req_cnt,http_req_rate(75m),http_err_rate(75m)

frontend gateway
   mode tcp
   option tcplog
   bind :443
   tcp-request inspect-delay 5s
   # ACL - "SSL handshake failure" ??
   acl handerror < TBD "SSL handshake failure" check >
   acl trigger  src_inc_gpc0(gatekeeper)  gt  0
   tcp-request connection reject if handerror trigger
   # ACL - https://example.com
   acl example_sni req.ssl_sni example.com
   use_backend rt_https if example_sni

backend rt_https                     
   server fe_https unix@/var/lib/haproxy/haproxy_https.sock send-proxy-v2

frontend fe_https
   bind unix@/var/lib/haproxy/haproxy_https.sock ssl alpn h2,http/1.1 strict-sni crt /ssl/example.com.pem accept-proxy user haproxy mode 600
   # Track requests keyed by client ip once the https session is negotiated                                        
   tcp-request session track-sc0 src table gatekeeper                                                              
   # ACL                                     
   acl uncivilized method !METH_GET                                                                                                       
   http-request reject if uncivilized trigger                                                       
   http-request reject if banned                                         

backend bk_https                                                                
   mode http                                                                                                                                                       
   server .nginx. unix@/var/lib/haproxy/nginx.sock       

Thank you for your time. Any tips or pointers to the configuration manual are welcome.

# haproxy -V
HAProxy version 2.4.16-9d532c4 2022/04/29

I have not found a method in Haproxy to add the ip address of a client who triggers a “SSL handshake failure” log line. Since the client does not negotiate the TLS connection many of the ACL checks are not applicable.

An alternate solution is to grep the web logs for “SSL handshake failure” lines and use the haproxy_stats socket to add the ip address to the “gatekeeper” table. Here is a proof of concept script in case anyone is interested.

The script tails the last 2000 lines looking for the string “SSL handshake failure” and extracts client ip address who has triggered more than three(3) log lines. The script then pipes the output to socat adding each ip address to the “gatekeeper” table and setting the ip’s global counter to one(1).

set -euf -o pipefail

WHITELIST=" 66\.249\.| 192\.168\."

# Haproxy "SSL handshake failure"

Failure=$(/usr/bin/tail -2000 $LOGFILE | \
          /bin/egrep "haproxy.*(SSL handshake failure)" | \
          /bin/egrep -v "$WHITELIST" | \
          /usr/bin/awk '{print $7}' | \
          /bin/sed 's|\(.*\):.*|\1|' | \
          /usr/bin/sort -n | \
          /usr/bin/uniq -c | \
          /usr/bin/awk -v limit=3 '$1 > limit{print $2}')

for ip in $Failure
  echo "set table gatekeeper key ::ffff:$ip data.gpc0 1" | /usr/bin/socat unix-connect:/var/lib/haproxy/haproxy_stats.sock stdio
 #printf "$ip\n"

## EOF ##

I will continue looking for a way to trigger Haproxy to add “SSL handshake failure” ip address to a stick-table internal to Haproxy.

Any tips or pointers to the configuration manual are welcome.