Keycloak Connector

Provides app-user mappings stored in Keycloak.

The Keycloak connector reads the users in a realm so that Atolio can resolve each signed-in identity to an Atolio user. Every user is ingested under their Keycloak user ID, which is the same identifier Keycloak puts in the sub claim of an OIDC token. That match is what lets Atolio connect a login back to the right user.

Note: As part of Atolio Configuration, this connector should be selected as Identity Provider if it is used to resolve app-user mappings. Point it at the same realm your users authenticate against in Keycloak authentication.

Setting up the connector takes three steps:

  1. Create a read-only service account in Keycloak (done by your Keycloak admin).
  2. Deploy the connector pod into the Atolio cluster (done by your Deployment Engineer).
  3. Configure the source with atolioctl (done by your Deployment Engineer).

Step 1: Create a Service Account in Keycloak

The connector talks to the Keycloak Admin REST API as a realm user, so we recommend creating a dedicated service account rather than using a personal admin account. The connector only reads users; it does not write to Keycloak or read any other resource.

  1. In the admin console, select your realm and create a user under “Users” (e.g. atolio-connector).
  2. On the new user’s “Credentials” tab, set a password and turn off “Temporary” so it does not expire on first use.
  3. On the “Role mapping” tab, click “Assign role”, filter by clients, and assign the view-users role from realm-management. This is the lowest-privilege role that lets the connector list users in the realm.

The connector authenticates with this username and password against the realm’s built-in admin-cli client using the OAuth2 password grant, then refreshes the resulting token as needed. No additional client setup is required, since admin-cli exists in every realm by default.

Note: view-users is scoped to the realm you assign it in. If your users live in more than one realm, deploy a connector instance per realm, each with its own service account. If you rotate the service account’s password in Keycloak, update the password secret (Step 3) so the connector can keep authenticating.

Collect these values to use in the following steps:

ValueDescriptionExample
Base URLRoot URL of your Keycloak instance. Include a context path such as /auth if your Keycloak uses one.https://keycloak.example.com
RealmThe realm that holds your users and the service account.employees
UsernameThe service account username created above.atolio-connector
PasswordThe service account password. Provided as the password secret.

Step 2: Deploy the Connector

Before the source can be configured, the connector pod must be running in the cluster. Connectors are deployed by enabling them in values-lumen.yaml. See Deploying Connectors for the full details, including how instances work.

connectors:
  keycloak:
    default:
      enabled: true

default is the instance identifier. Treat it as immutable once set, because the instance is part of every resource identifier the connector ingests. Apply the change by upgrading the lumen Helm chart with create-infra.sh (recommended) or helm upgrade as described in the sources overview.

Step 3: Configure the Source

Source configuration is applied with atolioctl using a YAML file. If you have not already installed atolioctl, follow Configuring Sources first. The commands below also need an API token in JWT_TOKEN and the address the feeder server’s gRPC endpoint, both described next.

atolioctl prerequisites

JWT_TOKEN holds the API token atolioctl uses to authenticate to the feeder. Generate one from the deployment’s Helm values directory, which holds the values-lumen.yaml and values-lumen-templated.yaml files produced by create-infra.sh. The token is signed with the deployment’s jwtSecretKey, and the atolio:*:*:* match pattern grants access across all connectors, instances, and resources:

export JWT_TOKEN=$(atolioctl connector create-jwt --raw --config-dir . "atolio:*:*:*")

--feeder-address is the gRPC address of the feeder server that is used to read and write source configuration through. The standard path is to port-forward the in-cluster service/feeder and point atolioctl at it on localhost:

kubectl port-forward -n atolio-svc service/feeder 8889

With that port-forward running in another terminal, the examples below use --feeder-address :8889. The empty host means localhost, and because the port is not 443 the connection is made in plaintext, which is what the port-forward exposes. If the feeder is instead published on a load balancer, pass that address with port 443, for example --feeder-address feed.search.example.com:443, which connects over TLS.

Configuration schema

In the exported file, every source lives at connectors.<connector>.<instance>.<source>, so the Keycloak source sits at connectors.keycloak.default.keycloak. Its settings are grouped into common, secrets, and configuration sections:

