Skip to main content

OCI OKE cluster-admin Role Usage Should Be Restricted

More Info:

The cluster-admin ClusterRole grants unrestricted access to every API. Bind it only to a small, audited set of break-glass identities; daily operations should use scoped roles.

Risk Level

High

Address

Compliance, Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Using Console

Below is how to tighten and restrict cluster-admin usage for an OKE cluster using only the OCI Console (with Cloud Shell and kubectl).


1. Open the OKE cluster in OCI Console

  1. Sign in to the OCI Console.
  2. In the left menu, go to Developer Services → Kubernetes Clusters (OKE).
  3. Select the Compartment that contains your cluster.
  4. Click the name of the target OKE cluster.

2. Get kubeconfig for the cluster

  1. On the cluster details page, click Access Cluster.
  2. In the panel that opens:
    • Choose Cloud Shell as the client environment (or “Local” if you prefer local kubectl, but Cloud Shell keeps you in console).
    • Copy the kubectl / kubeconfig command snippet shown under Cloud Shell.

Example (you’ll see something similar):

oci ce cluster create-kubeconfig \
--cluster-id <cluster-ocid> \
--file $HOME/.kube/config \
--region <region> \
--token-version 2.0.0 \
--kube-endpoint PUBLIC_ENDPOINT

3. Open Cloud Shell and configure kubectl

  1. In the top-right of the OCI Console, click the Cloud Shell icon (>_).
  2. When Cloud Shell opens, paste and run the create-kubeconfig command you copied.
  3. Test connectivity:
kubectl get nodes

If you see nodes listed, you’re connected.


4. Identify who currently has cluster-admin

In Cloud Shell:

kubectl get clusterrolebindings.rbac.authorization.k8s.io -o wide

Look for bindings that reference the cluster-admin ClusterRole, for example:

Name: cluster-admin-binding
Role: ClusterRole/cluster-admin
Subjects:
- Kind: User
Name: some-user@example.com
- Kind: Group
Name: system:authenticated

Take note of:

  • Bindings that give cluster-admin to broad subjects, e.g.:
    • system:authenticated
    • system:serviceaccounts
    • Wildcard-like or large external groups
  • Any custom ClusterRoleBinding whose roleRef.name is cluster-admin.

5. Remove or tighten dangerous ClusterRoleBindings

5.1. Delete overly broad ClusterRoleBindings

If you find a binding such as:

NAME ROLE
everyone-admin ClusterRole/cluster-admin

Inspect it:

kubectl get clusterrolebinding everyone-admin -o yaml

If it is broad (e.g., system:authenticated or a large IAM group), delete it:

kubectl delete clusterrolebinding everyone-admin

Repeat for any other non-essential or overly broad cluster-admin bindings.

Do NOT delete the critical system bindings you don’t fully understand (e.g., system ones created by OKE / Kubernetes), unless you are sure they’re not required.


6. Replace cluster-admin with least-privilege roles

Instead of giving cluster-admin, create narrow roles and bind them.

6.1. Create a least-privilege ClusterRole (example)

In Cloud Shell, create a file:

cat > limited-admin-role.yaml << 'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: limited-admin
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
EOF

Apply it:

kubectl apply -f limited-admin-role.yaml

6.2. Bind specific users/groups to the limited role

Example binding a specific group:

cat > limited-admin-binding.yaml << 'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: limited-admin-binding
subjects:
- kind: Group
name: my-oke-admins-group # map this to your IdP / OIDC group or user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: limited-admin
apiGroup: rbac.authorization.k8s.io
EOF

Apply:

kubectl apply -f limited-admin-binding.yaml

This way, people who previously had cluster-admin get only the permissions they actually need.


7. Restrict who can reach cluster-admin via OCI IAM

OKE access is a combination of:

  • OCI IAM Policies (who can manage or use clusters and node pools)
  • Kubernetes RBAC (what they can do inside the cluster)

To restrict who can even talk to the cluster as admins:

  1. In the OCI Console, go to Identity & Security → IAM → Policies.

  2. In the correct compartment / tenancy, locate policies like:

    allow group <SomeGroup> to manage cluster-family in compartment <CompartmentName>
  3. Tighten them as needed:

    • Limit to a smaller group (e.g., oke-admins).
    • Change manage to use if full management is not required.
  4. Save the updated policy.

