Can I use wildcard SNI matching with HAProxy? - haproxy

I'm looking around trying to find an example of HAProxy matching SNI wildcards, and my searching is bringing up similarly titled, but unrelated questions about certificates.
Specifically I need to route nonce domains for dvsni with acme / letsencyrpt.
frontend foo_ft_https
mode tcp
option tcplog
bind 0.0.0.0:443
acl foo_app_letsencrypt req.ssl_sni -i *.acme.invalid
use_backend foo_bk_letsencrypt if foo_app_letsencrypt
default_backend foo_bk_default
backend foo_bk_letsencrypt
mode tcp
option tcplog
server foo_srv_letsencrypt 127.0.0.1:3443
backend foo_bk_default
mode tcp
option tcplog
server foo_srv_default 127.0.0.1:8443
Note: all arbitrary names are prefixed with 'foo_' so that the reader can easily distinguish them from keywords, directives and such.

Change
acl foo_app_letsencrypt req.ssl_sni -i *.acme.invalid
to
acl foo_app_letsencrypt req.ssl_sni -m end .acme.invalid
It's not mentioned in the official documentation
https://cbonte.github.io/haproxy-dconv/configuration-1.5.html explicitly, but I was able to find other resources that lead me to the correct result:
https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#7.1.3
http://comments.gmane.org/gmane.comp.web.haproxy/14602
Note that if you were to try the first example, it would "work", but the "" would be interpreted as a literal "", not a wildcard.

Even this is very old question, I would like to share this solution, because this is still among first google's results:
The solution given by CoolAJ86 doesn't work for me (it probably works for older version of HAProxy). You can instead use ssl_fc_sni_end instead of ssl_fc_sni like this:
use_backend apache if { ssl_fc_sni_end domain.com }
It will do the work!

Related

Is it possible to redirect a TCP connection based on host name?

What I want to be able to do is connect to a postgres server like this:
psql -h postgres-a.example.com -p 9000
That connection should be received by a proxy server (like nginx or haproxy) and it will be redirected to database A because of host name postgres-a.example.com. If I use postgres-b.example.com and the same port, it should go to database B.
I have been researching this, but I am still not 100% sure of how this would work. I read that the only way to redirect a TCP connection (psql) based on host name is using the SNI header. But I still don't understand if we will need a SSL certificate for this, or if we will need to use https://postgres-a.example.com (That doesn't make any sense to me). How it will work?
Can someone help me understand this?
Yes. You will need a certificate for TLS/SSL and you can route the requests based on req.ssl_sni to the proper backend.
I'm not sure if psql uses SNI but i think this have you check.
frontend public_ssl
bind :::9000 v4v6 crt /usr/local/etc/haproxy-certs
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use-server postgres-a if { req.ssl_sni -i postgres-a.example.com }
use-server postgres-b if { req.ssl_sni -i postgres-b.example.com }
backend postgres-a
server postgres-a FURTHER SERVER PARAMS
backend postgres-b
server postgres-b FURTHER SERVER PARAMS
I have created a blog post with a picture for a more detailed description.
https://www.me2digital.com/blog/2019/05/haproxy-sni-routing/

Haproxy Route Traffic Based on Querystring

I want to config Haproxy to route traffic based on querystring. In particular...
If /lookup is in the URL, go to xxx.xxx.xxx.xxx
If /related and ?loc= is in the url, go to yyy.yyy.yyy.yyy
If /related and no ?loc=, go to zzz.zzz.zzz.zzz
Any ideas how to do this? Thanks in advance!
You're probably looking for urlp (aka url_param) to fetch sample from query string.
Something like this perhaps?
acl lookup path_beg /lookup
acl related path_beg /related
acl loc urlp(loc) -m found
use_backend xxx if lookup
use_backend yyy if related loc
use_backend zzz if related !loc
NOTE: lookup and related ACLs check if the URL begins with the path (not "in the" path). Also loc ACL checks if the query parameter exists even if it's empty. You need to change it a bit if it doesn't exactly fit your case.

Transmission Torrent behind HAProxy - HTTP Response Header used as session identifier and stickiness token

