How to Setup Apache Superset with Keycloak

Writing Code in Dim Office
Reading Time: 2 minutes

In this blog, we will learn to deploy & set up Apache Superset with Keycloak authentication. Apache Superset is an open-source software application for data exploration and data visualization able to handle data at a petabyte scale. Keycloak is an open-source software product to allow single sign-on with Identity and Access Management aimed at modern applications and services.

Prerequisites

  • Keycloak server
    • Keycloak Realm
    • Keycloak Client & broker configured with OIDC protocols.
    • client_secret.json
  • A PostgreSQL Instance deployed on the same Kubernetes cluster
  • Helm installed in system & basic knowledge of Helm.
  • Admin username & password of PostgreSQL instance
  • Superset Database created in PostgreSQL (If not then follow step 3)
  • Optionally Cert-manager if you want to have SSL certificates on hostnames.

Before You begin

1 Clone this Repo for the scripts

git clone https://github.com/knoldus/apache-superset-with-keycloak.git

2 Change directory into the cloned repo

cd apache-superset-with-keycloak

Steps to setup

1. Generate your client_secret.json file for your client authentication

The content of file should look like this. Change & replace with your values

{
    "web": {
      "issuer": "http://<keycloakdomain>/auth/realms/<realmName>",
      "auth_uri": "http://<keycloakdomain>/auth/realms/<realmName>/protocol/openid-connect/auth",
      "client_id": "<ClientID>",
      "client_secret": "<ClientSecret>",
      "redirect_uris": ["http://<keycloakdomain>/*"],
      "userinfo_uri": "http://<keycloakdomain>/auth/realms/<realmName>/protocol/openid-connect/userinfo",
      "token_uri": "http://<keycloakdomain>/auth/realms/<realmName>/protocol/openid-connect/token",
      "token_introspection_uri": "http://<keycloakdomain>/auth/realms/<realmName>/protocol/openid-connect/token/introspect"
    }
}

Flatten the JSON file with this command

cat client_secret.json|tr -d "\n"

2. Replace the copied flatten content of client_secret.json file in .Values.extraSecrets.client_secret.json

extraSecrets:
  client_secret.json: |
    <YOUR_CLIENT_SECRET_JSON_STRING>

3. Replace placeholders with username, password, and service name for PostgreSQL

supersetNode:
  connections:
    db_host: <POSTGRES_SERVICE_NAME>.<NAMESPACE>.svc.cluster.local
    db_user: <USERNAME>
    db_pass: <PASSWORD>
extraEnv:
  DATABASE_HOST: <POSTGRES_SERVICE_NAME>.NAMESPACE.svc.cluster.local
  DATABASE_PASSWORD: <PASSWORD>
  DATABASE_USER: <USERNAME>
  POSTGRES_USER: <USERNAME>
  POSTGRES_PASSWORD: <PASSWORD>
   

4. Add Realm name of keycloak

extraEnv:
  OIDC_OPENID_REALM: <YOUR_REALM_NAME>

5. Keycloak_security_manager.py script in keycloak-values.yaml

Values.extraSecrets.keycloak_security_manager.py

from flask_appbuilder.security.manager import AUTH_OID
    from superset.security import SupersetSecurityManager
    from flask_oidc import OpenIDConnect
    from flask_appbuilder.security.views import AuthOIDView
    from flask_login import login_user
    from urllib.parse import quote
    from flask_appbuilder.views import expose
    from flask import request, redirect
    
    
    class OIDCSecurityManager(SupersetSecurityManager):
    
        def __init__(self, appbuilder):
            super(OIDCSecurityManager, self).__init__(appbuilder)
            if self.auth_type == AUTH_OID:
                self.oid = OpenIDConnect(self.appbuilder.get_app)
            self.authoidview = AuthOIDCView
    
    
    class AuthOIDCView(AuthOIDView):
    
        @expose('/login/', methods=['GET', 'POST'])
        def login(self, flag=True):
            sm = self.appbuilder.sm
            oidc = sm.oid
            superset_roles = ["Admin", "Alpha", "Gamma", "Public", "granter", "sql_lab"]
            default_role = "Gamma"
    
            @self.appbuilder.sm.oid.require_login
            def handle_login():
                user = sm.auth_user_oid(oidc.user_getfield('email'))
    
                if user is None:
                    info = oidc.user_getinfo(['preferred_username', 'given_name', 'family_name', 'email', 'roles'])
                    roles = [role for role in superset_roles if role in info.get('roles', [])]
                    roles += [default_role, ] if not roles else []
                    user = sm.add_user(info.get('preferred_username'), info.get('given_name', ''), info.get('family_name', ''),
                                       info.get('email'), [sm.find_role(role) for role in roles])
    
                login_user(user, remember=False)
                return redirect(self.appbuilder.get_url_for_index)
    
            return handle_login()
    
        @expose('/logout/', methods=['GET', 'POST'])
        def logout(self):
            oidc = self.appbuilder.sm.oid
    
            oidc.logout()
            super(AuthOIDCView, self).logout()
            redirect_url = request.url_root.strip('/')
            # redirect_url = request.url_root.strip('/') + self.appbuilder.get_url_for_login
    
            return redirect(
                oidc.client_secrets.get('issuer') + '/protocol/openid-connect/logout?redirect_uri=' + quote(redirect_url))

6. Bootstrap Script

Values.extraSecrets.bootstrapScirpt

#!/bin/bash
  rm -rf /var/lib/apt/lists/* && \
  pip install \
    psycopg2-binary==2.9.1 \
    itsdangerous==2.0.1 \
    flask-oidc==1.4.0 \
    Flask-OpenID==1.3.0 \
    redis==3.5.3 && \
  if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi

7. Optionally if you are using ingress controller & cert-manager

ingress:
  annotations:
    nginx.ingress.kubernetes.io/client-body-buffer-size: 10M
    nginx.ingress.kubernetes.io/proxy-body-size: 10M
    nginx.ingress.kubernetes.io/proxy-buffering: "on"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "10M"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
  enabled: true
  ingressClassName: nginx
  path: /
  pathType: ImplementationSpecific
  hosts:
    - <YOUR_HOST_NAME>
  tls:
   - secretName: <TLS_SECRET_NAME>
     hosts:
       - <YOUR_HOST_NAME>

Run the chart

1. Create a database in Postgres instance

kubectl port-forward service/<SERVICE_NAME_OF_POSTGRES> 5432
createdb -h 127.0.0.1 -p 5432 -U <USERNAME> superset

2. After replacing all values run this command

helm upgrade --install superset superset-keycloak -f superset-keycloak/keycloak-values.yaml --namespace superset --create-namespace

Conclusion

This way we can now authenticate Apache superset with Keycloak with OIDC protocols.

For more interesting blogs checkout Knoldus blogs site

More Blogs from Rahul Soni

Written by 

Rahul Soni is a Software Consultant at Knoldus Software. He is always charged up for new things & learnings. He is dedicated to his work and believes in quality output. He loves to take deep dives into cloud technologies & different tools.