Skip to main content

OCI OKE Roles and ClusterRoles Should Avoid Wildcard Use

More Info:

Wildcards in Kubernetes Roles and ClusterRoles grant unintended permissions and break least-privilege. Replace wildcards with explicit verbs and resources to limit blast radius if a service account or user is compromised.

Risk Level

High

Address

Compliance, Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Using Console

In OKE this is a Kubernetes RBAC issue, so you fix it inside the cluster (via kubectl), not by flipping an OKE setting. You can do all of this from the OCI Console using Cloud Shell.

1. Open Cloud Shell from the OCI Console

  1. Sign in to OCI Console.
  2. Click the Cloud Shell icon (top-right of the console).
  3. A terminal opens at the bottom of the browser.

2. Get kubeconfig for the OKE cluster

  1. In the left menu: Developer Services → Kubernetes Clusters (OKE).

  2. Select the correct Compartment.

  3. Click your Cluster.

  4. Click Access Cluster.

  5. Under “Cloud Shell access”, click Copy next to the oci ce cluster create-kubeconfig command.

  6. Paste it into Cloud Shell and run it, for example:

    oci ce cluster create-kubeconfig \
    --cluster-id ocid1.cluster.oc1..xxxx \
    --file $HOME/.kube/config \
    --region <your-region> \
    --token-version 2.0.0
  7. Verify access:

    kubectl get nodes

3. Identify Roles and ClusterRoles using wildcards

Check ClusterRoles:

kubectl get clusterroles -o yaml | grep -n "verbs: \[\\"*\\\"\|resources: \[\\"*\\\"" -n

Or list them and inspect one by one:

kubectl get clusterroles
kubectl get clusterrole <name> -o yaml

Check namespace-scoped Roles:

kubectl get roles -A
kubectl get role <name> -n <namespace> -o yaml

Look for rules like:

rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

or any use of "*" in apiGroups, resources, resourceNames, or verbs.

4. Plan least-privilege replacements

For each offending Role/ClusterRole:

  • Replace resources: ["*"] with the exact resources needed (e.g. ["pods", "deployments"]).
  • Replace verbs: ["*"] with the minimal verbs (e.g. ["get", "list", "watch"]).
  • Avoid apiGroups: ["*"]; specify actual groups (e.g. ["", "apps"]).

Example: From overly broad:

rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]

To least-privilege:

rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]

5. Edit the Roles/ClusterRoles

Use kubectl edit in Cloud Shell:

  • ClusterRole:

    kubectl edit clusterrole <name>
  • Role:

    kubectl edit role <name> -n <namespace>

This opens an editor (usually vi); modify the rules section to remove * and save.

Or apply updated YAML manifests:

  1. Dump existing definition:

    kubectl get clusterrole <name> -o yaml > cr-<name>.yaml
  2. Edit the file in Cloud Shell:

    nano cr-<name>.yaml
    • Under rules:, replace any "*" with specific resources/verbs/groups.
    • Ensure you do not modify system roles prefixed with system: unless you know exactly what you’re doing.
  3. Apply:

    kubectl apply -f cr-<name>.yaml

Repeat similarly for namespace Roles.

6. Verify remediation

Recheck for wildcards:

kubectl get clusterroles -o yaml | grep "\*"
kubectl get roles -A -o yaml | grep "\*"

If nothing returns (or only comments), the wildcard usage is removed.

7. Additional notes

  • Avoid changing Kubernetes built-in system:* roles unless absolutely necessary.
  • If a third-party Helm chart installed broad roles, consider:
    • Overriding its RBAC values to use custom, restricted roles.
    • Reinstalling/upgrading the chart with more restrictive RBAC configuration.
Using CLI

In OKE you can only reach the cluster via OCI CLI; the RBAC objects themselves are standard Kubernetes and must be changed with kubectl. So the remediation flow is:

  1. use OCI CLI to fetch kubeconfig
  2. use kubectl to find Roles/ClusterRoles that use *
  3. replace wildcards with explicit verbs/resources and apply

Below is a minimal, step‑by‑step process.


1. Get kubeconfig for the OKE cluster using OCI CLI

# Variables
COMPARTMENT_OCID="<your_compartment_ocid>"
CLUSTER_OCID="<your_cluster_ocid>"
KUBECONFIG_FILE="$HOME/.kube/config-oke"

# Generate kubeconfig for the OKE cluster
oci ce cluster create-kubeconfig \
--cluster-id "$CLUSTER_OCID" \
--file "$KUBECONFIG_FILE" \
--region <your_region> \
--token-version 2.0.0 \
--kube-endpoint PUBLIC_ENDPOINT # or PRIVATE_ENDPOINT as applicable

