← Oracle ACE articles

ACE-U field notes

Using OCI Instance Principals without leaking credentials

How I let workloads call OCI APIs with no API keys on disk, and keep the blast radius small.

Shubham Anandani··3 min read

Problem

Every OCI SDK sample starts the same way: drop an API key and a config file on the host. That is fine on a laptop. On a fleet of compute instances it becomes a quiet liability, because those keys never expire on their own, they get copied into images, and nobody remembers which ones are still in use.

Instance principals remove that whole class of problem. The instance proves who it is with a certificate that OCI issues and rotates for it, and IAM decides what it may do through a dynamic group and a policy. There is nothing to ship, nothing to rotate by hand, and nothing to leak from a config file.

Options and trade-offs

  • API keys in a config file: the simplest option, but it leaves a long-lived secret on disk and puts rotation on you.
  • Session or delegation tokens: shorter lived, but you still have to mint, distribute and refresh them from somewhere.
  • Instance principals with a dynamic group: no client-side secret at all. The cost is that you have to think carefully about which instances land in the group.

My recommendation

For code running on OCI compute, I make instance principals the default and write the dynamic group rule before anything else. The rule should name a compartment and a tag, and the policy should name a compartment, a resource family and, where it is practical, a single bucket. Tenancy-wide verbs are the exception and need a reason.

Worked example

The Java below runs only on an instance that belongs to the dynamic group. It reads from one bucket and has no credentials in code, environment variables or files. The namespace and bucket name come from configuration.

import com.oracle.bmc.auth.InstancePrincipalsAuthenticationDetailsProvider;
import com.oracle.bmc.objectstorage.ObjectStorageClient;
import com.oracle.bmc.objectstorage.requests.ListObjectsRequest;
 
public class ListConfigObjects {
    public static void main(String[] args) throws Exception {
        var provider = InstancePrincipalsAuthenticationDetailsProvider.builder().build();
        try (var os = ObjectStorageClient.builder().build(provider)) {
            var req = ListObjectsRequest.builder()
                    .namespaceName(System.getenv("OCI_NAMESPACE"))
                    .bucketName(System.getenv("CONFIG_BUCKET"))
                    .build();
            os.listObjects(req).getListObjects().getObjects()
              .forEach(o -> System.out.println(o.getName()));
        }
    }
}

The matching rule and policy for the dynamic group. The tag makes membership an explicit decision rather than a side effect of where an instance was created.

ALL {instance.compartment.id = '<apps_compartment_ocid>',
     tag.department.runtime.value = 'payments'}
 
Allow dynamic-group app-runtimes to read objects in compartment apps
  where target.bucket.name = 'app-config'
Allow dynamic-group app-runtimes to use log-content in compartment apps

If your dynamic group lives in a non-default identity domain, prefix the group name with the domain, for example dynamic-group MyDomain/app-runtimes.

Checklist

  1. Write the dynamic group rule with both a compartment and a tag.
  2. Grant the narrowest verb and resource family, and add a bucket or resource condition where you can.
  3. Remove every API key and config file from the host once the SDK uses the instance principal provider.
  4. Review the audit log and confirm the principal on each call is the instance you expected.
  5. Control who can apply the tag, because tagging an instance is what adds it to the group.
  6. Write down the rollback: edit the rule, then adjust the policy.

Where this doesn't apply

Serverless functions and other managed resources have their own identity, so use resource principals there. Anything running outside OCI has no instance principal at all and needs a different authentication path.

One honest limitation

Anyone who can get a shell on the instance inherits its permissions. Instance principals take away the secret, not the access, so SSH access and the policy have to be designed together.

Sources

  • OCI Documentation: Calling Services from an Instance
  • OCI Documentation: Managing Dynamic Groups
  • OCI Documentation: OCI SDK for Java, Instance Principals authentication