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.