Minimize the number of OCI IAM Policy Statements required to implement your OCI Authorization Model - Part 2

May 29, 2024 | 8 minute read
Gordon Trevorrow
Principal Cloud Architect
Text Size 100%:

Consolidation

Introduction

In Part 1, we discussed several techniques for optimizing the number of required policy statements in an OCI account. In Part 2, we will continue to consider a few more techniques.

Combine policy statements using direct permission references

In the first part of this discussion, we covered how verb and resource-type combinations in policy statements map to a set of permissions associated with those verb-resource pairs.

Let's consider the policies that are required to launch a new compute instance in Compartment A. The instance will be attached to a subnet in Compartment A and assigned a NetworkSecurityGroup defined in Compartment A:

Allow group X to use subnets in Compartment A

 

will grant the following permissions to the members of group X within the scope of Compartment A and subcompartments :

- SUBNET_READ

- SUBNET_ATTACH

- SUBNET_DETACH

 

Allow group X to manage instances in Compartment A

 

will grant the following permissions to the members of group X:

- INSTANCE_INSPECT

- INSTANCE_READ

- INSTANCE_UPDATE

- INSTANCE_CREATE_IMAGE

- INSTANCE_POWER_ACTIONS

- INSTANCE_ATTACH_VOLUME

- INSTANCE_DETACH_VOLUME

- INSTANCE_CREATE

- INSTANCE_DELETE

- INSTANCE_ATTACH_SECONDARY_VNIC

- INSTANCE_DETACH_SECONDARY_VNIC

- INSTANCE_MOVE

 

Allow group X to read app-catalog-listing in Compartment A

 

- APP_CATALOG_LISTING_INSPECT

- APP_CATALOG_LISTING_READ

 

Allow group X to read instance-images in Compartment A

 

- INSTANCE_IMAGE_INSPECT

- INSTANCE_IMAGE_READ

 

Allow group X to use vnics in Compartment A

 

- VNIC_READ

- VNIC_ATTACH

- VNIC_DETACH

- VNIC_CREATE

- VNIC_DELETE

- VNIC_UPDATE

- VNIC_ASSOCIATE_NETWORK_SECURITY_GROUP

- VNIC_DISASSOCIATE_NETWORK_SECURITY_GROUP

 

Allow group X to use network-security-groups in Compartment A

 

- NETWORK_SECURITY_GROUP_INSPECT

- NETWORK_SECURITY_GROUP_READ

- NETWORK_SECURITY_GROUP_LIST_SECURITY_RULES

- NETWORK_SECURITY_GROUP_LIST_MEMBERS

- NETWORK_SECURITY_GROUP_UPDATE_MEMBERS

- NETWORK_SECURITY_GROUP_UPDATE

- NETWORK_SECURITY_GROUP_CREATE

- NETWORK_SECURITY_GROUP_DELETE

- NETWORK_SECURITY_GROUP_MOVE

- NETWORK_SECURITY_GROUP_UPDATE_SECURITY_RULES

If, as in the example above, the same group is granted multiple permissions to two or more resource types within the same compartment, using separate policy statements, you can merge these statements into a single policy statement. To do this, explicitly list the combined derived permissions in a replacement policy statement. Use the format of an OCI IAM policy statement, where the verb and resource tuples are replaced with the list of combined permissions:

 

Allow group X { SUBNET_READ, SUBNET_ATTACH, SUBNET_DETACH, INSTANCE_INSPECT, INSTANCE_READ, INSTANCE_UPDATE, INSTANCE_CREATE_IMAGE, INSTANCE_POWER_ACTIONS, INSTANCE_ATTACH_VOLUME, INSTANCE_DETACH_VOLUME , VNIC_READ, VNIC_ATTACH, VNIC_DETACH ,VNIC_CREATE, VNIC_DELETE, VNIC_UPDATE, VNIC_ASSOCIATE_NETWORK_SECURITY_GROUP, VNIC_DISASSOCIATE_NETWORK_SECURITY_GROUP,NETWORK_SECURITY_GROUP_INSPECT, NETWORK_SECURITY_GROUP_READ, NETWORK_SECURITY_GROUP_LIST_SECURITY_RULES, NETWORK_SECURITY_GROUP_LIST_MEMBERS, NETWORK_SECURITY_GROUP_UPDATE_MEMBERS, NETWORK_SECURITY_GROUP_UPDATE, NETWORK_SECURITY_GROUP_CREATE, NETWORK_SECURITY_GROUP_DELETE, NETWORK_SECURITY_GROUP_MOVE, NETWORK_SECURITY_GROUP_UPDATE_SECURITY_RULES } in Compartment A

 

