haproxy configuration for RESTful loadbalancing - creating resurces - rest

I am trying to come up with a haproxy configuration
for a cluster of rest servers where the creation of a resource
should be sent in round-robin to the servers
POST /somecollection
responds with the created resource uri (eg /collection/instance01 )
but then any further use of the created resources
GET /collection/instance01/blah
PUT /collection/instance01/foo
DELETE /collection/instance01/
must be sticky to the server instance that handled the POST
(where the resource has been created).
And I would like to do that without cookies :-)
Any ideas ?

One way to achieve this is via cookie:
Example:
backend rest_backend
...
cookie __sticky_rest insert indirect nocache
server rest1 x.x.x.1:xxxx cookie 1
server rest2 x.x.x.2:xxxx cookie 2
...

Related

Protect my RESTful services by a Keycloak Oauth2 Provider Using NGINX

We have some Restful services on a certain URI and we wanted to publish our services on the web to use them in our mobile app(written in java),
Our services was on a server which cannot handle too much requests at a same time and used it's proxy_pass functionality for this,
So I used Nginx on an intermediate server to control access to our REST server,
Now we want to protect our services by Oauth2 with Password or Client Credentials(as our mobile users should not login into our servers we cannot display any login page to them),
I setup a Keycloak server which is working and I could get token for my client. I'm going to give my auth/token URI to our mobile developers to get Oauth2 token at first and use it in their requests.
The problem is I don't know how to configure Nginx to authorize incoming REST requests with provided token in request header.
Should I config Keycloak to Handle requests and forward authorized ones to NGINX?
Thanks for your help
After some tries I found this solution:
1- you have to add njs module to nginx this is necessary, you have to compile it first(so in windows it will be much trouble, I tried mingw and stopped at a dependency called expect which is not written for mingw and it wasted a lot of time from me, actually I moved our IAM to ubuntu and compiling njs and nginx in there was done in few minutes!)
2- Introspection is the key subject here, and keycloak supports it, its URI is the same as token URI plus introspect, using Basic Authorization in header and token in body
3- nginx also supports introspection after adding njs module to it, which will let nginx support js code inculde inside config file, a great exaple is NGINX Demoes- Oauth2 Introspection OSS, just copy config file and oauth2.js file and its done. I added api directive at location tag in nginx config file to let callers know it is protected.
4- create one client for nginx in keycloak to do the introspection operation, it should be in confidential mode and Service Accounts should be enabled for it.
5- nginx should forward(proxy pass) auth/token request to IAM, so a location for this should be added in config file.
6- [for ubuntu] I have an error in nginx which tells me it cannot resolve localhost! and installing Bind 9 fixes this for me(another time wasting effort was done here).
7- So any one wants to use our service should request token first and then sends its request attached with token to nginx, nginx introspects token and if token was ok and {"active": true} were received forwards request to resource and passws reply to requester.
All done.

haproxy return empty response

I'd like to have health checks to an haproxy instance fail solely based on whether haproxy is running. In other words I don't want the health check to be proxied to a backend server.
I see there is a way to do this by returning a static file. This would work for me, but I was wondering: Is there a way to return an empty response with just a status code without having to return a file and deal with the false 503? The solution linked seems hacky and I worry that behavior will not be allowed in some later version.
monitor-uri <uri>
Intercept a URI used by external components' monitor requests
May be used in sections :
When an HTTP request referencing will be received on a frontend,
HAProxy will not forward it nor log it, but instead will return either
"HTTP/1.0 200 OK" or "HTTP/1.0 503 Service unavailable", depending on
failure conditions defined with "monitor fail". This is normally
enough for any front-end HTTP probe to detect that the service is UP
and running without forwarding the request to a backend server. Note
that the HTTP method, the version and all headers are ignored, but the
request must at least be valid at the HTTP level. This keyword may
only be used with an HTTP-mode frontend.
Example :
# Use /haproxy_test to report haproxy's status
frontend www
mode http
monitor-uri /haproxy_test

How to implement the code which redirect clients request to another node

I'm very curious about how to implement redirect code in a back server node.
For example: Client A request web server C, there is a load balance node B between A and C. So the graph is A=>B=>C=>A (not A=>B=>C=>B=>A). Actually C get requests from B, so I'm wondering how does C create a socket to connect to A and send data to A. I highly appreciate if you would share me some code snippet about this, Thanks!
I think this is the question you are asking:
"I have multiple web servers behind a load balancer, so how can I create a persistent http socket connection to a back end server from a client without it being redirected to another server and therefore breaking connection?"
The answer to that question is through cookie injection. For example, with HAProxy you can set a cookie depending on the server that the request is routed to first, then the load balancer will know to stick that request in future to the specified server.
An example in HAProxy backend configuration:
backend socket-servers
timeout server 120s
balance leastconn
# based on cookie set in header
# haproxy will add the cookies for us
cookie SRVNAME insert
server node-1 127.0.0.1:5000 cookie S1 check
server node-2 127.0.0.1:5001 cookie S2 check
This example was taken from http://toon.io/configuring-haproxy-multiple-engine-io-servers/.
Upon a new request it will see no cookie, and route to the best server based on the server with the least number of connections. As it does so, it sets a cookie SRVNAME=node-1 or SRVNAME=node-2 depending on the server it goes to. Every subsequent request from the client goes to the node specified in the cookie.

