Skip to content

Durable IAM Revocations

Source status, 2026-09-16: #191 and #192 are merged into main, but are absent from published Server 20260903. They change persistent IAM state and require a coordinated upgrade. Follow the upgrade and recovery runbook and SN-2026-013.

SILO retains the version of a deleted IAM record so an offline site cannot restore an older identity or grant when it reconnects. This covers built-in users, service accounts, groups, policy documents, and policy mappings in their actual user/STS-parent/group namespaces. It also revokes the deleted built-in user’s older service accounts, STS credentials and group grants across deliberate same-name recreation.

Ordering and persistence

Deletion records occupy the original IAM configuration paths. They contain the originating timestamp, Deleted, and, where required, RevokedBefore; identity deletion records contain no secret key or session token. Normal IAM listings and authorization hide deleted records. Object storage and etcd both serialize each path’s version comparison and write with a distributed lock. The source timestamp is persisted without replacing it with the receiving node’s clock.

Older events cannot overwrite a newer revision. At an identical timestamp, a deletion wins over a live record. A local deliberate recreation receives a version newer than the stored deletion. An older user/group deletion arriving after recreation retains its revocation boundary while preserving the newer live record. Receiving an already-applied tombstone does not rewrite or advance it. These rules also apply after cold loading persistent IAM state.

The user or group revision is the commit point of deletion. Cleanup of mappings and children follows that commit; cleanup failure cannot undo it. The API returns the cleanup error and still notifies sibling nodes to reload the committed state. Such an error does not mean the identity is still active. Retry the intended revocation only while IAM writes and same-name recreation are paused. When the API returns a committed-cleanup error, the admin handler does not send its immediate cross-site hook; cross-site propagation relies on the normal deletion-healing retry. Sibling deletion notifications reload current shared state, so a delayed notification cannot delete a subsequently recreated identity. Etcd siblings also receive persistent changes through watches. Failed notifications/watches remain eventual propagation, not a distributed instantaneous revocation transaction.

Keep node clocks synchronized and monitor offsets. Ordering uses source wall clock timestamps, with monotonic advancement for local writes to the same path. It does not establish causal order between concurrent writers at different sites or resolve conflicting live updates with identical timestamps deterministically.

Users, child credentials and groups

A recreated user retains RevokedBefore. New service accounts and built-in STS issuances carry a signed siloParentRevocation claim identifying the parent boundary known at issuance. Editing or replaying an old child does not update this claim. Old children remain invalid even when their own update timestamp is newer than the parent deletion; children issued for the recreated parent remain valid. Claims are read from verified tokens on credential load/write.

A newer replicated service-account snapshot can replace an older service with the same access key, including a deliberate change of owner or secret. Local duplicate creates remain rejected. Snapshots preserve disabled status and the service’s own revocation boundary, so earlier mappings cannot attach to the new service. Equal-version retries reload the committed identity without rewriting it. Periodic live healing compares source versions even when the public status summary is unchanged, and includes disabled identities as healing sources. Service snapshots also preserve their absolute expiration. The receiving site does not reapply the local minimum lifetime for a newly issued credential. A newer already-expired snapshot still supersedes the old key, is denied by authentication, and is collected into a durable service tombstone by normal loading. Cache/claims loading failures are returned for retry, not acknowledged; after a committed replacement the stale cached secret is evicted immediately. Collisions with an existing built-in or cached STS identity report an error and require an explicit administrative resolution; replication cannot change its credential kind. Concurrent conflicting service updates with exactly the same timestamp can retain different winners at different sites; the status summary does not resolve that case.

Each group member has its own MemberGrants timestamp. Changing another member or the group’s enabled status does not reissue everyone else’s grants. Effective membership requires the grant to be newer than both the user’s and the group’s retained boundaries. Listings and policy evaluation use the same effective membership. Peer snapshots preserve grant times, including unknown legacy grant times; they cannot treat a recent snapshot time as a fresh grant to a revoked identity. A new explicit administrative group grant can restore access.

This does not implement a general conflict-resolution protocol for all group membership edits. In particular, the inherited live-group snapshot merge adds members and does not reconcile a missed ordinary member removal. Removing a member from a live group during a site outage is a separate known limitation; do not infer that this change resolves it. User/group deletion boundaries and same-name recreation are covered here.

Retention and expiration

