5. User Authentication

xGT’s user authentication can be configured to use four different mechanisms:

  • Linux’s Pluggable Authentication Modules (PAM) system to support multiple authentication sources.

  • Generic Security Service Application Program Interface (GSSAPI) to support Kerberos single-sign-on authentication.

  • Public Key Infrastructure (PKI) certificate-based authentication.

  • OpenID Connect (OIDC) bearer token authentication via an external identity provider.

xGT supports using PAM to authenticate users. It can retrieve user group membership based on different directory services available on an installed Linux system. Both UNIX and LDAP can be used to define the users and groups.

For username / password authentication, the username and password provided by an xGT user in their Python connection object are passed directly to the PAM authentication service (or services) configured on the Linux system running the xGT server application (xgtd daemon).

For Kerberos authentication, the user will have obtained tickets using a client utility such as kinit. The xGT client will then request Kerberos tickets specifically to authenticate against the service principal for the xGT server they are connecting to.

For PKI authentication, the use of SSL and mutual TLS is required (see Using an SSL Secure Channel for more details). Client x509 certificates must include a userId field if PKI authentication is desired. The server validates the client’s certificate using information provided on the encrypted channel and extracts its userId field. If the userId is not empty, then the connection is authenticated and used to compute group membership based on the configured directory services on the system running xGT. Note that a password or other credentials are not required when using PKI authentication in this manner.

For more information on how a user will pass credentials to the client see User Authentication.

When a user is authenticated, a session is created, access to the xGT server is granted, and the user’s group membership is retrieved and used to populate their set of security labels. The session will persist while the Python client connection is maintained to the server for a given session lifetime as configured by security.session_ttl. Note that further actions on the xGT server are now subject to access control validation.

5.1. Group Membership

The xgtd daemon derives group membership for the user from the groups reported to it by Linux. For the pam_unix module, these correspond to the UNIX groups set up by the system administrator on the local Linux system running xgtd. For the pam_sss module, the groups reported back to xGT might not exist on the local Linux system, but on a remote LDAP server. The GSSAPI mechanism doesn’t provide a way to determine groups, so one of these mechanisms must still be used to setup groups. Group membership will be mapped to the security labels configured by the administrator of the server instance. For more discussion on security labels and their mapping see User Labels and Validation.

5.2. PAM Setup

xGT supports using PAM to do no authentication or username / password authentication using either UNIX or LDAP. PAM configuration for xGT is typically set up in a system file located at /etc/pam.d/xgtd.

5.2.1. No Authentication

If running xGT in an exploratory capacity or as a single-user server, one may want to bypass the user authentication step. In that case a user would want to use the pam_permit module:

#
# /etc/pam.d/xgtd - specify the PAM behavior for xgtd
#

auth       required     pam_permit.so
account    required     pam_permit.so
session    required     pam_permit.so

Warning

Using pam_permit is insecure and should be used with caution.

The pam_permit module simply authenticates any given username and password. User groups can still be retrieved if xgtd is given a valid UNIX user (a valid password is not necessary to retrieve the groups).

5.2.2. UNIX User Password Authentication

xGT supports using UNIX users to authenticate. This setup allows users already configured as UNIX users in the system to authenticate to the xGT server. Users would use their regular Linux user identification and password to authenticate against xGT. The following sample PAM configuration file illustrates that case:

#
# /etc/pam.d/xgtd - specify the PAM behavior for xgtd
#

auth       required     pam_unix.so
account    required     pam_unix.so
session    required     pam_unix.so

With the pam_unix module, a user’s group membership will be determined by the user’s UNIX groups membership.

5.2.3. LDAP User Password Authentication

xGT can be deployed to authenticate against the Lightweight Directory Access Protocol (LDAP) enterprise-level directory service. PAM on xGT’s supported Linux platforms provides a module to interface to an LDAP directory server for authentication. It is common to deploy LDAP service on an enterprise via the combination of SSSD (System Security Services Daemon) and IPA (Identity, Policy and Audit).

The Linux server running the xgtd daemon should be configured as a client of the LDAP server and the SSSD service should be configured to validate users and groups from LDAP as well. The addition of these two services allows for xGT to authenticate users existing in the LDAP directory without them needing to have a UNIX account on the local Linux server running xGT. A sample configuration file for setting up xGT to authenticate against LDAP sources using SSSD is as follows:

#
# /etc/pam.d/xgtd - specify the PAM behavior for xgtd
#

auth       required     pam_sss.so
account    required     pam_sss.so
session    required     pam_sss.so

