Haproxy path manipulation - url

In Google Cloud Platform we do have a haproxy which serves as proxy for our internal VPN and do URL redirects from GCP DNS to proxy.mycompany.com (our haproxy) where we do further manipulation.
Haproxy is from image 2.0.8-alpine
Nginx is based on image 1-alpine
Lately I have installed nginx which serves static files.
Nginx is setup for autoindex on, accesible on url static-files.mycompany.com, and configured to show root of a gcs-bucket. Therefore if you visit static-files.mycompany.com you will see content of gcs-bucket:
directory static-files
any file in a bucket.
static-files directory contains static website files which were generated by hugo.
Those files contain paths like /docu/file.md
I've managed to configure haproxy that it will forward any requests for url website.mycompany.com to static-files.mycompany.com/static-files and show the generated static website by configuring this in backend section of haproxy config:
acl p_root path -i /
http-request set-path /static-files/\1 if p_root
Every time I visit website.mycompany.com the path is redirected to website.mycompany.com/static-files (which is actually static-files.mycompany.com/static-files )
However, the site is broken:
css is not loading, file is located in / and the requests aims for website.mycompany.com/mycss.file, but the file can be found under website.mycompany.com/static-files/mycss.file due to path manipulation above
Every link on site dead, requests website.mycompany.com/my-link-to-file but file is under
website.mycompany.com/static-files/my-link-to-file
I have a limited possibility to configure haproxy, most probably I can add parameters to existing backend and frontend section, here I am providing parameters I cannot change:
global
log stdout format raw local0 info
maxconn 30000
tune.ssl.default-dh-param 2048
resolvers vpc
parse-resolv-conf
hold valid 120s
defaults
mode http
log global
option dontlognull
#option tcplog
option httplog
option forwardfor
option redispatch
maxconn 3000
retries 3
timeout connect 5s
timeout client 50s
timeout server 50s
timeout tunnel 1h
timeout client-fin 30s
timeout http-keep-alive 4s
balance roundrobin
default-server check resolvers vpc
default-server on-marked-down shutdown-sessions
default-server max-reuse 100
frontend http
bind *:80
redirect scheme https code 302 if !{ ssl_fc }
monitor-uri /_healtz
use_backend %[hdr(host),lower]
frontend https
bind *:443 ssl no-sslv3 crt /config/haproxy_certificate.pem alpn http/1.1,h2
http-response add-header Via 1.1\ %[env(HOSTNAME)]
http-request add-header Via 1.1\ %[env(HOSTNAME)]
http-request add-header X-Forwarded-Proto https
http-request capture req.hdr(Host) len 40
http-request capture req.hdr(User-Agent) len 120
use_backend %[hdr(host),lower]
backend proxy.mycompany.com
stats enable
stats uri /
I would like to achieve the following:
By visiting website.mycompany.com I will be forwarded to static-files.mycompany.com/static-files in the background, however the URL will remain as website.mycompany.com and therefore the generated static website paths remain working and so the static site. Or Am I mistaken ?
I am open to any reasonable suggestion.
thank you

