Skip to content

Hbase 30402 - #8786

Closed
Apache9 wants to merge 22 commits into
apache:masterfrom
Apache9:HBASE-30402
Closed

Apache9 wants to merge 22 commits into
apache:masterfrom
Apache9:HBASE-30402

Conversation

@Apache9

@Apache9 Apache9 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Add RefreshAclState/RefreshAclStateData/RefreshAclRemoteStateData/AclEntryUpdate
to MasterProcedure.proto (import AccessControl.proto for UsersAndPermissions).
Payloads carry a master assigned timestamp for RS side watermark filtering.
Update the UsersAndPermissions comment: it is no longer the /hbase/acl znode
content, but a per-entry permissions snapshot.
…Manager

Fixed rules (evaluated before cached grants): hbase:meta READ is open to any
user, other meta actions and all hbase:acl actions are superuser only. A null
table name keeps its historical global scope semantics and is not subject to
the rules.

New APIs for the ACL de-ZK design: applyUpdates(List<EntryUpdate>) applies
incremental per-entry replace/delete, applyFullSnapshot(...) swaps in a full
entry->permissions snapshot (e.g. from PermissionStorage.loadAll) built aside
so readers never see a partial cache.
Drop the hbase:meta READ short circuit in AccessChecker.permissionGranted;
AuthManager now owns the fixed rules for hbase:meta / hbase:acl.

Narrow AccessControlFilter's blanket INCLUDE from all system tables to
hbase:meta only, so reads of hbase:acl and other system tables go through
the regular permission checks instead of being exposed to any user that
reaches the filter (e.g. with hbase.security.access.early_out=false).
…otonic timestamps

Add PermissionStorage.AclWriteHook: when installed (master side), every write
to the hbase:acl table is stamped with a hook assigned monotonic timestamp
(so the cell timestamp carries the master assigned ts) and the resulting
state of the written entry is announced after the write succeeds. Entry
state is read back over the same table instance, CF m (snapshot HDFS acl
markers) is ignored.

All mutating entry points are covered from inside PermissionStorage so
future write paths cannot forget to trigger the refresh pipeline.
…ark filtering

AclRefresher owns all AuthManager cache mutations through one serial queue:

- Incremental batches and master pushed full snapshots carry the master
  assigned timestamp of their writes; batches not newer than the last
  applied one are dropped, so stale deliveries (e.g. remote procedures
  replayed after a master failover) cannot regress the cache or resurrect
  deleted entries.
- reloadFromTable() scans hbase:acl and swaps in the result as one queue
  task: updates enqueued before it are already reflected in the scan,
  updates arriving during the scan apply after the swap, so a reload never
  loses a concurrent update.

AclReloadChore runs the full reload periodically
(hbase.security.acl.reload.period.msec, default 5min) as the fallback for
lost updates and direct table writes.

Also pair PermissionStorage write hook notifications with the write
timestamp, and extend loadAll with the max cell timestamp of the scan
(AclSnapshot), used as the version watermark of a full reload.
RefreshAclRemoteProcedure (per-RS child task of the future
RefreshAclProcedure) carries a change set over ExecuteProceduresRequest and
is retried against the same server while it is alive; when the server
disappears the task is dropped and the restarted server reloads from
hbase:acl.

RefreshAclRemoteCallable applies the change set on the RS through
AclRefresher (serial queue + timestamp watermark).

Registrations: ServerOperationType.REFRESH_ACL + ServerQueue lock case,
EventType.M_RS_REFRESH_ACL, ExecutorType.RS_REFRESH_ACL and its RS thread
pool (hbase.regionserver.executor.refresh.acl.threads, default 1).
Minimal wiring: create AclRefresher next to AccessChecker in
HBaseRpcServicesBase and expose it via HBaseServerBase.
…e push pipeline

ACLManager assigns the monotonic timestamps of hbase:acl writes (seeded at
activation from the acl table content and the wall clock, so they never move
backwards across restarts or failovers), tracks the pending entry updates
(latest per entry) and pushes change sets to all registered region servers
through a single in-flight RefreshAclProcedure. Updates arriving while a push
runs merge into the next one; the local AuthManager cache is updated as soon
as the write lands in the table.

