Set whitelist for TLS interception (ssl crt option)


My current frontend is configured like this:

bind *:443 ssl crt <cert file> ca-sign-file <ca-sign-file>.

It intercepts https traffic and gives the client a self-signed certificate for SSL Termination at the proxy.

However, for certain domains (medical websites, bank websites, etc.) I want to make an exception and let HAProxy forward it and not create his own certificate for that specific domain so it won’t be decrypted (privacy/legal reasons).

How can I achieve this? I thought of something with an ACL but I can’t bind on 443 twice…

Any help would be appreciated.

I’ve tried it with the following config:
Should check if domain is whitelisted, if whitelisted: skip the SSL termination and just forward it.

If not whitelisted, just do what it’s doing before I tried to implement this whitelist.

(I’m sending it to a second HAproxy server (

However, when I visit the “whitelisted” domain, it gives an SSL_ERROR_RX_RECORD_TOO_LONG


But my proxy is still handing out a certificate for the whitelisted domain. Tried different versions for the acl with hdr(host) and req.ssl_sni

It looks like you are setting the destination port to a transaction variable named x-forwarded-for. I’m assuming you are setting this variable somewhere else?

Either way, the destination ports needs to be 443.

Please either set the port to 443 manually, or configure the destination server correctly.

http-request set-dst-port int(443)
server backend

I fixed the error already, but the ACL is not doing it’s magic. When I visit the domain I’ve put in the ACL, it still goes to the wrong backend.

In the configuration you shared, the passthrough backend missing a lot of configuration.

How do you come to the conclusion that you are hitting the wrong backend?

Because the proxy is still handing out a SSL CRT while the passthrough isn’t containing the SSL CRT stuff. It should just pass the request along.

Share the current full configuration in textform and the log outputs.


	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
	stats timeout 30s
	user haproxy
	group haproxy
	# Default SSL material locations
	#ca-base /etc/haproxy/cert/
	#crt-base /etc/ssl/private

	# See:
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

	log	global
	maxconn 500
	option	httplog
	option	http-keep-alive
	option	originalto
	option	dontlognull
        option	forwardfor
	timeout connect 50000
        timeout client  50000
        timeout server  50000
	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
resolvers dns
	nameserver ip
	hold valid 15s
	hold nx 20s
	hold other 20s
	hold obsolete 20s
	accepted_payload_size 8192

listen haproxy-tcp-in
	mode tcp
	bind *:443
#	tcp-request inspect-delay 5s
#	tcp-request content accept if { req.ssl_hello_type 1 }
	acl whitelist req_ssl_sni -i
	use_backend passthrough if whitelist
	server intercept

listen haproxy-web-in
	mode http
#	bind *:443 ssl crt /etc/haproxy/cert/server.pem generate-certificates ca-sign-file /etc/haproxy/cert/ca-chain.pem ssl verify none
	bind *:80
	option originalto
	http-request do-resolve(txn.myip,dns,ipv4) hdr(Host),lower
	default_backend haproxy-tobackend

backend haproxy-tobackend
	mode http
	http-request do-resolve(txn.myip,dns,ipv4) hdr(Host),lower
	http-request set-dst var(txn.myip)
	http-request set-dst-port hdr(txn.x-forwarded-for)
	server haproxybackend sni req.hdr(host)

listen backend_terminate
	mode http
	bind *:10443 ssl crt /etc/haproxy/cert/server.pem generate-certificates ca-sign-file /etc/haproxy/cert/ca-chain.pem ssl verify none
	http-request do-resolve(txn.myip,dns,ipv4) hdr(Host),lower
	http-request set-dst var(txn.myip)
#	http-request set-dst-port hdr(txn.x-forwarded-for)
	server backend

backend passthrough
	mode tcp
#	http-request do-resolve(txn.myip,dns,ipv4) hdr(Host),lower
#	http-request set-dst var(txn.myip)
	server pass

I have a second HAproxy machine that the request should go to (also on “passthrough”), but that’s not in this config yet.

Logs (sorry, can’t show this any better easily):

There is a lot wrong with this configuration.

You need uncomment tcp-request* configuration in listen haproxy-tcp-in, otherwise the ACL will not work, certainly not reliably.

In backend passthrough, you need the http-request do-resolve configuration, otherwise haproxy won’t connect to anything.

And also, you need ssl verify none everywhere on the server configurations, when you expect to reencrypt, otherwise you will be sending plaintext HTTP to HTTPS servers on port 443. Also use mode http here.

1 Like

Noted & changed.

I tried two different kinds of ACL definitions

acl whitelist hdr(host) -i > did nothing
acl whitelist req.ssl_sni -i > got the SSL_ERROR-RX_RECORD_TOO_LONG error again

Seeing NOSRV in logs when getting the error mentioned above.

I’ve been thinking and trying and nothin works out.

NOSRV → Is that because the fact i’m NOT terminating the SSL connection, so the do-resolve cannot access the hdr(host)?

This statement was wrong.

In backend passthrough, you do need the http-request set-dst var(txn.myip) statement, however NOT the do-resolve statement, because hdr(Host) accesses a HTTP header (the Host header), which is not available in the passthrough scenario (which is encrypted).

Also, I’m not sure if in this case you can use the txn variable (since we are not doing HTTP here).

Please implement this, then try, if it doesn’t work try using sess variable instead of txn variable at least to get passthrough working. If it still doesn’t work, share again the entire configuration.

My config at the moment ^.

Figured that. Tried only the txn.myip and sess.myip but it did not have effect. Is there another way to get the host from the https request? I’m starting to think there is no way to make this possible.

Will the “mode http” work if it’s not plain http?

(Sorry for the edits btw, I’m trying out and thinking constantly)

The is no way to access plaintext HTTP headers, when the only thing you have is encrypted SSL. That is the point of HTTPS, otherwise it would not be secure at all.

The SNI value is the only way. We need to replace http-request with tcp-request content directives and use sess instead of txn variables:

In the frontend listen haproxy-tcp-in:

tcp-request content do-resolve(sess.myip,dns,ipv4) req_ssl_sni

in backend passthrough :

tcp-request content set-dst var(sess.myip)
1 Like

That resolved the NOSRV :slight_smile: But it is now causing a ‘CD’ code. And I think it’s still trying to terminate some of the packets.

CD The client unexpectedly aborted during data transfer. This can be
caused by a browser crash, by an intermediate equipment between the
client and haproxy which decided to actively break the connection,
by network routing issues between the client and haproxy, or by a
keep-alive session between the server and the client terminated first
by the client.

The browser crash or intermediate equipment between the client and haproxy shouldn’t be the issue imo (there’s a firewall, but that’s not blocking anything outbound since the TLS termination and re-encryption works fine).

I don’t have the solution. I’d have to recreate your setup here, which I’m currently unable to do.

1 Like

Right now I’m getting another error. I separated my frontend and backend on two different machines, like I have when it is terminating and re-encrypting for non-whitelisted domains.

HAProxy Error code is now ‘SC’. Firefox shows an PR_END_OF_FILE_ERROR. I did some research on it, but everything people say on forums are things I already should have configured.

Alright, got it sorted (the error mentioned above). My config was alright but I needed to increase my tcp-request inspect-delay.