User info fetcher

The User info fetcher allows for additional information to be obtained from the configured backend (for example, Keycloak). You can then write Rego rules for OpenPolicyAgent which make an HTTP request to the User info fetcher and make use of the additional information returned for the username or user id.

You can enable the User info fetcher sidecar as follows:

apiVersion: opa.stackable.tech/v1alpha1
kind: OpaCluster
metadata:
  name: opa
spec:
  image:
    productVersion: 1.0.0
  clusterConfig:
    userInfo: (1)
      backend:
        keycloak:
          hostname: keycloak.my-namespace.svc.cluster.local
          port: 8443
          tls:
            verification:
              server:
                caCert:
                  secretClass: tls (2)
          clientCredentialsSecret: user-info-fetcher-client-credentials (3)
          adminRealm: master (4)
          userRealm: master (4)
      cache: # optional, enabled by default
        entryTimeToLive: 60s # optional, defaults to 60s
  servers:
    roleGroups:
      default: {}
---
apiVersion: v1
kind: Secret
metadata:
  name: user-info-fetcher-client-credentials
stringData:
  clientId: user-info-fetcher (3)
  clientSecret: user-info-fetcher-client-secret (3)
1 Enable the user-info-fetcher sidecar
2 Enable TLS verification using the CA from the tls SecretClass.
3 Obtain Keycloak API credentials from the specified secret. The Secret must have clientId and clientSecret entries.
4 Refer to the applicable realm in your Keycloak server.

Currently the following backends are supported:

Backends

The user info fetcher can fetch data from a few different backends. We currently recommend the Keycloak backend.

Keycloak

Fetch groups and extra credentials, but not roles.

The OAuth2 Client in Keycloak must be given the view-users Service Account Role for the realm that the users are in.

The user-info-fetcher requires a service account in Keycloak with the permissions to read user objects. To create such as user you need to take the following steps:

With your user realm selected click on Clients at the left side and use the Import client button:

1

Create a file with the following JSON object. Swap out secret with your desired password and potentially redirectUris and webOrigins.

{
  "clientId" : "user-info-fetcher",
  "surrogateAuthRequired" : false,
  "enabled" : true,
  "alwaysDisplayInConsole" : false,
  "clientAuthenticatorType" : "client-secret",
  "secret" : "XXX",
  "redirectUris" : [ "*" ],
  "webOrigins" : [ "*" ],
  "notBefore" : 0,
  "bearerOnly" : false,
  "serviceAccountsEnabled" : true,
  "publicClient" : false,
  "frontchannelLogout" : true,
  "protocol" : "openid-connect",
  "attributes" : {
    "oidc.ciba.grant.enabled" : "true",
    "oauth2.device.authorization.grant.enabled" : "false"
  },
  "authenticationFlowBindingOverrides" : { },
  "fullScopeAllowed" : true
}

Upload the file to the client importer and click on Save.

2

Afterwards you need to modify the created user service-account-user-info-fetcher. To achieve this open the user and click on the Role mapping tab:

3

Assign the role view-users as shown below. This is necessary to allow the user to read other users' information.

4

Afterwards you can store the user-info-fetcher credentials in Kubernetes in a Secret:

apiVersion: v1
kind: Secret
metadata:
  name: user-info-fetcher-client-credentials
stringData:
  clientId: user-info-fetcher
  clientSecret: XXX # replace with your chosen password

Active Directory

The Active Directory backend is experimental, and subject to change.

Fetches user attributes and groups over LDAP.

For this to work user-info-fetcher needs to be provided with a Kerberos keytab that enables it to access Active Directory. This is provided by a configurable SecretClass.

spec:
  clusterConfig:
    userInfo:
      backend:
        experimentalActiveDirectory: (1)
          ldapServer: sble-addc.sble.test (2)
          baseDistinguishedName: DC=sble,DC=test (3)
          customAttributeMappings: (4)
            country: c (5)
          additionalGroupAttributeFilters: (6)
            foo: bar
          kerberosSecretClassName: kerberos-ad (7)
          tls:
            verification:
              server:
                caCert:
                  secretClass: tls-ad (8)
      cache: # optional, enabled by default
        entryTimeToLive: 60s # optional, defaults to 60s
1 Enables the Active Directory backend
2 The hostname of the domain controller
3 The distinguished name to search, users and groups outside of this will not be seen
4 Arbitrary LDAP attributes can be requested to be fetched
5 c stores the ISO-3166 country code of the user
6 Groups can be filtered by LDAP attributes to reduce the load in searching for membership. * can be used as a wildcard in these filters.
7 The name of the SecretClass that knows how to create Kerberos keytabs trusted by Active Directory
8 The name of the SecretClass that contains the Active Directory’s root CA certificate(s)

