Page MenuHomePhabricator

Configure OIDC Authentication for Superset on K8S
Closed, ResolvedPublic

Description

There is some additional detail on this task available in Superset on Kubernetes design document.

Superset is built with the Flask Application Builder (FAB) and its authentication system is therefore very flexible.

References:

Our current Superset instance use the REMOTE_USER authentication type and this authentication is performed by an Apache reverse proxy, linked to the CAS-SSO system.

We would like to take the opportunity to review this authentication mechanism during the migration to Kubernetes and remove the Apache reverse proxy, if possible.
The best way to do that would be to use the OpenID Connect (OIDC) protocol within FAB, linked directly to the same CAS-SSO system. We have several exmples of where the OIDC protocol has been successfully implemented implemented in this way, including DataHub, GitLab, and Netbox.

There are several guides available as to how this can be achieved:

The simplest looking approach appears to be this one, which uses the Authlib package.

Other approaches include this and this, both of which use the flask-oidc package.

Event Timeline

Gehel triaged this task as High priority.Jan 8 2024, 1:50 PM
Gehel moved this task from Incoming to Quarterly Goals on the Data-Platform-SRE board.

Change 1002462 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: Register superset and superset-next IDP services

https://gerrit.wikimedia.org/r/1002462

Change 1002462 merged by Brouberol:

[operations/puppet@production] idp_test: Register superset and superset-next IDP services

https://gerrit.wikimedia.org/r/1002462

Change 1003747 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/deployment-charts@master] superset: enable OIDC login

https://gerrit.wikimedia.org/r/1003747

Change 1003749 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: Register superset and superset-next IDP services

https://gerrit.wikimedia.org/r/1003749

Change 1003749 merged by Brouberol:

[operations/puppet@production] idp: Register superset and superset-next IDP services

https://gerrit.wikimedia.org/r/1003749

Change 1003790 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: restrict the superset services to the *-k8s.wikimedia.org domains

https://gerrit.wikimedia.org/r/1003790

Change 1003790 merged by Brouberol:

[operations/puppet@production] idp: restrict the superset services to the *-k8s.wikimedia.org domains

https://gerrit.wikimedia.org/r/1003790

Change 1003791 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: fix typo in superset config

https://gerrit.wikimedia.org/r/1003791

Change 1003791 merged by Brouberol:

[operations/puppet@production] idp: fix typo in superset config

https://gerrit.wikimedia.org/r/1003791

Change 1004042 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/puppet@production] idp: register superset_k8s/superset_next_k8s services in IDP

https://gerrit.wikimedia.org/r/1004042

Change 1004042 merged by Brouberol:

[operations/puppet@production] idp: register superset_k8s/superset_next_k8s services in IDP

https://gerrit.wikimedia.org/r/1004042

I managed to integrate our IDP server with the OIDC Superset login. However, I think we're missing some data in LDAP if we want to provision user accounts with username, first name, last name and email in Superset. This is what we get for my account:

{'auth_time': 1708005874,
 'client_id': 'superset-next',
 'email': 'brouberol@wikimedia.org',
 'id': 'brouberol',
 'name': 'Brouberol',
 'preferred_username': 'brouberol',
 'service': 'https://superset-next.svc.eqiad.wmnet/oauth-authorized/CAS',
 'sub': 'brouberol'}

Change 1003747 merged by Brouberol:

[operations/deployment-charts@master] superset: enable OIDC login

https://gerrit.wikimedia.org/r/1003747

I managed to integrate our IDP server with the OIDC Superset login. However, I think we're missing some data in LDAP if we want to provision user accounts with username, first name, last name and email in Superset. This is what we get for my account:

{'auth_time': 1708005874,
 'client_id': 'superset-next',
 'email': 'brouberol@wikimedia.org',
 'id': 'brouberol',
 'name': 'Brouberol',
 'preferred_username': 'brouberol',
 'service': 'https://superset-next.svc.eqiad.wmnet/oauth-authorized/CAS',
 'sub': 'brouberol'}

That's interesting.
There's a useful comment here: T305874#9098627 which explains the mappings we should get between the LDAP records and the OIDC results set.

You can also use an LDAP lookup to the back-end database which may help.

e.g.

btullis@seaborgium:~$ ldapsearch -x uid=brouberol
# extended LDIF
#
# LDAPv3
# base <dc=wikimedia,dc=org> (default) with scope subtree
# filter: uid=brouberol
# requesting: ALL
#

# brouberol, people, wikimedia.org
dn: uid=brouberol,ou=people,dc=wikimedia,dc=org
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: ldapPublicKey
objectClass: posixAccount
objectClass: shadowAccount
cn: Brouberol
gidNumber: 500
homeDirectory: /home/brouberol
loginShell: /bin/bash
uidNumber: 45143
uid: brouberol
sshPublicKey: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKj4plRYipEdxuY7LqzlDkHDwJIR
 8uuf8jl65JgGnCkE brouberol@wikimedia.org