RefreshAclProcedure drains the pending queue as its first step (persisted in
the procedure store before dispatch, replayed at-least-once after a failover;
stale replays are dropped by the RS side timestamp watermark), fans out one
RefreshAclRemoteProcedure per online server and releases the single-flight
gate on completion (completionCleanup).

Wired into MasterServices/HMaster: constructed before the procedure executor
starts so recovered procedures can consult it.
…ests until loaded

Region server: after reportForDuty succeeds, load the full hbase:acl content
into the local cache (retrying with backoff; a missing acl table counts as
loaded with an empty cache) and only then open the gate in
RSRpcServices.checkOpen: non superuser requests get a retryable
PleaseHoldException until the cache is loaded, superusers pass so bootstrap
and operations keep working. Authorization disabled servers open immediately.

Master: ACLManager.initialize() runs at the end of active master
initialization (seed timestamps, load the local cache, push one full round),
and unregisters the write hook on shutdown.

Both: schedule AclReloadChore (periodic full reload, the fallback for lost
updates and direct table writes) when authorization is enabled.
The hbase:acl table is the authoritative store now; masters push changes to
the servers (ACLManager/RefreshAclProcedure) and servers load the table at
startup and periodically, so the postPut/postDelete mirroring (updateACL),
the acl region postOpen full flush (initialize) and the znode deletes on
table/namespace removal are gone.

preGrant/preRevoke now reject table level grants on hbase:meta and
hbase:acl: fixed rules govern access to those two tables and stored grants
would never take effect.
The watcher is obsolete: ACL updates reach the servers through the master
push pipeline and the startup/periodic loads. Remove the class, its test,
the lifecycle in HBaseRpcServicesBase and getZKPermissionWatcher from
MasterServices/RegionServerServices/HBaseServerBase and the test mocks.
The znode payload format is gone with ZKPermissionWatcher: drop
PermissionStorage.writePermissionsAsBytes/readUserPermission/readPermissions
and the old HbaseObjectWritable compatibility branch, the AuthManager
*FromWritable cache refreshers and the HBASE-22503 ZK data compatibility
branch (updateGlobalCache now swaps the cache atomically). The acl table
cell format (action code bytes) is unaffected.

Remove TestTablePermissions.testSerialization which round tripped the
mirror blob.
Add hbase.security.acl.reload.period.msec (periodic full ACL reload, default
5min) to hbase-default.xml and drop the obsolete zookeeper.znode.acl.parent
entry; setting the old key now logs a startup warning and is ignored.
…er cache updates

applyEntry stripped the '@' prefix when applying namespace permissions but
not when deleting the entry, so deletes never hit the cache. Delete paths
also skipped the mtime bump, which the test synchronization relies on. Drop
the now unused removeTable/removeNamespace helpers.
…alescing

TestAuthManager: hbase:meta READ open to all / other actions superuser only,
hbase:acl superuser only, global and namespace grants do not penetrate,
table == null keeps its global scope semantics, EntryUpdate apply/delete and
full snapshot swap.

TestAclRefresher: timestamp watermark drops stale updates and stale full
snapshots (e.g. replayed procedures after a master failover), deletes are
not resurrected, newer updates apply on top of a snapshot.

TestACLManager: monotonic timestamps, per entry coalescing of the pending
queue, single flight push submission.
submitPushProcedure() and flush() are the hooks the unit tests use to
observe push submission and to wait for queued updates.
- Run the RS startup ACL load on a background thread: blocking the run loop
  kept regionServerReport from being sent, and the master only assigns
  regions to servers that have reported once (HBASE-25032), so meta never
  came online and the load could not finish either.
- Do not hold calls without a request user in the checkOpen gate: those are
  in-process internal paths (e.g. postOpenDeployTasks) and holding them
  aborted the RS while opening hbase:meta. Only non superuser client
  requests are held until the cache is loaded.