Permanent identities, groups, policy documents and mappings have no automatic tombstone TTL. A disconnected peer or an old backup may return arbitrarily late. Successful replay acknowledgements are an optimization, not permission to garbage-collect this history.

Natural expiration of an immutable STS token physically removes its token-key record and any legacy token-key mapping, without generating a permanent tombstone. An early STS revocation is retained until that token’s expiration plus the existing clock-skew allowance. Replaying the same revoked token with a later event timestamp cannot recreate it. Etcd uses an expiration lease; object storage collects expired STS tombstones during its existing credential loading/purge. A record with unknown expiration is retained conservatively. Cleanup writes are best effort and use a short lock budget. If one fails, that load stops optional reclamation, reports the error and still loads healthy users; expired credentials stay denied and retain their existing durable version. The next load retries. Healthy cleanup has no fixed record quota. The reusable STS parent policy mapping is not assigned the token’s TTL by deletion cleanup. External-IDP disablement is an early revocation, not natural token expiration, and its cached STS and service accounts are included in cleanup. Expiring service accounts retain a durable revision because their access keys are reusable and an older version might have no expiration.

Direct per-token RevokeTokens delivery between sites is not a new guarantee of this change. The guarantee for built-in parent deletion follows from the durable parent boundary, including children not currently present in the deleting node’s cache.

Healing, failures and operational cost

Each process starts one healing loop. Losing its distributed leadership lease pauses work until leadership is reacquired; it does not permanently terminate healing. Configuration reloads do not create extra loops. The 30-second interval starts after leadership is acquired and after each completed pass. Initial lock retries and endpoint recovery can add further delay; it is not a convergence SLA.

The normal IAM loaders maintain an in-memory index of deletion records and retained boundaries, without secrets. Healing uses this index; it does not add a second full walk of config/iam/ every cycle. Existing full IAM loading still scans persistent records, including tombstones, at startup and on refresh.

Each peer receives batches of at most 128 records. The sender remembers which path/version each peer acknowledged. Unrelated new changes at either site do not reset that progress. A failed batch remains pending while later independent batches can progress; a lost response may cause safe idempotent replay. A pass has a bounded duration, and its successful acknowledgements survive that timeout. The protocol reports each node name and process instance. Switching between known node instances behind a load balancer preserves acknowledgements. A new node instance conservatively invalidates prior acknowledgements once; repeated switches among those known instances do not reset progress. Restore persistent state only with the affected processes stopped, so a restore cannot reuse an old process acknowledgement.

Steady-state healing still traverses/sorts the retained in-memory set and checks the peer’s protocol status. It suppresses repeated deletion PUTs once acknowledged. Memory use scales with retained paths and peers; startup storage reads scale with history. This release does not provide general history compaction. After upgrading, older live records whose receiving sites originally assigned different timestamps can require an initial reconciliation wave. Allow for its storage writes and sibling notifications when planning the maintenance window.

The cluster IAM metrics include revocation_records, revocation_heal_failures, revocation_heal_duration_millis, and revocation_heal_last_success_timestamp_seconds. Errors are also logged. A nominal 30-second scheduler interval is not a convergence deadline: outages, large backlogs, lock contention and failed requests can require more passes.

Cached credential lookup checks the in-memory parent revision index and performs no additional storage read. STS issuance and cold credential loading still consult the persistent parent revision. Revision I/O and distributed lock waits release the IAM cache lock while a separate local writer mutex preserves write order. These operations have bounded contexts, including etcd lock and lease cleanup.

Protocol and supported upgrade

The server-owned versioned route is /minio/admin/v3/site-replication/peer/iam-revisions. It carries source versions, member grant times and distinct user/group revocation items without changing the admin client SDK or S3 API. Older servers reject this route. The sender reports the failure and does not fall back to a route that would discard the metadata. The existing legacy IAM route remains readable for best-effort compatibility; this does not confer the new guarantees on an older peer.

