LDAP Users LDAP Groups - LDAP Driven Group Scoped STS Demo

Prev Next

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.

New to OIDC, JWTs, or IAM trust policies? Skim the Glossary 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.


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.


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.


Architecture in one diagram

STS Architectural Diagram

LDAP group → Keycloak realm role → Keycloak client (with conditional auth flow) → JWT aud claim → VAST trust policy → VAST IAM role → identity policy → S3 permissions.


Read the docs

Two documents in the repo cover everything:

  • KB.md - 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 — 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.


Reproduce in six steps

Detailed walkthrough is in CHEATSHEET.md. 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 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 to add the four per-group clients with conditional flows that gate token issuance on group membership.

  5. Run create-iam.sh 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 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):

# 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 and create-iam.sh read this same variable, so setting it once keeps the issuer in the JWT and the issuer in the trust policy in lockstep.

export LDAP_URL='ldap://192.168.1.10:389'
export KC_FRONTEND_URL='http://keycloak.lab.example.com:8080'

What's in the repo

File

What it is

configure-keycloak-ldap.sh

KC Phase 1: realm + LDAP federation + sync

configure-keycloak-clients.sh

KC Phase 2: per-group clients with conditional flows

create-iam.sh

VMS REST: 4 policies + 4 roles

test-ldap-sts.sh

17-cell base access-matrix verification

test-hidden-prefix.sh

10-cell hidden-prefix verification

ldap-files/ous.ldif

ou=users, ou=groups parents

ldap-files/users.ldif

8 inetOrgPerson users

ldap-files/groups.ldif

4 groupOfNames groups

iam-roles/rw-identity-policy.json

full s3:* on demo-bucket

iam-roles/ro-identity-policy.json

read-only on demo-bucket

iam-roles/wo-identity-policy.json

write-only on demo-bucket

iam-roles/na-identity-policy.json

explicit Deny *

bucket-policies/hidden-prefix-policy.json

Trust policies are generated in-script by create-iam.sh 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.


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.:

    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.