Oracle Cloud Vault KMS – Replicating AWS Secrets

I recently had to do some tinkering in Oracle Cloud Infrastructure (OCI) with Compute Instances and the Key and Secrets Management in Oracle Vault. I wanted to be able to replicate AWS EC2 instance capability to access secrets based upon the instance’s permissions without requiring any usernames, passwords, tokens, etc… to be stored on the instance itself. In AWS, I already know how to do this via IAM Roles and Policies. Before we begin, I’m assuming that you already have created a Vault and Secret that will be accessed. You can reference the Oracle Cloud documentation on creating secrets and vaults here. I also assume that you have a Compute instance created that is running some type of Linux. Mine is running OL8.

The OCI documentation does a great job at pointing you to interacting with secrets via CLI, REST API, and console. I struggled to find clear instructions on allowing a compute instance to access secrets until I stumbled upon Dynamic Groups. A Dynamic Group can be referenced in policies and OCI resources can be added as members. This is what I needed!

Creating a Dynamic Group

This part was pretty easy. Within the Identity section of the console, you can find Dynamic Groups.

I created one that was called ComputeGroup. When creating a Dynamic Group, you need to create rules that determine what OCI resources will be members of the group. OCI refers to these rules as Matching Rules. The documentation states that the following are supported variables for rules:

  • instance.compartment.id – the OCID of the compartment where the instance resides
  • instance.id – the OCID of the instance
  • tag.<tagnamespace>.<tagkey>.value – the tag namespace and tag key. For example, tag.department.operations.value.
  • tag.<tagnamespace>.<tagkey>.value='<tagvalue>' – the tag namespace, tag key, and tag value. For example, tag.department.operations.value='45'

I decided to make my rule flexible by using a tag like the below:

Any {tag.permissions.getSecrets.value = 'true'}

You can find some additional details regarding tags and namespaces in the OCI documentation here.

Updating Secret Policies

Now that I had a Dynamic Group created, I needed to allow that group in the secrets policy. Policies are also located under Identity.

I already had an existing policy called AccessSecrets that I was using to allow certain users to access secrets via API calls. I used the Let users read, update, and rotate all secrets example to create my first statement in the policy.

Allow group KeyReaders to use secret-family in tenancy	

This statement allows members of my KeyReaders group to be able to access secrets. I needed a new statement that allowed my Dynamic Group access to the secrets as well. I added the below statement to my policy.

Allow dynamic-group ComputeGroup to use secret-family in tenancy	

Adding a Tag to an Instance

The next step is to apply the tag to whatever instance I’d like to allow access to my secret. You can do this by opening the instance details and clicking the Add Tags item from the More Actions menu. Select the tag and value specified in the policy.

Once you have your tag, click the Add Tags button and we’re ready to move on.

Example Code

In order to make life easier, I created my example code based upon the example code located here in OCI’s Python SDK examples. This example makes use of a config file and would expect an API token in order to connect. We’re not doing that so I made the below changes to their example:

$ diff secretclient_example.py my_secretclient_example.py 
32c32
< if len(sys.argv) != 3:
---
> if len(sys.argv) != 2:
37d36
< oci_profile = sys.argv[2]
39,41c38
< config = config = oci.config.from_file(
<     "~/.oci/config",
<     oci_profile)
---
> signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
43c40
< secret_client = oci.secrets.SecretsClient(config)
---
> secret_client = oci.secrets.SecretsClient(config={}, signer=signer)

This script expects the OCID of the secret to be provided as an argument and then run the command to get the secret’s details:

$ python my_secretclient_example.py ocid1.vaultsecret...a
Reading vaule of secret_id ocid1.vaultsecret...a.
Decoded content of the secret is: {
  "username" : "admin",
  "password" : "Password1!"
}.

Additional Reference

After going through all of this and having difficulty navigating the documentation. I stumbled upon this Oracle document :facepalm: that also provided just about all of the details that I needed. Like the title of the blog states, these are just My Battles With Technology.