I’m trying to document the correct way to proxy to netdata with haproxy, on 127.0.0.1:19999. The problem I’m having is - only when running with a path - the styling fails. I assume I have to do something to rewrite the path, I’m not sure how to translate the known working configuration from nginx to haproxy.
The accepted nginx configuration is:
upstream netdata {
# the Netdata server
server 127.0.0.1:19999;
keepalive 64;
}
server {
listen 81;
# the virtual host name of this subfolder should be exposed
server_name netdata.domain.tld;
location = /netdata {
return 301 /netdata/;
}
location ~ /netdata/(?<ndpath>.*) {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_http_version 1.1;
proxy_set_header Connection "keep-alive";
proxy_store off;
proxy_pass http://netdata/$ndpath$is_args$args;
}
}
The configuration I’ve tried using is:
frontend http_frontend
## HTTP ipv4 and ipv6 on all ips ##
bind :::80 v4v6
# URL begins with /netdata
acl is_netdata url_beg /netdata
## Backends ##
use_backend netdata_backend if is_netdata
# Other requests go here (optional)
default_backend www_backend
backend netdata_backend
option forwardfor
server netdata_local 127.0.0.1:19999
http-request set-header Host %[src]
http-request set-header X-Forwarded-For %[src]
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header Connection "keep-alive"
I tried using a rewrite to remove the sub path but wasn’t able to get it to work. Is there a equivalent configuration I should be using to the nginx configuration to get the styling working?
By looking at your NGinx configuration I see that you are in fact dropping the /netdata part of the path when sending your request to the actual server. Meanwhile you don’t do the same for the HAProxy backend.
You should add inside the backend section something like this:
HAProxy does not actually serve anything from the filesystem, therefore that error is from the downstream backend server. (Check the HTTP response headers to see which server generates this error message.)
Please note that the regsub pattern I gave you above doesn’t also replace /netdata, but requires /netdata/, you should try ^/netdata(/|$) as regular expression.
So it works when I add the trailing slash but when I don’t add the trailing slash, I got it to work, as in to send me to the right page, but styling is still broken.
I had to modify your example because haproxy doesn’t support parentheses in regular expressions, so I used the following:
acl nd_path_slash path_end /
http-request set-path %[path,regsub(^/netdata/,/)] if nd_path_slash
http-request set-path %[path,regsub(^/netdata,/)] if ! nd_path_slash
Not sure if you have a more ideal solution.
Here is the response I get with styling removed when I visit the URL without the trailing slash. For some reason it’s removing the subdomain which is breaking CSS and JavaScript:
General
Request URL: https://example.com/main.css?v=5
Request Method: GET
Status Code: 404 Not Found
Remote Address: REDACTED:443
Referrer Policy: no-referrer-when-downgrade
Request Headers
Provisional headers are shown
DNT: 1
Referer: https://subdomain.example.com/netdata
With the trailing slash these are the request headers:
It is really hard for me to make a mental map of what works and what doesn’t. Cloud you provide a simple “list” like:
when the URL browser is /netdata/, and HAProxy rewrites it as /, it works;
when the URL browser is /netdata, and HAProxy rewrites it as /, it doesn’t work;
etc.
Indeed, reviewing the documentation it seems that closing ) doesn’t work.
I would approach this a different way:
redirect if the URL is exactly /netdata;
do the replace of /netdata/ with /; it keeps things simpler;
As expected, and as hinted in my previous replies, just by rewriting the request path won’t magically make your web application work if it was not designed to support this method of serving.
The reason is how the browser works when it requests relative paths. If your URL in the browser is /netdata, and the page embeds an ./main.css (or just main.css), then the browser would ask for /main.css (as it is relative to the “folder” of the /netdata which is /); if however your browser URL is /netdata/, then that relative URL becomes /netdata/main.css. However if your webserver returns in its body an URL like /main.css, i.e. an absolute one, then the browser will just ignore the current URL, and always ask for /main.css.
Therefore I would strongly advise to double check the documentation and see if they support this way of working, or alternatively just use an entire subdomain dedicated to the monitoring tool, and don’t do any path mangling.
Then if /netdata/ works, use the redirect to /netdata/ if it is only /netdata, and use that single regular expression to replace it with /.
I would strongly suggest to use a different subdomain, dedicated to each “internal” service, not only it simplifies things, but especially since the entire web security model (as implemented in the browsers) is based on the concept of origin, which includes only the schema, domain, and port.
(For example if your applications use cookies for authentication, and they don’t mention a path when they set the cookie (which is by default in many cases), then if someone compromises say one of your least important services running on /whatever, then they could also steal your cookies for the service running on /important.)
(Not to mention that a compromised application running on /whatever can easily initiate AJAX requests to your /important directly from the browser.)