I've been trying, and failing so far, to run Transmission behind HAProxy.
If I just add a new backend and route traffic as follows:
frontend http-in
bind *:80
reqadd X-Forwarded-Proto:\ http
acl host1 hdr_end(host) -i web.host1.host
use_backend apache_backend if host1
acl transmission_host hdr_end(host) -i transmission.host1.host
use_backend transmission_backend if transmission_host
Then I get a 409 conflict error stating I have an invalid session-id header. That's pretty obvious and expected since there's a proxy in the middle.
I thought of recompiling transmission to get rid of the check, but decided in the end to face the challenge of learning a bit more of HAProxy. What did I have in mind?
Client reaches HAProxy
HAProxy connects to transmission-daemon
Daemon replies with X-Transmission-Session-Id
HAProxy stores the Session-Id somehow and replaces Session-Id sent by the client with the one captured by HAProxy.
After a lot of Googling and playing with the settings, I got an almost working configuration:
frontend http-in
bind *:80
reqadd X-Forwarded-Proto:\ http
capture response header X-Transmission-Session-Id len 48
acl host1 hdr_end(host) -i web.host1.host
use_backend apache_backend if host1
acl transmission_host hdr_end(host) -i transmission.host1.host
use_backend transmission_backend if transmission_host
backend transmission_backend
mode http
http-request set-header X-Transmission-Session-Id %hs
server transmission-daemon transmission.intranet:9091
My configuration examples are summarized.
It works, sort of. I get a login prompt for transmission, but the page loads incredibly slow. I'm more than 10 minutes in and still don't have it fully loaded.
More pages go through this proxy: HTTP, HTTPS, TCP, some load balanced, some set as fail-overs. They all load normally and fast. If I connect directly to the transmission-daemon server, it loads fast as well.
I'll keep looking around.
Any ideas?
Thanks in advance!
3 years later,
from what I've seen in https://gist.github.com/yuezhu/93184b8d8d9f7d0ada0a186cbcda9273
you should capture request and response in frontend http-in,
I didn't dug much more, but the backend seems to need
stick-table type binary len 48 size 30k expire 30m
stick store-response hdr(X-Transmission-Session-Id)
stick on hdr(X-Transmission-Session-Id)
to work

haproxy: set timeout if <condition>

The question seems to be quite straight and easy, however I have not been able to find a proper answer.
In haproxy I have 1 backend, say:
backend-1
and 2 frontends, say:
frontend-1
frontend-2
In the backend stanza I want to set a "timeout server" parameter, but, only if the connection comes from frontend-1.
As I didn't find anything I tried to figure it out myself:
backend backend-1
bind *:80
option <blahblah_option>
timeout server 1d if frontend frontend-1
This syntax does not work, and I am mentioning it to let understand what I am trying to achieve.
This is not doable yet in HAProxy.
Later, you will be able to set timeouts using tcp-request and http-request rules.
What we usually do to workaround this for now, is that we setup 2 backends using the same parameters, but different timeout servers.
This is useful when a few urls only deserve a long server timeout.
Edit followup your comment about multiple health checks:
Well, that's why the server's 'track' directive exists:
backend my_app
server srv1 10.0.0.1:80 check
backend my_app_longtime
server srv1 10.0.0.1:80 track my_app/srv1
In the conf above, the server in my_app_longtime backend won't be checked. That said, it will follow up the same state than srv1 in the backend my_app.
Baptiste
Baptiste
I did it like this and it worked. It made it possible to extend timeout on specific app urls, which are more time consuming. Used that trace health check - thanks Babtiste.
frontend www-http
bind 10.0.0.1:80
default_backend app
acl long_url path_beg -i /long_url
use_backend app-extended if long_url
backend app
server web-1 10.0.0.2:80 check
backend app-extended
server web-1 10.0.0.2:80 trace app/web-1
timeout server 10m

Reduce duplication in haproxy acl with multiple frontend sections

I'm using haproxy with stunnel handling SSL (and using the proxy mode to preserve the original IP from haproxy).
I have several acl tests that redirect to different backends depending on the domain, headers, or path.
The problem is that these are identical whether you're coming in via http or https, but I have to duplicate them in the config. Is there any way to reduce the duplication?
Here's a sample config:
global
user haproxy
group haproxy
#etc...
frontend http-in
bind *:80
acl files_path path_beg /files/
acl beta_host hdr_beg(host) -i beta.
use_backend files if files_path
use backend beta_host
default_backend appservers
frontend https-in
bind *:442 accept-proxy
acl files_path path_beg /files/
acl beta_host hdr_beg(host) -i beta.
use_backend files if files_path
use backend beta_host
default_backend appservers
backend appservers
balance roundrobin
option forwardfor
server appserver_1 localhost:8080 weight 1
server appserver_2 192.168.1.101:8080 weight 1
backend files
balance roundrobin
option forwardfor
server file1 192.168.1.102 weight 1
server file2 192.168.1.103 weight 1
backend beta
balance roundrobin
server beta1 192.168.1.104 weight 1
The http-in and https-in have different ports, and the https-in has to sepcify accept-proxy so that stunnel can use the proxy protocol to pass it the original IP of the user. But other than that they are identical, and should always be identical. Is there any way to reduce this duplication? (haproxy 1.5-dev)
you could simply bind one http in frontend to both.
frontend http-in
bind *:80
bind 0.0.0.0:443 transparent
Unfortunately, haproxy manual (http://haproxy.1wt.eu/download/1.5/doc/configuration.txt) stays that acl can be defined only in frontend, listen and backend sections.
If https and http frontends are same, you can define few bind sentences in one frontend.