HAProxy: unable to forward client-certificate in a header without validation - haproxy

I have a mutual-TLS setup with HAProxy terminating incoming SSL connections. I need to perform client certificates validation on the backend, not on haproxy side since we have a dynamic truststore and I cannot just set a single ca-file and delegate all the validation logic to haproxy.
Regular setup works fine (where haproxy validates a client cert against CA cert):
bind *:443 ssl crt /etc/certs/haproxy.pem verify required ca-file /etc/certs/ca.crt
http-request redirect scheme https unless { ssl_fc }
http-request set-header X-SSL-ClientCert %{+Q}[ssl_c_der,base64]
Backend receives X-SSL-ClientCert correctly, but this is not enough.
If verify required ca-file /etc/certs/ca.crt is removed to skip validation on haproxy, X-SSL-ClientCert is empty when read on the backend, that is, HAProxy does not set the header with the client certificate anymore. Any ideas on how to fix this?

Okay, after some tinkering I stumbled upon the following:
HAProxy documentation https://cbonte.github.io/haproxy-dconv/2.3/configuration.html#5.2-verify states two options for verify: [none|required], but it seems there is an undocumented option, namely, optional which seems to do the trick:
bind *:443 ssl crt /etc/certs/haproxy.pem verify optional ca-file /etc/certs/ca.crt
http-request set-header X-SSL-Client-Cert %{+Q}[ssl_c_der,base64]
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
http-request set-header X-SSL-Client-Verify %[ssl_c_verify]

You do have the right thought.
On the other hand, I think that there is a more readable writing.
In your /etc/hapee/hapee-lb.cfg
bind :443 ssl crt-list /etc/hapee/certs-file
In your /etc/hapee/certs-file
cert_example1.pem [verify optional ca-file /etc/hapee/certs/ca.crt]
cert_example2.pem
So you can easily list all your certificates and possibly the verify options according to your SNI.
Backend who match
backend
http-request set-header X-SSL-Client-DN %[ssl_c_s_dn]
http-request set-header X-SSL-Client-Cert %{+Q}[ssl_c_der,base64]
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
http-request set-header X-SSL-Client-Verify %[ssl_c_verify]

Related

Redirect short/host name to subdomain

I have a collection of hosts that currently use just hostnames for reference, I'm switching over to using haproxy as an https proxy and would like to do a rewrite or redirect from http://server1/ to https://server1.internal.mydomain.com/.
# do a redirect for insecure connections
http-request redirect scheme https code 301 if !{ ssl_fc }
I have the HTTPS redirect/upgrade working as expected it's getting the rewrite/redirect configured properly that I'm hung up on, and I'm not really sure on the right verbiage to use when asking the question to get a relevant answer.
defaults
mode http
timeout client 10s
timeout connect 5s
timeout server 10s
timeout http-request 10s
frontend mydomain_frontend
# Listen for both http and https requests
bind *:80
bind *:443 ssl crt /etc/ssl/certs/Wildcard_mydomain_web_server.pem
# Setup conditional ACLs for hosts
acl server1_hosts hdr_beg(host) -i server1. server1 server1-2. server1-2
acl server2_hosts hdr_beg(host) -i server2. server2 server2-2. server2-2
# Setup Conditional ACLs for redirecting short/host names to FQDNs
acl is_internal hdr_sub(host) -i internal.mydomain.com
# I think the ACL is right, I'm just not sure how I would do the redirect, Is there string substitution?
http_request redirect location https://ORIGINALHOST.internal.mydomain.com if !is_internal
# do a redirect for insecure connections
http-request redirect scheme https code 301 if !{ ssl_fc }
use_backend server1_bend if server1_hosts
use_backend server2_bend if server2_hosts
default_backend server1_bend
# Setup DNS resolution
resolvers default
nameserver ns1 10.10.10.1:53
nameserver ns2 10.10.10.15:53
backend server1_bend
mode http
option forwardfor if-none
# server site 11.11.11.11:80 check resolvers default
server site server1.internal.mydomain.com:80 check resolvers default
backend server2_bend
mode http
option forwardfor if-none
# server site 10.10.10.10:80 check resolvers default
server site server2.internal.mydomain.com:80 check resolvers default
** EDIT **
I added an acl and a partial redirect statement to my example configuration, I think it is the beginning of what I am looking for but I don't know if it will work without string substitution in the redirect.
Updating the http-request line to use hdr(host) and pathq solved the issue for me.
http-request redirect location https://%[hdr(host)].internal.mydomain.com%[pathq] if !is_internal
hdr(host) - is the host from the URI minus the path and queries.
pathq - is the path including any queries. if we wanted just the path we could use just path.
The %[] pattern is important for triggering the substitution.

Consult for redirecting http to https via HAproxy