When retrieving user groups from Active Directory, the user info fetcher filters by both upn as well as sAmAccountName using the following query:

(&(objectClass=user)(|(userPrincipalName=<upn>@<realm>)(userPrincipalName=<upn>)(sAMAccountName=<upn>)))

where <upn> is the user principal name of the user and <realm> is the realm of the user.

The above is to accommodate for different Active Directory user management strategies and is subject to change in future releases.

Entra

The Entra backend is experimental, and subject to change.

Fetch groups but not roles for a user from Entra.

The client in Entra must use the client_credentials flow and requires the User.ReadAll and GroupMemberShip.ReadAll permissions.
spec:
  clusterConfig:
    userInfo:
      backend:
        experimentalEntra: (1)
          tenantId: 00000000-0000-0000-0000-000000000000 (2)
          clientCredentialsSecret: user-info-fetcher-client-credentials (3)
1 Enables the Entra backend
2 The Entra tenant ID
3 A secret containing the clientId and clientSecret keys

OpenLDAP

The OpenLDAP backend is experimental, and subject to change.

Fetch user attributes and groups over LDAP from OpenLDAP servers using simple bind authentication.

OpenLDAP supports different group schemas:

  • groupOfNames (default): Groups contain a member attribute with full user DNs

  • posixGroup: Groups contain a memberUid attribute with usernames

spec:
  clusterConfig:
    userInfo:
      backend:
        experimentalOpenLdap: (1)
          hostname: openldap.default.svc.cluster.local (2)
          port: 1636 (3)
          searchBase: dc=example,dc=org (4)
          bindCredentials: (5)
            secretClass: openldap-bind-credentials (6)
          userIdAttribute: entryUUID (7)
          userNameAttribute: uid (8)
          groupsSearchBase: ou=groups,dc=example,dc=org (9)
          groupMemberAttribute: member (10)
          customAttributeMappings: (11)
            email: mail
            displayName: cn
            givenName: givenName
            surname: sn
          tls: (12)
            verification:
              server:
                caCert:
                  secretClass: openldap-tls (13)
      cache: # optional, enabled by default
        entryTimeToLive: 60s # optional, defaults to 60s
1 Enables the OpenLDAP backend
2 The hostname of the LDAP server
3 The port of the LDAP server. Defaults to 636 for LDAPS, or 389 for plain LDAP
4 The base distinguished name to search. Users outside of this will not be seen
5 Configuration for LDAP bind credentials
6 The name of the SecretClass that provides the bind credentials. The secret must contain user and password keys
7 LDAP attribute used for the user’s unique identifier. Defaults to entryUUID
8 LDAP attribute used for the username. Defaults to uid
9 LDAP search base for groups. If not specified, uses the main searchBase
10 LDAP attribute on group objects that contains member references. Use member for groupOfNames (default) or memberUid for posixGroup
11 Arbitrary LDAP attributes can be requested to be fetched and returned in the user info response. Use this to map custom LDAP attributes to custom attribute names in the response
12 Optional TLS configuration for secure LDAP connections
13 The name of the SecretClass that contains the LDAP server’s root CA certificate(s)

When retrieving user information from OpenLDAP, the user info fetcher first searches for the user by the userNameAttribute (defaults to uid) or userIdAttribute (defaults to entryUUID) depending on the request type. When a user is found, it searches for groups containing the user:

  • If groupMemberAttribute is memberUid: Searches for groups where memberUid equals the username (for posixGroup)

  • Otherwise (e.g. if groupMemberAttribute is member, which is the default): Searches for groups where member equals the user’s full DN (for groupOfNames)

User info fetcher API

User information can be retrieved from regorules using the functions userInfoByUsername(username) and userInfoById(id) in data.stackable.opa.userinfo.v1.

An example of the returned structure:

{
  "id": "af07f12c-a2db-40a7-93e0-874537bdf3f5",
  "username": "alice",
  "groups": [
    "/admin"
  ],
  "customAttributes": {}
}
The exact formats of id and groups will vary depending on the backend in use. This example is using the Keycloak backend.

Debug request

To debug the user-info-fetcher you can curl it’s API for a given user. To achieve this shell into the opa container and execute

curl --header "Content-Type: application/json" -d '{"username":"my-user"}' http://127.0.0.1:9476/user

You can also use -d '{"id":"123456"}' to query by the user ID.

Rego rule library

The HTTP API exposed by the user-info-fetcher can be called directly using the rego function http.send. However, we provide a convenience rego rule library, which we ship with OpaClusters by default.

For example, the following rule allows access for users in the /admin group:

package test

default allow := false

allow if {
    user := data.stackable.opa.userinfo.v1.userInfoById(input.userId)
    "/admin" in user.groups
}