FieldSectionRequiredDescription
enabledcommonyesSet to true to run the source.
identity-providercommonyesSet to true so Atolio uses this connector to resolve logins to users.
cron-speccommonnoCron expression for periodic re-sync of users. Omit to sync only on demand.
passwordsecretsyesService account password from Step 1.
base_urlconfigurationyesRoot URL of your Keycloak instance.
realmconfigurationyesRealm holding your users and the service account.
usernameconfigurationyesService account username from Step 1.

Edit and apply the configuration

Source configuration is stored as a single document that covers every configured connector. The supported workflow is to export the current configuration, edit it, then import it back. Always start from a fresh export so you do not overwrite a change made elsewhere.

Export the current configuration to a local file:

atolioctl configure export --jwt-token-sdk ${JWT_TOKEN} --feeder-address :8889 lumen-connectors.yaml

Add a Keycloak entry alongside the connectors already in the file, substituting the values you collected in Step 1:

connectors:
  # ...existing connectors left in place...
  keycloak:
    default:
      keycloak:
        connector: keycloak
        instance: default
        source: keycloak
        common:
          enabled: true
          identity-provider: true      # This connector is the identity provider
          cron-spec: 0 7 * * *         # Re-sync users on this schedule
        secrets:
          password:
            value: "<SERVICE_ACCOUNT_PASSWORD>"
        configuration:
          base_url: https://keycloak.example.com
          realm: employees
          username: atolio-connector

Exported entries also carry a rendezvous-completed flag and a status block reporting each source’s current state and last validation result. Those fields are managed by Atolio. Leave them untouched on existing entries, and omit them when adding a new entry by hand as shown above.

Import the edited file to write the configuration back into the stack:

atolioctl configure import lumen-connectors.yaml --jwt-token-sdk ${JWT_TOKEN} --feeder-address :8889
Writing key: /lumen/connectors/keycloak/default/keycloak

Import writes the configuration into the stack after checking that the YAML is well formed. It does not contact Keycloak. The connector validates the credentials and connection itself when it starts and picks up the configuration, and it reads users after that. If something is wrong, the source reports as misconfigured and the connector pod logs the error. See Troubleshooting for the specific messages.

Troubleshooting

Almost all errors surface in the connector pod logs, not in the atolioctl output. The import command only checks that the YAML is well formed before writing it to the stack, so a malformed file is the one thing it catches directly. Everything else is reported by the connector as it runs through its lifecycle.

The connector validates first. When it starts and picks up the configuration, it logs in to Keycloak to check the credentials and connection. If that fails, the source moves to a misconfigured state and stops before reading any users. Once validation passes, the connector lists users during its first sync, which is where a missing role shows up. Use the lifecycle phase in the table below to tell the two apart.

MessagePhaseCauseResolution
YAML decode error from atolioctl configure importImportThe configuration file is not valid YAML or does not match the expected structure.Fix the YAML. Re-export with atolioctl configure export to get a known-good starting point.
missing secret "password"ValidationNo password secret was provided.Add the password secret with the service account password from Step 1.
base_url reported as invalidValidationbase_url is empty or not a valid HTTP or HTTPS URL.Set base_url to a full URL, e.g. https://keycloak.example.com.
credential validation: login failed: 401 ... invalid_grantValidationWrong username or password, or the account is disabled.Confirm the service account credentials and that the user is enabled in the realm.
credential validation: login failed: 404 ...ValidationThe realm value does not match a realm that holds the service account.Set realm to the realm where you created the account.
credential validation: login failed: ... dial tcp / no such hostValidationbase_url is unreachable from the cluster, or a context path is missing.Verify base_url is reachable from the cluster, including a context path such as /auth if your Keycloak uses one.
export user: user page: get users: 403 ...User syncThe service account logged in but lacks permission to list users.Assign the view-users role from realm-management on the account’s “Role mapping” tab.
Logins fail to resolve to a userAt sign-inThe connector realm differs from the realm used for authentication.Point the connector at the same realm configured in Keycloak authentication.