keycloak + spring adapter +spring security reverse proxy redirecting to root - apache

We've been using a Apache 2.2 reverse proxy to have multiple apps running on the same VM. Everything worked fine till the moment we added Keycloak adapter 3.4.0.Final + spring security 1.5.9.RELEASE.
So this is how it works:
VM1
app1
app2
VM2
Keycloak
laptop on the same network
Browser
app1 - for dev purposes
Scenarios:
1) Everything worked fine between laptop - VM2 with the app.
2) Everything worked fine between browser laptop - VM1 - VM2 When no reverse proxy was in place (so directly accessing the app port).
3) Problems when reverse proxy in place. browser laptop - VM1 (Apache with mod_prox) - VM2
I followed all the advice in the documentation:
http://www.keycloak.org/docs/1.9/server_installation_guide/topics/clustering/load-balancer.html
http://www.keycloak.org/docs/latest/server_installation/index.html#_setting-up-a-load-balancer-or-proxy
Those were my rules before:
LoadModule proxy_module modules/mod_proxy.so
ProxyRequests On
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPassMatch ^/MyAPP/(.+) http://localhost:8585/MyAPP/$1
ProxyPassReverse ^/MyAPP/(.+) http://localhost:8585/MyAPP/$1
ProxyPreserveHost On
Observed:
GET VM1/MyAPP/index.html -> redirected to VM2 keycloak with the right redirect url
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /
GET / -> there's nothing on root so it would end here
First change:
I've seen that the cause of this was a keycloak success handler that was redirecting me to root.
So I changed it to redirect it to the same page with this:
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean( KeycloakAuthenticationProcessingFilter filter){
FilterRegistrationBean registrationBean = new
FilterRegistrationBean(filter);
successHandler = new AuthenticationSuccessHandler();
successHandler.setDefaultTargetUrl("/MyAPP/index.html");
filter.setAuthenticationSuccessHandler(successHandler);
}
Observed:
GET VM1/MyAPP/index.html -> redirected to VM2 keycloak with the right redirect url
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
GET VM1/MyAPP/index.html?State=xxxx -> redirected to /MyAPP/index.html
....
Infinite loop.I've also noticed that the application was doing the authentication everytime.
Suspicious.
So I started suspecting it was apache that was causing problems and decided to give a shot to a different proxy:
haproxy.
It failed with the same problem.
But I was being redirected to /sso/login intead
So next step was to debug the different requests: Proxied vs non Proxied
VM1/MyAPP vs VM1:8585/MyAPP
So I discovered that it would authenticate the request every time the request had the authorization header.This was only happening with the proxy version.
So with apache 2.2 you cannot remove headers. That was just added on 2.4
So again haproxy, and tried it in the same way forcing the removal of authorization header. And it almost worked. I was getting some problem with the request method being 1 instead of GET. Odd...
So long story short It was obvious now that basic auth was the problem.
How to solve this problem?
Update:
My question now is:
How can I change KeycloakAuthenticationEntryPoint loginUri to contain the subcontext of my reverse proxy in the configuration?
- Having a reverse proxy "sso/login" on root doesn't let me have more than 1 keycloak app in the same server.

How did I solve the problem:
1) So the first step was to disable basic auth on the apache that was interfering with keycloak authorization process or at least with spring security.
What I started notice after this was that I was always being redirected to /sso/login on root.
2) To solve this problem I added a new proxy pass rule to redirect sso/login request to my server.
ProxyPassMatch ^/sso/login(.*) http://localhost:8585/sso/login$1
ProxyPassReverse ^/sso/login(.*) http://localhost:8585/sso/login$1
After this I was still being redirected to /sso/login or just to the root.
After trying with incognito mode I found it actually worked.
Tried different browser and it worked as well.
3) Some old session status was still on my browser so cleared all the cookies closed all the browser tabs and restarted chrome and it started working.
4) Bullet point 2 has a big flaw. It won't allow you to have multiple keycloak adapter app in the same server. To change the behavior on spring side I solve it with two processes.
- The first one is to re-set the login uri on keycloakAuthenticationEntryPoint
#Autowired
AdapterDeploymentContext adapterDeploymentContext;
private static final String OAUTH_LOGIN_URL = "/sso/login";
#Override
protected void configure(HttpSecurity http) throws Exception{
KeycloakAuthenticationEntryPoint keycloakAuthenticationEntryPoint =
new KeycloakAuthenticationEntryPoint(adapterDeploymentContext);
keycloakAuthenticationEntryPoint.setLoginUri(realSubContext + OAUTH_LOGIN_URL);
...
}
The second one was to add a redirect rewrite rule for sso login
keycloak:
redirect-rewrite-rules :
"^/sso/login(.*)$": "/MYSUBCONTEXT/sso/login$1"

