HAProxy forward header value in a custom header to backend - ssl

I am trying to forward certificate received in the header in frontend to a backend with different header name. However, the value is not forwarded. I am not able to identify in the documentation, how it should be done.
I am trying to do the following:
receive header ssl-client-cert with the Base64 encoded certificate
set new header X-CERTIFICATE with the value from ssl-client-cert header
send the request with X-CERTIFICATE to backend
My current haproxy.cfg looks like this:
global
log stdout format raw local0 debug
defaults
log global
timeout connect 5s
timeout client 1m
timeout server 1m
frontend api_gateway
mode http
bind :8080
http-request capture req.hdr(ssl-client-cert) len 64
acl PATH_api path_beg -i /api
use_backend core if PATH_api
default_backend fe
backend fe
mode http
server fe fe-service:80
backend core
mode http
http-request add-header X-CERTIFICATE %[capture.req.hdr(0)]
server core core-service:8080

You can try this.
http-request add-header X-CERTIFICATE -----BEGIN\ CERTIFICATE-----\ %[ssl_c_der,base64]\ -----END\ CERTIFICATE-----\ # don't forget last space

Related

HAproxy : replace port by a path

i'm in a OPNsense and i want to connect to my website with https://etb1.ac-test.fr/sso and not https://etb1.ac-test.fr:8443. I define rules and acl but it doesn't work. There is my configuration :
frontend rev-proxySSO
bind 0.0.0.0:8443 name 0.0.0.0:8443 ssl crt-list /tmp/haproxy/ssl/623af90d1db1d9.86320486.certlist
bind 0.0.0.0:3128 name 0.0.0.0:3128 ssl crt-list /tmp/haproxy/ssl/623af90d1db1d9.86320486.certlist
mode http
option http-keep-alive
default_backend sso
# tuning options
timeout client 30s
# logging options
# ACL: sso
acl sso path_beg -i /sso
# ACTION: sso
use_backend sso if sso
backend sso
# health checking is DISABLED
mode http
balance source
# stickiness
stick-table type ip size 50k expire 30m
stick on src
# tuning options
timeout connect 30s
timeout server 30s
http-reuse safe
server sso scribe.dompedago.etb1.lan ssl verify none
If someone can help me because i don't understand how it doesn't work

metallb round robin not working when accessed from external HAProxy

I have a sample app running in a kubernetes cluster with 3 replicas. I am exposing the app with type=LoadBalancer using metallb.
The external ip issued is 10.10.10.11
When I run curl 10.10.10.11 I get a different pod responding for each request as you would expect from round robin. This is the behaviour I want.
I have now setup HAProxy with a backend pointing to 10.10.10.11, however each time I access the HAProxy frontend, I get the same node responding to each request. If I keep refreshing I intermittently get different pods, sometimes after 20 refreshes, sometimes after 50+ refreshes. I have tried clearing my browser history, but that has no effect.
I assume it is my HAProxy config which is the cause the problem, perhaps caching? but I have not configured any caching. I am a HAProxy newbie, so I might be missing something.
Here is my HAProxy config.
I have tried both mode tcp and mode http, but both give the same result (the same pod responding to each request)
global
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
daemon
# Default SSL material locations
ca-base /home/simon/haproxy/haproxy_certs
crt-base /home/simon/haproxy/haproxy_certs
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
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
defaults
log global
option httplog
option dontlognull
timeout connect 5000
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
frontend https
bind *:443 ssl crt /home/simon/haproxy/haproxy_certs
timeout client 60s
mode tcp
#Hello App
acl ACL_hello_app hdr(host) -i hello.xxxxxxxxxdomain2.com
use_backend hello_app if ACL_hello_app
#Nginx App
acl ACL_nginx_app hdr(host) -i nginx.xxxxxxxxxdomain1.com
use_backend nginx_app if ACL_nginx_app
backend hello_app
timeout connect 10s
timeout server 100s
mode tcp
server hello_app 10.10.10.11:80
backend nginx_app
mode tcp
server nginx_app 10.10.10.10:80
UPDATE
Upon further testing the the issue seems to be related to the timeout client, timeout connect, timeout server. I reduce these to 1 second, then I get a different POD every 1 second, however these times are so short, I also get intermittent connection failures.
So, I also have the question. Is HAProxy able to work as a reverse proxy in front of another load balancer, or do I need to use another technology such as Nginx?
I eventually found the answer. I needed to use option http-server-close in my frontend settings.
frontend https
bind *:443 ssl crt /home/simon/haproxy/haproxy_certs
http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
timeout client 5000s
option http-server-close
mode http
#Hello App
acl ACL_hello_app hdr(host) -i hello.soxprox.com
use_backend hello_app if ACL_hello_app
#Nginx App
acl ACL_nginx_app hdr(host) -i nginx.soxprox.com
use_backend nginx_app if ACL_nginx_app
With these settings I get correct round robin results from metallb

