SSL Handshake failure on ssh

Trying to add specific routing depending on SSH destination fails.

My haproxy.cfg looks like this:

global
	log /dev/log	local0 info
	log /dev/log	local1 info 
	chroot /var/lib/haproxy
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	tune.ssl.default-dh-param 2048
	ssl-default-bind-options no-sslv3 no-tls-tickets
	ssl-default-bind-ciphers ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA

defaults
	log	global
	#mode	http
	option	httplog
	option	dontlognull
	timeout connect 50000
	timeout client  500000
	timeout server  500000
	errorfile 400 /etc/haproxy/errors/400.http
	errorfile 403 /etc/haproxy/errors/403.http
	errorfile 408 /etc/haproxy/errors/408.http
	errorfile 500 /etc/haproxy/errors/500.http
	errorfile 502 /etc/haproxy/errors/502.http
	errorfile 503 /etc/haproxy/errors/503.http
	errorfile 504 /etc/haproxy/errors/504.http

frontend ssh
	bind *:22 ssl no-sslv3 crt /path/to/cert.pem
	mode tcp
	option tcplog
	log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq dst:%[var(sess.dst)] "
	tcp-request content set-var(sess.dst) ssl_fc_sni
	tcp-request inspect-delay 5s
	acl valid_payload req.payload(0,7) -m str "SSH-2.0"
	tcp-request content reject if !valid_payload
	tcp-request content accept if { req_ssl_hello_type 1 }
	acl gitlab ssl_fc_sni gitlab.contoso.com
#	tcp-request content reject if !gitlab
	default_backend bck_gitlab_ssh

backend bck_gitlab_ssh
	mode tcp
	server gitlab a.b.c.d:22 check

frontend http-in
	bind *:80
	bind *:443 ssl no-sslv3 crt /path/to/cert.pem
	mode	http
	option	httplog
        [...]

I launched the openssl s_client -connect subdomain.contoso.com:22 with following result:

CONNECTED(00000003)
— Certificate chain 0 s:CN = *.subdomain.contoso.com i:C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN =
Sectigo RSA Domain Validation Secure Server CA 1 s:C = GB, ST =
Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA
Domain Validation Secure Server CA i:C = US, ST = New Jersey, L =
Jersey City, O = The USERTRUST Network, CN = USERTrust RSA
Certification Authority 2 s:C = US, ST = New Jersey, L = Jersey City,
O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
i:C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network,
CN = USERTrust RSA Certification Authority
— Server certificate
-----BEGIN CERTIFICATE----- […]
-----END CERTIFICATE----- subject=CN = *.subdomain.contoso.com

issuer=C = GB, ST = Greater Manchester, L = Salford, O = Sectigo
Limited, CN = Sectigo RSA Domain Validation Secure Server CA

— No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: RSA-PSS Server Temp Key: X25519, 253 bits
— SSL handshake has read 5242 bytes and written 396 bytes Verification: OK
— New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE
Expansion: NONE No ALPN negotiated Early data was not sent Verify
return code: 0 (ok)
— HTTP/1.0 400 Bad request Cache-Control: no-cache Connection: close Content-Type: text/html

400 Bad request

Your browser sent an invalid request.

— Post-Handshake New Session Ticket arrived: SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: FF80D974E787152BE195D533AF095842F2257CD340F8EB40AF5EA8D70D661229
Session-ID-ctx:
Resumption PSK: A4A117A4D8C504590DFBACDB00B46A2D87AA15FDFE4D0AA83CAE9341330AABFE4488C3D7EB73BAE8386EE748240E3514
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
0000 - b4 05 de bd c7 b1 d3 71-53 99 b3 28 6e 46 6d 23 …qS…(nFm#
0010 - a6 fc cc bd c9 a2 c7 86-61 6a 25 f5 80 83 29 33 …aj%…)3

Start Time: 1664029485
Timeout   : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0

— read R BLOCK
— Post-Handshake New Session Ticket arrived: SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: 6AC8A5104001D9DA6A4ACF750352B1429D258452FED10584B4C6398CF6DC740C
Session-ID-ctx:
Resumption PSK: 354465EF5C784F3B021A721FFD739E7F0FB7BBF666B7D165CC324A313C7B446A7FF6E65CB54081E00DE9389F780AC177
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
0000 - 27 2d ca 6c 4d af 6b ce-dc cb 67 39 f3 60 20 3a '-.lM.k…g9.` :
0010 - b0 a5 25 58 6d 4a ef db-7d db 0d 97 5a 54 aa 03 …%XmJ…}…ZT…

Start Time: 1664029485
Timeout   : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0

— read R BLOCK closed

However when I do a SSH command to subdomain contoso.com it fails with an SSL handshake failure.

Aside note:

  • The default works for the http(s) frontend
  • If i comment the ssl no-sslv3 crt and what is after it works (except that it disables the routing part…)

Any help on this?

My Haproxy version :

HA-Proxy version 1.8.19-1+deb10u3 2020/08/01
Copyright 2000-2019 Willy Tarreau willy@haproxy.org

Build options :
TARGET = linux2628
CPU = generic
CC = gcc
CFLAGS = -O2 -g -O2 -fdebug-prefix-map=/build/haproxy-OkbS59/haproxy-1.8.19=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fno-strict-aliasing -Wdeclaration-after-statement -fwrapv -Wno-format-truncation -Wno-null-dereference -Wno-unused-label
OPTIONS = USE_GETADDRINFO=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 USE_LUA=1 USE_SYSTEMD=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_NS=1

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

Built with OpenSSL version : OpenSSL 1.1.1d 10 Sep 2019
Running on OpenSSL version : OpenSSL 1.1.1n 15 Mar 2022
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.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.32 2018-09-10
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

SSH and SSL are two completely different protocols, they don’t have anything to do with each other.

I think that I indeed totally mistaken about this so I will maybe reformulate my question.

Is there a way to redirect traffic depending on the SSH host choosen by end-user like it’s done for HTTP(S)?

ssh user@gitlab.contoso.com redirecting to a.b.c.d:22
ssh user@sonarqube.contoso.com redirecting to w.x.y.z:22
and so on ?

No, you cannot.