More information on SSSD is available at https://sssd.io/docs/introduction.html. More information on IPA is available at https://www.freeipa.org.

Note that these services (SSSD and LDAP) are external to the xgtd daemon. The SSSD daemon is needed on the machine running xgtd as it provides authentication services to processes running on that machine. The LDAP daemon can be run on a different machine. The machine running xgtd becomes a client to the LDAP server. A straightforward setup of SSSD and IPA enables login access via ssh to the machine running xgtd. The users’ identity and credentials will be validated against the provided directory for login access to the server running xgtd. The same identity and credentials will also allow access to the xgtd server.

The file /etc/sssd/sssd.conf controls the behavior of the SSSD daemon on the machine running the xgtd server. An example setup is as follows:

[sssd]
config_file_version = 2
services = nss, pam, ifp, ssh, sudo
domains = ipa.domain

The critical configuration needed for xgtd to use PAM authentication against the configured LDAP directory server is the services entry requiring the pam service.

5.3. GSSAPI Setup

xGT implements an interface for Kerberos-based single-sign-on authentication through the use of the GSSAPI libraries. In this framework, users authenticate against a third party Authentication Server (AS), which communicates to a Key Distribution Center (KDC) server. In many cases, the AS and KDC servers run on the same host. If the client’s credentials are correct, the KDC server returns a timestamped Ticket Granting Ticket (TGT) back to the client. When the client wants to communicate to the xGT server, it request validation against the Ticket Granting Service (TGS). When configured to use Kerberos authentication, the xGT server automatically registers with the TGS using a service principal name. After verifying the tickets provided by the client, the TGS issues a service-specific ticket and session keys back to the client. The client then proceeds to send those to the xGT server.

The user identity is derived from the Kerberos ticket information automatically by the xGT server. The identity corresponds to the initiator of the Kerberos connection. Group membership is determined in the same manner as described in Section Group Membership based on the user’s identity.

5.3.1. Kerberos Configuration

The Kerberos configuration on the xGT client and server machines must be set so that they are on the same Kerberos realm. Typically, a Kerberos realm corresponds to the internet domain (or a sub-domain) of the enterprise’s network. Realms are specified using a capital letter form of the domains such as ROCKETGRAPH.COM. In addition, both the xGT client and server must have access to the same Key Distribution Center (KDC) server(s).

A typical configuration for the MIT Kerberos 5 framework would be as follows (specified in the file /etc/krb5.conf):

[libdefaults]
     default_realm = ROCKETGRAPH.COM
...
[realms]
     ROCKETGRAPH.COM = {
             kdc = kdc.rocketgraph.com
             ...
     }

5.3.2. xGT Server Configuration for Kerberos

When running the xGT server, the configuration variable security.use_kerberos must be set to true. By default, xgtd uses the network name of the host machine prefixed by the string xgtd/ as the name of the Kerberos service principal it listens on: e.g. xgtd/xgtd_server@ROCKETGRAPH.COM. The service principal to use can be specified using the configuration variable security.server_krb_principal if the default is not acceptable.

Typically, the xGT server itself is executed by a non-root user on the host machine: e.g. user xgtd. By default, non-root users do not have access to reading the Kerberos configuration, in particular the list of principals that can authenticate against it. For this reason, xgtd needs a list of the principals that can authenticate against it in a file that is readable by the user running the xGT server. In addition, there must be an entry in that list for the xgtd service principal corresponding to that network host (e.g. xgtd/xgtd_server@ROCKETGRAPH.COM). The list of principals is specified in a keytab file:

slot

KVNO

Principal

1

3

alice@ROCKETGRAPH.COM

2

3

bob@ROCKETGRAPH.COM

3

3

charlie@ROCKETGRAPH.COM

4

3

xgtd/xgtd_server@ROCKETGRAPH.COM

The xGT server will look for a keytab file specified by the KRB5_KTNAME environment variable. The default value of the KRB5_KTNAME variable on an installed xGT server is “FILE:/etc/xgtd/xgtd.keytab”. The file must be readable by the user running the xGT server.

keytab files can be generated using the ktadd command in the kadmin utility on the Kerberos server. More information on Kerberos configuration and keytab files can be found at: https://web.mit.edu/kerberos/krb5-latest/doc/index.html.

Details on how the client must connect to use Kerberos authentication are in Section Kerberos Authentication.

5.4. Encrypted Connections to xGT and PKI Authentication

By default xGT uses an insecure channel for its remote connection. Secure Sockets Layer (SSL) should be enabled in a production environment where a server expects remote client connections. For information on setting up SSL with xGT see Using an SSL Secure Channel.