In this example, we have combined six policy statements into one statement.

 

Note :
An added benefit of explicitly listing permissions in a policy statement is that it makes it easier to understand the permissions granted by the statement, as long as the permission list isn't too long. Also, we can better guarantee that our policies adhere to the principle of least privilege. For example, If our intent is to only grant Group X the permissions needed to launch a new compute instance attached to a subnet in Compartment A with an assigned Network Security Group then the derived list of permissions we arrived at above is more than what is needed. We can shorten the statement by only listing the permissions that will allow our use case:


Allow group X { SUBNET_ATTACH, INSTANCE_CREATE, VNIC_READ, VNIC_ATTACH ,VNIC_CREATE,VNIC_ASSOCIATE_NETWORK_SECURITY_GROUP,NETWORK_SECURITY_GROUP_UPDATE_MEMBERS, APP_CATALOG_LISTING_INSPECT, APP_CATALOG_LISTING_READ, INSTANCE_IMAGE_READ } in Compartment A

Multiple Groups

This optimization can be combined with the policy statement optimization we discussed in Part 1 of this series if there are multiple groups that require the permissions listed in the policy statements:

 

Allow group X to use subnets in Compartment A
Allow group Y to use subnets in Compartment A
Allow group X to use instances in Compartment A
Allow group y to use instances in Compartment A

 

Can become:

 

Allow group X, Y to use subnets in Compartment A 
Allow group X,Y to use instances in Compartment A

 

That in turn becomes:

 

Allow group X,Y { SUBNET_READ, SUBNET_ATTACH, SUBNET_DETACH, INSTANCE_INSPECT, INSTANCE_READ, INSTANCE_UPDATE, INSTANCE_CREATE_IMAGE, INSTANCE_POWER_ACTIONS, INSTANCE_ATTACH_VOLUME, INSTANCE_DETACH_VOLUME } in Compartment A

Policy Conditions

If we want to optimize policy statements with conditions using this method, we must evaluate if the conditions can be consolidated into a single statement. Typically, if the conditions pertain to non-conflicting permissions and operations, we can successfully combine the policies. For example, let's look at these two policy statements::

 

Allow group X to use instances in Compartment A where request.permission!=INSTANCE_POWER_ACTION

 

That translates to the following permissions:

- INSTANCE_INSPECT

- INSTANCE_READ

- INSTANCE_UPDATE

- INSTANCE_CREATE_IMAGE

- INSTANCE_ATTACH_VOLUME

- INSTANCE_DETACH_VOLUME

and 

 

Allow group X to manage subnets in Compartment A where request.operation!=ChangeSubnetCompartment

 

That translates to the following permissions:

- SUBNET_READ

- SUBNET_ATTACH

- SUBNET_DETACH

- SUBNET_CREATE

- SUBNET_UPDATE

- SUBNET_DELETE

- SUBNET_MOVE

When the conditions are merged into the same policy statement, they do not conflict. Therefore, we can combine the two statements into one:

 

Allow group X { SUBNET_READ, SUBNET_ATTACH, SUBNET_DETACH, SUBNET_CREATE, SUBNET_UPDATE, SUBNET_DELETE, SUBNET_MOVE, INSTANCE_INSPECT, INSTANCE_READ, INSTANCE_UPDATE, INSTANCE_CREATE_IMAGE, INSTANCE_ATTACH_VOLUME, INSTANCE_DETACH_VOLUME } where request.operation!=ChangeSubnetCompartment

 

Notice that `INSTANCE_POWER_ACTION` is not included in the list of permissions being granted in the optimized policy statement. This is because one of the original policies has a condition that excludes this particular permission:

 

request.permission!=INSTANCE_POWER_ACTION.

Incompatible Conditions

If we have incompatible conditions as in the following example:

 

Allow group X to use instances in Compartment A where request.permission!=INSTANCE_POWER_ACTION

 

and

 

Allow group X to manage subnets in Compartment A where request.user.mfaTotpVerified='true'

 

We cannot combine the two policy statements since the mfa requirement does not apply to both original statements.

Another example of incompatible conditions occurs when one condition references a resource-specific variable. In these cases, the conditions cannot be combined if the permissions listed in the policy span more than one resource type.

This is because when a condition references a resource-specific variable that is not defined for the resource being accessed, the condition will evaluate to false. In our mixed subnets and instances policy example, this is not a consideration since those resource types both belong to the core OCI resources that all share the same resource variables. But consider the following example :

 

Allow group X to manage repos in compartment A

