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
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.
- In the OCI Console, go to: Identity & Security → Identity → Users
- 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 tenancyAllow 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:
- Go to Identity & Security → Identity → Groups
- Click your admin group (e.g.,
Administrators). - Go to the Members tab and see all admin users.
2. Remove API Signing Keys from Admin Users
For each admin user:
- Go to Identity & Security → Identity → Users.
- Click the admin user.
- Go to the API Keys tab.
- 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)
- Go to Identity & Security → Cloud Guard.
- If not enabled, click Enable Cloud Guard.
- Choose:
- Target: usually your tenancy.
- Detector recipes: use the Oracle-managed recipes to start.
4.2 Use or Customize an IAM Detector
- In Cloud Guard, go to Detector Recipes.
- Select the IAM Detector Recipe associated with your target.
- 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.
- 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.
- 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:
- Go to Cloud Guard → Responder Recipes.
- Create a custom responder recipe (copy from Oracle-managed if needed).
- 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).
- 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-resourcesor 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:
- Identifying admin users.
- Listing their API signing keys.
- Deleting those keys.
Assumptions:
- You have OCI CLI configured (
oci setup configdone). - You have tenancy OCID available.
- “Admin users” = users in groups that effectively grant
manage all-resourceson 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 deletefor admin users automatically.
- Calls
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:
- Find IAM users with Admin privileges
- Check if they have API signing keys
- Optionally delete those API keys (remediation)
Assumptions:
- “Admin users” = users who get
manage all-resourcesontenancy(or equivalent). - You have:
- OCI tenancy with IAM enabled
- API signing config set up locally (
~/.oci/config) or environment variables ociPython 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:
- List all policies in the root compartment.
- Parse their statements.
- 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)
- Get the tenancy OCID.
- List all groups and policies in the root compartment.
- From policies, extract groups that have
manage all-resourceson tenancy. - For each such “Admin group”:
- List group members (users).
- For each “Admin user”:
- List user API keys.
- If keys exist → violation.
- (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
- Deploy as a scheduled job:
- Use an OCI Functions function or a Compute instance + cron to run the script periodically.
- Run in DRY_RUN = True to:
- Log non-compliant users and keys.
- Send alerts (e.g., via OCI Notifications, email, Slack, etc.).
- Once processes are agreed with security/operations:
- Switch to
DRY_RUN = Falseto automatically remediate by deleting keys.
- Switch to
5. Hardening / production tips
- Tighten the
is_admin_policy_statementlogic 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:
(Adjust least-privilege as needed – at minimum, it needs to list users, groups, policies, and manage API keys.)Allow dynamic-group <DynGroupName> to manage users in tenancyAllow dynamic-group <DynGroupName> to manage groups in tenancy
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.