Root-Level Policy Sprawl After a Contractor Release
Northwind Logistics runs one OCI tenancy in eu-frankfurt-1 with eleven compartments, including Shared-Services, Net, Prod-App, Prod-Data, Stage, Dev and four team compartments. During a release freeze last quarter a contractor added this statement at the tenancy root: Allow group AppDevs to manage all-resources in tenancy. Separately, so an external marketing agency could fetch website assets, someone added: Allow any-user to read object-family in tenancy. Last month the security team noticed 40,000 Object Storage GET requests from three addresses in a country where the company has no staff. The AppDevs group has 62 members, nine of whom have left the company. The CI/CD pipeline runs on four Compute instances in the Dev-CI subnet and authenticates with the personal API key of a senior engineer who is on parental leave. Leadership wants the root policy deleted within five days without stopping daily deployments, and wants to know whether the GET requests are an incident.
1. Explain why the any-user statement is the more serious of the two, and give replacement statements for both that preserve the business outcome.
Read the worked answer
any-user vs. a group. The AppDevs statement is reckless but bounded by group membership: your joiner/mover/leaver process operates on groups, so removing someone from AppDevs removes their access, and a quarterly access review can see the 62 names. The any-user statement bypasses that entirely. It grants every IAM user in the tenancy, including accounts created tomorrow, read access to every bucket in every compartment that exists now or later. Your leaver process is blind to it, which is why nine departed employees are still a live question.
Evaluation model. OCI policies are additive across the hierarchy: a statement at the tenancy root applies to all child compartments, and access is granted if any applicable statement allows it (deny statements, where used, take precedence). So the root statement silently widens every future compartment. That is also why you cannot fix this by creating tighter compartment policies - the root allow still unions in.
Replacements. For marketing, stop using an identity at all: issue a pre-authenticated request scoped to the bucket prefix with a short expiry, or, if they must have identity, a dedicated group with Allow group WebAssets to read objects in compartment Prod-App where target.bucket.name = 'marketing-assets'. For AppDevs, split into AppDev-Dev and AppDev-Prod groups, grant manage objects only in Dev, read objects in Prod, and drop the tenancy-root placement entirely.
2. Replace the senior engineer's API key on the four CI runner instances with instance principals. Give the dynamic group rule, the policies, and the residual human credentials you cannot eliminate.
Read the worked answer
Matching rule. Put the runners in their own compartment and match on it, plus a tag so the rule survives a compartment rebuild:
ALL {instance.compartment.id = 'ocid1.compartment.oc1..devci', tag.ci.role.value = 'runner'}
The compartment clause matters for reviewability; the tag clause matters because compartment moves are common. Both mean the rule is only as tight as the ability to launch an instance in Dev-CI - so restrict manage instances in that compartment to the platform group, otherwise anyone who can create a VM inherits the runner's rights.
Policies. Grant only what the pipeline does, by resource and verb:
Allow dynamic-group ci-runners to read secret-bundles in compartment Shared-Services where target.secret.id = 'ocid1.vaultsecret...'Allow dynamic-group ci-runners to use objects in compartment ArtifactsAllow dynamic-group ci-runners to read repos in compartment Artifacts
Instance principals are issued short-lived certificates by the instance metadata service; there is no secret on disk to leak, and the identity cannot sign in to the Console.
Residual humans. Someone must still create the dynamic group, the policy and the compartment, and someone must be able to change IAM in an emergency. Keep that on a federated admin identity with MFA, not on a shared break-glass key, and never let the CI identity hold manage keys or manage policies.
3. You have five days. Give the order of operations to remove the root AppDevs statement without breaking deployments, and the evidence you collect at each step.
Read the worked answer
Day 1 - inventory, do not change anything. Pull 90 days of Audit records for every AppDevs member and group by resource type and compartment. You are looking for the handful of API families that actually matter (typically objects, instances, functions, secrets) versus the ones granted but never used. Interview the two loudest developers. Confirm whether any consumer relies on the any-user statement, or whether marketing moved to pre-authenticated requests already.
Day 2 - build the replacement. Create AppDev-Dev and AppDev-Prod, add the narrow compartment-scoped statements, and move users. Do not delete anything at the same time: if membership and policy change together and a build fails, you cannot tell which caused it.
Day 3 - shadow validate. Two pilot developers work only from the new groups. Filter Audit for 4xx responses from AppDev principals; every 403 is a missing statement. OCI gives you no policy simulator, so observed denials are your test.
Day 4 - remove any-user first, then the root statement. any-user first because it is the exposure; the AppDevs root statement second, in a low-traffic window, with the replacement already proven.
Day 5 - verify and close. Deactivate the nine departed users and audit for API keys they still own; a group removal does not stop an any-user grant, and it does not stop an orphaned key. Then confirm no 4xx spike and resolve the Cloud Guard findings.