If the server is configured to use mutual TLS, then client connections with an x509 certificate containing a valid userId field can be used for authentication. This is another mechanism (in addition to Kerberos) to use password-less authentication for xGT.

5.5. OIDC Authentication

Experimental: This feature is experimental and may change in future releases.

xGT supports bearer token authentication through an external OpenID Connect (OIDC) identity provider (IdP). When a user connects with OidcAuth or OidcCredentialsAuth, the xGT Python client obtains an access token from the IdP and presents it to the xGT server. The server validates the token against the configured issuer and extracts the user identity and group membership from the token’s claims.

5.5.1. Server Configuration

OIDC authentication is enabled by adding settings to xgtd.conf. The required settings depend on the validation mode chosen (see Token Validation Modes below), but a typical JWT-based setup looks like:

{
  "security" : {
    "oidc" : {
      "issuer"          : "https://idp.example.com/realms/xgt",
      "client_id"       : "xgtd-client",
      "audience"        : "xgtd-client",
      "username_claim"  : "preferred_username",
      "groups_claim"    : "groups",
      "validation_mode" : "jwt"
    }
  }
}

The full set of configuration settings is as follows:

Setting

Description

security.oidc.issuer

The OIDC issuer URL. Used to validate the iss claim and to locate the IdP discovery document. Also returned to clients via GetOidcIssuer for auto-discovery.

security.oidc.client_id

The OAuth2 client ID registered with the IdP. Not required for server-side token validation, but recommended and typically needed for client auto-discovery via GetOidcIssuer. If unset, the server falls back to security.oidc.audience when returning client_id to clients.

security.oidc.audience

The expected audience value in the token. Must match the aud claim. Required for all modes except openshift_userapi. Returned to clients via GetOidcIssuer when use_server_audience=True is set on the client config.

security.oidc.validation_mode

How the server validates incoming tokens. One of jwt, introspection, introspection_userinfo, openshift_userapi, or auto. Defaults to jwt if not set. See Token Validation Modes for details.

security.oidc.username_claim

The claim name used as the xGT username, applied to whichever payload the validation mode produces (JWT payload, introspection response, or userinfo response). Defaults to preferred_username. Typically preferred_username for Keycloak or sub for other providers. Dot notation is supported for nested claims (e.g. metadata.name).

security.oidc.groups_claim

The claim name containing the user’s group memberships, applied to the same payload as username_claim. Defaults to groups. The groups are used to compute security labels (see Group Membership). Dot notation is supported for nested claims. Leave empty to disable group extraction.

security.oidc.scopes

Space-separated list of OAuth2 scopes advertised to clients via GetOidcIssuer. Defaults to openid profile email. When set, the xGT Python client uses these scopes instead of its default (openid profile email) when requesting a token from the IdP. Required for providers that reject the standard scopes, such as OpenShift which requires user:info.

security.oidc.jwks_uri

Override the JWKS endpoint URL. If not set, the value is taken from the IdP discovery document. Only used by the jwt and auto modes.

security.oidc.introspection_uri

Override the token introspection endpoint URL. If not set, the value is taken from the IdP discovery document. Required for modes that use introspection if discovery is unavailable.

security.oidc.introspection_client_id

Client ID used to authenticate against the introspection endpoint. Used by introspection-based modes when the IdP requires client authentication for the introspection endpoint. Commonly required by providers such as Keycloak, but not enforced by xGT itself.

security.oidc.introspection_client_secret

Client secret used to authenticate against the introspection endpoint. Used by introspection-based modes when the IdP requires client authentication for the introspection endpoint. Commonly required by providers such as Keycloak, but not enforced by xGT itself.

security.oidc.userinfo_uri

Override the userinfo endpoint URL. If not set, the value is taken from the IdP discovery document. Only used by the introspection_userinfo and auto (opaque path) modes.

security.oidc.openshift_user_api_uri

The OpenShift User API URI used by the openshift_userapi mode. Required when using that mode.

security.oidc.ca_cert

Path to a CA bundle file for HTTPS requests made by the server to the IdP (discovery, JWKS, introspection, userinfo). Use this when the IdP uses a private or self-signed certificate.

When security.oidc.issuer, security.oidc.client_id, and security.oidc.audience are all configured, the xGT Python client can connect with just xgt.OidcAuth() and no additional arguments; the client fetches these values from the server automatically.

5.5.2. Token Validation Modes

The security.oidc.validation_mode setting controls how xGT validates incoming bearer tokens and where it reads the username_claim and groups_claim from.

jwt