# Point kubectl to this kubeconfig
export KUBECONFIG="$KUBECONFIG_FILE"

2. Identify Roles and ClusterRoles using wildcards

2.1 ClusterRoles with wildcards

# Any ClusterRole that has verbs: ["*"]
kubectl get clusterrole -o json \
| jq -r '.items[]
| select(.rules[]? | (.verbs[]? == "*"))
| .metadata.name'

# Any ClusterRole that has resources: ["*"]
kubectl get clusterrole -o json \
| jq -r '.items[]
| select(.rules[]? | (.resources[]? == "*"))
| .metadata.name'

# Optional: inspect full definition of a specific role
kubectl get clusterrole <name> -o yaml

2.2 Namespaced Roles with wildcards

# All Roles in all namespaces that use verbs: ["*"]
kubectl get role -A -o json \
| jq -r '.items[]
| select(.rules[]? | (.verbs[]? == "*"))
| "\(.metadata.namespace)/\(.metadata.name)"'

# All Roles in all namespaces that use resources: ["*"]
kubectl get role -A -o json \
| jq -r '.items[]
| select(.rules[]? | (.resources[]? == "*"))
| "\(.metadata.namespace)/\(.metadata.name)"'

3. Replace wildcard rules with least‑privilege rules

For each identified (Cluster)Role:

  1. Export it to a file:

    kubectl get clusterrole <name> -o yaml > cr-<name>.yaml
    # or for Role:
    kubectl get role <name> -n <namespace> -o yaml > r-<namespace>-<name>.yaml
  2. Edit the file and remove wildcards:

    Example BEFORE:

    rules:
    - apiGroups: ["*"]
    resources: ["*"]
    verbs: ["*"]

    Example AFTER (replace with explicit groups/resources/verbs you actually need):

    rules:
    - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
    - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list"]

    Key principles:

    • Do not use * in verbs, resources, or apiGroups.
    • List only the verbs required (get, list, watch, create, update, patch, delete, deletecollection).
    • List only the resources needed.
  3. Apply the updated role:

    kubectl apply -f cr-<name>.yaml
    # or
    kubectl apply -f r-<namespace>-<name>.yaml

4. Verify there are no remaining wildcards

# ClusterRoles
kubectl get clusterrole -o json \
| jq -e '.items[]
| select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*"))' \
>/dev/null && echo "Still have wildcard ClusterRoles" || echo "No wildcard ClusterRoles"

# Roles
kubectl get role -A -o json \
| jq -e '.items[]
| select(.rules[]? | (.verbs[]? == "*" or .resources[]? == "*" or .apiGroups[]? == "*"))' \
>/dev/null && echo "Still have wildcard Roles" || echo "No wildcard Roles"

If you share a sample Role/ClusterRole manifest that currently uses *, I can suggest an explicit least‑privilege replacement for that exact case.

Using Python

Below is a practical, Python-based approach to identify and remediate OCI OKE Role and ClusterRole objects that use wildcard ("*") verbs or resources.

Assumptions:

  • You have kubectl access to the OKE cluster.
  • You can install Python dependencies on a machine that has network access to the cluster.
  • You understand what the correct least-privilege permissions should be (you must decide the replacement for "*").

1. Set up Python environment

pip install kubernetes pyyaml

Ensure your kubeconfig points to the OKE cluster (e.g. from OCI Console, “Access Cluster” > kubectl setup).

export KUBECONFIG=/path/to/oke-kubeconfig
kubectl get nodes

2. Connect to the OKE cluster from Python

from kubernetes import client, config

# Load kubeconfig from default location or explicit path
config.load_kube_config() # or config.load_kube_config(config_file="path/to/kubeconfig")

rbac_api = client.RbacAuthorizationV1Api()

3. Detect Roles and ClusterRoles using wildcards

def has_wildcard(rule):
# Detect if rule has wildcard in verbs, resources or apiGroups
return (
any(v == "*" for v in (rule.verbs or [])) or
any(r == "*" for r in (rule.resources or [])) or
any(ag == "*" for ag in (rule.api_groups or []))
)

def find_wildcard_roles():
bad_roles = []
bad_clusterroles = []

# Namespaced Roles
roles = rbac_api.list_role_for_all_namespaces().items
for role in roles:
bad_rules = [r for r in role.rules or [] if has_wildcard(r)]
if bad_rules:
bad_roles.append((role, bad_rules))

