HAProxy with multiple backends without conditions - url-rewriting

I would like to use HAProxy to forward an inbound request to two servers, an old one and a new one, depending on which one is up and running, or randomly if both of them are up.
If the request hits the new server I also need a request rewrite with a special path (hence the http-request below):
frontend myservice
bind:1111
use_backend back-a
use_backend back-b
backend back-a
server back-0 hostname_old:2222 check
backend back-b
http-request set-path /special-path/%[path]
server back-1 hostname_new:443 check-ssl ssl verify none
If the hostname_old is down I would like the request to be forwarded to hostname_new, and viceversa.
However with the configuration above only the first use_backend is used.
So if hostname_old is down I get an error.
If hostname_old is up it works.
Similarly if I swap back-a with back-b I only get a reply when hostname_new is up, otherwise I get an error.
Any ideas how I can get it to forward the request to back-a or back-b depending on which one is up and running?

It looks like A/B testing.
We will need nbsrv, rand, checking server state and probably cookies to stick to the randomly chosen backend with the help of http-request set-var with txn (request and response) scope.
Below i describe config lines relevant to the A/B testing:
acl cookie_backend_a req.cook(backend) a - is there a cookie named backend telling us with value a?
acl backend_up_a nbsrv(backend-a) gt 0 - check number of working servers in backend-a and assume that more than 0 is good enough
acl rand_20 rand(100) ge 20 - roll 0-99 and return true if >= 20, so 80% chance to use this, tweak as desired
http-request set-var(txn.backend) req.cook(backend) - store request cookie for response processing
use_backend backend-a if backend_up_a cookie_backend_a - use backend-a if it is up and we have cookie for it
use_backend backend-a if backend_up_a backend_up_b rand_20 - no cookie, both backends up and we rolled >=20 so go to backend-a
use_backend backend-b if backend_up_a backend_up_b - both backends up, but roll failed, so haproxy goes to next config line and chooses backend-b
use_backend backend-a if backend_up_a - no cookie, only one backend up, so go to working one
default_backend maint - you may want to send clients somewhere else when both backends are down
acl cookie_backend_a var(txn.backend) a - i put this ACL in backends, we check cookie value for our backend, but this is for http-response
http-response add-header set-cookie "backend=a; path=/" unless cookie_backend_a - add set-cookie header, which will bind our client to this backend if they didn't have cookie before and rolled into this backend
All put together below:
frontend foo
bind *:1080
acl cookie_backend_a req.cook(backend) a
acl cookie_backend_b req.cook(backend) b
acl backend_up_a nbsrv(backend-a) gt 0
acl backend_up_b nbsrv(backend-b) gt 0
acl rand_20 rand(100) ge 20
http-request set-var(txn.backend) req.cook(backend)
use_backend backend-a if backend_up_a cookie_backend_a
use_backend backend-b if backend_up_b cookie_backend_b
use_backend backend-a if backend_up_a backend_up_b rand_20
use_backend backend-b if backend_up_a backend_up_b
use_backend backend-a if backend_up_a
use_backend backend-b if backend_up_b
default_backend maint
backend backend-a
acl cookie_backend_a var(txn.backend) a
option httpchk
http-check send meth HEAD uri / ver HTTP/1.1 hdr host localhost
http-response add-header set-cookie "backend=a; path=/" unless cookie_backend_a
server a 127.0.0.1:80 check
backend backend-b
acl cookie_backend_b var(txn.backend) b
option httpchk
http-check send meth HEAD uri / ver HTTP/1.1 hdr host localhost
http-response add-header set-cookie "backend=b; path=/" unless cookie_backend_b
server b 127.0.0.1:81 check
backend maint
(...)
In the backend sections you can tweak request as you did in your question and whatever else you need.
In this config if the client had cookie for backend-a, but it's down, then he goes to backend-b, gets its cookie and stays there until backend-b fails or cookie expires (which i didn't set up in this config). Tweak as needed.

Related

Using Cloudfront as a HAProxy backend server with https

I have a CloudFront resource sitting in front of my S3 bucket. It's accessible at —
https://<id>.cloudfront.net
but if I hit —
<id>.cloudfront.net:443
I get a 400 Bad Request. I want to point to CloudFront in my HAProxy configuration, but I can't use the 443 port because of the above-mentioned issue. Nor can I use the https URL protocol in the server statement.
backend my_cloudfront_app
http-response set-header Strict-Transport-Security max-age=31536000
server my_server <id>.cloudfront.net:443
How can I hit HTTPS cloudfront from this server block in HAProxy?
I assume You will need to add some infos to the request headers for the cloudfront backend.
This example works with HAProxy 2.0
backend my_cloudfront_app
http-response set-header Strict-Transport-Security max-age=31536000
# Add backend header for cloudfront backend request
http-request set-header Host <id>.cloudfront.net
# maybe you will need to add a S3 prefix to the request path
# http-request set-path <CLOUDFRONT_S3_Prefix>%[path]
server my_server <id>.cloudfront.net:443 sni str(<id>.cloudfront.net) ssl verify none