The server decodes the token as a JSON Web Token and verifies its signature using the IdP’s JWKS endpoint. Claims are read directly from the JWT payload. This is the most efficient mode and requires no round-trip to the IdP per connection. Use this when the IdP issues JWT access tokens (the common case for Keycloak and most OIDC providers). Requires security.oidc.audience.

introspection

The server calls the IdP’s RFC 7662 token introspection endpoint for every connection, confirming the token is active and reading claims from the introspection response. Use this when the IdP issues opaque (non-JWT) access tokens. Claims must be present in the introspection response; there is no fallback to the userinfo endpoint. Requires security.oidc.introspection_client_id, security.oidc.introspection_client_secret, and security.oidc.audience.

introspection_userinfo

The same as introspection, but if the username_claim or groups_claim is absent from the introspection response, the server makes a follow-up request to the userinfo endpoint to retrieve them. If all required claims are already present in the introspection response, the userinfo call is skipped. Use this when the IdP returns opaque tokens and the required claims are only available from the userinfo endpoint rather than the introspection response. Requires the same settings as introspection.

openshift_userapi

The server calls the OpenShift User API, which simultaneously validates the bearer token and returns user metadata. Claims are read from the API response. Use this for OpenShift clusters where the built-in OAuth server issues tokens. security.oidc.audience is not required for this mode. Requires security.oidc.openshift_user_api_uri.

auto

The server inspects the token format to decide which path to take: if the token appears to be a JWT (contains two dots), jwt validation is used; otherwise introspection_userinfo is used. This is convenient when the token format may vary, but requires settings for both paths since the token format is not known until runtime: security.oidc.audience is required regardless of which path is taken, and the introspection credentials (introspection_client_id and introspection_client_secret) are needed only if the token turns out to be opaque.

5.5.3. Keycloak

The IdP must be configured to include group memberships in the claim named by security.oidc.groups_claim. For JWT and auto modes this claim must be present in the access token itself. For introspection modes it must be present in the introspection or userinfo response.

More information on installing Keycloak is available at https://www.keycloak.org/guides#getting-started. More information on the Keycloak server administration guide is available at https://www.keycloak.org/docs/latest/server_admin/.

Keycloak issues JWT access tokens, so validation_mode: jwt (or the default auto) is appropriate. The standard scopes (openid profile email) work without any special configuration.

First, create a client in Keycloak with the client ID matching security.oidc.client_id (e.g. xgtd-client). Enable Standard flow for interactive login and Service accounts roles if using client credentials. Keycloak will automatically create a dedicated client scope named xgtd-client-dedicated for this client.

The client must have the redirect URI for the xGT Python client registered in its Valid redirect URIs list. The default loopback URI used by the authorization code flow is http://127.0.0.1:8765/callback. If Mission Control is also in use, add the URI configured as MC_OIDC_REDIRECT_URI in Mission Control (e.g. https://<mission-control-host>/api/login/oidc/callback) to the same list. If users run the client on a non-standard port (via callback_port on OidcAuth), that URI must be registered as well.

Two protocol mappers must be created in the dedicated scope. The dedicated scope does not include any mappers by default. These mappers control what claims Keycloak includes in both the access token and the introspection endpoint response, so they are required regardless of the validation_mode configured on the xGT server.

In the Keycloak admin console, open the client, go to the Client scopes tab, click the dedicated scope entry (xgtd-client-dedicated), then select Add mapper > By configuration. A list of available mapper types will appear; search for and select the type described below, fill in the fields, and save. Repeat for each mapper.

Group membership mapper:

  • Mapper type: Group Membership

  • Token Claim Name: groups (must match security.oidc.groups_claim)

  • Full group path: off (unless your grouplabel.csv uses full paths)

  • Add to access token: on if using validation_mode: jwt or auto

  • Add to token introspection: on if using any introspection-based mode

Audience mapper:

  • Mapper type: Audience

  • Included Client Audience: xgtd-client (must match security.oidc.audience)

  • Add to access token: on if using validation_mode: jwt or auto

  • Add to token introspection: on if using any introspection-based mode

If using an introspection-based validation_mode, Keycloak requires the caller to authenticate to the introspection endpoint with a confidential client. Enabling Client authentication on xgtd-client would also require a secret for the authorization code token exchange, which is inconvenient for interactive users. The recommended approach is to create a second dedicated client (e.g. xgtd-introspection) with Client authentication enabled. The secret for this client is found in its Credentials tab in the Keycloak admin console. Set security.oidc.introspection_client_id and security.oidc.introspection_client_secret to point at this client, leaving xgtd-client as a public client for the authorization code flow. No mappers are needed on xgtd-introspection because it is used only to authenticate introspection requests. The claims returned (groups, username, etc.) come from the token that was originally issued to xgtd-client, via the mappers configured on xgtd-client-dedicated.