Well, it was quite simple in the end
http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/static-files\2
Did the trick.
edit:
to avoid and infinite loop of /static-files/static-files/
acl remove_static-files path_beg -i /architecture-diagrams
http-request replace-uri ([^/:]*://[^/]*)?(.*) \1/static-files\2 if !remove_static-files

Related

Issues proxying request with HAproxy

We're trying to set up HAproxy enterprise for reverse proxying requests to our internal microservices. The configuration we have created accepts all traffic on port 80 and has multiple backends to proxy requests to. But the requests are not getting proxied all the time in a stable manner. We have around 10 URLs and everytime we restart the hapee service some of the URLs stop working.
frontend example.com
bind *:80
mode http
acl ACL_abc hdr(host) -i abc.example.com
acl ACL_url1 hdr(host) -i abc.example.com && path /url1
acl ACL_def hdr(host) -i def.example.com
acl ACL_pqr hdr(host) -i pqr.example.com
use backend abc if ACL_abc
use backend url1 if ACL_url1
use backend def if ACL_def
use backend pqr if ACL_pqr
backend abc
http-request set-header Host abc.internal.com
server server1 abc.internal.com
backend url1
http-request set-header Host url1.internal.com
server server1 url1.internal.com
backend def
http-request set-header Host def.internal.com
server server1 def.internal.com
backend pqr
http-request set-header Host pqr.internal.com
server server1 pqr.internal.com

How Install SSL Certificate on ha proxy dockerfile using letsencrypt

I am using haproxy to route the domains and subdomains and it is deployed on port 80. I want that all the domains should be https or using SSL certificate.
global
log xx.xx.90.28 local0
log xx.xx.90.28 local1 notice
maxconn 2048
defaults
log global
mode http
option httplog
option dontlognull
option redispatch
option forwardfor
option http-server-close
retries 3
timeout connect 5000
timeout client 10000
timeout server 10000
frontend balancer
bind *:80
mode http
stats enable
stats uri /stats
stats refresh 15s
stats show-node
stats auth admin:admin
acl domain hdr_dom(host) -i www.example.com
acl subdomain hdr_dom(host) -i app.example.com
acl subdomain1 hdr_dom(host) -i examplecom
use_backend go_app_1 if domain
use_backend go_app_2 if subdomain
use_backend go_app_3 if subdomain1
backend go_app_1
balance roundrobin
mode http
option forwardfor
server go xx.xx.90.28:8081 check
backend go_app_2
balance roundrobin
mode http
option forwardfor
server go xx.xx.90.28:8082 check
backend go_app_3
balance roundrobin
mode http
option forwardfor
server go xx.xx.90.28:8081 check
And here is the DockerFile
FROM haproxy:2.1
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
Now I want to use letsencrypt to secure these URL please guide me how I can do this?
I think we need to setup letsencrypt locally first.
And inject the result setting to the container via volume mount.
Example command from Docker Hub:
docker run -d --name my-running-haproxy -v /path/to/etc/haproxy:/usr/local/etc/haproxy:ro haproxy:1.7
Something like below:
-v /my/letsencrypt/setting/:/etc/ssl/

HaProxy forward proxy works on HTTP but gives 503 on HTTPS

I'm expecting the following config to receive HTTPS requests, do the SSL offloading and send HTTP requests to my backends, however with HTTPS I get "503 service unavailable".
all ACLs work correctly on HTTP and the stats page shows them as online
The stats page works correctly on HTTPS
These are all in a docker compose file, docker is doing the name resolution to internal IP correctly
Perhaps I'm missing something obvious? Quite new to attempting this so any help is appreciated.
global
tune.ssl.default-dh-param 2048
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
backend certbot
option httpchk GET /
default-server init-addr libc,none
server certbot_server certbot check port 80
backend client
option httpchk HEAD /
server client_server client check port 80
backend api
option httpchk OPTIONS /api/healthcheck
server api_server api check port 80
frontend app
bind *:80
bind *:443 ssl crt /certs/productpedia.co.uk.pem
use_backend certbot if { path_beg -i /.well-known/acme-challenge/ }
use_backend api if { path_beg /api }
default_backend client
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
EDIT:
Attached a wireshark trace of the the 503 request, looks like server is resetting the connection, but not sure where I can go from here, or what would be causing this?
After a full day of debugging, it looks like simply specifying port 80 did the trick, although I would have expected the default port to be 80 perhaps it carries through a default port of 443? I could also get rid of the port 80 after check after this change which was the original trigger hint that something might be off there.
backend certbot
option httpchk GET /
default-server init-addr libc,none
server certbot_server certbot:80 check
backend client
option httpchk HEAD /
server client_server client:80 check
backend api
option httpchk OPTIONS /api/healthcheck
server api_server api:80 check

How to run stats in HAProxy 1.6.4 on same port as frontend?

I'm using HAProxy 1.6.4 and want to enable the stats. (/haproxy?stats)
Here is my cfg:
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
frontend http-in
bind *:8080
default_backend testb
backend testb
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
listen statistics
bind *:8080
mode http
stats enable
If I run statistics on other port than 8080 it works, but how can I run it on the same port as my frontend (8080), which is running in the default mode tcp?
You can do it by redirecting to your self and using access list like this:
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
listen stats :1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
stats auth myUser:myPassword
frontend http-in
bind *:8080
acl is_www hdr_end(host) -i www.mysite.com
acl is_stat hdr_end(host) -i stat.mysite.com
use_backend srv_www if is_www
use_backend srv_stat if is_stat
backend srv_www
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
backend srv_stat
server Local 127.0.0.1:1936
When going to your server with www, it takes you to the web server.
But using stat, it redirects you from your input port 8080 to 1936 whee stat is running
This is just an educated guess. You can't serve the stats page in tcp mode because it's proxying at layer4. In this mode, haproxy only knows IPs and ports from incoming packets and routes it accordingly based on defined rules.
Unlike http mode (layer7), it has more info to work on like HTTP headers where path is available and use it to know when to serve the /haproxy?stats page.
If you are happy to use a your path, it really easy.
this should work
global
log 127.0.0.1 local2
daemon
maxconn 256
defaults
log global
timeout connect 5000
timeout client 10000
timeout server 10000
frontend http-in
bind *:8080
stats enable
stats uri /stats
default_backend testb
backend testb
balance roundrobin
server s1 123.456.789.0:443 maxconn 32
server s2 123.456.789.1:443 maxconn 32
you can then access the haproxy stats from
http://< hostname >:8080/stats
(tested on haproxy 2.5.5)

HAProxy Sticky Sessions Node.js iOS Socket.io

I am trying to implement sticky sessions with HAProxy.
I have a HAProxy instance that routes to two different Node.js servers, each running socket.io. I am connecting to these socket servers (via the HAProxy server) using an iOS app (https://github.com/pkyeck/socket.IO-objc).
Unlike when using a web browser, the sticky sessions do not work, it is like the client is not handling the cookie properly and so the HAProxy server just routes the request to wherever it likes. Below you can see my HAProxy config (I have removed the IP addresses):
listen webfarm xxx.xxx.xxx.xxx:80
mode http
stats enable
stats uri /haproxy?stats
stats realm Haproxy\ Statistics
stats auth haproxy:stats
balance roundrobin
#replace XXXX with customer site name
cookie SERVERID insert indirect nocache
option httpclose
option forwardfor
#replace with web node private ip
server web01 yyy.yyy.yyy.yyy:8000 cookie server1 weight 1 maxconn 1024 check
#replace with web node private ip
server web02 zzz.zzz.zzz.zzz:8000 cookie server2 weight 1 maxconn 1024 check
This is causing a problem with the socket.io handshake, because the initial handshake routes to server1 then subsequent heartbeats from the client go to server2. This causes server2 to reject the client because the socket session ID is invalid as far as server 2 is concerned, when really all requests from the client should go to the same server.
Update the haproxy config file /etc/haproxy/haproxy.cfg by the following:
global
daemon
maxconn 256
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http-in
bind *:80
default_backend servers
option forwardfor
backend servers
cookie SRVNAME insert
balance leastconn
option forwardfor
server node1 127.0.0.1:3001 cookie node1 check
server node2 127.0.0.1:3002 cookie node2 check
server node3 127.0.0.1:3003 cookie node3 check
server node4 127.0.0.1:3004 cookie node4 check
server node5 127.0.0.1:3005 cookie node5 check

Resources