How Keycloak sign-in gets short-lived S3 credentials from Ceph RGW
A platform team that wants users and workloads to reach RGW buckets without distributing long-lived access keys can use AssumeRoleWithWebIdentity. This RGW STS operation exchanges an OpenID Connect or OAuth 2.0 token issued by Keycloak for temporary S3 credentials. The supported method is documented in the Ceph RGW STS documentation, which states that Keycloak has been tested and integrated with RGW. This page covers the decisions behind that flow: registering Keycloak as an OIDC provider in RGW, writing role trust policies that match token claims, using session tags for attribute-based access, and setting rgw_sts_key consistently across the zone.
The flow has three parts. A user or workload authenticates to Keycloak and receives an OIDC token. The client then calls RGW with that token, and RGW checks it against the registered provider and the role trust policy. If the checks pass, RGW returns credentials that expire. STS-based S3 authentication is off by default and must be enabled with rgw_s3_auth_use_sts. The default session length for AssumeRole is 3600 seconds, so the team should decide whether that duration fits its workloads and set it deliberately rather than inheriting it.
Before you start, confirm three things with your own environment: which Keycloak version and database you run, whether your Keycloak deployment is a single cluster or multi-cluster, and whether RGW runs across more than one zone or multisite realm. The rgw_sts_key value must match on every RGW instance in a zone and across a multisite realm, so treat it as a shared secret with a rotation plan. The sections that follow walk through each of these settings in order.
What you need in place before configuring STS with Keycloak
The STS setup depends on four things being settled before anyone edits an RGW configuration: the Ceph release, a Keycloak realm that RGW can reach, trust for the RGW endpoint, and a naming plan for buckets and roles. Each card below is a decision or check that is hard to change once the trust policies and clients are in use.
Confirm the Ceph release
Check the active release list before you plan anything. Tentacle (20.2.x) and Squid (19.2.x) are the actively maintained release lines, and your cluster should run one of them or have a planned upgrade to one of them. Record the exact point release you run, because STS behavior and settings can differ between releases. Confirm the RGW STS settings for that release against the Ceph documentation for that version, not an older tutorial.
Keycloak realm with a reachable issuer
Create or select the Keycloak realm that will issue the identities, and note its issuer URL, which is the value RGW uses to fetch the OpenID Connect discovery document and signing keys. The issuer URL must be reachable from every RGW instance, not only from user browsers. Test the discovery endpoint from an RGW host, including DNS resolution and firewall paths, before you register the provider.
TLS trust for the RGW endpoint
S3 clients and the STS endpoint on RGW must be reached over TLS in most production designs, so decide which certificate authority issues the RGW certificate and make sure every client, workload and automation host trusts it. Also confirm that RGW trusts the Keycloak certificate it uses to reach the provider. Certificate chain gaps are a common reason token exchange fails, and they look like authentication errors rather than TLS errors.
Bucket and role naming plan
Agree on the naming scheme for buckets, roles and Keycloak groups or claims before writing trust policies. Role names, bucket prefixes and the claim values you match on (such as aud, sub or azp) become hard to rename once applications depend on them. Document which Keycloak clients may assume which roles, and who owns each bucket, so that trust policies map cleanly to your access model.
Confirm RGW STS settings for your release
Enabling STS-based S3 authentication is a deliberate change. The rgw_s3_auth_use_sts setting defaults to false, the AssumeRole DurationSeconds default is 3600, and rgw_sts_key must be identical on every RGW instance in a zone and across a multisite realm. Before changing any of these, confirm the names, defaults and any release-specific behavior in the STS documentation for your exact Ceph version, and ask your team who will manage the shared key securely.
Configuration sequence for Keycloak and RGW STS
This sequence turns a running Ceph Object Gateway and a Keycloak realm into a working path where authenticated users and workloads exchange an OIDC token for short-lived S3 credentials, scoped by role trust policies and session tags. Each step ends with a check so that a failure is found at the point where it was introduced.
Confirm the Keycloak and RGW baseline
Before changing RGW, confirm that the RGW endpoint is reachable over HTTPS from the clients that will call it, that the Keycloak realm is reachable from RGW hosts, and that the time on all RGW hosts and the Keycloak server is synchronized, since token validation depends on expiry times. Record which Ceph release and which Keycloak release you run, and confirm both against the vendor documentation for your versions. The step is done when a plain S3 request with existing credentials succeeds and the Keycloak OpenID Connect discovery document loads from a host on the RGW network.
Enable STS authentication for S3 requests
Set rgw_s3_auth_use_sts to true in the configuration database for the RGW service, because the default is false and S3 requests that carry STS session tokens are rejected until it is enabled. Apply the change through cephadm or your configuration management, then restart or reconfigure the RGW daemons as the Ceph STS documentation describes for your release. The step is done when
ceph config getreturns true for the RGW daemons and an existing long-term key still authenticates, which shows that enabling STS did not break static access.Set rgw_sts_key identically across the zone and realm
Generate one secret value for rgw_sts_key and set the same value on every RGW instance in the zone. In a multisite realm, every zone must use the same key as well, because a session token issued by one RGW must be decryptable by another. Store the value in your secrets manager, not in shell history or plain configuration files, and plan a rotation procedure before go-live. The step is done when a session token issued through one RGW endpoint is accepted by a request routed to a different RGW instance in the same zone.
Register the Keycloak OIDC provider in RGW
Create an OpenID Connect provider object in RGW that points at the Keycloak realm issuer URL and lists the client IDs (audiences) that RGW should accept. Use the IAM OIDC provider API exposed by RGW, following the STS documentation for the exact request fields in your release. Confirm the issuer URL matches the iss claim in tokens that Keycloak issues, character for character, including the realm path. The step is done when the provider appears in the RGW listing and a token from the expected Keycloak client is accepted during a test call.
Create roles with trust policies on token claims
Create an IAM role for each access pattern, with a trust policy that restricts AssumeRoleWithWebIdentity to the provider and to conditions on the aud, azp or sub claims that Keycloak places in the token. Map each role to the least-privilege S3 permission policy it needs, and keep one role per application or group rather than one shared role for everyone. Ask your security team to review the claim conditions, because a broad sub condition or a missing aud check widens access. The step is done when the role trust policy saves and the role is listed with the expected condition text.
Define session tag rules for attribute-based access
If access should depend on user attributes such as department or project, add session tags to the Keycloak token claims and reference them in the role permission policy with condition keys that match the tag names. Keep the tag set small and documented, because each tag becomes part of the access decision for every request. Test with two users whose attributes differ. The step is done when the same role returns allowed access for the tagged bucket prefix for one user and denied access for the other user.
Verify allowed and denied token exchanges
Run AssumeRoleWithWebIdentity with a valid Keycloak token from the expected client and confirm that temporary access key, secret key and session token are returned, with DurationSeconds left at the default of 3600 unless you have set a different value. Then repeat the call with a token from a client that the trust policy does not list, and with an expired token, and confirm that both are denied. Finally, use the temporary credentials for one allowed bucket operation and one operation outside the policy. The step is done when the allowed calls succeed, every denied call fails, and the results are saved as evidence for your change record.
Choosing how users and workloads map to roles
People who sign in to Keycloak from a browser or a desktop client are usually the easiest callers to map, because their identity is stable and their token carries a subject that identifies one person. The trust policy for these users should match the audience the token was issued for, so that a token minted for another application cannot be exchanged for S3 credentials. Matching on the authorized party (azp) helps when several Keycloak clients share a realm and only one of them should reach RGW. Because a person may sign in several times a day, the default DurationSeconds of 3600 is usually adequate, and the user can request new credentials when the session expires.
- Match audience first: Require the
audvalue that corresponds to the RGW-facing Keycloak client, so tokens from unrelated clients are rejected. - Restrict the authorized party: Use
azpto allow only the approved browser or CLI client to request credentials. - Map groups to roles through claims: Create one role per access level and scope its trust policy to the claim values that represent that group.
- Use `sub` for named exceptions: Match a specific subject only for individual access that must be granted or revoked person by person.
- Keep the default lifetime for people: A one-hour session suits interactive work, and a shorter session can be set in Keycloak if your policy requires it.
Confirm with your Keycloak administrators which claims your realm emits by default and which need a mapper, because a trust policy cannot match a claim the token does not contain.
Applications that run continuously, such as a backup agent or a data service, authenticate with a client identity rather than a person. Their tokens identify the service account through sub and the calling client through azp, and these values are stable across restarts. The trust policy should be narrow: it should match the exact client and subject for each workload, so that one compromised service cannot assume the role of another. Because a workload may run for hours, the one-hour default means the application must refresh credentials before they expire, and the refresh logic belongs in the application or the SDK configuration rather than in a manual step.
- Give each workload its own client: Separate Keycloak clients per service let
azpandsubdistinguish callers and let you revoke one without affecting others. - Match subject and client together: Combining
subwithazpin the trust policy prevents a token from another client with the same subject from being accepted. - Plan credential refresh: Confirm that the SDK or client library renews STS credentials before the DurationSeconds window closes.
- Set DurationSeconds deliberately: The value can be raised within the limit RGW allows, but a longer lifetime widens the window in which a leaked credential works, so confirm the value against your rotation policy.
- Scope bucket permissions tightly: The role's permission policy should list only the buckets and actions the workload needs.
Before go-live, test a forced expiry in a non-production environment to confirm the application recovers without a restart.
Continuous integration jobs are the most short-lived callers. A pipeline may start, need storage for a few minutes, and end, which makes the default one-hour lifetime generous for most jobs. The main risk is breadth: if a single client serves every pipeline in an organization, any job can assume any role. Trust policies for CI should distinguish the pipeline's client and, where the token supports it, the claims that identify the project or branch. Session tags carried in the web token can also enable attribute-based access control, so that a job receives access only to buckets tagged for its project.
- Separate CI clients by trust level: Use one Keycloak client for production deploy jobs and another for ordinary build jobs.
- Use session tags for project scope: Where your token carries the attributes you need, session tags can limit access to buckets for one project.
- Keep lifetimes short: The default of 3600 seconds is usually longer than a job needs, so ask whether a shorter DurationSeconds value is appropriate for your pipelines.
- Avoid shared long-lived secrets: The client secret that obtains the token should live in the CI secret store, not in repository files.
- Log every assumption: Record which role each job assumed so that unusual access can be traced to a pipeline run.
Confirm with your platform team which token claims your CI identity provider emits, because the claims available to a trust policy depend on the Keycloak mapper configuration.
Mapping Keycloak token claims to RGW roles and session tags
This table shows which Keycloak token fields feed which RGW controls, and which side of the integration each mapping belongs to. Read it before writing any role trust policy, because a claim that is absent from the token cannot be matched later. The main takeaway is that the session tag rules only work when the tags are present in the web token Keycloak issues.
| Token element | Defined in Keycloak | Used in RGW | What to verify |
|---|---|---|---|
| Audience (aud) | Client scope or audience mapper that adds the RGW client ID to the access token | Trust policy condition on aud to accept only tokens meant for RGW | Decode a test token and confirm the expected client ID appears in aud |
| Authorized party (azp) | Client that requests the token, usually the application or workload client | Trust policy condition on azp to limit which clients may assume the role | Confirm azp matches the client used by each application, since a second client will be rejected |
| Subject identifier (sub) | Stable user or service account ID assigned by Keycloak | Trust policy condition on sub to bind a role to one identity | Use sub rather than username for long-lived bindings, because usernames can change |
| Custom attributes (for example department or project) | User attribute plus a protocol mapper that writes it into the token | Session tags passed through AssumeRoleWithWebIdentity for attribute-based access control | Confirm each attribute is set for every user in scope and that mapper output is in the token |
| Session tags | Claims in the web token that RGW maps to session tags (team to define the names) | Bucket policy conditions that compare a tag with the bucket or prefix being accessed | Missing tags cause denied requests, so test with a token that carries and omits each tag |
| Role and group claims | Group membership or role mapper, if the team uses them for coarse access | Trust policy condition on a claim value, if supported by the RGW release in use | Confirm the claim name and format with the Ceph RGW STS documentation for the release you run |
| Signing key and issuer | Realm issuer URL and key set published through the OIDC discovery endpoint | OIDC provider registration that RGW trusts for the realm | Confirm the issuer string matches exactly, including trailing path segments |
Hardening the trust chain between Keycloak and RGW
Temporary S3 credentials are only as trustworthy as the chain that issues them: the shared RGW secret, the Keycloak tokens, the role trust policies and the network path between them. The cards below cover the controls to set and the checks to run on each link before you enable STS-based S3 authentication.
Keep rgw_sts_key secret and consistent
The rgw_sts_key setting is shared by every RGW instance in a zone and across a multisite realm, so all instances must hold the same value. Store it in your secrets manager, restrict who can read the cephadm configuration that holds it, and rotate it through a planned change in which every instance receives the new value. Confirm that a mismatch between instances fails visibly rather than issuing credentials that some gateways reject.
Restrict trust policies to specific clients
A role trust policy that accepts any token from your Keycloak realm lets every registered client obtain S3 access. Match the aud or azp claim to the one client ID that is allowed to request each role, and match sub when only certain users or service accounts should assume it. Review these conditions whenever a client is added to the realm, because a new client can otherwise satisfy an existing broad condition.
Keep session durations short
AssumeRole requests default to a DurationSeconds value of 3600, which is the ceiling a stolen credential stays useful. Set a shorter duration in each caller's request for workloads that run briefly, and align the Keycloak access token lifetime with it so a revoked user cannot keep renewing S3 access. Test expiry with a real workload, because clients that cache credentials may fail late when the session ends.
Encrypt traffic between RGW and Keycloak
RGW fetches the signing keys it uses to validate web tokens from the identity provider, and a tampered key set would let an attacker mint accepted tokens. Serve Keycloak over HTTPS, give RGW a CA bundle that trusts the Keycloak certificate, and confirm that the issuer URL in the OIDC provider registration matches the certificate name exactly. Keep the path off untrusted networks and log certificate errors on the gateways.
Separate STS and sign-in clients
Use a dedicated Keycloak realm or client for workloads that obtain S3 credentials, and keep it apart from the realm that signs staff into applications. Applications often grant broad realm roles, mappers and client scopes, and any of those can shape the claims your trust policies rely on. Review who administers the STS realm and which mappers feed its tokens, so that a change for application sign-in cannot widen S3 access.
Keep STS disabled until ready
STS-based S3 authentication stays off until rgw_s3_auth_use_sts is set to true, so the gateways accept no temporary credentials before you enable it. Make the setting part of a reviewed change with the trust policies, the OIDC provider and the Keycloak client already in place. Confirm which RGW instances have the setting and that a rollback path exists if a policy fails in production.
Common failures when Keycloak tokens do not yield RGW credentials
This table pairs each common failure in the Keycloak to RGW STS path with its likely cause and the first fix to try. Work down the rows in order, since the first two checks (STS enabled and matching issuer) rule out the most common problems. Vendor-specific details, such as how your Keycloak version exposes claims, should be confirmed against your own environment.
| Failure | Likely cause | Fix |
|---|---|---|
| AssumeRoleWithWebIdentity is refused or S3 requests ignore the role | STS-based S3 authentication is still disabled, since rgw_s3_auth_use_sts defaults to false | Set rgw_s3_auth_use_sts to true on the RGW instances, then confirm the setting took effect after restart |
| Token rejected with an invalid signature | The OIDC provider registered in RGW does not match the realm's current signing keys, or the wrong realm or client is referenced | Re-register the OIDC provider using the realm's issuer and certificate endpoint, then retry with a freshly issued token |
| Token rejected for issuer mismatch | The issuer URL in the token (iss) differs from the URL registered in RGW, often because of a hostname, port or path difference | Copy the issuer value exactly from a decoded token and use it when registering the provider in RGW |
| Role assumption denied despite a valid token | The trust policy matches aud, sub or azp against values the token does not carry, such as a client ID that differs from the audience | Decode a sample token, compare its aud, sub and azp claims with the trust policy conditions, and align one or the other |
| Credentials work on one RGW host but fail on another | rgw_sts_key differs between RGW instances in the same zone or across the multisite realm, so encrypted session data cannot be read | Set one identical rgw_sts_key on every RGW instance in the zone and realm, then restart the gateways |
| Session tags have no effect on access decisions | The session tags were not included in the web token, so attribute-based access control has nothing to match | Confirm the claims mapped to session tags exist in the token and that the policy references the same tag names |
| Credentials expire sooner than expected or the token is refused as expired | Clock skew between Keycloak, RGW hosts and clients, or a token lifetime shorter than the requested session | Synchronize time on all hosts with NTP, then check token lifetime in Keycloak against the DurationSeconds value requested (default 3600) |
| Temporary credentials stop working during long jobs | Session duration ends before the work does, and the client does not refresh the web token | Request a longer DurationSeconds where policy allows, or have the workload obtain a new token and call AssumeRoleWithWebIdentity again |
Questions teams ask before configuring Keycloak with Ceph RGW STS
What drives the cost and effort of configuring Keycloak with Ceph RGW STS?
The main drivers are the number of roles you need, how complex the token claims are, whether you run a multisite realm, and how much testing you require. Each role needs its own trust policy matched to specific claims such as aud, sub or azp, so more roles and more claim conditions mean more design, review and test cases. A multisite realm adds work because rgw_sts_key must be identical across every RGW instance in the zone and across the realm. Ask any provider for a quote that lists roles, claim rules, session tag use, environments and the test cases each one needs.
Can our team configure Keycloak with Ceph RGW STS without outside help?
A team that already runs Keycloak and Ceph can usually do the work itself, provided it has someone comfortable with OpenID Connect client settings, JSON Web Token claims and Ceph RGW administration. The Ceph RGW STS documentation covers AssumeRoleWithWebIdentity, trust policies and session tags, and Keycloak has been tested and integrated with RGW. The harder parts are designing role boundaries, testing claim matching and handling the shared key correctly. Outside help is most useful when the design needs review or when the setup must be documented for an audit.
What do we need from our Keycloak and Ceph owners before starting?
You need the Keycloak owner to confirm the realm, the client that will request tokens, the claims that will be present in the token and the protocol used. The Ceph owner must confirm the RGW endpoints, the zone and realm layout, how rgw_s3_auth_use_sts will be enabled and who can change RGW configuration. Both owners should agree on who holds rgw_sts_key, how it is stored and how it is rotated across all RGW instances. Without these decisions, trust policies end up written against claims that do not exist or differ between environments.
How are long-lived S3 access keys retired after STS is in place?
STS does not remove existing access keys on its own, so retirement is a separate step you plan. Identify every user and workload that uses static keys, move each one to a role with a trust policy, and confirm the workload works with temporary credentials before revoking the old key. Keep a record of which keys were revoked and when, because auditors will ask how credential lifetime was controlled. Also check that old keys are not stored in configuration files or pipelines after cutover.
What changes when we upgrade Keycloak or Ceph?
Upgrades can change token behavior, admin settings and the Ceph configuration that STS depends on, so each upgrade needs its own test pass. Keycloak plans four minor releases a year and says breaking changes in a minor release will be opt-in, but you should still read the backwards compatibility notes and the upgrading guide before each step. Keycloak migrates its database schema automatically at startup by default, and the upgrading guide suggests manual migration for large deployments. Confirm each Ceph upgrade path separately with the release notes for your version, and rerun the claim-matching tests after either system changes.
What is the default session length for temporary credentials, and can we change it?
The default DurationSeconds for AssumeRole is 3600 seconds, which is one hour. Shorter or longer sessions should be set deliberately and tested with the clients that consume them, because some S3 tools cache credentials and may not refresh them as expected. Confirm the supported range and the way the duration is requested in the Ceph RGW STS documentation for your release before you set a policy.
What should we test before moving production workloads to STS credentials?
Test the full path: token issuance in Keycloak, the AssumeRoleWithWebIdentity call to RGW, and the S3 requests made with the returned credentials. Include a token that should be accepted and tokens that should be refused, including wrong audience, wrong subject and missing session tag values. Test expiry, refresh behavior in your S3 clients and access to buckets outside the role. Record the results so the same tests can be repeated after each Keycloak or Ceph change.
Keeping Keycloak and RGW upgrades from breaking STS
Treat every Keycloak or RGW upgrade as a change to the STS path and re-test AssumeRoleWithWebIdentity before the change reaches production. The two systems move on their own schedules, and a working trust chain can break in either one. Keycloak plans four minor releases a year and a major release every two to three years. Since Keycloak 26.0, the project says breaking changes in a minor release will be opt-in, which lowers the risk of a surprise, but it does not remove the need to read the release notes for every version you adopt. Your test should confirm that the realm, the client that issues the web token, and the aud, sub and azp claims still arrive in the form your role trust policies expect. A change to a claim name, audience mapping or token lifetime can make AssumeRoleWithWebIdentity fail while S3 access looks healthy for existing sessions.
Keycloak also migrates its database schema automatically at startup by default. That is convenient for small deployments, but a startup migration during an upgrade means the schema changes before anyone has checked the new version against your realm. For large deployments, Keycloak supports manual migration, where administrators apply the SQL themselves, and the upgrading guide suggests this approach for them. Decide which method you will use before the upgrade window, and keep a tested database backup. The Keycloak Operator documentation strongly recommends manual approval mode for operator updates, so a new Keycloak version is not rolled out automatically. Pair that approval with a staging run of your STS test: obtain a web token, call AssumeRole with a known role, and confirm the session tags and the DurationSeconds you expect (the default is 3600).
Re-test RGW as well. Confirm that rgw_s3_auth_use_sts is still set as intended, since it defaults to false, and that rgw_sts_key is identical on every RGW instance in the zone and across the multisite realm. Record the Ceph release in use, because the upgrade of one system does not validate the other. Check the current Keycloak release in the Keycloak project site and the Red Hat build's supported version if you use that distribution, then confirm the pairing with your own tests rather than assuming compatibility. Keep a documented test script, the expected claim and role outputs, and the rollback steps with each change record so that the next upgrade starts from evidence.
How DeployOpen works on this
DeployOpen plans, builds, tests and documents Keycloak as the OpenID Connect provider and Ceph RGW as the STS endpoint inside infrastructure you control. Work starts from a written scope that fixes which identity flows, roles and buckets are covered before any trust policy is written.
How we scope
- Confirm which Keycloak realm, clients and token claims will drive STS role assumption for each user group or workload.
- Map which buckets, prefixes and actions each role may reach, and which session tags carry attribute-based access rules.
- Record the RGW zones and any multisite realm, since the shared rgw_sts_key must be planned across all of them.
How we build
- Register the Keycloak OpenID Connect provider with RGW and write role trust policies that match aud, sub or azp claims.
- Enable rgw_s3_auth_use_sts deliberately, set DurationSeconds limits for each role, and test AssumeRoleWithWebIdentity end to end.
- Test denied paths as well as allowed ones, including expired tokens, wrong audiences and tokens missing required session tags.
How we hand over
- Deliver the role definitions, trust policies, claim mappings and rgw_sts_key handling as versioned, documented configuration.
- Document the runbook for rotating the shared key, adding a Keycloak client and changing a role's bucket access.
- Walk your identity and storage teams through the test results and the list of open items agreed in the scope.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer, scoped separately from the original build.
- Plan Keycloak and Ceph upgrades as separate changes, checking the STS configuration after each one against the written scope.
- Confirm with your team which Keycloak and Ceph versions you run before quoting, since supported releases change over time.

