Version 3

    What is Keycloak

     

    Keycloak is a solution for Identity Management (IDM) and Single Sign On (SSO). Some notable features:

    • user registration: if enabled on a per-realm basis, shows a "register" button on the login screen, allowing users to register themselves. Useful for public applications
    • social login: if enabled on a per-realm basis, allows the user to login via a social login provider, such as Facebook, Google+, GitHub and/or Twitter.
    • single sign on: logging in into an application from a realm allows seamless login into a a second application from the same realm
    • single sign off: same as above, but for logout
    • 2-factor auth: allows the user to scan a QR code and have a TOTP generated on an user's device (Google Authenticator/FreeOTP on a mobile phone, for instance)
    • LDAP integration
    • UI templating: each realm can have its own "skin" (login screen, account management, emails, ...)

     

     

    Keycloak components

     

    Keycloak has the following components:

    • Keycloak server
    • Keycloak adapter

     

    The server is the web application that allows a "realm administrator" to manage the realm (users, roles, applications, external OAuth clients, ...). It can be deployed on a WildFly instance or the "appliance distribution" can be used (based on WildFly). For production purposes, an external database can be used for persisting data. Keycloak also has basic clustering capabilities.

     

    The adapter is a component that makes it easier for applications to integrate with the server. It is "installed" side by side with the application to be protected and comes in different flavors:

    • For Servlet-based applications deployed on Tomcat/Undertow, it provides a security interceptor that checks every incoming request for the required credentials.
    • For Java EE applications deployed on WildFly/EAP, it additionally provides JAAS integration, so that applications can make use of the standard security features of Java EE to protect the resources (EJBs, REST endpoints, Servlets) and to query the user capabilities (Subject, roles, ...).
    • For HTML5 single-page applications, it provides a JavaScript library that handles the login and provides an interface for querying the user's data (user name, full name, ...) and roles. AngularJS support is not "native", but can be easily worked around. In fact, the Keycloak server's web applications are written in AngularJS, so, they provide a good example on how to use it.

     

    Multi tenancy in Keycloak

     

    On Keycloak's server side, multi tenancy is achieved by the concept of realms. Each realm could be viewed as a tenant. A realm consists of a logical grouping of data, such as:

    • users
    • applications
    • roles
    • external OAuth clients

     

    Each application registered inside a realm has an "application type". For our case, the following types are the relevant ones:

    • public: for applications where the user could have access to the keycloak.json file, such as HTML5 applications
    • bearer-only: for applications that don't interact directly with the user, consuming only an authentication token that is to be converted into a Subject, such as REST endpoints receiving requests from a browser client (ie: the browser has authenticated the user and sends the token on each request)
    • direct-access: for applications that requires access to the username and password to authenticate the user. For instance REST endpoints that are meant to be used by non-interactive users (CLI, for instance).

     

    There's no concept of "super realm" or nested realms, although Keycloak does have a "master" realm, which is used for managing the Keycloak server. That said, nothing would prevent two realms from sharing the same LDAP base or social login credentials.

     

    Multi tenancy on the adapter's side is a bit different, though. As Keycloak itself cannot know to which tenant the request is intended to, it delegates the tenant resolution to the application. So, for each request, Keycloak calls a code on the application passing the request data and expects an object representation of the "keycloak.json". This means that the application should know beforehand, for all requests, to which tenant the request belongs. This can be achieved by requiring all requests to have a specific HTTP header, or requiring all tenant-specific URLs to contain the tenant ID on the path (/tenants/{tenantId}/metrics).

     

    Integration

     

    The basic Hawkular integration can be done as follows:

    • UI console determines the tenant for the current user. If the tenant cannot be determined, shows a list of known realms for selection.
    • UI console uses keycloak.js to authenticate the user and sends a bearer token on each request
    • REST backend determines the tenant for the request, based on the request data provided by Keycloak.
    • Keycloak consumes the bearer token and authorizes the request
    • REST endpoint is executed and delivers data related only from the tenant whose user belongs to, based on the Subject provided by Keycloak
    • Metrics collector installed on a machine belonging to the tenant uses direct-access (username/password) to send metrics to the backend

     

    Sample TypeScript code to query the realm:

     

    module Foo {
      export class BarService {
        public static $inject = ['Auth'];
        constructor(private Auth:Services.AuthService) {
        }
        doSomething = () => {
          this.Auth.realm();
        }
      }
    }
    
    
    

     

    Sample Java code to protect an EJB/REST endpoint:

     

    @RolesAllowed("admin") // all methods will require "admin", except for methods which explicitly define their own RolesAllowed
    @Stateless
    @Path("/foo")
    public class FooService { }
    
    
    

     

    Integration problems

     

    • At this point, it seems there's a mismatch between the concepts of tenants for Hawkular and Keycloak. Because of that, the Keycloak integration might differ, depending on what we want to achieve in the future.
      • How to implement the concept of nested tenants? This is needed in the following scenario:
        • company "acme" has departments "accounting", "sales", "operations". Each department is a tenant, but the team "operations" should have at least "read-only" rights on other tenants.
    • InfluxDB authentication:
      • currently, the Keycloak adapter supports only basic-auth for direct-access (ie, the type of application that accepts username/password on the request itself, instead of tokens). For InfluxDB compatibility, the username and password needs to be also possible to be passed via query string
    • Even though each tenant is a realm, we expect the same role names to exist in every realm. For instance, user "jdoe" has a role named "operations" on realm "acme". User "jsmith" has a role "operations" on realm "emca". Definition of the role names is crucial for securing the endpoints, but this is a discussion that should be done outside of the scope of Keycloak, as it's a general topic.
    • SaaS: tenant auto registration. While Keycloak has the option to enable user registration, we want one level "up" on this, as we want new tenants to register themselves. So, a registration process would need to interact with the Keycloak server to register this new tenant. There is/was a proof of concept for this part, but I've removed it from the PR.
    • Quick start script: because of the complexity that Keycloak adds to the initial setup, I've also adapted the quick start script to create the necessary keys/realm descriptions. It seems, though, that the idea is to move away from shell scripts in favor of more portable solutions. Once this portable solution is done for the current state of Hawkular, I'll adapt it to also set a Keycloak instance up.