Getting 404 when call request by haproxy (directly works fine)

I directly call a web service with url
curl http://venesh.ir/webservice/oauth/token
and I got error 403,
but when I call it by reverse proxy from some server I got 404,is it possible that haproxy change my address?
haproxy config:
frontend localhost
bind *:8081
option tcplog
mode tcp
acl isVenesh dst_port 8081
use_backend venesh if isVenesh
default_backend venesh
backend venesh
mode tcp
balance roundrobin
server web01 venesh.ir:80 check
when I call mySerevrIp:8081/webservice/oauth/token I expect getting the result that I directly call
curl http://venesh.ir/webservice/oauth/token that is 403,
but when I call curl mySerevrIp:8081/webservice/oauth/token I get error 404,
Is a problem with my haproxy or my config or is it possible that this problem is because of venesh.ir website?
It appears that http://venesh.ir/webservice/oauth/token expects the host header to be venesh.ir. You can test this from the command line. If the host header is not venesh.ir, it will return 404:
$ curl -I -H 'Host: 1.1.1.1' http://venesh.ir/webservice/oauth/token
HTTP/1.1 404 Not Found
Date: Mon, 24 Jun 2019 17:48:56 GMT
Server: Apache/2
Content-Type: text/html; charset=iso-8859-1
You can add the host header to your configuration if you change your mode to http:
frontend localhost
bind *:8081
option httplog
mode http
default_backend venesh
backend venesh
mode http
balance roundrobin
http-request set-header Host venesh.ir
server web01 venesh.ir:80 check
The answer of #mweiss was true, and an alternative way that I found is Setting HOST value to venesh.ir in my request header then the tcp reverse proxy works fine.

Using ID in URL for load balancing with HAProxy

I know it is possible to make connections sticky based on url a parameter:
https://serverfault.com/questions/495049/using-url-parameters-for-load-balancing-with-haproxy?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
Is it also possible to do it based on an ID in the url path?
If my url is: /objects/:objectId
Can I somehow use that :objectId to make the connection sticky?
EDIT
I was able to load balance making the request sticky on the url path using the configuration below:
global
#daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
balance roundrobin
stick-table type string size 200k expire 30m
stick on path
server server1 127.0.0.1:8000
server server2 127.0.0.1:8001
listen stats
bind 127.0.0.1:9000
mode http
log global
maxconn 10
stats enable
stats hide-version
stats refresh 5s
stats show-node
stats auth admin:password
stats uri /haproxy?stats
The problem now is that if one of the servers go down the stick-table is not updated. How can I make it so that if one of the servers is not reachable the entries in the stick-table that point to that server are deleted?
Final Answer
Ok, I was able to figure that out. The configuration below makes the requests stick on the url path and HAProxy will make an HTTP GET to /health at every 250ms and if it doesn't returns 200 it will consider the server to be down and that will remove all entries from the stick-table.
global
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
backend servers
balance roundrobin
stick-table type string size 200k expire 30m
option httpchk GET /health
http-check expect status 200
stick on path,word(2,/) if { path_beg /objects/ }
server server1 127.0.0.1:8000 check inter 250
server server2 127.0.0.1:8001 check inter 250
listen stats
bind 127.0.0.1:9000
mode http
log global
maxconn 10
stats enable
stats hide-version
stats refresh 5s
stats show-node
stats auth admin:password
stats uri /haproxy?stats
Use this:
stick on path,word(2,/) if { path_beg /objects/ }

HAProxy - Add response header to indicate the server that was chosen

Consider the following HAProxy Config:
frontend front
default_backend default
backend default
balance roundrobin
http-response set-header X-RGN us-east-1
server app-1a app.us-east-1a.example.com:443 ssl verify none check
server app-1c app.us-east-1c.example.com:443 ssl verify none check
server app-1b app.us-east-1b.example.com:443 ssl verify none check
I would like to return a response header the indicates the server that was chosen. For example, if the frontend receives a request, it will balance roundrobin and forward the request to a backend server, when it responds, I would like to see in my browser which server was used.
The config might look something like this:
frontend front
default_backend default
backend default
balance roundrobin
http-response set-header X-RGN us-east-1
server app-1a app.us-east-1a.example.com:443 ssl verify none check
server app-1c app.us-east-1c.example.com:443 ssl verify none check
server app-1b app.us-east-1b.example.com:443 ssl verify none check
http-response set-header X-Server app-1a if server -i app-1a
http-response set-header X-Server app-1b if server -i app-1b
http-response set-header X-Server app-1c if server -i app-1c
Has anyone tried this before?
Assuming HAProxy 1.5 or later:
http-response set-header X-Server %s