# ClusterRoles
clusterroles = rbac_api.list_cluster_role().items
for cr in clusterroles:
bad_rules = [r for r in cr.rules or [] if has_wildcard(r)]
if bad_rules:
bad_clusterroles.append((cr, bad_rules))

return bad_roles, bad_clusterroles

bad_roles, bad_clusterroles = find_wildcard_roles()

print("Roles with wildcard rules:")
for role, rules in bad_roles:
print(f" Role: {role.metadata.namespace}/{role.metadata.name}")
print("ClusterRoles with wildcard rules:")
for cr, rules in bad_clusterroles:
print(f" ClusterRole: {cr.metadata.name}")

4. Define a least-privilege replacement policy

You must decide how "*" should be replaced.
Example: convert some typical wildcards to explicit sets.

# Example mapping – adjust to your environment and security policy
VERB_REPLACEMENTS = {
"*": ["get", "list", "watch"] # Or full CRUD set: ["get","list","watch","create","update","patch","delete"]
}

RESOURCE_REPLACEMENTS = {
"*": [
"pods", "pods/log",
"deployments", "replicasets", "statefulsets",
"services", "configmaps", "secrets"
# Add only what is really required
]
}

API_GROUP_REPLACEMENTS = {
"*": ["", "apps", "batch"] # Core group is ""; add only needed groups
}

5. Function to “de-wildcard” a single rule

from copy import deepcopy

def replace_wildcards_in_rule(rule):
new_rule = deepcopy(rule)

# Replace verbs
new_verbs = []
for v in rule.verbs or []:
if v == "*" and "*" in VERB_REPLACEMENTS:
new_verbs.extend(VERB_REPLACEMENTS["*"])
else:
new_verbs.append(v)
new_rule.verbs = list(sorted(set(new_verbs)))

# Replace resources
new_resources = []
for r in rule.resources or []:
if r == "*" and "*" in RESOURCE_REPLACEMENTS:
new_resources.extend(RESOURCE_REPLACEMENTS["*"])
else:
new_resources.append(r)
new_rule.resources = list(sorted(set(new_resources)))

# Replace apiGroups
new_api_groups = []
for ag in rule.api_groups or []:
if ag == "*" and "*" in API_GROUP_REPLACEMENTS:
new_api_groups.extend(API_GROUP_REPLACEMENTS["*"])
else:
new_api_groups.append(ag)
new_rule.api_groups = list(sorted(set(new_api_groups)))

return new_rule

6. Create updated Role / ClusterRole specs without wildcards

def sanitize_role(role_obj):
new_role = deepcopy(role_obj)
new_rules = []

for r in role_obj.rules or []:
if has_wildcard(r):
new_rules.append(replace_wildcards_in_rule(r))
else:
new_rules.append(r)

new_role.rules = new_rules
return new_role


def sanitize_clusterrole(cr_obj):
new_cr = deepcopy(cr_obj)
new_rules = []

for r in cr_obj.rules or []:
if has_wildcard(r):
new_rules.append(replace_wildcards_in_rule(r))
else:
new_rules.append(r)

new_cr.rules = new_rules
return new_cr

7. Apply the changes back to the cluster

Use the Kubernetes API replace_* methods.
Best practice: print a diff or backup yaml before applying.

7.1 Backup originals to YAML

import yaml
from pathlib import Path

backup_dir = Path("./rbac-backup")
backup_dir.mkdir(exist_ok=True)

def backup_obj(obj, kind):
ns = getattr(obj.metadata, "namespace", None)
name = obj.metadata.name
file_name = f"{kind}_{ns+'_' if ns else ''}{name}.yaml"
with open(backup_dir / file_name, "w") as f:
yaml.safe_dump(client.ApiClient().sanitize_for_serialization(obj), f)

for role, _ in bad_roles:
backup_obj(role, "Role")

for cr, _ in bad_clusterroles:
backup_obj(cr, "ClusterRole")

7.2 Replace Roles and ClusterRoles

# Sanitize and apply Roles
for role, _ in bad_roles:
ns = role.metadata.namespace
name = role.metadata.name
updated_role = sanitize_role(role)
print(f"Updating Role {ns}/{name}")
rbac_api.replace_namespaced_role(
name=name,
namespace=ns,
body=updated_role
)

# Sanitize and apply ClusterRoles
for cr, _ in bad_clusterroles:
name = cr.metadata.name
updated_cr = sanitize_clusterrole(cr)
print(f"Updating ClusterRole {name}")
rbac_api.replace_cluster_role(
name=name,
body=updated_cr
)