Allow group X to use clusters in compartment A where target.cluster.id= ocid..

 

if we try and combine the policies :

 

Allow group X {REPOSITORY_INSPECT,REPOSITORY_READ,REPOSITORY_CREATE,REPOSITORY_DELETE,REPOSITORY_UPDATE,REPOSITORY_MANAGE,CLUSTER_INSPECT, CLUSTER_READ, CLUSTER_USE} where target.cluster.id=ocid..

 

The condition will evaluate to false if the resource that the action is being performed on is "repos." This means that none of the desired repository-related permissions will be allowed by the combined policy statement

Pattern-based optimization

The OCI IAM policy language supports the use of "patterns" in policy conditions for string variable value comparisons. For example, a condition such as target.compartment.name=/ProjectX*/ will match all compartments whose names start with "ProjectX". /*ProjectX/ in turn will match all compartments with names that end with "ProjectX". And finally, /*ProjectX*/ will match compartment names that contain "ProjectX" in the name.

This feature allows us to use naming conventions to increase the reach of individual OCI IAM policy statements.

For example, consider the scenario where

 

Allow group ProjectXAdmin to manage all-resources in compartment ProjectXProduction
Allow group ProjectXAdmin to manage all-resources in compartment ProjectXDev

 

and

 

Allow group ProjectYAdmin to manage all-resources in compartment ProjectYProduction
Allow group ProjectYAdmin to manage all-resources in compartment ProjectYDev

 

Can be combined to:

 

Allow group ProjectXAdmin to manage all-resources in tenancy where target.compartment.name=/ProjectX*/
Allow group ProjectYAdmin to manage all-resources in tenancy where target.compartment.name=/ProjectY*/

 

When using a naming convention, as in the example above, it's important to remember that the permissions to create and name compartments effectively become permissions to set access rights. In other words, the act of naming a compartment determines the set of policies that will be in effect within its scope.

Readers will notice that comparable optimization is achievable by grouping all project-related environment compartments under a shared parent compartment. Policies granting the specific project admins access to their associated project sub-compartments can be attached to the shared parent.

Figure 1
Figure 1

An advantage of the presented naming convention approach is that it will work regardless of the compartment hierarchy underneath the compartment where pattern-based policies are attached. For example, Allow group ProjectXAdmin to manage all-resources in tenancy where target.compartment.name=/ProjectX*/, will work as expected in all the sample compartment hierarchies shown in Figure 2.

Figure-8
Figure 2

A Hybrid Optimization

We can combine aspects of both optimization strategies discussed in this article as follows:

You may notice that Oracle uses a naming convention when naming the OCI IAM permissions (as well as operations). The convention for permissions is often resource-type_verb. We can use this knowledge along with patterns to come up with the following version of the repos and cluster resource-type optimization we presented above:

 

Allow group X {REPOSITORY_INSPECT,REPOSITORY_READ,REPOSITORY_CREATE,REPOSITORY_DELETE,REPOSITORY_UPDATE,REPOSITORY_MANAGE,CLUSTER_INSPECT, CLUSTER_READ, CLUSTER_USE} in compartment A

 

Can be expressed as:

 

Allow group X to manage all-resources in Compartment A where any {request.permission=/REPOSITORY_*/, request.permission='CLUSTER_INSPECT',request.permission='CLUSTER_READ',request.permission='CLUSTER_USE'} 

 

Note:

Caution is necessary with this approach, as different resource types can have similiar permissions naming conventions. For instance, the pattern /CLUSTER_*/ will match all permissions for the cluster-family resource types :

  • clusters
  • cluster-node-pools
  • cluster-pod-shapes
  • cluster-virtualnode-pools
  • cluster-work-requests
  • cluster-workload-mappings
 Additional conditions would be required to exclude the unwanted permissions in this case, for example `all{request.permission=/CLUSTER_*/, request.permission!=/CLUSTER_NODE_POOL_*/,...}`.

 

Conclusion

As mentioned in the introduction of the first post, some of the optimizations presented here are temporary measures for accounts approaching their policy statement limits. A more permanent solution is often to adopt an OCI tag-based approach and/or split the account into multiple accounts. For instance, you can use the OCI Organizations feature to separate the production environment into a distinct OCI account from non-production environments.

Resources

Using Tags to Manage Access

OCI IAM Policy Reference

Gordon Trevorrow

Principal Cloud Architect


Previous Post

OCI IPv6 Unique Local IPv6 Unicast Addresses usage and considerations

Andrei Stoian | 4 min read

Next Post


Extending Oracle Fusion SaaS with OCI - Security

Mani Krishnan | 16 min read