This reduces who can obtain kubeconfigs and then become cluster admins.


8. Verify the restriction

  1. In Cloud Shell:

    kubectl auth can-i '*' '*' --as <some-regular-user>

    It should return no (or only yes for allowed verbs/resources).

  2. Confirm that only designated admins (limited group) have the permissions they need.


If you tell me how cluster-admin is currently granted in your OKE (e.g., sample clusterrolebinding output), I can give you an exact “before/after” YAML to apply.

Using CLI

Below is a practical, step‑by‑step way to restrict cluster-admin usage in OKE, focusing on OCI CLI (plus kubectl, which you invoke after generating kubeconfig via OCI CLI).


0. Prereqs

  • OCI CLI configured with a user that has permission to manage IAM and OKE.
  • kubectl installed.
  • You know:
    • Your compartment OCID.
    • Your cluster OCID.
    • The tenancy or IAM group OCIDs you want to allow as cluster admins.

1. Tighten OCI IAM access to the cluster

Cluster-admin in OKE is only possible if OCI IAM lets a user manage the cluster and generate kubeconfigs. First restrict who can do that.

1.1 List current policies

oci iam policy list \
--compartment-id <compartment_ocid> \
--all

Inspect policies that look like:

Allow group <some-group> to manage cluster-family in compartment <name>
Allow group <some-group> to manage node-pools in compartment <name>

And especially any that are overly broad:

Allow group Administrators to manage all-resources in tenancy

1.2 Update policies to narrow access

Example: restrict cluster management to a dedicated group oke-admins.

  1. Create a JSON file policy-statements.json:
{
"statements": [
"Allow group oke-admins to manage cluster-family in compartment <compartment_name>",
"Allow group oke-admins to manage node-pools in compartment <compartment_name>"
]
}
  1. Update an existing policy:
oci iam policy update \
--policy-id <policy_ocid> \
--statements file://policy-statements.json
  1. Remove or modify any policies that give broader rights than needed (e.g., manage all-resources in tenancy) to regular users/groups.

2. Generate admin kubeconfig only for the right IAM group

Make sure only your intended admin group members can get a kubeconfig with admin‑level access.

For an admin user in the oke-admins group:

oci ce cluster create-kubeconfig \
--cluster-id <cluster_ocid> \
--file ~/.kube/config-oke-admin \
--region <region> \
--token-version 2.0.0 \
--kube-endpoint PUBLIC_ENDPOINT

Then:

export KUBECONFIG=~/.kube/config-oke-admin

Confirm access:

kubectl get nodes

Ensure other users/groups that should NOT be cluster-admin:

  • Do not have policies allowing manage cluster-family (step 1),
  • Or only have limited policies (e.g., use cluster-family / specific verbs).

3. Restrict Kubernetes cluster-admin RBAC

Now restrict actual Kubernetes RBAC bindings that grant the cluster-admin ClusterRole.

3.1 List all cluster-admin bindings

kubectl get clusterrolebindings -o wide | grep cluster-admin || true

Also inspect all bindings:

kubectl get clusterrolebindings -o yaml > crb-all.yaml

Look for subjects (users/groups/serviceaccounts) that should not have admin.

3.2 Remove or replace broad cluster-admin bindings

Common problematic ones:

  • Bindings granting cluster-admin to a wide group like system:masters or to all authenticated users.

Delete unwanted ClusterRoleBindings:

kubectl delete clusterrolebinding <binding_name>

Examples:

kubectl delete clusterrolebinding cluster-admin-binding
kubectl delete clusterrolebinding <any-other-problem-binding>

If a binding is valid but has too many subjects, you can patch it:

kubectl patch clusterrolebinding <binding_name> \
--type=json \
-p='[{"op":"replace","path":"/subjects","value":[{"kind":"Group","name":"oke-admins","apiGroup":"rbac.authorization.k8s.io"}]}]'

(Adjust group name as needed.)

3.3 Create a minimal, explicit cluster-admin binding for your admin group

Create cluster-admin-oke-admins.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-oke-admins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: oke-admins

Apply:

