Siyao Meng created HDDS-16855:
---------------------------------

             Summary: Tenant create that fails in Ranger after creating the 
admin role leaves the role behind, so the tenant can never be created again
                 Key: HDDS-16855
                 URL: https://issues.apache.org/jira/browse/HDDS-16855
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: MC-6-undo-ranger-changes-of-failed-tenant-create.patch, 
TestBugMC6OrphanAdminRole.java

h3. Mechanism
{{OMMultiTenantManagerImpl.AuthorizerOp.createTenant}} makes four Ranger calls 
in order: create the admin role, create the user role (with the admin role as 
delegated admin), create the VolumeAccess policy, create the BucketAccess 
policy. When a later call fails, the catch block only wraps the exception into 
{{TENANT_AUTHORIZER_ERROR}}. Nothing created so far is removed, although the 
Javadoc of the method says "In case of failure: Undo all Ranger State", and 
{{OMTenantCreateRequest.preExecute}} relies on the Ranger background sync to 
"clean up any leftover policies and roles".

The sync cannot do that for roles without policies. 
{{OMRangerBGSyncService.loadAllPoliciesAndRoleNamesFromRanger}} learns about 
Ranger roles only from the role ACLs of the policies labeled {{OzoneTenant}}. A 
role that no such policy refers to is never seen, never deleted, and the sync 
still records the Ranger service version as in sync. {{createRole}} fails when 
a role with the same name exists (HDDS-15982 kept creation fail fast and made 
only deletes idempotent), so every later create of that tenant stops at the 
first {{createRole}}.

Related to HDDS-16188 (open, assigned), which covers recovery of tenant 
creation in general. This issue covers only undoing the Ranger changes of a 
failed create and does not resolve HDDS-16188: a create that timed out after 
Ranger created the entity, and reconciliation with entities that already exist, 
stay open there.

h3. Trigger
# {{ozone tenant create tenant1}}. Ranger creates {{tenant1-AdminRole}}, then 
the create of {{tenant1-UserRole}} fails once (HTTP 5xx, timeout, Ranger 
restart).
# The request fails with {{TENANT_AUTHORIZER_ERROR}} in {{preExecute}}. Nothing 
reaches Ratis, OM has no tenant, Ranger keeps the admin role.
# Any number of sync runs leave the role in place. {{ozone tenant delete 
tenant1}} fails with {{TENANT_NOT_FOUND}} (argued from 
{{OMTenantDeleteRequest.preExecute}}, not run).
# Every retry of {{ozone tenant create tenant1}} fails with 
{{TENANT_AUTHORIZER_ERROR}}, cause "Role already exists".

A failure of the VolumeAccess policy create leaves both roles behind in the 
same way. A failure of the BucketAccess policy create leaves the VolumeAccess 
policy, which refers to both roles, so the sync does clean that case up.

h3. Impact
The tenant id cannot be created again until an operator deletes the leftover 
roles in Ranger by hand. No data is lost, and the leftover roles carry no users 
and are not referenced by any policy, so they grant nothing. Creating a tenant 
requires cluster admin privilege.

h3. Reproduction
PASS, deterministic, unmodified source. [^TestBugMC6OrphanAdminRole.java] 
({{hadoop-ozone/ozone-manager}}) runs the real 
{{OMTenantCreateRequest.preExecute}}, the real {{AuthorizerOp}}, the real 
{{OMRangerBGSyncService.triggerRangerSyncOnce}} and a real OM DB. Ranger is the 
in-memory controller that OM uses with {{ozone.om.tenant.dev.skip.ranger}}, 
wrapped in a Mockito spy that fails the first create of the user role. Output 
on the unmodified source:
{noformat}
create #1: TENANT_AUTHORIZER_ERROR; admin role in Ranger: true, user role in 
Ranger: false, tenant in OM DB: false
sync run 1: completed true, admin role in Ranger: true
sync run 2: completed true, admin role in Ranger: true
retry 1: TENANT_AUTHORIZER_ERROR java.io.IOException: Role already exists.
retry 2: TENANT_AUTHORIZER_ERROR java.io.IOException: Role already exists.
{noformat}
A passing test means the defect is present. Not run against a real Ranger.

h3. Suggested fix
Undo in {{createTenant}} what this call created, as its Javadoc says. Roles 
have to be deleted in reverse order of creation: Ranger refuses to delete a 
role that a policy, another role or a security zone refers to 
({{RoleDBStore.ensureRoleDeleteAllowed}}), and the user role names the admin 
role as delegated admin. Only objects whose create returned successfully are 
removed, so a role with the same name that existed before (another cluster 
sharing the Ranger, a manual role, or a concurrent duplicate create) is never 
deleted. Two alternatives were rejected: letting the sync find tenant roles by 
name or description is unsafe because Ranger roles are not scoped to a service, 
so the sync of one cluster could delete the tenant roles of another cluster 
using the same Ranger; adopting an existing role in {{createTenant}} needs the 
state aware validation that HDDS-16188 is about.

The undo narrows the problem but does not remove every case: when the undo 
deletes fail too, or when a create timed out on the client but succeeded in 
Ranger, roles can still be left behind. For example, if the user role create 
times out but succeeds, Ranger refuses the undo's delete of the admin role, so 
both roles stay, as without the patch, and the refused delete is logged at WARN.

h3. Patch
[^MC-6-undo-ranger-changes-of-failed-tenant-create.patch], against 
a6b7bdb937109ba2893688b41a89470bc95c88cf. It also applies to master at 
b0aa6475b78.

On failure, {{createTenant}} now deletes the policies and roles it has created, 
best effort, policies first and then the user role before the admin role, 
logging each delete that fails. The deletes are the idempotent ones from 
HDDS-15982. The success path is unchanged. The comment in 
{{OMTenantCreateRequest.preExecute}} that sends all leftovers to the sync is 
updated accordingly.

Covered by two tests in the existing {{TestOMMultiTenantManagerImpl}}. 
{{testCreateTenantUndoesRangerChangesOnFailure}} fails the user role create, 
then the BucketAccess policy create, checks that nothing is left in Ranger, and 
that the retry succeeds. Without the change it fails with "tenant2-AdminRole 
left in Ranger". {{testCreateTenantKeepsExistingRangerRoleOnFailure}} starts 
with a user role that existed before the call: the create fails at the user 
role, the undo deletes the admin role it created and keeps the existing user 
role. Without the change it also fails, because the admin role is left. With 
the change the multi-tenancy unit suites in {{hadoop-ozone/ozone-manager}} (35 
tests) and {{TestOzoneTenantShell}} and {{TestMultiTenantVolume}} on a mini 
cluster (11 tests) pass, checkstyle is clean. With the patch the reproduction 
fails at its first check, because the admin role is gone after the failed 
create.

Found by TLA+ model checking and code review of the OM multi-tenancy and Ranger 
sync paths under HDDS-15926, on commit 
a6b7bdb937109ba2893688b41a89470bc95c88cf. Checked against HDDS issues and 
apache/ozone pull requests for duplicates before filing. The attached patch is 
a proposal for review. Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to