A typical xgtd.conf block for Keycloak using JWT validation:

{
  "security" : {
    "oidc" : {
      "issuer"         : "https://keycloak.example.com/realms/xgt",
      "client_id"      : "xgtd-client",
      "audience"       : "xgtd-client",
      "username_claim" : "preferred_username",
      "groups_claim"   : "groups"
    }
  }
}

With introspection:

{
  "security" : {
    "oidc" : {
      "issuer"                      : "https://keycloak.example.com/realms/xgt",
      "client_id"                   : "xgtd-client",
      "audience"                    : "xgtd-client",
      "validation_mode"             : "introspection",
      "introspection_client_id"     : "xgtd-introspection",
      "introspection_client_secret" : "<secret>",
      "username_claim"              : "preferred_username",
      "groups_claim"                : "groups"
    }
  }
}

5.5.4. OpenShift

More information on OpenShift authentication and authorization is available at https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/.

OpenShift’s built-in OAuth server does not expose a standard JWKS endpoint and does not accept the standard OIDC scopes. Use validation_mode: openshift_userapi instead of JWT validation. In this mode the server calls the OpenShift User API with the bearer token, which simultaneously validates it and returns the user’s metadata. The audience setting is not required for this mode.

The username is found at metadata.name in the User API response, which requires dot notation in username_claim. Groups are returned as a top-level groups array. These correspond to OpenShift Group objects the user belongs to. If LDAP groups are synced into OpenShift using oc adm groups sync, those memberships will appear here automatically and can be mapped to xGT security labels via security.grouplabelfile. More information on syncing LDAP groups is available at https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/ldap-syncing.

The OAuthClient registered in OpenShift must have user:info in its scopeRestrictions and the xGT client’s redirect URI in its redirectURIs field. Add http://127.0.0.1:8765/callback to the redirectURIs list to match the default callback_port of OidcAuth. If Mission Control is also in use, add the URI configured as MC_OIDC_REDIRECT_URI in Mission Control (e.g. https://<mission-control-host>/api/login/oidc/callback) to the same list. You will likely need to register a new OAuthClient for xGT. See registering an additional OAuth client in the OpenShift documentation for details.

If Keycloak is available in the environment, it can also be used as the identity provider directly, which may be simpler than going through OpenShift’s OAuth layer.

A typical xgtd.conf block for OpenShift:

{
  "security" : {
    "oidc" : {
      "issuer"                 : "https://oauth-openshift.apps.example.com",
      "client_id"              : "xgtd-client",
      "validation_mode"        : "openshift_userapi",
      "openshift_user_api_uri" : "https://api.example.com:6443/apis/user.openshift.io/v1/users/~",
      "username_claim"         : "metadata.name",
      "groups_claim"           : "groups",
      "scopes"                 : "user:info"
    }
  }
}

When using CodeReady Containers (CRC) or another cluster with self-signed certificates, also set security.oidc.ca_cert to the cluster CA bundle and pass ca_cert_path on the client side (see TLS Considerations below).

5.5.5. TLS Considerations

If the IdP uses a self-signed or private CA certificate, both the server and the client need to be configured to trust it.

On the server side, if the CA certificate has been added to the system’s trusted certificate store, no additional configuration is needed. Otherwise, set security.oidc.ca_cert in xgtd.conf to the path of the CA bundle. This is used for all HTTPS requests the server makes to the IdP (JWKS, introspection, userinfo, discovery):

"security.oidc.ca_cert" : "/path/to/ca-bundle.pem"

On the client side, if the CA certificate has been added to the system’s trusted certificate store, no additional configuration is needed. Otherwise, pass ca_cert_path to OidcClientConfig to point the Python client at the CA bundle directly:

conn = xgt.Connection(host = "graph_server",
                      auth = xgt.OidcAuth(ca_cert_path = '/path/to/ca-bundle.pem'))

5.5.6. Token Validation Failures

When token validation fails, whether due to an expired token, a signature mismatch, a missing required claim, or a connectivity error reaching the IdP, the xGT server raises an XgtSecurityError and rejects the connection. The Python client receives this as an XgtSecurityError. The server logs will include the underlying OIDC-related error message, such as token validation, introspection, audience, issuer, or claim-extraction failures, which is useful for diagnosing misconfiguration.

For details on how a user connects to xGT using OIDC see OIDC Authentication.