kubectl apply -f cluster-admin-oke-admins.yaml

This ensures only the Kubernetes group oke-admins (mapped from your OCI IAM group via OKE auth) has full cluster-admin rights.


4. Validate

  1. As an admin (in oke-admins):
kubectl auth can-i '*' '*' --all-namespaces
# should be "yes"
  1. As a non‑admin user (with a separate kubeconfig):
export KUBECONFIG=~/.kube/config-nonadmin
kubectl auth can-i '*' '*' --all-namespaces
# should be "no"
  1. Confirm only your intended bindings reference cluster-admin:
kubectl get clusterrolebindings -o yaml | grep -A5 "name: cluster-admin"

If you provide your current policy statements and any clusterrolebinding YAML, I can give you exact oci and kubectl commands tailored to your environment.

Using Python

To restrict cluster-admin usage in an OCI OKE cluster using Python, you essentially need to:

  1. Connect to the OKE cluster’s Kubernetes API.
  2. Enumerate all ClusterRoleBinding objects that reference cluster-admin.
  3. Remove or tighten those bindings so only specific, approved subjects (users/groups/service accounts) retain cluster-admin.

Below is a step‑by‑step approach and example Python code using the Kubernetes Python client.


1. Prerequisites

  1. Have kubectl access to the OKE cluster (so your kubeconfig works):

    oci ce cluster create-kubeconfig \
    --cluster-id <cluster_ocid> \
    --file $HOME/.kube/config \
    --region <region> \
    --token-version 2.0.0 \
    --kube-endpoint PUBLIC_ENDPOINT
  2. Install Python dependencies:

    pip install kubernetes
  3. Ensure your Python environment can read the same kubeconfig used by kubectl (usually $HOME/.kube/config).


2. Decide Your Policy

Before running the script, decide:

  • Which subjects are allowed to have cluster-admin:
    • Type: User, Group, or ServiceAccount
    • Namespaces (only for ServiceAccount)
    • Names (e.g., oke-cluster-admins group)

For example, you might allow only:

- Group: "oke-cluster-admins"
- ServiceAccount: "admin-sa" in namespace "kube-system"

3. Python Script: Audit and Remediate cluster-admin Bindings

This script:

  • Connects to the cluster using local kubeconfig.
  • Lists all ClusterRoleBinding objects.
  • Finds those whose roleRef.name == "cluster-admin".
  • For each such binding:
    • Keeps only approved subjects.
    • If no approved subjects remain, deletes the binding.
    • If some remain, updates the binding with a reduced subject list.
from kubernetes import client, config

# ----------------- CONFIGURE ALLOWED SUBJECTS HERE -----------------
# Each subject is a dict with kind, name and optional namespace.
# kind must be one of: "User", "Group", "ServiceAccount".
ALLOWED_SUBJECTS = [
# Example allowed Group
{"kind": "Group", "name": "oke-cluster-admins"},
# Example allowed ServiceAccount
{"kind": "ServiceAccount", "name": "admin-sa", "namespace": "kube-system"},
]

# -------------------------------------------------------------------

def subject_matches_allowed(subject, allowed_subjects):
"""Return True if the Kubernetes Subject matches one of the allowed subjects."""
for allowed in allowed_subjects:
if subject.kind != allowed["kind"]:
continue
if subject.name != allowed["name"]:
continue
# ServiceAccount requires namespace match
if subject.kind == "ServiceAccount":
if subject.namespace != allowed.get("namespace"):
continue
return True
return False


def main():
# Load kubeconfig from default location (~/.kube/config)
config.load_kube_config()

rbac_api = client.RbacAuthorizationV1Api()

# List all ClusterRoleBindings
crbs = rbac_api.list_cluster_role_binding().items

for crb in crbs:
if crb.role_ref.kind == "ClusterRole" and crb.role_ref.name == "cluster-admin":
print(f"Processing ClusterRoleBinding: {crb.metadata.name}")

original_subjects = crb.subjects or []
print(" Original subjects:")
for s in original_subjects:
print(f" - {s.kind} {s.name}"
f"{' (ns=' + s.namespace + ')' if s.kind == 'ServiceAccount' else ''}")