mail: brouberol@wikimedia.org
givenName: Balthazar
sn: Brouberol

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
btullis@seaborgium:~$

...and for group membership...

btullis@seaborgium:~$ ldapsearch -A -x member=uid=brouberol,ou=people,dc=wikimedia,dc=org dn
# extended LDIF
#
# LDAPv3
# base <dc=wikimedia,dc=org> (default) with scope subtree
# filter: member=uid=brouberol,ou=people,dc=wikimedia,dc=org
# requesting: dn 
#

# wmf, groups, wikimedia.org
dn: cn=wmf,ou=groups,dc=wikimedia,dc=org

# ops, groups, wikimedia.org
dn: cn=ops,ou=groups,dc=wikimedia,dc=org

# project-bastion, groups, wikimedia.org
dn: cn=project-bastion,ou=groups,dc=wikimedia,dc=org

# project-analytics, groups, wikimedia.org
dn: cn=project-analytics,ou=groups,dc=wikimedia,dc=org

# project-deployment-prep, groups, wikimedia.org
dn: cn=project-deployment-prep,ou=groups,dc=wikimedia,dc=org

# search result
search: 2
result: 0 Success

# numResponses: 6
# numEntries: 5

@SLyngshede-WMF has been working on the IDP project a lot, so he may have some additional insights if you run into trouble with getting OIDC working with Superset.

I found the cause of the missing data.

First off, CAS is set up with a discovery URL, that displays every supported URL, parameter, claim, algorithm, etc. By querying it, we can see all the claims an OAuth client can make:

~ ❯ curl -s https://idp.wikimedia.org/oidc/.well-known | jq .
{
  "issuer": "https://idp.wikimedia.org/oidc",
  "scopes_supported": [
    "openid",
    "profile",
    "email",
    "groups"
  ],
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "id_token token",
    "device_code"
  ],
  "response_modes_supported": [
    "query",
    "fragment",
    "form_post"
  ],
  "subject_types_supported": [
    "public",
    "pairwise"
  ],
  "claim_types_supported": [
    "normal"
  ],
  "claims_supported": [
    "sub",
    "name",
    "groups",
    "email",
    "family_name",
    "preferred_username",
    "profile",
    "memberOf"
  ],
  "grant_types_supported": [
    "authorization_code",
    "password",
    "client_credentials",
    "refresh_token",
    "urn:ietf:params:oauth:grant-type:uma-ticket"
  ],
  "id_token_signing_alg_values_supported": [
    "none",
    "RS256",
    "RS384",
    "RS512",
    "PS256",
    "PS384",
    "PS512",
    "ES256",
    "ES384",
    "ES512",
    "HS256",
    "HS384",
    "HS512"
  ],
  "dpop_signing_alg_values_supported": [
    "RS256",
    "RS384",
    "RS512",
    "ES256",
    "ES384",
    "ES512"
  ],
  "id_token_encryption_alg_values_supported": [
    "RSA1_5",
    "RSA-OAEP",
    "RSA-OAEP-256",
    "A128KW",
    "A192KW",
    "A256KW",
    "A128GCMKW",
    "A192GCMKW",
    "A256GCMKW",
    "ECDH-ES",
    "ECDH-ES+A128KW",
    "ECDH-ES+A192KW",
    "ECDH-ES+A256KW"
  ],
  "id_token_encryption_enc_values_supported": [
    "A128CBC-HS256",
    "A192CBC-HS384",
    "A256CBC-HS512",
    "A128GCM",
    "A192GCM",
    "A256GCM"
  ],
  "userinfo_signing_alg_values_supported": [
    "none",
    "RS256",
    "RS384",
    "RS512",
    "PS256",
    "PS384",
    "PS512",
    "ES256",
    "ES384",
    "ES512",
    "HS256",
    "HS384",
    "HS512"
  ],
  "userinfo_encryption_alg_values_supported": [
    "RSA1_5",
    "RSA-OAEP",
    "RSA-OAEP-256",
    "A128KW",
    "A192KW",
    "A256KW",
    "A128GCMKW",
    "A192GCMKW",
    "A256GCMKW",
    "ECDH-ES",
    "ECDH-ES+A128KW",
    "ECDH-ES+A192KW",
    "ECDH-ES+A256KW"
  ],
  "userinfo_encryption_enc_values_supported": [
    "A128CBC-HS256",
    "A192CBC-HS384",
    "A256CBC-HS512",
    "A128GCM",
    "A192GCM",
    "A256GCM"
  ],
  "acr_values_supported": [
    "mfa-webauthn"
  ],
  "request_object_signing_alg_values_supported": [
    "none",
    "RS256",
    "RS384",
    "RS512",
    "PS256",
    "PS384",
    "PS512",
    "ES256",
    "ES384",
    "ES512",
    "HS256",
    "HS384",
    "HS512"
  ],
  "request_object_encryption_alg_values_supported": [
    "RSA1_5",
    "RSA-OAEP",
    "RSA-OAEP-256",
    "A128KW",
    "A192KW",
    "A256KW",
    "A128GCMKW",
    "A192GCMKW",
    "A256GCMKW",
    "ECDH-ES",
    "ECDH-ES+A128KW",
    "ECDH-ES+A192KW",
    "ECDH-ES+A256KW"
  ],
  "request_object_encryption_enc_values_supported": [
    "A128CBC-HS256",
    "A192CBC-HS384",
    "A256CBC-HS512",
    "A128GCM",
    "A192GCM",
    "A256GCM"
  ],
  "introspection_endpoint_auth_methods_supported": [
    "client_secret_basic"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "client_secret_post",
    "client_secret_jwt",
    "private_key_jwt"
  ],
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ],
  "prompt_values_supported": [
    "none",
    "login",
    "consent"
  ],
  "claims_parameter_supported": true,
  "request_uri_parameter_supported": true,
  "request_parameter_supported": true,
  "backchannel_logout_supported": true,
  "frontchannel_logout_supported": true,
  "authorization_endpoint": "https://idp.wikimedia.org/oidc/oidcAuthorize",
  "token_endpoint": "https://idp.wikimedia.org/oidc/oidcAccessToken",
  "userinfo_endpoint": "https://idp.wikimedia.org/oidc/oidcProfile",
  "pushed_authorization_request_endpoint": "https://idp.wikimedia.org/oidc/oidcPushAuthorize",
  "registration_endpoint": "https://idp.wikimedia.org/oidc/register",
  "end_session_endpoint": "https://idp.wikimedia.org/oidc/oidcLogout",
  "introspection_endpoint": "https://idp.wikimedia.org/oidc/introspect",
  "revocation_endpoint": "https://idp.wikimedia.org/oidc/revoke",
  "backchannel_logout_session_supported": true,
  "frontchannel_logout_session_supported": true,
  "jwks_uri": "https://idp.wikimedia.org/oidc/jwks"
}

