I've recently tried to set up SNI-based routing on HAProxy for mongodb mongodb+srv
protocol connection.
I made it working, but it wasn't until I put
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
in my frontend configuration that it started working properly
Without these (or with just one of these) I kept getting sporadic connection resets for around 70% of my requests. Note that sometime the connection did manage to get established.
According to http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-tcp-request%20content
Content-based rules are evaluated in their exact declaration order. If
no rule matches or if there is no rule, the default action is to
accept the contents. There is no specific limit to the number of rules
which may be inserted.
So it should have been working by default.
Either I'm missing some fundamental TCP concept or some specific Mongo connection detail.
But i've noticed that almost everyone has the same rule defined when they go for SNI-based routing, so it must make sense for most folks.
Working HAProxy configuration:
frontend fe_mongo-nonprod
bind :27017
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net
use_backend be_mongo-nonprod if mongoAtlas-01 || mongoAtlas-02 || mongoAtlas-03
backend be_mongo-nonprod
mode tcp
acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net
use-server server-01 if mongoAtlas-01
use-server server-02 if mongoAtlas-02
use-server server-03 if mongoAtlas-03
server server-01 host1.mongodb.net:27017
server server-02 host2.mongodb.net:27017
server server-03 host3.mongodb.net:27017
It is also worth mentioning that HAProxy is deployed in Kubernetes (Google-managed cluster in GCP), has an istio-sidecar and is exposed via Internal LoadBalancer k8s service.
As stated the above config works perfectly well. What I'm interested in is why tcp-request content accept
is absolutely required and connection is not [always] accepted by default here.