Zuul reverse proxy with Keycloak server
Asked Answered
C

2

7

I'm configuring a Spring Cloud (Angel.SR6) application using the Zuul reverse proxy utility, in order to hide the internal service ports. My zuul (edge) service is published in the 8765 port and my organizations service is in the 8083 one. Everything goes smoothly when I access the application with no security, http://localhost:8765/organization/organizations returns the JSON with all the organizations.

However, now I want to integrate a Keycloak SSO (OAuth2) server for authorization purposes. I have added the Spring Security adapter in my organization service and configured it to authenticate in http://localhost:8080/auth. Everything goes well, except that zuul performs a redirection instead of proxying. So when authentication is successful, I get redirected to http://localhost:8083/organizations instead of http://localhost:8765/organization/organizations. Here there are my browser requests:

enter image description here

That's because the keycloak adapter creates a token verification endpoint in the http://localhost:8083/sso/login, from which it performs a redirection to the authorization server in order to validate the token. When authorization server acknowledges it, a redirection is sent to the organization service, with the /organization path, so the end url being loaded is http://localhost:8083/organizations. But I would like the first requested url to be loaded instead.

Which choice do I have?

Complemental answered 19/5, 2016 at 16:35 Comment(2)
One thing I can say for sure, is that zuul does no redirects by itself, it only forwards responses, so if the downstream service sends a redirect, zuul will forward it on to the browser.Overboard
@spencergibb, I know, in fact the problem lies on the downstream service, which has a login endpoint configured which redirects to the SSO server. The SSO server receives the url to redirect to as a url param, like /auth/realm/master?redirectUri=http://localhost:8083/sso/login. So a redirect is performed by the SSO server to that url, which also redirects to the final http://localhost:8083/organizations path. A solution would be to secure only the zuul service, so I would have every request redirected to zuul itself, but that would involve leaving the rest of the services exposed.Complemental
C
0

(Migrated from comment to answer)

I ended up making a Github project in order to explain my problem to the keycloak team, and got a pull request from one of the development team members trying to help me out. Following their recommendations, I came into the conclusion that zuul is good to hide stateless services (bearer only ones), but not the ones that user directly interacts with. Here it is the whole thread in the mailing list.

Complemental answered 9/3, 2020 at 15:49 Comment(0)
A
5

Recently I've had the same problem. I've solved it by:

  1. Add to application.properties in Zuul

    zuul.sensitive-headers=Cookie,Set-Cookie

  2. Introduce KeycloakFilterRoute in Zuul

    class KeycloakFilterRoute extends ZuulFilter {
    
    private static final String AUTHORIZATION_HEADER = "authorization";
    
    @Override
    public String filterType() {
        return "route";
    }
    
    @Override
    public int filterOrder() {
        return 0;
    }
    
    @Override
    public boolean shouldFilter() {
        return true;
    }
    
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        if (ctx.getRequest().getHeader(AUTHORIZATION_HEADER) == null) {
            addKeycloakTokenToHeader(ctx);
        }
        return null;
    }
    
    private void addKeycloakTokenToHeader(RequestContext ctx) {
        RefreshableKeycloakSecurityContext securityContext = getRefreshableKeycloakSecurityContext(ctx);
        if (securityContext != null) {
            ctx.addZuulRequestHeader(AUTHORIZATION_HEADER, buildBearerToken(securityContext));
        }
    }
    
    private RefreshableKeycloakSecurityContext getRefreshableKeycloakSecurityContext(RequestContext ctx) {
        if (ctx.getRequest().getUserPrincipal() instanceof KeycloakAuthenticationToken) {
            KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) ctx.getRequest().getUserPrincipal();
            return (RefreshableKeycloakSecurityContext) token.getCredentials();
        }
        return null;
    }
    
    private String buildBearerToken(RefreshableKeycloakSecurityContext securityContext) {
        return "Bearer " + securityContext.getTokenString();
    }
    

    }

Arteriotomy answered 8/6, 2016 at 22:52 Comment(0)
C
0

(Migrated from comment to answer)

I ended up making a Github project in order to explain my problem to the keycloak team, and got a pull request from one of the development team members trying to help me out. Following their recommendations, I came into the conclusion that zuul is good to hide stateless services (bearer only ones), but not the ones that user directly interacts with. Here it is the whole thread in the mailing list.

Complemental answered 9/3, 2020 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.