Should the need arise to replace CAS, it may be possible to do so and "fool" all applications which use the LoginFilter.

How would you do this?

To best describe the technique:


  1. We will assume some kind of authentication proxy sits in front of all applications, which is client to an upstream provider (SAML/Shibboleth or OpenID Connect usually). Usually, an authentication proxy is oauth2_proxy, vouch, or one of the SAML/shibboleth equivalents, either for nginx or apache. 
  2. We will assume that the authentication proxy can set an appropriate header which corresponds to the crowd
  3. For this example, we will assume that the header is passed along under the REMOTE_USER header by that proxy
  4. The LoginFilter looks at the last time a session was checked against CAS (cas.lasthandshake.time). I believe it's set as a cookie but also in the session and we only trust the session. We need to ALWAYS update that last checked time for the session to be the time of the request, so that it never attempts a redirect to CAS (We can do this because the proxy is enforcing authentication)
  5. To spoof the session without recompiling everything - we need to use a Tomcat Valve. Pseudocode below
  6. Logout would probably be handled at the layer above. Generally speaking, you don't usually logout of the SSO types of applications though.

The CrawlerSessionManagerValve is an example of a valve that is introspecting and handling sessions, but with slightly different goals.

https://github.com/apache/tomcat/blob/main/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java


@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
    // Some requests may be trusted, like from other servers
    // This assumes your proxy is setup to allow those requests
    // to punch through
    boolean trustedRequest = isTrustedRequest(request);

    // Get the remote user, or set it as an empty string
    String remoteUser = request.getHeader("REMOTE_USER");
    if (remoteUser == null){
        remoteUser = "";
    }

    if (trustedRequest){
        handleTrustedRequest(request);
    } else {
        if (!remoteUser.isEmpty()){
            spoofAuthenticatedUserSession(request, response);
        } else {
            // Untrusted request and/or 
            // Proxy not enforcing login (anonymous us)
            spoofAnonymousUserSession(request, response);
        }
    }
    
    getNext().invoke(request, response);
}


It should be noted that proxy configuration might need to be adjusted carefully if maintaining anonymous login is desired - especially true for datacat and the like. The valve may also need to be carefully written to handle that.

  • No labels

1 Comment

  1. Just a note - Usually with a proxy, all users end up authenticated before ever getting to the backend - so there's not usually a way to let anonymous users through by default. There's ways around that, depending on how a proxy is deployed, but unless all or most applications really need to be publicly readable - there's not a huge benefit to this.


    The caveat to this is APIs/trusted servers. Those should be let through. For that, the proxies have some way of helping you punch through the proxy (by IP or with an HTTP Basic Password).