Skip to main content

OCI IAM Admin Users Should Not Have API Signing Keys

More Info:​

Administrator users should not have API signing keys. API keys for admin accounts increase the risk of privilege escalation if keys are leaked or compromised

Risk Level​

Medium

Address​

Compliance, Security

Compliance Standards​

  • APRA CPS 234 (Australia)
  • AWS Startup Security Baseline
  • AWS Well Architected Framework
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS AWS
  • CIS Critical Security Controls v8
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • Cloudanix Best Practice
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • FedRAMP
  • GDPR
  • HIPAA
  • HITRUST CSF
  • ISO 27001
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST
  • NIST CSF
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • PCI
  • Reserve Bank of India (RBI) Cyber Security Framework
  • Reserve Bank of India (RBI) Master Direction – Information Technology Framework
  • SOC2
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation​

Remediation​

Using Console

Here’s how to remediate “OCI IAM Admin Users Should Not Have API Signing Keys” using the OCI Console, focusing on IAM and monitoring.


1. Identify Your Admin Users​

In OCI, “admin” users are typically those in groups that have powerful policies like manage all-resources in tenancy.

  1. In the OCI Console, go to: Identity & Security → Identity → Users
  2. For each user you consider an “admin”:
    • Click the user name.
    • Go to the Groups tab.
    • Note any group that has admin-level policies, e.g.:
      Allow group <GroupName> to manage all-resources in tenancy
      Allow group <GroupName> to manage users in tenancy
    • Any user in such groups should be treated as an admin for this control.

If you’re using a dedicated Administrators or similar group, you can just list that group’s members:

  1. Go to Identity & Security → Identity → Groups
  2. Click your admin group (e.g., Administrators).
  3. Go to the Members tab and see all admin users.

2. Remove API Signing Keys from Admin Users​

For each admin user:

  1. Go to Identity & Security → Identity → Users.
  2. Click the admin user.
  3. Go to the API Keys tab.
  4. For each API key listed:
    • Click the Actions (⋯) menu next to the key.
    • Click Delete.
    • Confirm the deletion.

Repeat this for all admin users that should not have API signing keys.


3. (Optional) Restrict Future Use of API Keys for Admins​

While OCI doesn’t have a direct toggle “disallow API keys,” you can reduce the risk by:

  • Using federated SSO (IdP) for admins so they don’t need local OCI user credentials.
  • Keeping admin access in separate groups and periodically auditing API keys for that group’s members (see section 4).

4. Set Up Monitoring / Detection in OCI (Cloud Guard)​

If you’re using Cloud Guard for IAM monitoring, you can detect this issue when it happens again.

4.1 Enable and Configure Cloud Guard (if not already)​

  1. Go to Identity & Security → Cloud Guard.
  2. If not enabled, click Enable Cloud Guard.
  3. Choose:
    • Target: usually your tenancy.
    • Detector recipes: use the Oracle-managed recipes to start.

4.2 Use or Customize an IAM Detector​

  1. In Cloud Guard, go to Detector Recipes.
  2. Select the IAM Detector Recipe associated with your target.
  3. Look for a detector rule related to:
    • “Users with high-privilege policies with API keys” or similar IAM/API-key related rule. Names may vary by tenancy/version.
  4. If available:
    • Make a copy of the Oracle-managed recipe (you cannot edit an Oracle-managed one directly).
    • In your custom IAM detector recipe, enable the relevant rule and set:
      • Risk level (e.g., High).
      • Condition: ensure it applies to:
        • Users in groups with admin-level privileges.
  5. Attach the custom IAM detector recipe to your Cloud Guard target.

Now, whenever an admin user gets an API key, Cloud Guard will generate a Problem.


5. (Optional) Add Automatic or Guided Remediation (Responder)​

If you want Cloud Guard to help remediate:

  1. Go to Cloud Guard → Responder Recipes.
  2. Create a custom responder recipe (copy from Oracle-managed if needed).
  3. Add or enable a responder rule for the corresponding IAM detector:
    • Example action: Notify (via email) or automatic Function that removes keys (requires custom function; console-only deletion is manual).
  4. Attach the custom responder recipe to your Cloud Guard target.

At minimum, set notifications so security/IAM admins are alerted when a new API key is added to an admin user.