- Unwrap UncheckedIOException in PermissionStorage.loadAllSnapshot so
  callers can recognize a missing hbase:acl table (fresh cluster) and treat
  it as empty content.
- Run ACLManager.initialize() after the master is marked initialized: the
  acl table scan needs the master registry, which serves only then.

TestTablePermissions now passes end to end.
RefreshAclRemoteCallable now waits until the change has been applied to the
cache before reporting completion, so a finished push procedure means the
change is visible on that server.

SecureTestUtil.updateACLs synchronizes on the master assigned timestamps
instead of cache modification times: a server that has applied timestamp T
reflects every write up to T, so waiting for this call's timestamp is
exact. The mtime counter cannot attribute a bump to a specific update and
was tripped by concurrent pushes (table create owner grants, previous test
cleanups), letting verifications run before their own grant was applied.

TestAccessController passes fully (84 tests).
…mirror

TestAccessController2: hbase:acl access is superuser only now, stored grants
of any scope must not penetrate it (testACLTableAccess); drop
testACLZNodeDeletion which asserted the removed ZooKeeper mirror.

TestRpcAccessChecks.testExecProcedure runs its permission check against a
user table: hbase:meta admin actions are superuser only under the fixed
rules, while the test verifies the RPC level admin check.
…TableManager

PermissionStorage is a stateless util, the static write hook it carried was
an awkward backdoor. Replace it with AclTableManager, the master side facade
over hbase:acl:

- grant/revoke/getUserPermissions/hasUserPermissions move out of
  MasterRpcServices into AclTableManager (the RPC methods keep the protocol
  plumbing and coprocessor hooks and delegate);
- the AccessController master hooks (table create/modify/truncate/delete,
  namespace delete) write through it too, so every write path announces its
  update: the entry state is applied to the local cache via AclRefresher and
  queued for broadcast via ACLManager;
- PermissionStorage goes back to a pure util: writes take the timestamp to
  stamp explicitly (the master assigned monotonic timestamp) and have no
  side effects.
AuthManager mixed two concerns: maintaining the in-memory permission
caches and making authorization decisions. Split them along the usage
line:

- AclSnapshot holds the global/namespace/table permission caches and
  the EntryUpdate delta type, and feeds them through applyUpdates and
  applyFullSnapshot;
- AclView is the narrow read-only view of the snapshot;
- AuthManager becomes pure decision logic over an injected AclView and
  never mutates anything.

Also drop the dead mtime bookkeeping and the unused Configuration of
AuthManager. PermissionStorage.AclSnapshot (the hbase:acl scan result)
is renamed to AclTableContent to free the name.
Merge AclTableManager into ACLManager: the master now has a single ACL
controller that owns the authoritative AclSnapshot of hbase:acl,
performs every permission update and query on the table, assigns the
write timestamps and drives the broadcast to the region servers through
RefreshAclProcedure. The refresh procedure only updates the region
servers; the master applies each change at write time.

Serialize the whole write path (timestamp assignment, table write, entry
read back, local apply, queueing for broadcast) under one lock. This
makes the local apply order match the timestamp order and fixes a lost
update window: previously two concurrent grants to different entries
could apply out of order and the refresher watermark would drop the
earlier one from the master's cache until the next periodic reload.
The region server now has one class maintaining its current ACL
snapshot: AclStore, merging what AclRefresher and AclReloadChore did.

- the snapshot is fed through exactly one place: the startup full load,
  the master pushes (RefreshAclRemoteCallable) and the periodic reload;
- applies are synchronous (the single-thread queue and the flush barrier
  are gone), so a completed refresh procedure means the change is
  already visible on the server;
- the timestamp watermark rules are unchanged: stale batches are
  dropped, a reload never moves the watermark backwards, and a reload
  serializes with applies so an update arriving during its scan is
  applied after the swap;
- the periodic reload chore becomes internal to AclStore and runs on the
  region server only; the master keeps its snapshot in step at write
  time and does not need one.

The store is created before registering with the master, so a push
dispatched right after registration cannot land before it exists.
@Apache9 Apache9 closed this Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant