SSL Termination

I have an issue around connecting to the cluster.

Each node has eventstore and haproxy providing tls termination; which on the face of it works fine; but when it comes to the client fails (configs below).

The cluster works (status has all 3 nodes) but it fails at the client on discovery.

Is this related to ?

I’ve tried flipping settings around http-prefixes but I’m assuming this only affects the UI component.

Using this code

static void Main(string[] args)



var esS = EventStoreConnection.Create(seed.FirstOrDefault());


var settings = ConnectionSettings.Create()







.UseSslConnection(“ges-haproxy-tcp.service.consul”, false)

.SetDefaultUserCredentials(new UserCredentials(“admin”, “changeit”))



var clusterSettings = ClusterSettings.Create()


.SetGossipSeedEndPoints(seed.Select(x => new IPEndPoint(x.Address, 2113)).ToArray())




var eventStoreConnection = EventStoreConnection.Create(settings, clusterSettings, “testconnection”);


for (var i = 0; i < 1000; i++)


var s = eventStoreConnection.AppendToStreamAsync(Guid.NewGuid().ToString(“N”), -2,

new List() {new EventData(Guid.NewGuid(), “t”, false, new byte[0], new byte[0])}).ConfigureAwait(false);




With 2113 set (which goes via HTTP to the GES nodes) it works fine and resolves the nodes to 1114 which is TLS terminated TCP at haproxy.

Set to 2114 which I’d expect to be right it just throws discovery errors.

So am I right in thinking (and going off the github issue) that you can’t terminate TLS discovery?

Is there anyway around this currently? Why do I even need discovery when I’ve fetched the cluster to feed the seeds?


RunProjections: system

StartStandardProjections: true

Log: “/var/log/eventstore”

Db: /gesdata





IntHttpPort: 2112

ExtHttpPort: 2113

IntTcpPort: 1112

ExtTcpPort: 1113

ExtTcpPortAdvertiseAs: 1114

ExtHttpPortAdvertiseAs: 2114

IntTcpHeartbeatTimeout: 2000

ExtTcpHeartbeatTimeout: 2000

IntTcpHeartbeatInterval: 5000

ExtTcpHeartbeatInterval: 5000

GossipTimeoutMs: 2000

DiscoverViaDns: true

AdminOnExt: true

GossipOnExt: true

ClusterSize: 3

ClusterDns: “eventstore.service.consul”

ClusterGossipPort: 2112

IntHttpPrefixes: http://*:2112/

ExtHttpPrefixes: http://*:2113/

AddInterfacePrefixes: false




tune.ssl.default-dh-param 2048

user haproxy

group haproxy


log local0

log local1 notice

timeout connect 5000ms # max time to wait for a connection attempt to a server to succeed

timeout client 50000ms # max inactivity time on the client side

timeout server 50000ms # max inactivity time on the server side

frontend http_frontend

bind *:2114 ssl crt /etc/certs/server.pem ca-file /etc/certs/ca.crt

mode http

option forwardfor

option http-server-close

option httpclose

we add this so the backend servers know the request was ssl

otherwise we could end up in a redirect loop

reqadd X-Forwarded-Proto:\ https

reqadd X-Forwarded-Scheme:\ https

reqadd X-Forwarded-Port:\ 2114

default_backend http_backend

backend http_backend

mode http

timeout connect 5s

timeout server 30s

server ui check

frontend gestcp_frontend

bind *:1114 ssl crt /etc/certs/server.pem ca-file /etc/certs/ca.crt

mode tcp

option tcplog

timeout client 1m

default_backend ges_backend

backend ges_backend

mode tcp

option tcplog

option log-health-checks

option redispatch

log global

timeout connect 10s

timeout server 1m

server i-x check

just FYI the first

var esS = EventStoreConnection.Create(seed.FirstOrDefault());


was just to check a random test.

I’ll add though that over https on the external port of a node



Ok looking at the netcore client it connects and calls out to the gossip url on http

{Method: GET, RequestUri: ‘http://52.xxxxx:2114/gossip?format=json’, Version: 1.1, Content: <nu

Which spawns from the ClusterDnsEndpointDiscovery > TryGetGossipFrom using HTTP schema regardless of the ssl settings passed

   private ClusterMessages.ClusterInfoDto TryGetGossipFrom(GossipSeed endPoint)


        //_log.Debug("ClusterDnsEndPointDiscoverer: Trying to get gossip from [{0}].", endPoint);

        ClusterMessages.ClusterInfoDto result = null;

        var completed = new ManualResetEventSlim(false);

        var url = endPoint.EndPoint.ToHttpUrl(EndpointExtensions.HTTP_SCHEMA, "/gossip?format=json");




            response =>


                if (response.HttpStatusCode != HttpStatusCode.OK)


                    //_log.Info("[{0}] responded with {1} ({2})", endPoint, response.HttpStatusCode, response.StatusDescription);






                    result = response.Body.ParseJson<ClusterMessages.ClusterInfoDto>();

                    //_log.Debug("ClusterDnsEndPointDiscoverer: Got gossip from [{0}]:\n{1}.", endPoint, string.Join("\n", result.Members.Select(x => x.ToString())));


                catch (Exception)


                    //_log.Info("Failed to get cluster info from [{0}]: deserialization error: {1}.", endPoint, e.Message);




            e =>


                //_log.Info("Failed to get cluster info from [{0}]: request failed, error: {1}.", endPoint, e.Message);


            }, endPoint.HostHeader);


        return result;


The inability to pass in a custom httpclient handler of course makes it a bugger to alter the client cert validation / pass client certs up.

On the upside bashing that chunk of code with a rock fixes the issue around tls termination

Raise this a a github issue its not a lot of work and is reasonable.

I’ve done the code but for the netcore client

Added with PR