Summary of console actions:

  • Identify admin users (groups with manage all-resources or similar).
  • For each admin user: Users → [User] → API Keys → Delete all keys.
  • Enable/Configure Cloud Guard IAM detector recipes to monitor for this condition going forward.
Using CLI

Below is a concise, CLI-focused way to remediate “OCI IAM Admin Users Should Not Have API Signing Keys” by:

  1. Identifying admin users.
  2. Listing their API signing keys.
  3. Deleting those keys.

Assumptions:

  • You have OCI CLI configured (oci setup config done).
  • You have tenancy OCID available.
  • “Admin users” = users in groups that effectively grant manage all-resources on the tenancy or equivalent broad admin policies.

1. Identify Administrator Groups​

First, find groups with admin‑level policies.

TENANCY_OCID="<your-tenancy-ocid>"
HOME_REGION="<home-region>" # e.g. us-phoenix-1

List policies in the root compartment (tenancy):

oci iam policy list \
--compartment-id "$TENANCY_OCID" \
--all \
--query "data[].{name:name,statements:statements}" \
--region "$HOME_REGION"

Look for policy statements like:

  • Allow group <GroupName> to manage all-resources in tenancy
  • Or any equivalent that gives broad admin rights.

Collect the group names you determine are “admin groups”. Suppose they are:

ADMIN_GROUPS=("Administrators" "SecurityAdmins")

2. List Users in Admin Groups​

For each admin group, list users:

for GROUP_NAME in "${ADMIN_GROUPS[@]}"; do
echo "=== Group: $GROUP_NAME ==="
GROUP_OCID=$(oci iam group list \
--compartment-id "$TENANCY_OCID" \
--name "$GROUP_NAME" \
--query "data[0].id" \
--raw-output \
--region "$HOME_REGION")

oci iam group list-users \
--group-id "$GROUP_OCID" \
--query "data[].{name:name,id:id}" \
--all \
--region "$HOME_REGION"
done

Capture the user OCIDs you want to check (admin users).

You can also assemble an array:

ADMIN_USER_IDS=()

for GROUP_NAME in "${ADMIN_GROUPS[@]}"; do
GROUP_OCID=$(oci iam group list \
--compartment-id "$TENANCY_OCID" \
--name "$GROUP_NAME" \
--query "data[0].id" \
--raw-output \
--region "$HOME_REGION")

USER_IDS=$(oci iam group list-users \
--group-id "$GROUP_OCID" \
--query "data[].id" \
--raw-output \
--all \
--region "$HOME_REGION")

for U in $USER_IDS; do
ADMIN_USER_IDS+=("$U")
done
done

3. List API Signing Keys for Each Admin User​

For each admin user, list their API keys:

for USER_ID in "${ADMIN_USER_IDS[@]}"; do
echo "=== API keys for user: $USER_ID ==="
oci iam user api-key list \
--user-id "$USER_ID" \
--query "data[].{keyId:key-id,fingerprint:fingerprint,timeCreated:\"time-created\"}" \
--all \
--region "$HOME_REGION"
done

Review this output. Decide which keys must be removed (typically all keys for admin users to satisfy the control).


4. Delete API Signing Keys for Admin Users​

To delete a specific key by its key-id:

oci iam user api-key delete \
--user-id "<user-ocid>" \
--fingerprint "<api-key-fingerprint>" \
--force \
--region "$HOME_REGION"

If you’re satisfied removing all keys for all admin users, you can script it:

for USER_ID in "${ADMIN_USER_IDS[@]}"; do
KEY_FPS=$(oci iam user api-key list \
--user-id "$USER_ID" \
--query "data[].fingerprint" \
--raw-output \
--all \
--region "$HOME_REGION")

for FP in $KEY_FPS; do
echo "Deleting API key $FP for user $USER_ID"
oci iam user api-key delete \
--user-id "$USER_ID" \
--fingerprint "$FP" \
--force \
--region "$HOME_REGION"
done
done

5. (Optional) Prevent Reintroduction​

To reduce recurrence (useful for IAM Monitoring / continuous compliance):

  • Use IAM policies and process controls so that:
    • Admins primarily use federation (IdP/SAML) or ephemeral tokens rather than user API keys.
  • Use Cloud Guard / IAM Monitoring recipes to detect:
    • Any API key creation for users in admin groups.
  • Hook Cloud Guard detector → responder rule that triggers automation (e.g., OCI Function or script) that:
    • Calls oci iam user api-key delete for admin users automatically.

If you share how IAM Monitoring/Cloud Guard is currently reporting this finding (detector type / payload), I can give a CLI-based remediation script tailored to that detector output.

Using Python

Below are concrete steps and a Python example using the OCI SDK to:

  1. Find IAM users with Admin privileges
  2. Check if they have API signing keys
  3. Optionally delete those API keys (remediation)

Assumptions:

  • “Admin users” = users who get manage all-resources on tenancy (or equivalent).
  • You have:
    • OCI tenancy with IAM enabled
    • API signing config set up locally (~/.oci/config) or environment variables
    • oci Python SDK installed: pip install oci

1. Define what “Admin user” means in your tenancy​

In OCI, privileges come from policies, not from user flags.
Common examples of Admin policies:

Allow group Administrators to manage all-resources in tenancy
Allow group tenancy-admins to manage all-resources in tenancy

You will:

  1. List all policies in the root compartment.
  2. Parse their statements.
  3. Identify those granting:
    • manage all-resources
    • at tenancy (or root compartment)

Then any user in the groups referenced by those policies is an “Admin user”.


2. Monitoring logic (high level)​

  1. Get the tenancy OCID.
  2. List all groups and policies in the root compartment.
  3. From policies, extract groups that have manage all-resources on tenancy.
  4. For each such “Admin group”:
    • List group members (users).
  5. For each “Admin user”:
    • List user API keys.
    • If keys exist → violation.
  6. (Optional remediation) Delete the API keys for those users.

3. Python example: Detect & remediate​

WARNING: Deleting API keys is destructive.
First run in read-only (dry-run) mode and log what would be deleted.
Only then enable the delete section.

import oci
import re

# -----------------------
# CONFIG
# -----------------------
PROFILE_NAME = "DEFAULT" # profile in ~/.oci/config
DRY_RUN = True # True = detect only, False = actually delete keys

# Regex patterns to identify admin-like policy statements
ADMIN_ACTION_PATTERN = re.compile(r"\bmanage\s+all-resources\b", re.IGNORECASE)
ADMIN_SCOPE_PATTERN = re.compile(r"\b(in\s+tenancy|in\s+compartment\s+tenancy)\b", re.IGNORECASE)

def is_admin_policy_statement(stmt: str) -> bool:
"""
Very simple parser: checks if a statement contains "manage all-resources"
and is scoped "in tenancy" (or equivalent).
E.g.: "Allow group Administrators to manage all-resources in tenancy"
"""
return bool(ADMIN_ACTION_PATTERN.search(stmt) and ADMIN_SCOPE_PATTERN.search(stmt))

def main():
# 1. Set up config and clients
config = oci.config.from_file("~/.oci/config", PROFILE_NAME)
tenancy_ocid = config["tenancy"]

identity_client = oci.identity.IdentityClient(config)

# 2. Get root compartment (tenancy) details
tenancy = identity_client.get_tenancy(tenancy_ocid).data
root_compartment_id = tenancy.id # same as tenancy OCID

# 3. List all groups
groups = oci.pagination.list_call_get_all_results(
identity_client.list_groups,
compartment_id=root_compartment_id
).data
groups_by_name = {g.name: g for g in groups}

# 4. List all policies in the tenancy (root compartment)
policies = oci.pagination.list_call_get_all_results(
identity_client.list_policies,
compartment_id=root_compartment_id
).data

admin_group_ocids = set()

# 5. Identify admin groups from policies
for policy in policies:
for stmt in policy.statements:
if is_admin_policy_statement(stmt):
# Typical pattern: "Allow group <GroupName> to manage all-resources in tenancy"
# Extract group name after 'group'
m = re.search(r"Allow\s+group\s+([^\s]+)", stmt, re.IGNORECASE)
if not m:
continue
group_name = m.group(1)
group_obj = groups_by_name.get(group_name)
if group_obj:
admin_group_ocids.add(group_obj.id)

if not admin_group_ocids:
print("No admin groups detected based on policies.")
return

print("Admin groups detected:")
for g in groups:
if g.id in admin_group_ocids:
print(f" - {g.name} ({g.id})")

# 6. For each admin group, list members
admin_user_ocids = set()

for group_id in admin_group_ocids:
memberships = oci.pagination.list_call_get_all_results(
identity_client.list_user_group_memberships,
compartment_id=root_compartment_id,
group_id=group_id
).data

for m in memberships:
admin_user_ocids.add(m.user_id)

if not admin_user_ocids:
print("No users found in admin groups.")
return

print("\nAdmin users (candidates to check for API keys):")
users = []
for user_id in admin_user_ocids:
user = identity_client.get_user(user_id).data
users.append(user)
print(f" - {user.name} ({user.id})")

# 7. For each admin user, list and (optionally) delete API keys
print("\nChecking API keys for admin users...")
for user in users:
api_keys = oci.pagination.list_call_get_all_results(
identity_client.list_api_keys,
user_id=user.id
).data

if not api_keys:
print(f"User {user.name} ({user.id}) has NO API signing keys.")
continue

print(f"User {user.name} ({user.id}) has {len(api_keys)} API signing key(s):")
for key in api_keys:
print(f" - Key fingerprint: {key.fingerprint}, key_id: {key.key_id}")

if DRY_RUN:
print(" [DRY RUN] Would delete this key.")
else:
print(" Deleting this key...")
identity_client.delete_api_key(user_id=user.id, key_id=key.key_id)
print(" Deleted.")

print("\nDone.")

if __name__ == "__main__":
main()

4. How to use this for monitoring​

  1. Deploy as a scheduled job:
    • Use an OCI Functions function or a Compute instance + cron to run the script periodically.
  2. Run in DRY_RUN = True to:
    • Log non-compliant users and keys.
    • Send alerts (e.g., via OCI Notifications, email, Slack, etc.).
  3. Once processes are agreed with security/operations:
    • Switch to DRY_RUN = False to automatically remediate by deleting keys.

5. Hardening / production tips​

  • Tighten the is_admin_policy_statement logic to match your exact policies.
  • Maintain an allowlist of exceptions if needed.
  • Log actions (before/after) to Object Storage or a SIEM.
  • Run under a dedicated service principal (dynamic group + policy) with:
    Allow dynamic-group <DynGroupName> to manage users in tenancy
    Allow dynamic-group <DynGroupName> to manage groups in tenancy
    (Adjust least-privilege as needed – at minimum, it needs to list users, groups, policies, and manage API keys.)

If you share your exact admin policy text, I can refine the parsing logic and permission policies.

Using Terraform
# Example: API keys are managed with oci_identity_api_key
# Remediation: do NOT create api keys for admin users; remove/destroy any existing ones.

resource "oci_identity_user" "admin_user" {
# Replace with your admin user details
compartment_id = var.tenancy_ocid
name = "ADMIN_USERNAME" # <- substitute with the actual admin username
description = "Admin user without API signing keys"
email = "ADMIN_EMAIL@example.com"
}

# If you currently have something like this, it must be removed to remediate:
# resource "oci_identity_api_key" "admin_api_key" {
# user_id = oci_identity_user.admin_user.id
# key_value = file("PATH_TO_PUBLIC_API_KEY_PEM") # <- this creates an API signing key
# description = "API key for admin user"
# }

# Instead, only create API keys for NON-admin users, e.g.:
resource "oci_identity_user" "non_admin_user" {
compartment_id = var.tenancy_ocid
name = "NON_ADMIN_USERNAME" # <- substitute with the actual non-admin username
description = "Non-admin user allowed to have API signing keys"
email = "NON_ADMIN_EMAIL@example.com"
}

resource "oci_identity_api_key" "non_admin_api_key" {
user_id = oci_identity_user.non_admin_user.id
key_value = file("PATH_TO_NON_ADMIN_PUBLIC_API_KEY_PEM") # <- path to the PEM public key
description = "API key for non-admin user"
}

Removing or editing an oci_identity_api_key resource for an admin user will cause Terraform to delete that API key from OCI; this is irreversible and the corresponding private key will no longer work.

If any API signing keys were created for admin users outside Terraform, they cannot be removed via Terraform; delete them in the OCI Console under Identity & Security → Users → ADMIN_USERNAME → API Keys.

For verification, terraform plan should show that all oci_identity_api_key resources associated with admin users are planned for destruction (or no longer exist in configuration), and only non-admin users retain oci_identity_api_key resources.