All participating servers must be upgraded for the guarantee in this document. Mixed old/new nodes sharing an IAM backend and rolling downgrade are unsupported: older binaries do not interpret tombstones or signed parent boundaries correctly. Use a maintenance window for coordinated upgrade:

  1. Pause IAM changes and isolate any offline site or backup whose state is unknown.
  2. Back up each site’s complete IAM storage and required encryption material. A live IAM admin export omits deletion history and is not an adequate backup.
  3. Stop all nodes sharing each site’s IAM backend, replace their binaries, and restart them on the upgraded version. Complete this for every participating site before relying on the new revocation semantics.
  4. Check IAM loading, site-replication errors and revocation convergence. Verify representative old credentials are denied and deliberately reissued ones work.
  5. Resolve pre-upgrade revocations explicitly. Absence cannot reconstruct an already-lost deletion version: remove surviving old records on the sites that still have them, and rebuild stale offline peers from approved state before admitting them. Do not reconnect an unknown old snapshot just to discover its deleted credentials.

Credentials issued by an older server for a recreated parent lack the required signed boundary and must be reissued by an upgraded server. Parents without any retained revocation history preserve existing credential behavior.

A pristine built-in policy remains protected from local deletion. If an administrator explicitly overrides that policy and later deletes the override, the durable deletion now suppresses automatic recreation of the built-in policy on reload. This prevents reload from undoing the deletion. Restore the policy by an explicit policy-create operation if desired. Local deletion of a nonexistent policy remains idempotent and does not create a new tombstone; replicated unknown deletions retain their version.

For rollback, stop and isolate the affected sites and assess changes since the backup before restoring compatible state. Restoring an older backup can itself lose later revocations and requires reconciliation/rekeying before access is reopened. Do not delete tombstones online or convert only live IAM records to make an older binary start. Server, client, Console, package and deployment acceptance remain separate delivery gates of the maintained PGSTY stack.

Regression and performance checks

Focused coverage is in iam-revocation_test.go, iam-revision_test.go, iam-revision-lock_test.go, iam-revision-boundary_test.go, iam-replication-protocol_test.go, iam-credential-retention_test.go, iam-peer-reload_test.go, and iam-replay_test.go. Set SILO_TEST_IAM_REVOCATION_ETCD to a disposable etcd endpoint to include backend lifecycle/locking/boundary tests; they use isolated key namespaces.

BenchmarkIAMCachedCredential and BenchmarkIAMSetTempUser can be run against the pre-change source for a comparable local baseline. BenchmarkIAMRevisionConvergedHealing covers 1,000 and 10,000 retained records; it measures steady-state index/network work and asserts zero repeated PUTs. Its fake peer does not measure durable catch-up throughput. BenchmarkIAMColdLoadExpiredServices measures loading and cleanup with 100 or 1,000 expired reusable credentials; run it with -benchtime=1x. Use actual multi-site signed S3/STS tests and deployment-specific latency/scale measurements in addition to these component tests.

Errors and observability

An admin delete can return HTTP 500 after the authoritative revocation has committed but dependent cleanup failed. It is not evidence that the old identity remains usable. Retry the intended revocation while IAM changes and same-name recreation are paused, then verify old and reissued credentials at every site. When a persistent parent revision cannot be read or issuance cannot be committed, STS fails closed through an internal-error path, including STSInternalError; it must not mint a credential by guessing a missing boundary.

Scrape each process’s authenticated /minio/metrics/v3/cluster/iam endpoint:

Metric Interpretation
minio_cluster_iam_revocation_records Retained deletion records and parent boundaries in this process’s index
minio_cluster_iam_revocation_heal_failures Failed convergence passes since process start
minio_cluster_iam_revocation_heal_duration_millis Duration of the last pass
minio_cluster_iam_revocation_heal_last_success_timestamp_seconds Unix time of the last successful pass

The index is per process; summing it across siblings double-counts shared state. Healing requires site replication and a leadership lease. Shared-backend deployments without site replication do not run the site healing pass; zero healing metrics are not evidence of a failure there. A healthy counter on one leader does not prove every peer’s credential checks pass.

Why simpler alternatives fail

Physical deletion alone loses the ordering evidence an offline peer needs. Comparing only the child’s update timestamp is also insufficient: editing an old child after a parent deletion would appear newer and could restore access. The signed issuance boundary remains unchanged by such an edit. Replacing a received source revision with local receive time breaks source order and can turn a retry into a new mutation. The implementation preserves source timestamps and separately advances only deliberate local writes. Tombstone TTL or restoring only live export records discards the very history that prevents replay.

The versioned source and metrics definitions define this contract. This record replaces the former in-repository IAM design; it is not a production recovery certificate.