[ 
https://issues.apache.org/jira/browse/HDDS-16855?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Siyao Meng updated HDDS-16855:
------------------------------
    Attachment: MC-6-undo-ranger-changes-of-failed-tenant-create.patch

> 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
>            Priority: Major
>         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