Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Use OCI Functions and OCI Queue to Authorize User Capabilities without Exposing Admin Privilege to Approvers
Introduction
Updating user capabilities is a key need for many enterprises especially when the permission authorized to users are sensitive like the console capability or API key. However, as far as we have found in Oracle Cloud Infrastructure (OCI) documentation, only users in the administrators group are able to authorize user capability even when there are Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) policies that explicitly allow it. For more information, see Managing Users.
This tutorial is to demonstrate a solution for the scenario where users are not in the administrators group of an OCI IAM domain but still need to have the capability to authorize the user capabilities by utilizing the newly released feature of OCI Connector Hub to integrate OCI Queue and OCI Functions.
Description of the illustration SolutionArchitecture.png
Objectives
-
Authorize user capabilities by leveraging the integration feature of OCI Connector Hub between OCI Queue and OCI Functions.
-
Learn a cloud native approach to develop solutions to decouple the privileges to approve and to execute the user requests as a fine grained IAM implementation on OCI.
-
Learn how to manage users using python API in OCI Functions.
-
Learn the usage of dynamic groups for the use of authorizing resource principles on OCI.
Task 1: Create the Messaging Channel from OCI Queue to OCI Functions
The essential part of the solution is to decouple the permission required by changing user capabilities from the permission that the approver needs to approve a request.
-
Create the OCI Queue for incoming requests. For more information, see Creating a Queue.
-
Create the OCI Function used to authorize users. For more information, see Creating Functions.
-
Configure OCI Queue and OCI Functions integration through OCI Connector Hub. Make sure the source is specified with the OCI Queue created, and the target should be OCI Functions. We will leave the optional task empty in this tutorial. For more information, see Announcing the availability of OCI Queue as a source in OCI Connector Hub.
Task 2: Configure OCI IAM Policies and Dynamic Group
After we have split the roles of the approver into the OCI Queue to take in the requests and the OCI Functions to run the request, we need to configure strict OCI IAM policies to make sure the permission is not abused. Since we built this solution to support a customer who insists to use the root compartment for the implementation, we will demo all configs from root compartment for the OCI IAM part.
-
Allow only the approver to push queue messages into the target OCI Queue.
Allow group 'testApprover' to use queues in tenancy
By adding this policy, we allow the users in the testApprover group to use the queue to receive the console access requests. We are able to put more limitations on the group by only specifying the permission on queue-push resource type as shown in below example.
use queue-push in compartment <compartment> where target.queue.id = '<queue_ocid>'
-
Configure the dynamic group to include OCI Functions by specifying the following matching rule.
ALL{resource.type='fnfunc',resource.id='ocid.fnfunc.oc1.....'}
This dynamic group will then be authorized with an Oracle Identity Cloud Service role to manage the users in the domain.
-
Configure dynamic group Oracle Identity Cloud Service role.
-
Configure the OCI Functions to be only able to be invoked by the OCI Queue. The following example only limit the source of the invocation to queues, we may actually make it more strict by using tags on the resources. For more information, see Using Tags to Manage Access
Allow service faas to use functions-family in tenancy where request.principal.type='queues'
Task 3: Compose the OCI Functions Code
We need to compose a piece of Python code for the OCI Functions to actually run the request taken from the OCI Queue.
-
The OCI Queue message will be passed into the function in data object.
def handler(ctx, data: bytes = None) -> response.Response: try: # Parse the message from the OCI Queue if data: message = json.loads(data.getvalue().decode('utf-8')) else: message = "no useremail received"
-
Authenticate by using resource principal.
Note: The following line of code might not be working in an IDE.
identity = IdentityClient({}, signer=oci.auth.signers.get_resource_principals_signer(), region=region)
-
Convert the user email passed into user Oracle Cloud Identifier (OCID).
def get_user_ocid_by_email(identity_client,email,tenancy_id): # List all users in the tenancy users = oci.pagination.list_call_get_all_results(identity_client.list_users,tenancy_id).data # Find the user with the matching email address for user in users: if user.email.lower() == email.lower(): return user.id return None
-
Update the user capability with resource principal using the IdentityClient function.
# Get the user by email address user_ocid = get_user_ocid_by_email(identity,user_email,tenancy) # Update user capabilities (example: enable API keys) update_details = oci.identity.models.UpdateUserCapabilitiesDetails( can_use_api_keys=True, can_use_auth_tokens=True, can_use_console_password=True, can_use_customer_secret_keys=True, can_use_db_credentials=True, can_use_o_auth2_client_credentials=True, can_use_smtp_credentials=True ) # Update the user identity.update_user_capabilities(user_ocid, update_details)
-
Deploy the function code into the OCI function we just created. For detailed steps of deploying an OCI function, please see Configuration Notes for OCI Functions
Task 4: Test the Changes
Test the changes by sending a message to the target queue. We may use the OCI Console or use any SDK including OCI Cloud Shell.
Description of the illustration send-message.png
Description of the illustration examine.png
Troubleshooting
Configuring OCI Function to use OCIR
If you are using OCIR as the repo of OCI function, a couple of issues might be blocking you and it is not so easy to find a resolution as for now no clear guide is proved specifically to avoid such issues.
-
Configuring specific compartment for both the OCIR repo and for OCI function as the function context. By default, the context of OCIR repo is pointing to the root compartment even if you follow the instruction page on the console to run below cloud shell commands.
fn update context oracle.compartment-id ocid1.tenancy.oc1.....
Note: This part is quite misleading as with
oracle.compartment-id
, it makes people think this property is for both the function and the OCIR however, we do have a seperate property for OCIR which isimage-compartment-id
. So if you are using a named compartment to run your function and to store your image, make sure both of the compartment-id are explicitly set with following OCI Cloud Shell commands. Appropriate OCI IAM policy must also be configured to allow the actions between the OCIR and the function too if they are from different named compartments.fn update context oracle.image-compartment-id <compartment-ocid> fn update context oracle.compartment-id <compartment-ocid>
In case this is not done correctly, you will run into wired 403 errors and since you might not be even aware of the
image-compartment-id
, it is hard to find a clue. -
Configuring the function to use OCIR while the OCI Cloud Shell is launched from a network environment that is accessible to docker.io as we must apply docker APIs to process the image and typically a OCI service network will not allow you to get access. We suggest you to use the public network for container deployment if you are using the OCI Cloud Shell.
For more information, see Functions: Get Started using Cloud Shell.
-
Another potential blocker here is when you are trying to pass the queue message to the function inputs. Multiple reference can be found on other articles about how to pass in the variable but it is actually as simple as to input the json and received in data object in Python code.
Note: Avoid using Javascript type of json with using single quotes or no quotes as for now only the strict syntax could be recognized by the OCI Queue.
{"name":"John"}
Related Links
Acknowledgments
- Author - Henry Deng (Principal Solution Engineer)
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Use OCI Functions and OCI Queue to Authorize User Capabilities without Exposing Admin Privilege to Approvers
G16798-03
October 2024