Behaviour of SAML when HTTP Server used for high availability

I have implemented the supporting of SAML SSO to have my application act as the Service Provider using Spring Security SAML Extension. I was able to integrate my SP with different IDPs. So for example I have HostA,HostB, and HostC, all these have different instances of my application. I had an SP metadata file specified for each host and set the AssertionConsumerServiceURL with the URL of that host( EX:https:HostA.com/myapp/saml/sso ). I added each metadata file to the IDP and tested all of them and it is working fine.
However, my project also supports High Availability by having an IBM HTTP Server configured for load balancing. So in this case the HTTP Server will configure the hosts(A,B,C) to be the hosts used for load balancing, the user will access the my application using the URL of the HTTP server: https:httpserver.com/myapp/
If I defined one SP metadata file and had the URL of the HTTP Server specified in the AssertionConsumerServiceURL( https://httpserver.com/saml/sso ) and changed my implementation to accept assertions targeted to my HTTP Server, what will be the outcome of this scenario:
User accesses the HTTPServer which dispatched the user to HostA(behind the scenes)
My SP application in HostA sends a request to the IDP for authentication.
The IDP sends back the response to my httpserver as: https://httpserver.com/saml/sso .
Will the HTTP Server redirect to HostA, to have it like this: https://HostA.com/saml/sso
Thanks.
When deploying same instance of application in a clustered mode behind a load balancer you need to instruct the back-end applications about the public URL on the HTTP server (https://httpserver.com/myapp/) which they are deployed behind. You can do this using the SAMLContextProviderLB (see more in the manual). But you seem to have already successfully performed this step.
Once your HTTP Server receives a request, it will forward it to one of your hosts to URL e.g. https://HostA.com/saml/sso and usually will also provide the original URL as an HTTP header. The SAMLContextProviderLB will make the SP application think that the real URL was https://httpserver.com/saml/sso which will make it pass all the SAML security checks related to destination URL.
As the back-end applications store state in their HttpSessions make sure to do one of the following:
enable sticky session on the HTTP server (so that related requests are always directed to the same server
make sure to replicate HTTP session across your cluster
disable checking of response ID by including bean EmptyStorageFactory in your Spring configuration (this option also makes Single Logout unavailable)

Route by using existing cookie

How can I route requests in haproxy using a cookie that was set on the app servers?
Example: SESS=<hash-of-username>
haproxy should not insert cookies by itself in any case.
For testing a specific server behind haproxy I can recommend this approach:
frontend http
acl is_cookie_hack_1 hdr_sub(cookie) server_test_hack=server1
acl is_cookie_hack_2 hdr_sub(cookie) server_test_hack=server2
... insert your normal acl rules here
use_backend bk_server_1 if is_cookie_hack_1
use_backend bk_server_2 if is_cookie_hack_2
... insert your normal use_backend expressions here
backend bk_server_1
...
backend bk_server_2
...
I insert the server_test_hack cookie by javascript in my browser's js console by this script:
document.cookie="server_test_hack=server1";
You can't use your existing cookie for balancing, the way you could use the URI parameter. You can't just take the md5() or build the hash table of the cookie, at least that is not documented. You could use prefix parameter for the cookie to achieve a different result. It might be what you are looking for (if you want to avoid creation of yet another cookie).
So in your case the config would look like this:
backend bk_web
balance roundrobin
cookie SESS prefix indirect nocache
server s1 192.168.10.11:80 check cookie s1
server s2 192.168.10.21:80 check cookie s2
When the request arrives without a cookie, any server is chosen by round-robin and request is redirected to it. When response arrives from the backend, HAProxy checks for the SESS cookie and if it's set, it prepends the server name (sX) to the cookie and sends it to the client. In the browser, the cookie looks like sX~, but when the next request is sent with that cookie, the backend server only sees in the cookie, as HAProxy strips the sX~ part
Source: load balancing, affinity, persistence, sticky sessions: what you need to know
If you just want to read cookies in the request and route accordingly, you can do something like this in your configuration:
frontend http
acl cookie_found hdr_sub(cookie) COOKIENAME
use_backend app_server if cookie_found
backend app_server
balance roundrobin
server channel1 X.X.X.X:PORT #Host1
server channel2 Y.Y.Y.Y:PORT #Host2