I am trying to configure the HAproxy to make it redirect the http traffic to https, but Chrome failed with "ERR_SSL_PROTOCOL_ERROR". However, if I used https directly it will work.
For example:
If I typed https://:5443, it will display the page of backend servers.
If I typed http://:5006, it should be redirected but just failed with the error mentioned above.
Here is my config:
frontend simple_webapp
mode http
bind *:5006
bind *:5443 ssl crt /root/Downloads/simple_webapp_all.pem
http-request redirect scheme https unless { ssl_fc }
default_backend simple_webapp
backend simple_webapp
balance roundrobin
server centos8-1 <server ip1>:5006 check
server centos8-2 <server ip2>:5006 check
Please correct me if there is any misconfiguration.
You probably expected that you changed port with scheme, but you haven't. Your request to http://foo:5006/ gets redirected to https://foo:5006, which doesn't support SSL. If you omit port in URL then browsers and web clients are smart enough to use port 80 for HTTP and 443 for HTTPS. That's why it works for default ports.
So try to change your redirect to something like this:
http-request redirect prefix https://your_domain_as_explicit_string:5443 unless { ssl_fc }
You can experiment with headers, like
http-request redirect prefix https://%[hdr(host),regsub(:5006,:5443)] unless { ssl_fc }
hdr(host) includes in this case :5006, so that's changed with regsub
Once I changed the port from 5006 to others, it works fine.
No clue what happened on this port.

haproxy forward client certificate x509

I have configured haproxy to forward client certificate with
http-request set-header X-SSL-Client-cert %[ssl_c_der,base64]
is it possible to forward with x.509 (PEM) format? Now it forwards with binary.
Maybe too late, but it is possible this way:
http-request set-header X-SSL-ClientCert %{+Q}[ssl_c_der,base64]

HAProxy - Add Trailing Slash

I am working on a project where I need requests destined to a particular page to be routed to a separate backend.
For example, all requests for https://mycooldomain.com will go to backend "A". But, if navigating to https://mycooldomain.com/secretpage I want it to go to backend "B".
Now, I have this working but running into an issue where I need the trailing slash for this to work correctly.
So, I need a way to say if request is https://[whateverhostnameisused]/secretpage redirect to https://[whateverhostnameisused]/secretpage/.
Here is a sample of my config so far:
frontend f_https
bind *:443 ssl crt cert.pem
reqadd X-Forwarded-Proto:\ https
#define hosts
acl host_a hdr(host) -i a.mycooldomain.com
acl host_b hdr(host) -i b.mycooldomain.com
acl host_c hdr(host) -i c.mycooldomain.com
#custom acls
acl secret path_beg -i /secretpage
#Custom redirects
##define backend
use_backend b_secret if secret
use_backend b_a if host_a
use_backend b_b if host_b
use_backend b_c if host_c
default_backend b_https
backend b_secret
server secret 192.168.15.15:5575 check
It seems that you are looking for something like this:
http-request redirect scheme https drop-query append-slash if { path -m str /secretpage }
This should work if applied to either the frontend or the backend.
http://cbonte.github.io/haproxy-dconv/1.6/configuration.html#4.2-redirect%20scheme
Specifying the scheme is only necessary because the syntax requires one of location | prefix | scheme, and with the other two options, you have to reassemble the URL yourself in the config.
Note also that reqadd is not officially deprecated, but the preferred way to add that request header is like this:
http-request set-header X-Forwarded-Proto https
Note that no : is specified and the space after the header name must not be escaped with \. This accomplishes the same result, but it uses a different code path inside HAProxy, and should be a more efficient operation. You will want to use the the http-request and http-response directives instead of reqxxx and rspxxx where possible, as they are also better suited to more complex manipulations.

how to balance to a specific server if hostname matches x.domaine.com (Haproxy)

as mentioned in the title, i've set an Haproxy loadbalancer with a basic configuration, what i'd like to do is to always redirect request to the first server if the hostname matches x.domaine.com, but keep the balancing for domaine.com, is it possible with Haproxy, and if so how can i do it.
her's my configuration
listen webcluster *:80
mode http
balance roundrobin
option httpchk HEAD / HTTP/1.0
option forwardfor
cookie LSW_WEB insert
option httpclose
server bigSRV 192.168.1.10:8082 cookie LSW_WEB01 check
server miniSRV 192.168.2.10:8082 cookie LSW_WEB01 check
thanks in advence
after hours of digging i finally got it to work, so i'm going to answer my own question in case if samone have the same issue
generally i created a frontend that listen on port:80 and in which i defined 2 ACLs that uses the "if" statement to check the http header and then redirect to one of the backends defined, if no request matches the conditions, we redirect to default backend, here's how it's done (on haproxy.cfg) :
frontend http-proxy
bind *:80
acl is_www hdr(host) -i www.domain.com
acl is_x hdr(host) -i x.domain.com
use_backend clusterWWW if is_www
use_backend clusterX if is_x
default_backend clusterWWW
backend clusterWWW
server bigSRV 192.168.1.10:8082 cookie LSW_WEB01 check
server miniSRV 192.168.2.10:8082 cookie LSW_WEB01 check
backend clusterX
server bigSRV 192.168.1.10:8082 cookie LSW_WEB01 check