8. Validate post-remediation

kubectl get roles -A -o yaml | grep -n "\*" || echo "No wildcards in Roles"
kubectl get clusterroles -o yaml | grep -n "\*" || echo "No wildcards in ClusterRoles"

Additionally test workloads and CI/CD pipelines that depend on these RBAC objects to ensure nothing breaks.


9. OCI/OKE-specific notes

  • These steps work the same in OKE as in any Kubernetes cluster, because RBAC is Kubernetes-native.
  • If any ClusterRole/Role is managed by OCI add-ons or Helm charts (e.g. oci-volume-provisioner, oci-cloud-controller-manager, ingress controller, etc.), update the chart/manifests as well, or your changes may be overwritten on upgrade or redeploy.

If you share an example of an actual Role/ClusterRole from your OKE cluster, I can give a concrete “before/after” Python transformation for that object.

Using Terraform
# This finding cannot be remediated on the OCI OKE *cluster* resource itself.
# Kubernetes RBAC (Roles/ClusterRoles) is managed via the Kubernetes API,
# so you must use the Kubernetes provider (or Helm) rather than
# `oci_containerengine_cluster` / `oci-containers-oke-cluster`.

provider "kubernetes" {
host = var.CLUSTER_ENDPOINT # OKE API server endpoint
cluster_ca_certificate = base64decode(var.CLUSTER_CA) # OKE cluster CA cert
token = var.BEARER_TOKEN # Or use exec/auth-provider as appropriate
}

# Example of a Role that previously used wildcards:
# verbs = ["*"]
# resources = ["*"]
# Replace wildcards with explicit verbs and resources.

resource "kubernetes_role" "app_namespace_reader" {
metadata {
name = "APP_ROLE_NAME" # e.g. "app-namespace-reader"
namespace = "TARGET_NAMESPACE" # e.g. "app-namespace"
}

rule {
api_groups = [""]
resources = ["pods", "services", "configmaps"]
verbs = ["get", "list", "watch"]
}

rule {
api_groups = [""]
resources = ["secrets"]
verbs = ["get"] # no list/watch to reduce exposure
}
}

# Example of a ClusterRole that previously used wildcards:
# verbs = ["*"]
# resources = ["*"]
# Again, enumerate exactly what is required.

resource "kubernetes_cluster_role" "cluster_reader_limited" {
metadata {
name = "CLUSTER_ROLE_NAME" # e.g. "cluster-reader-limited"
}

rule {
api_groups = [""]
resources = ["nodes", "pods", "namespaces"]
verbs = ["get", "list", "watch"]
}

rule {
api_groups = ["apps"]
resources = ["deployments", "statefulsets", "daemonsets", "replicasets"]
verbs = ["get", "list", "watch"]
}
}

# Optionally bind these to service accounts/users instead of
# any broad existing bindings that assume wildcard permissions.

resource "kubernetes_role_binding" "app_namespace_reader_bind" {
metadata {
name = "APP_ROLE_BINDING_NAME" # e.g. "app-namespace-reader-binding"
namespace = "TARGET_NAMESPACE"
}

role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "Role"
name = kubernetes_role.app_namespace_reader.metadata[0].name
}

subject {
kind = "ServiceAccount"
name = "TARGET_SERVICE_ACCOUNT" # e.g. "app-sa"
namespace = "TARGET_NAMESPACE"
}
}

resource "kubernetes_cluster_role_binding" "cluster_reader_limited_bind" {
metadata {
name = "CLUSTER_ROLE_BINDING_NAME" # e.g. "cluster-reader-limited-binding"
}

role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = kubernetes_cluster_role.cluster_reader_limited.metadata[0].name
}

subject {
kind = "User"
name = "TARGET_USER_OR_OIDC_SUBJECT"
api_group = "rbac.authorization.k8s.io"
}
}

The oci_containerengine_cluster / oci-containers-oke-cluster resource has no arguments for Kubernetes Roles or ClusterRoles; RBAC is entirely managed via the Kubernetes API. Update or replace your existing kubernetes_role / kubernetes_cluster_role resources (or Helm charts) to remove resources = ["*"] and verbs = ["*"], enumerating only the specific verbs and resources each principal needs.

Verification: terraform plan should show updates to the affected kubernetes_role and/or kubernetes_cluster_role resources where verbs and resources change from ["*"] to explicit lists, with no changes to the OKE cluster resource itself.