Blocking single TLS SNI

Hello HAProxy community,

I have HAProxy acting as Load Balancer to a Kubernetes Cluster. I have a wildcard DNS entry for my “public” IP address on the HAProxy. I also have a wild card TLS Certificate.

When I deploy a new application on Kubernetes it appears as new_application.mykyubdomain.com. Internally I access this no problem. This is combination of wild card DNS, TLS SNI and HAProxy magic working.

I would like to block a single application (TLS SNI) on the HAProxy. So my understanding is I need to inspect the SNI to see which application (TLS SNI) I am looking at and block it using tcp-request content reject.

This a snippet from the frontend section of my HAProxy settings for Kubernetes Cluster.

tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl stop_app_ssl req_ssl_sni new_application1.mykyubdomain.com
tcp-request content reject if stop_app_ssl

This is all in mode tcp as it is TLS traffic. The TLS termination is on the Kubernetes Cluster. I am not off loading the encryption to the HAProxy.

I have set-up Wireshark and can the SNI “new_application1.mykyubdomain.com” in TLS extension SNI when I connect to new_application1 but HAProxy does not block the traffic.

I have the following version of HAProxy running on rhel 7.

What am I doing wrong? Any advice much appreciated. If I have not made anything clear please ask.

[root@rhoslb1 haproxy]# haproxy -vv
HA-Proxy version 1.5.18 2016/05/10
Copyright 2000-2016 Willy Tarreau willy@haproxy.org

Build options :
TARGET = linux2628
CPU = generic
CC = gcc
CFLAGS = -O2 -g -fno-strict-aliasing -DTCP_USER_TIMEOUT=18
OPTIONS = USE_LINUX_TPROXY=1 USE_GETADDRINFO=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 USE_PCRE=1

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

Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.7
Compression algorithms supported : identity, deflate, gzip
Built with OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
Running on OpenSSL version : OpenSSL 1.0.2k-fips 26 Jan 2017
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports prefer-server-ciphers : yes
Built with PCRE version : 8.32 2012-11-30
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND

Best Regards,

Kevin.

Share the entire configuration please.

Global settings

#---------------------------------------------------------------------
global
maxconn 20000
log /dev/log local0 info
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
user haproxy
group haproxy
daemon

# turn on stats unix socket
stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
#Global settings
#---------------------------------------------------------------------
global
maxconn 20000
log /dev/log local0 info
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
user haproxy
group haproxy
daemon

# turn on stats unix socket
stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
#common defaults that all the ‘listen’ and ‘backend’ sections will
#use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option tcplog
option dontlognull
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 300s
timeout server 300s
timeout http-keep-alive 10s
timeout check 10s
maxconn 20000

listen stats
bind :9000
mode http
stats enable
stats uri /

frontend atomic-openshift-api
bind 10.2.134.171:443
default_backend atomic-openshift-api
mode tcp
option tcplog

backend atomic-openshift-api
balance source
mode tcp
server master1.mydomain.com rhosmaster1.mydomain.com:443 check
server master2.mydomain.com rhosmaster2.mydomain.com:443 check
server master3.mydomain.com rhosmaster3.mydomain.com:443 check

frontend infrarouter
bind 10.2.134.172:80
default_backend infrarouter
mode http
option tcplog
option forwardfor except 127.0.0.0/8

# below is ACL to block by hostname

#acl restricted_page hdr(host) -i bird-test-kevintest.apps.mydomain.com
# ACL for specific IP addresses
#acl restrited_source_ip src 10.2.8.152
#acl restrited_source_ip_gs src 10.2.8.162
#http denty request based on the ACLs
#http-request deny if restricted_page restrited_source_ip_gs

backend infrarouter
balance source
mode http
server infra1.mydomain.com rhosinfra1.mydomain.com:80 check
server infra2.mydomain.com rhosinfra2.mydomain.com:80 check
server infra3.mydomain.com rhosinfra3.mydomain.com:80 check

frontend infrarouter-secure
bind 10.2.134.172:443
default_backend infrarouter-secure
mode tcp
option tcplog

tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl stop_app_ssl req_ssl_sni django-ex-kevintest.apps.mydomain.com
tcp-request content reject if stop_app_ssl

backend infrarouter-secure
balance source
mode tcp
server infra1.mydomain.com rhosinfra1.mydomain.com:443 check
server infra2.mydomain.com rhosinfra2.mydomain.com:443 check
server infra3.mydomain.com rhosinfra3.mydomain.com:443 check

"# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option tcplog
option dontlognull
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 300s
timeout server 300s
timeout http-keep-alive 10s
timeout check 10s
maxconn 20000

listen stats
bind :9000
mode http
stats enable
stats uri /

frontend atomic-openshift-api
bind 10.2.134.171:443
default_backend atomic-openshift-api
mode tcp
option tcplog

backend atomic-openshift-api
balance source
mode tcp
server master1.mydomain.com rhosmaster1.mydomain.com:443 check
server master2.mydomain.com rhosmaster2.mydomain.com:443 check
server master3.mydomain.com rhosmaster3.mydomain.com:443 check

frontend infrarouter
bind 10.2.134.172:80
default_backend infrarouter
mode http
option tcplog
option forwardfor except 127.0.0.0/8

#below is ACL to block by hostname

#acl restricted_page hdr(host) -i bird-test-kevintest.apps.mydomain.com
#ACL for specific IP addresses
#acl restrited_source_ip src 10.2.8.152

acl restrited_source_ip_gs src 10.2.8.162

# http denty request based on the ACLs

http-request deny if restricted_page restrited_source_ip_gs

backend infrarouter
balance source
mode http
server infra1.mydomain.com rhosinfra1.mydomain.com:80 check
server infra2.mydomain.com rhosinfra2.mydomain.com:80 check
server infra3.mydomain.com rhosinfra3.mydomain.com:80 check

frontend infrarouter-secure
bind 10.2.134.172:443
default_backend infrarouter-secure
mode tcp
option tcplog

tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl stop_app_ssl req_ssl_sni django-ex-kevintest.apps.mydomain.com
tcp-request content reject if stop_app_ssl

backend infrarouter-secure
balance source
mode tcp
server infra1.mydomain.com rhosinfra1.mydomain.com:443 check
server infra2.mydomain.com rhosinfra2.mydomain.com:443 check
server infra3.mydomain.com rhosinfra3.mydomain.com:443 check

We must not accept the the content earlier.

Try something like this:

acl stop_app_ssl req_ssl_sni django-ex-kevintest.apps.mydomain.com
tcp-request inspect-delay 5s
tcp-request content reject if { req_ssl_hello_type 1 stop_app_ssl }
tcp-request content accept if { req_ssl_hello_type 1 ! stop_app_ssl }
tcp-request content reject

Thanks for quick response!

That does not work unfortunately. It complains when I put the configuration in place. I think this is just a syntax issue?

Below is error.

root@rhoslb1 haproxy]# haproxy -f /etc/haproxy/haproxy.cfg -d
[ALERT] 224/092416 (67628) : parsing [/etc/haproxy/haproxy.cfg:117] : ‘tcp-request content reject’ : error detected in frontend ‘infrarouter-secure’ while parsing ‘if’ condition : ‘stop_app_ssl’ is not a number
[ALERT] 224/092416 (67628) : parsing [/etc/haproxy/haproxy.cfg:118] : ‘tcp-request content accept’ : error detected in frontend ‘infrarouter-secure’ while parsing ‘if’ condition : ‘!’ is not a number
[ALERT] 224/092416 (67628) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg
[ALERT] 224/092416 (67628) : Fatal errors found in configuration.
[root@rhoslb1 haproxy]#

Like I say I think syntax problem with the and for the tcp-request content accept / reject rules.

I have tried the following

acl stop_app_ssl req_ssl_sni django-ex-kevintest.mydomain.com
tcp-request inspect-delay 5s
tcp-request content reject if { req_ssl_hello_type 1 } stop_app_ssl
tcp-request content accept if { req_ssl_hello_type 1 } ! stop_app_ssl
tcp-request content reject

The configuration starts with no errors but the application (site django-ex-kevintest) is still accessible. I want to block it.

I am checking out what the correct syntax and in tcp-request content is?

Kevin.

I restarted the haproxy with the new entry below and it works.

acl stop_app_ssl req_ssl_sni django-ex-kevintest.apps.rhos.agriculture.gov.ie
tcp-request inspect-delay 5s
tcp-request content reject if { req_ssl_hello_type 1 } stop_app_ssl
tcp-request content accept if { req_ssl_hello_type 1 } !stop_app_ssl
tcp-request content reject

Thanks so much for solution Lukastribus.

You Rock!!!

1 Like