set dynamic host and port for Referer with HTTPRequestDefaults

How can I set dynamic host and port for Referer with HTTPRequestDefaults?
I set HTTPRequestDefaults and all hosts and ports of all http requests are blank, so when running the Jmeter they get the value from HTTPRequestDefaults but all referers are static.
forexample if referer is http://172.16.23.10:9080/mycontext/home-login.action?justLogin=true , then I want the referer to read 172.16.23.10 and 9080 from http request default
Note: I know I can use dynamic variables but I want to now if there's a solution with httprequestdefault.
To do this:
Define 2 variables host and port that you'll use in HTTP Request Defaults
Add a HTTP Header Manager as child of Test Plan and define in it the referrer as:
http://${host}:${port}/mycontext/home-login.action?justLogin=true

Apache rewrite rule based on a host

I have the following configuration, basically its a reverse proxy in front of grafana. I forward the /logout request to the auth server with redirect url and the /login url to /login/generic_oauth.
I want to redirect the /logout to auth server only when the request is from grafana itself that is only if the grafana ui makes the request.
If the request is made by any other origin it should go to the reverse proxy and back. Basically when I logout the user from my angular app I to logout the user my grafana app too. But as I have chained the /logout to the auth server redirect I get a cors error when I make the request.
RewriteEngine on
RewriteRule ^/logout - [C]
I want to excute this rule only if the request is from http://grafana-ip:3001
RewriteRule . "https://auth-server-ip:31443/auth/realms/MDC/protocol/openid-connect/logout?redirect_uri=http://auth-server-ip:8081/login" [NE,R=302,CO=grafana_sess:INVALID:;]
RewriteRule ^/login$ /login/generic_oauth [L,R=302]
ProxyPass / http://grafana-ip:3001/
ProxyPassReverse / http://grafana-ip:3001/
RequestHeader set X-Forwarded-Proto "https"
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "*"
Is there any way I can execute the rule as it is when request is from grafana-ip but not when it is form any other origin
Since you proxy / to the backend, and your clients only request URL's on your proxy w/ referers referencing your proxy, you really have no reason to make the /logout handling conditional. Which is good because you have no ability to discriminate on anything for the same reasons.

HTTPS and HTTP CORS

My questions is simple, but I cannot find an answer and I haven't got any resources to test it myself.
Can I make HTTPS CORS request from one domain to another HTTPS domain?
Can I make HTTP CORS request from one domain to another HTTPS domain?
I know that I can do HTTP CORS request from one domain to another HTTP domain, but I don't know if there is any difference when I use HTTPS.
Yes you can do a CORS request from a HTTPS domain to another HTTPS domain.
The only difference is because HTTPS is a secure origin, you can only make call to secure origin, so not to HTTP, the browser will block it with a message like:
Mixed Content: The page at 'https://example.com/index.html' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example2.com/endpoint'. This request has been blocked; the content must be served over HTTPS.
Warning: If you allow http requests to call your https webpage, it will be insecure because it means an attacker can force requests to your https webpage with the cookies of a victim and read the answer
Beware if you still need to support IE8/IE9 and are using XDomainRequest as it does not support cross-protocol requests. As per MDN:
The origin's security protocol must match that of the requested URL. (http to http, https to https). If these do not match, the request will error "Access is Denied".

Meteorjs Accounts login invalid redirect url

I have set up a meteorjs application in Amazon EC2 and its bind to subdomain like(dev.xyz.com).
I have added HAProxy to it with this settings of HAProxy:
frontend www-http
bind 0.0.0.0:80
reqadd X-Forwarded-Proto:\ http
default_backend www-backend
frontend www-https
bind 0.0.0.0:443 ssl crt /home/ubuntu/haproxycert/host.pem
reqadd X-Forwarded-Proto:\ https
default_backend www-backend
backend www-backend
redirect scheme https if !{ ssl_fc }
#server www-1 171.30.0.185:80 check
server www-1 127.0.0.1:3000 check
I have added loginButtons in the template but when I click the login with the services its taking the redirect url as http://xx.xxx.xxx.xx/_oauth/google?close , instead of
http://dev.xyz.com/_oauth/google?close.
xx.xxx.xxx.xx is the ip of the ec2 instance.
I would very much appreciate if anyone can point me what I have missed or what is wrong in the configuration.

Resources