# Filter subjects to allowed set only
filtered_subjects = [
s for s in original_subjects
if subject_matches_allowed(s, ALLOWED_SUBJECTS)
]

if not filtered_subjects:
# No allowed subjects left -> delete the binding
print(f" No allowed subjects remain. Deleting ClusterRoleBinding: {crb.metadata.name}")
rbac_api.delete_cluster_role_binding(name=crb.metadata.name)
else:
# Update the binding with the filtered subjects
print(f" Updating ClusterRoleBinding: {crb.metadata.name}")
crb.subjects = filtered_subjects
# Use replace to update the resource
rbac_api.replace_cluster_role_binding(
name=crb.metadata.name,
body=crb
)

print(" New subjects:")
for s in filtered_subjects:
print(f" - {s.kind} {s.name}"
f"{' (ns=' + s.namespace + ')' if s.kind == 'ServiceAccount' else ''}")

print("Remediation complete.")

if __name__ == "__main__":
main()

4. Run the Script

python restrict_cluster_admin.py

Review the output and verify:

kubectl get clusterrolebindings -o yaml | grep -A5 'name: cluster-admin'

To fully align with the control:

  • Ensure OCI IAM groups mapped to cluster access do not automatically grant cluster-admin via kubeconfig generation or admission policies.
  • Use more restrictive Kubernetes ClusterRole / Role bindings for regular dev/ops groups instead of cluster-admin.

If you tell me how you currently map OCI IAM groups to Kubernetes RBAC (e.g., via kubeconfig or admission-controller rules), I can provide a Python/OCI-SDK snippet for that side as well.

Using Terraform
# Provider configuration for OKE’s Kubernetes API
# Substitute the kubeconfig or endpoint/cert details for your cluster
provider "kubernetes" {
host = var.OKE_CLUSTER_ENDPOINT # e.g. "https://<OKE_API_ENDPOINT>"
cluster_ca_certificate = base64decode(var.OKE_CLUSTER_CA_CERT) # base64 CA from OKE
token = var.OKE_CLUSTER_BEARER_TOKEN # short‑lived token or OIDC token
}

# Restrict usage of the built‑in cluster-admin ClusterRole
# Bind it only to explicit “break-glass” subjects, and do not bind it to broad groups.
resource "kubernetes_cluster_role_binding_v1" "cluster_admin_break_glass" {
metadata {
name = "cluster-admin-break-glass"
}

role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "cluster-admin"
}

subject {
kind = "User" # or "Group" / "ServiceAccount" as needed
name = "BREAK_GLASS_USER" # replace with the exact subject name in your IdP/cluster
api_group = "rbac.authorization.k8s.io"
}

# Add additional tightly controlled “break-glass” subjects if absolutely necessary:
# subject {
# kind = "User"
# name = "SECOND_BREAK_GLASS_USER"
# api_group = "rbac.authorization.k8s.io"
# }
}

# Example: regular operators get a scoped role, *not* cluster-admin.
# Define a limited ClusterRole (or use existing ones like edit/view) and bind that instead.
resource "kubernetes_cluster_role_v1" "platform_ops_scoped" {
metadata {
name = "platform-ops-scoped"
}

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

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

resource "kubernetes_cluster_role_binding_v1" "platform_ops_binding" {
metadata {
name = "platform-ops-binding"
}

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

subject {
kind = "Group"
name = "PLATFORM_OPS_GROUP" # replace with your normal ops group
api_group = "rbac.authorization.k8s.io"
}
}

This change is in-place for the OKE cluster (no cluster replacement), but it will replace or update the ClusterRoleBinding objects if they already exist with a different configuration.

If you currently have broad cluster-admin bindings created outside Terraform (for example, bindings to system:authenticated or wide groups), you must either:

  • Import them into Terraform as kubernetes_cluster_role_binding_v1 resources and then narrow their subject blocks, or
  • Remove them manually with kubectl delete clusterrolebinding NAME so only the controlled Terraform-managed binding remains.

Verification: terraform plan should show creation or in-place update of kubernetes_cluster_role_binding_v1.cluster_admin_break_glass (and any other RBAC objects you added), with roleRef targeting cluster-admin and only the intended “break-glass” identities listed under subject.