Goal
Replace the API signing key used by a service or automation user (CI runner, Terraform, backup scripts) on a schedule, without a window where jobs fail with 401 NotAuthenticated.
This is the planned version. If a key has leaked, follow the incident runbook instead: revoke first, investigate second.
The approach is overlap: a user can hold up to three API signing keys at once, so you add the new key, move every caller to it, prove the old key is unused, then delete it.
Before you start
- You have permission to manage the user's API keys (
manage usersin the domain, or the user managing their own keys). - The user has at most two keys today. A third slot is needed for the new key.
- You know every place the current key is installed. If you don't, do the inventory below first.
- Someone who owns each consuming system is available during cutover.
export USER_OCID=ocid1.user.oc1..aaaa...
oci iam user api-key list --user-id "$USER_OCID" \
--query 'data[].{fingerprint:fingerprint, state:"lifecycle-state", created:"time-created"}' \
--output table
Warning
If the user already has three keys, you must delete one before uploading. Delete only a key you have proven idle (see "Prove the old key is idle"). Deleting the wrong one is an outage for whatever uses it.
Inventory the callers
Every caller identifies its key by fingerprint. Find where the current fingerprint is configured.
| Where | What to look for |
|---|---|
| OCI CLI / SDK hosts | ~/.oci/config fingerprint= and key_file= per profile |
| Terraform | provider "oci" block, TF_VAR_fingerprint, TF_VAR_private_key_path, or a CLI profile |
| CI/CD secrets | Pipeline variables or secret stores holding the PEM and fingerprint |
| Containers / Kubernetes | Secrets mounted as config and key files |
| Third-party tools | Backup, monitoring or cost tools configured with a user OCID + key |
On a Linux host, a quick search:
grep -RIl "aa:bb:cc:dd" /etc /opt /home ~/.oci 2>/dev/null
Confirm a private key file matches a fingerprint:
openssl rsa -pubout -outform DER -in ~/.oci/oci_api_key.pem 2>/dev/null | openssl md5 -c
The output (after (stdin)=) is the fingerprint OCI shows for the uploaded public key.
Check what the key is actually doing
Audit records the credential used on each request; the key's fingerprint appears in the event's identity credentials. Pull a recent window and filter with jq (Audit events are stored per compartment; repeat for the compartments the automation works in, or use Logging Search on the _Audit log with subcompartments included):
FP="aa:bb:cc:dd:..."
oci audit event list --compartment-id "$COMPARTMENT_OCID" \
--start-time 2026-11-11T00:00:00Z --end-time 2026-11-12T00:00:00Z --all \
| jq -r --arg fp "$FP" '.data[] | select(tostring | contains($fp))
| [.["event-time"], .data.identity.ipAddress // "", .data.eventName // ""] | @tsv' \
| sort | uniq -c | sort -rn | head -20
Field names inside the event can differ in case between CLI versions; check one raw event if a column comes back empty. Source IPs here are your real inventory: any IP you can't map to a known system is a caller you'll break.
Create and upload the new key
- Generate a new 2048-bit (or larger) RSA key pair on a trusted host:
umask 077
openssl genrsa -out ~/.oci/svc-ci-2026-11.pem 2048
openssl rsa -pubout -in ~/.oci/svc-ci-2026-11.pem -out ~/.oci/svc-ci-2026-11_public.pem
- Upload the public key:
oci iam user api-key upload --user-id "$USER_OCID" \
--key-file ~/.oci/svc-ci-2026-11_public.pem \
--query 'data.fingerprint' --raw-output
- Record the new fingerprint, the date and the owner in your secrets inventory.
- Store the private key in your secret manager (OCI Vault secret, CI secret store). Don't leave copies on laptops.
Tip
Name key files with the date. When three fingerprints are listed on a user, the file name is often the only way to tell them apart quickly.
Cut over the callers
Do one system at a time and test after each.
- Add a temporary CLI profile with the new key and test it before touching the real profile:
[NEWKEY]
user=ocid1.user.oc1..aaaa...
fingerprint=11:22:33:...
key_file=~/.oci/svc-ci-2026-11.pem
tenancy=ocid1.tenancy.oc1..aaaa...
region=eu-frankfurt-1
oci os ns get --profile NEWKEY
oci iam region-subscription list --profile NEWKEY --query 'data[].{"region":"region-name"}' --output table
- Update the real profile (
fingerprintandkey_filetogether) or the CI secret. - Trigger one real job per system (a
terraform plan, a backup run) and confirm it succeeds. - For containers, roll the pods or restart the service so the new secret is read.
| Symptom after cutover | Likely cause | Fix |
|---|---|---|
401 NotAuthenticated | Fingerprint and key file don't match | Recompute the fingerprint from the private key |
401 only on some runners | Old secret cached on some agents | Restart agents, check secret versioning |
| Works in CLI, fails in Terraform | Provider uses env vars, not the profile | Check TF_VAR_* and provider block |
401 everywhere, keys correct | Host clock skew | Fix NTP; signed requests are time-bound |
Prove the old key is idle
- Re-run the Audit query for the old fingerprint over a window that covers every schedule (daily jobs: 24-48 hours; monthly jobs: plan for them or run them manually).
- Zero hits for the old fingerprint, steady hits for the new one.
Delete the old key
oci iam user api-key delete --user-id "$USER_OCID" --fingerprint "aa:bb:cc:dd:..." --force
oci iam user api-key list --user-id "$USER_OCID" --query 'data[].fingerprint'
Caution
Deletion takes effect immediately and can't be undone. If a forgotten caller appears, you would have to upload a new key and reconfigure it. That's why the idle check comes first.
- Securely delete the old private key from hosts and secret stores (keep the secret version history if your policy requires it).
- Update the inventory: fingerprint removed, date, who did it.
Prevent it next time
- Prefer instance principals, resource principals or OKE workload identity for anything running inside OCI. No key to rotate.
- Keep one key in use per user; the spare slots are for rotation, not for sharing.
- One automation user per system, so a rotation affects one owner.
- Put the key's creation date in your inventory and alert on keys older than your rotation period (
time-createdfromapi-key list). - Run the Audit fingerprint query monthly; unknown source IPs are worth a look even outside rotation.