--- title: "LDAP Users LDAP Groups - LDAP Driven Group Scoped STS Demo" slug: "ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo" description: "Authenticate with OpenLDAP via Keycloak to receive short-lived S3 credentials on a VAST cluster, ensuring secure, group-scoped access." updated: 2026-10-28T20:02:03Z published: 2026-10-28T20:02:03Z canonical: "docs.vastdata.com/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.vastdata.com/llms.txt > Use this file to discover all available pages before exploring further. # LDAP Users LDAP Groups - LDAP Driven Group Scoped STS Demo LDAP-driven, group-scoped temporary S3 credentials on a VAST cluster. Keycloak handles the login flow as the OIDC identity provider; VAST issues short-lived S3 keys. End-to-end working demo with reproducible setup scripts and verification tests. > [!NOTE] > **New to OIDC, JWTs, or IAM trust policies?** Skim the [Glossary](/kb/docs/ldap-users-and-groups-ldap-driven-group-scoped-sts-demo#glossary6) below before reading the architecture diagram — every term in this README is defined there in plain English. **Status:** 17 / 17 base access-matrix assertions passing, 10 / 10 hidden-prefix assertions passing. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#what-this-proves)What this proves A user authenticates against an existing OpenLDAP directory via Keycloak, receives a JWT, trades it for short-lived VAST STS credentials, and operates on S3 — with their effective S3 access entirely determined by which LDAP group they belong to. No long-lived keys, no per-user policy bookkeeping in VAST, revocation by group membership change. The demo wires up four worked LDAP groups against one bucket: | LDAP group | Effective S3 access on `demo-bucket` | | --- | --- | | `rw-group` | full read/write | | `ro-group` | read-only | | `wo-group` | write-only | | `no-access-group` | explicit deny — proves the gate is real | It also includes a bonus pattern: a per-user **hidden prefix** inside the bucket, applied as a bucket-policy overlay, so a single user (e.g. `finance@example.com`) can read or write inside `s3://demo-bucket/finance/` while everyone else — including other rw-group members — is denied. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#glossary)Glossary Plain-English definitions of every acronym used below. Skip this if these are all familiar. - **OIDC** (OpenID Connect) — an internet standard for "log in with…" flows. Sits on top of OAuth 2.0. Keycloak speaks OIDC. - **IdP** (identity provider) — the system that proves who you are. In this demo, Keycloak is the IdP and OpenLDAP is its user database. - **JWT** (JSON Web Token) — a small signed JSON document an IdP hands back after a successful login. Anyone holding a valid JWT can present it as proof of identity until it expires. The signature lets the receiver verify the token came from the IdP without phoning home. - `iss` **claim** — one field inside a JWT. The URL of the IdP that minted the token. VAST uses it to look up the IdP's public signing key and verify the signature. - `aud` **claim** — another field inside a JWT. Says *which audience the token was minted for* — typically the `client_id` of the OIDC client that asked for it. **VAST gates role assumption on this field** in this demo. - **STS** (Security Token Service) — VAST's API for trading a long-term credential (here: a JWT) for short-lived S3 keys (`AccessKeyId`, `SecretAccessKey`, `SessionToken`). Mirrors the AWS STS API exactly, including the `AssumeRoleWithWebIdentity` call this demo uses. - **Trust policy** — a JSON document attached to an IAM role that says *who is allowed to assume the role*. Its conditions are evaluated against the incoming JWT's claims. We gate ours on `aud`. - **Identity policy** — a JSON document attached to an IAM role that says *what S3 actions the resulting session can perform*. Trust policy says **who**; identity policy says **what**. - **Bucket policy** — a JSON document attached directly to an S3 bucket. Applies to *every* caller regardless of which role they assumed; the right place for cross-cutting rules. The hidden-prefix bonus uses one. `Deny` in any policy beats `Allow` in any other. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#architecture-in-one-diagram)Architecture in one diagram ![STS Architectural Diagram](https://cdn.us.document360.io/89b91113-5dc6-49db-86ec-57b16dc8ce56/Images/Documentation/image(208).png?sv=2026-02-06&spr=https&st=2026-11-08T09%3A44%3A22Z&se=2026-11-08T10%3A44%3A22Z&sr=c&sp=r&sig=cxEtuFgDKg2Qf4wlEaJSzrBTa4%2BFAH8AjgDiyBO%2BhQw%3D) LDAP group → Keycloak realm role → Keycloak client (with conditional auth flow) → JWT `aud` claim → VAST trust policy → VAST IAM role → identity policy → S3 permissions. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#read-the-docs)Read the docs Two documents in the repo cover everything: - [**KB.md**](/kb/docs/vast-s3-with-ldap-driven-group-access-via-keycloak-sts) - The long-form article. Read this if you want to understand the architecture, why each piece exists, what each component is doing, and the full request flow end-to-end. Also has a reference section listing every condition key and API endpoint used. - [**CHEATSHEET.md**](/kb/docs/vast-sts-ldap-keycloak-field-cheat-sheet) — The field reproducer. Read this if you want to spin the whole thing up on a fresh host in 15 minutes. Has the diagrams from KB.md plus a one-page checklist of commands. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#reproduce-in-six-steps)Reproduce in six steps Detailed walkthrough is in [CHEATSHEET.md](/kb/docs/vast-sts-ldap-keycloak-field-cheat-sheet). The TL;DR: 1. **Run OpenLDAP and Keycloak as containers** on a host that can reach your VAST cluster's VMS REST endpoint and S3 VIPs. 2. **Load the LDAP test users and groups** from `ldap-files/`. **Edit** `users.ldif` **first** to replace `CHANGEME_BEFORE_LOAD` with the demo user password you want to use. 3. **Run** [configure-keycloak-ldap.sh](/kb/docs/sts-keycloak-files#configurekeycloakldapsh) to create the `vast` realm, federate from LDAP, sync groups, and create the base `vast-s3` client. Set `KC_PASS` and `LDAP_PASS` in your environment first. 4. **Run** [configure-keycloak-clients.sh](/kb/docs/sts-keycloak-files#configurekeycloakclientssh2) to add the four per-group clients with conditional flows that gate token issuance on group membership. 5. **Run** [create-iam.sh](/kb/docs/sts-keycloak-files#createiamsh2) to create the four identity policies and four IAM roles in your VAST tenant via the VMS REST API. 6. **Run** [test-ldap-sts.sh](/kb/docs/sts-keycloak-files#testldapstssh) to verify the 17-cell access matrix end-to-end. Required environment variables (every script reads from the env; the same names are used everywhere): ```bash # VMS — used by create-iam.sh (TENANT_NAME also used by tests) export VMS_HOST=vms.example.com export VMS_USER=admin export VMS_PASS='your-vms-admin-password' export TENANT_NAME=your-tenant-name # Keycloak + LDAP — used by configure-keycloak-*.sh AND create-iam.sh # (KC_URL also used by tests; KC_URL + KC_REALM are how create-iam.sh #  derives the OIDC issuer it bakes into the trust policies) export KC_URL=http://localhost:8080 export KC_USER=admin export KC_PASS='your-keycloak-admin-password' export KC_REALM=vast export LDAP_PASS='your-openldap-admin-password' # Tests — used by test-*.sh (also reuse KC_URL and TENANT_NAME above) export VAST_VIP=172.x.x.x export BUCKET=demo-bucket export PASSWORD='your-demo-user-password'   # the LDAP demo-user password # Optional — max STS credential lifetime baked into each IAM role by # create-iam.sh, in seconds. Default 900 (15 min, the VAST floor). # Raise for long-running jobs that can't re-auth mid-run. export MAX_SESSION_DURATION=900 ``` Two optional overrides cover non-default networking. `LDAP_URL` is the URL Keycloak uses to bind to LDAP — override it if Keycloak runs in a container without `--network=host`. `KC_FRONTEND_URL` is what gets baked into the JWT `iss` claim *and* into the trust policies that `create-iam.sh` generates — override it if VAST sees Keycloak at a different URL than this script does. Both [configure-keycloak-ldap.sh](/kb/docs/sts-keycloak-files#configurekeycloakldapsh) and [create-iam.sh](/kb/docs/sts-keycloak-files#createiamsh2) read this same variable, so setting it once keeps the issuer in the JWT and the issuer in the trust policy in lockstep. ```plaintext export LDAP_URL='ldap://192.168.1.10:389' export KC_FRONTEND_URL='http://keycloak.lab.example.com:8080' ``` --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#whats-in-the-repo)What's in the repo | File | What it is | | --- | --- | | [configure-keycloak-ldap.sh](/kb/docs/sts-keycloak-files#configurekeycloakldapsh) | KC Phase 1: realm + LDAP federation + sync | | [configure-keycloak-clients.sh](/kb/docs/sts-keycloak-files#configurekeycloakclientssh2) | KC Phase 2: per-group clients with conditional flows | | [create-iam.sh](/kb/docs/sts-keycloak-files#createiamsh2) | VMS REST: 4 policies + 4 roles | | [test-ldap-sts.sh](/kb/docs/sts-keycloak-files#testldapstssh) | 17-cell base access-matrix verification | | [test-hidden-prefix.sh](/kb/docs/sts-keycloak-files#testhiddenprefixsh) | 10-cell hidden-prefix verification | | [ldap-files/ous.ldif](/kb/docs/sts-keycloak-files#ousldif1) | ou=users, ou=groups parents | | [ldap-files/users.ldif](/kb/docs/sts-keycloak-files#usersldif1) | 8 inetOrgPerson users | | [ldap-files/groups.ldif](/kb/docs/sts-keycloak-files#groupsldif) | 4 groupOfNames groups | | [iam-roles/rw-identity-policy.json](/kb/docs/sts-keycloak-files#rwidentitypolicyjson1) | full s3:* on demo-bucket | | [iam-roles/ro-identity-policy.json](/kb/docs/sts-keycloak-files#roidentitypolicyjson2) | read-only on demo-bucket | | [iam-roles/wo-identity-policy.json](/kb/docs/sts-keycloak-files#woidentitypolicyjson) | write-only on demo-bucket | | [iam-roles/na-identity-policy.json](/kb/docs/sts-keycloak-files#naidentitypolicyjson2) | explicit Deny * | | [bucket-policies/hidden-prefix-policy.json](/kb/docs/sts-keycloak-files#hiddenprefixpolicyjson) | | Trust policies are **generated in-script** by [create-iam.sh](/kb/docs/sts-keycloak-files#createiamsh2) from the resolved OIDC issuer URL and the matching `aud` value — see `build_trust_policy()` in that file. Every trust policy in this demo is structurally identical except for the issuer + `aud`, so stamping them out from one template is more honest than checking in four near-identical files with a baked-in lab IP. All scripts are **idempotent** — re-running them is safe and only creates the objects that don't already exist. --- ## [](/docs/ldap-users-ldap-groups-ldap-driven-group-scoped-sts-demo#a-note-about-credentials-in-this-repo)A note about credentials in this repo Every credential in the source is a **placeholder** (`CHANGEME` or `CHANGEME_BEFORE_LOAD`). Before you run anything, you must either: - **For scripts**: export the listed environment variables (the scripts refuse to run with placeholder values). - **For** `ldap-files/users.ldif`: substitute the eight `CHANGEME_BEFORE_LOAD` values with the demo user password you want to use, e.g.: ```bash sed -i 's/CHANGEME_BEFORE_LOAD/your-demo-user-password/g' ldap-files/users.ldif ``` Then `ldapadd` the file. (After that, also `export PASSWORD=...` so the test scripts use the matching value.) Do not commit a real credential to this repo, even temporarily. Use the environment variable mechanism that the scripts already provide.