Related

HTTPD Proxy Change Response Address

My setup is as follows:
client -> proxy(dnsname eg. https://test.com) -> Jetty webapp(1.2.3.4)
The webapp sends a redirect response back (to an authentication webapp) to the client. It automatically points to the proxy via dnsname eg. https://proxy/auth and cannot be configured further.
The issue with this is the webapp will pass redirects back to the client and the client cannot resolve https://proxy as I can't make it a dns entry. Is it then possible for the proxy to intercept the traffic from the webapp (https://proxy) and change it to https://test.com? Even better can the proxy autodetect the entry dns name and append it to any responses from the webapp?
I'd envisioned the following:
client request https://test.com/page1-> hits proxy which resolves to webapp -> webapp gives redirect response via https://proxy/auth -> proxy intercepts and changes redirect to https://test.com/auth
I need this so that everything behind the proxy isn't machine nor ip specific. I can shift and deploy to any environment.
I figured this out eventually. You can just modify the redirect headers in the location field.
Header edit Location "(^http[s]?://proxy)" "https://whatevernameyouwant"

Jersey + Grizzly behind Apache reverse proxy - Resource not found

I am having a jersey based REST Application which is running on grizzly and configured it as follows behind an apache reverse proxy
e.g. www.example.com/base/json/helloworld/get should redirect to my REST service. (Multiple Resources). However, when I try to open the url in the browser I always get a Resource not found.
In my apache config:
ProxyPass /base/ http://localhost:8123/
ProxyPassReverse /base/ "http://localhost:8123/
My grizzly server configuration is really simple and works if I do a curl request: curl localhost:8123/base/json/helloworld/get => Response is correct.
However, if I acess the url in my browser, e.g.
example.com/base/json/helloworld I am getting the grizzly error:
Resource identified by path /json/helloworld/get', does not exist.
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(http://localhost:8123/base), resourceConfig, false);
Am I missing something? I already tried changing the url to the domain and so on, but I can't get my head around why it doesn't work.
I found my fault. In the grizzly server my url was configured with the path /base/, but the Proxy Pass did not include the /base/ path at the end of the url. Now it works fine.

Alfresco Share login error behind reverse proxy

I configure my Alfresco instance to be in a sub-URI (www.example.com/prefix/alfresco , www.example.com/prefix/alfresco/share) and all looks to be fine except that I can't log in Shared. The Catalina.out log this error:
ERROR [alfresco.web.site] [http-apr-28080-exec-10]
javax.servlet.ServletException: Possible CSRF attack noted when
asserting referer header
'http://www.example.com/prefix/alfresco/share/page/'. Request: POST
/prefix/alfresco/share/page/dologin, FAILED TEST: Assert referer POST
/prefix/alfresco/share/page/dologin :: referer:
'http://www.example.com/prefix/alfresco/share/page/' vs server &
context: http://10.140.8.144/ (string) or (regexp)
Then the browser show me this page (www.example.com/prefix/alfresco/share/dologin):
Something's wrong with this page...
We may have hit an error or something might have been removed or
deleted, so check that the URL is correct.
Alternatively you might not have permission to view the page (it could
be on a private site) or there could have been an internal error. Try
checking with your Alfresco administrator.
If you're trying to get to your home page and it's no longer available
you should change it by clicking your name on the Alfresco toolbar.
I tried to deactivate the CSRF filter in share-config-custom.xml, but then I can't log and I don't have any message in the log, the login page show:
Your authentication details have not been recognized or Alfresco may
not be available at this time.
My apache conf:
ProxyPass /prefix/alfresco
http://10.140.8.144:28080/prefix/alfresco ProxyPassReverse
/prefix/alfresco http://10.140.8.144:28080/prefix/alfresco
ProxyPass /prefix/alfresco/share
http://10.140.8.144:28080/prefix/share ProxyPassReverse
/prefix/alfresco/share http://10.140.8.144:28080/prefix/share
I could log before configure Alfresco for work in the reverse proxy.
There is no need to deactivate the CSRF filter. If you changed the context path as described in the documentation you need to make sure that the tomcat connector "knows" the outside context (hostname, port, context).
Either
set proxyName and proxyPort
set RemoteIpValve in tomcat server.xml and set required proxy header
variables in apache (x-forwarded-for, x-forwarded-by,
x-forwarded-proto)
use proxy_ajp instead of proxy_http and define a ajp connector in
tomcat

Glassfish - Domain wise redirection to application

I am running Glassfish Server on Linux 6 for my Oracle Apex applications running on Port 8080 and 8181(for https).
now, suppose my domain is mydomain.com,
and when I access
a.mydomain.com -> it should be redirected to application 1 (i.e a.mydomain.com:8080/apex/f?p=1)
b.mydomain.com -> it should be redirected to application 2 (i.e b.mydomain.com:8080/apex/f?p=2)
and so on...
if it is hard to figure out with Glassfish, I can also move to Apache Tomcat if required.
Any advice/idea would be greatly appreciable.
I don't know Glassfish, but you might try ProxyPass inside your virtual host config
ProxyPass / http://a.mydomain.com:8080/apex/f?p=1
Similar for the b.mydomain.com domain.
If you really want a redirect, i.e. the browser's URL changes, use Redirect instead
Redirect / http://a.mydomain.com:8080/apex/f?p=1

JSF behind Apache with SSL - h:commandLink with and without f:ajax

[Edit] - Thanks for the comments. I've tried to shape my question accordingly and I've added some additional information based on the suggestions offered.
I have a JSF web application running on JBoss AS7 that I front with Apache on SSL (port 443). Apache and JBoss are running on the same machine and communicate "in the clear" over HTTP with reverse proxy forwarding rules. With this setup, I have observed that clicking on any link created via the JSF tag <h:commandLink> (i.e. without <f:ajax> and where there is always a genuine backing bean action method that, after performing some business logic, returns an outcome with the ?faces-redirect=true suffix) will see the page redirect fail because the https scheme is dropped and replaced with http.
If I replace all instances of <h:commandLink> with <h:commandLink><f:ajax/></h:commandLink>, this redirection problem goes away - i.e. the https scheme is preserved in the resulting redirected URL.
Could anyone explain to me what I'm observing and what the "under-the-hood" difference between the non-ajax form submit and ajax submit might be in this case?
Additional Information:
My Apache reverse proxy rules:
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Order Allow,Deny
Allow from all
</Proxy>
ProxyPass / http://localhost:8080/ timeout=1800
ProxyPassReverse / http://localhost:8080/
The web console in Firefox shows the difference between the ajax form submit and non-ajax form submit HTTP requests as:
Ajax submit via <h:commandLink><f:ajax/></h:commandLink>
I see a GET request to the expected page on https (I can't yet post screen shots)
Non-ajax submit via <h:commandLink/>
The non-ajax version first POSTs back to the same page (which is expected) on https with a 302 status code 'Moved Temporarily' and then redirects to the target page from the action method on http.
Location in HTTP header for POST before failed GET redirect on http
Here's a screen shot. The location value for the POST shows as the URL of the GET request, on http and not https:
I guess I've gotten slightly "under-the-hood" at this point. Since the <f:ajax> approach achieves the desired result, I am happy leaving this alone. It seems there are at least a few other JSF posts related to this same sort of thing (here's the single URL I'm allowed to post):
JSF redirects from HTTPS to HTTP
Maybe the ajax-based approach is required to preserve the scheme in the URL originating from the client?
Thanks,
-Andy