The important bit being

"scopes_supported": [
  "openid",
  "profile",
  "email",
  "groups"
],

By adding groups to the claims made by the Superset OAuth client, I can now see the following when I log in via CAS:

{'auth_time': 1708592987,
 'client_id': 'superset_next_k8s',
 'email': 'brouberol@wikimedia.org',
 'id': 'brouberol',
 'memberOf': ['cn=wmf,ou=groups,dc=wikimedia,dc=org',
              'cn=ops,ou=groups,dc=wikimedia,dc=org',
              'cn=project-bastion,ou=groups,dc=wikimedia,dc=org',
              'cn=project-analytics,ou=groups,dc=wikimedia,dc=org',
              'cn=project-deployment-prep,ou=groups,dc=wikimedia,dc=org'],
 'name': 'Brouberol',
 'preferred_username': 'brouberol',
 'service': 'https://superset-next-k8s.wikimedia.org/oauth-authorized/CAS',
 'sub': 'brouberol'}

Change 1006547 had a related patch set uploaded (by Brouberol; author: Brouberol):

[operations/deployment-charts@master] superset: assign Superset roles from LDAP groups

https://gerrit.wikimedia.org/r/1006547

What we settled on in terms of group/role mapping is the following:

  • cn=ops,ou=groups,dc=wikimedia,dc=org gets mapped to Admin (for now)
  • cn=wmf,ou=groups,dc=wikimedia,dc=org and cn=nda,ou=groups,dc=wikimedia,dc=org both get mapped to the sql_lab role
  • each user gets assigned the Alpha role
  • we no longer rely on the WMF Analyst role, which was custom made and required manual maintenance

This way, everyone gets Alpha permissions and sql_lab.

We also plan to create a superset-admins LDAP group that would get mapped to Admin, as we assume that not all ops are interested in being Superset admins.

Change 1006547 merged by Brouberol:

[operations/deployment-charts@master] superset: assign Superset roles from LDAP groups

https://gerrit.wikimedia.org/r/1006547

Change 1006871 had a related patch set uploaded (by Btullis; author: Btullis):

[operations/deployment-charts@master] Use the superset-admins LDAP group to map to Admin rights

https://gerrit.wikimedia.org/r/1006871

Change 1006871 merged by jenkins-bot:

[operations/deployment-charts@master] Use the superset-admins LDAP group to map to Admin rights

https://gerrit.wikimedia.org/r/1006871