Skip to main content

Limit Use Of The Bind, Impersonate And Escalate Permissions

More Info:

The bind, impersonate, and escalate verbs allow privilege escalation beyond a principals assigned permissions. They should only be granted where strictly required for cluster operation.

Risk Level

High

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. List all Roles/ClusterRoles using bind/impersonate/escalate

    • Run on: any machine with kubectl access
    kubectl get clusterroles -o json \
    | jq -r '.items[]
    | select(.rules[]
    | select(.verbs[]? as $v
    | ["bind","impersonate","escalate"] | index($v)))
    | .metadata.name' | sort -u
    kubectl get roles --all-namespaces -o json \
    | jq -r '.items[]
    | select(.rules[]
    | select(.verbs[]? as $v
    | ["bind","impersonate","escalate"] | index($v)))
    | "\(.metadata.namespace):\(.metadata.name)"' | sort -u
  2. Exclude known Kubernetes system roles from modification

    • Run on: any machine with kubectl access
    • Identify roles that are likely required system roles and note them for special care (usually do not change without strong justification):
    kubectl get clusterroles \
    system:masters \
    clusterrole-aggregation-controller \
    system:controller:service-account-controller \
    -o yaml || true
    • For each ClusterRole/Role from step 1, check if its name starts with system: or is clearly a control-plane/controller role; mark those as “system – review but generally keep”.
  3. Inspect each non‑system ClusterRole/Role rule detail

    • Run on: any machine with kubectl access
    • For each name from step 1 that is not a system role, show full spec:
    # Example for a cluster role
    kubectl get clusterrole <clusterrole-name> -o yaml

    # Example for a namespaced role
    kubectl get role <role-name> -n <namespace> -o yaml
    • For each, record:
      • Which verb(s) are present: bind, impersonate, escalate
      • Which apiGroups, resources, and resourceNames they apply to
      • Whether the scope (cluster-wide vs namespace) matches a real operational need.
  4. Identify who is bound to these powerful roles

    • Run on: any machine with kubectl access
    # ClusterRoleBindings to roles found in step 1
    kubectl get clusterrolebindings -o yaml \
    | yq '.items[] |
    select(.roleRef.kind == "ClusterRole" and
    (.roleRef.name == "<clusterrole-name-1>" or
    .roleRef.name == "<clusterrole-name-2>" )) |
    {name: .metadata.name, role: .roleRef.name, subjects: .subjects}'
    # RoleBindings in all namespaces
    kubectl get rolebindings --all-namespaces -o yaml \
    | yq '.items[] |
    select(.roleRef.kind == "Role" and
    (.roleRef.name == "<role-name-1>" or
    .roleRef.name == "<role-name-2>" )) |
    {ns: .metadata.namespace, name: .metadata.name, role: .roleRef.name, subjects: .subjects}'
    • For each binding, decide if each subject (user, group, service account) truly requires these verbs for its function. Flag any human users or generic service accounts as high priority to restrict.
  5. Decide and apply least-privilege changes

    • Run on: any machine with kubectl access
    • For each non‑system role where use is not strictly required:
      • Prefer narrowing permissions before removal:
        • Remove unnecessary verbs (bind, impersonate, escalate) from the rules.
        • Restrict resources / resourceNames to the minimal required set.
      • If no subject legitimately needs those verbs, remove them entirely from the role.
    • Apply by editing the role definitions:
    # Edit a ClusterRole
    kubectl edit clusterrole <clusterrole-name>

    # Edit a namespaced Role
    kubectl edit role <role-name> -n <namespace>
    • If necessary, adjust bindings to use a different, less-privileged Role/ClusterRole instead of the powerful one.
  6. Re-verify the cluster for remaining bind/impersonate/escalate use

    • Run on: any machine with kubectl access
    kubectl get clusterroles -o json \
    | jq -r '.items[]
    | select(.rules[]
    | select(.verbs[]? as $v
    | ["bind","impersonate","escalate"] | index($v)))
    | .metadata.name' | sort -u
    kubectl get roles --all-namespaces -o json \
    | jq -r '.items[]
    | select(.rules[]
    | select(.verbs[]? as $v
    | ["bind","impersonate","escalate"] | index($v)))
    | "\(.metadata.namespace):\(.metadata.name)"' | sort -u
    • Confirm that:
      • Only necessary system roles and explicitly justified operational roles retain these verbs.
      • Non-system users and service accounts no longer have unjustified access to them via RoleBindings/ClusterRoleBindings.
Using kubectl
# 1) List all ClusterRoles that use bind, impersonate, or escalate
# Run on: any machine with kubectl access
kubectl get clusterroles -o json | jq '
.items[]
| select(
[.rules[].verbs[]?] | inside(["bind","impersonate","escalate"]) or
([.rules[].verbs[]?] | map(select(.=="bind" or .=="impersonate" or .=="escalate")) | length > 0)
)
| {name: .metadata.name, rules: .rules}
'

# 2) List all namespace Roles that use bind, impersonate, or escalate
kubectl get roles --all-namespaces -o json | jq '
.items[]
| select(
[.rules[].verbs[]?] | inside(["bind","impersonate","escalate"]) or
([.rules[].verbs[]?] | map(select(.=="bind" or .=="impersonate" or .=="escalate")) | length > 0)
)
| {namespace: .metadata.namespace, name: .metadata.name, rules: .rules}
'

What indicates a problem:

  • Any ClusterRole or Role in the output whose metadata.name or metadata.namespace suggests it belongs to:
    • Application-specific components (e.g. business apps, CI/CD jobs, monitoring agents), or
    • Human users (e.g. dev-*, qa-*, ops-*, admin-*), and whose rules contain verbs bind, impersonate, or escalate.
  • Roles not obviously part of Kubernetes’ own system roles/groups (for example, not starting with system: and not documented as core cluster controllers) that have these verbs.

You must manually decide:

  • Whether each non-system role truly needs bind, impersonate, or escalate for its function.
  • Whether these verbs can be removed or split into a more constrained role.

To inspect a specific suspicious role in more detail:

# Inspect a ClusterRole
kubectl get clusterrole <clusterrole-name> -o yaml

# Inspect a namespaced Role
kubectl get role <role-name> -n <namespace> -o yaml

To see who is using a suspicious role:

# ClusterRoleBindings referencing the role
kubectl get clusterrolebindings -o json | jq '
.items[]
| select(.roleRef.kind=="ClusterRole" and .roleRef.name=="<clusterrole-name>")
| {name: .metadata.name, subjects: .subjects}
'

# RoleBindings referencing a namespaced Role
kubectl get rolebindings -A -o json | jq '
.items[]
| select(.roleRef.kind=="Role" and .roleRef.name=="<role-name>" and .metadata.namespace=="<namespace>")
| {namespace: .metadata.namespace, name: .metadata.name, subjects: .subjects}
'

Verification after you make any manual changes:

# Re-run the discovery to confirm only intended system roles have these verbs
kubectl get clusterroles -o json | jq '
.items[]
| select([.rules[].verbs[]?] | map(select(.=="bind" or .=="impersonate" or .=="escalate")) | length > 0)
| .metadata.name
'

kubectl get roles --all-namespaces -o json | jq '
.items[]
| select([.rules[].verbs[]?] | map(select(.=="bind" or .=="impersonate" or .=="escalate")) | length > 0)
| {namespace: .metadata.namespace, name: .metadata.name}
'

The remaining names should be only Kubernetes system roles that you have consciously approved to retain these permissions (for example, system:masters, clusterrole-aggregation-controller, or other documented system roles in your environment).

Automation
#!/usr/bin/env bash
# Report Roles/ClusterRoles that use bind, impersonate, or escalate
# Run on: any machine with kubectl access and current-context set to the target cluster

set -euo pipefail

# Verbs of interest
VERBS='bind|impersonate|escalate'

echo "=== ClusterRoles with bind/impersonate/escalate ==="
kubectl get clusterroles -o json \
| jq -r '
.items[]
| {
name: .metadata.name,
rules: (
.rules // []
| map(
select(
(.verbs // []) | map(tostring) | map(ascii_downcase)
| join(" ")
| test("'"$VERBS"")
)
)
)
}
| select(.rules | length > 0)
| "ClusterRole: \(.name)\n" +
(
.rules[]
| " Resources: \(.resources // ["-""] | join(","))\n" +
" APIGroups: \(.apiGroups // ["-""] | join(","))\n" +
" Verbs: \(.verbs // ["-""] | join(","))\n"
)
' | sed '/^$/d' || echo "Failed to list ClusterRoles"

echo
echo "=== Namespaced Roles with bind/impersonate/escalate ==="
kubectl get roles --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
name: .metadata.name,
rules: (
.rules // []
| map(
select(
(.verbs // []) | map(tostring) | map(ascii_downcase)
| join(" ")
| test("'"$VERBS"")
)
)
)
}
| select(.rules | length > 0)
| "Role: \(.ns)/\(.name)\n" +
(
.rules[]
| " Resources: \(.resources // ["-""] | join(","))\n" +
" APIGroups: \(.apiGroups // ["-""] | join(","))\n" +
" Verbs: \(.verbs // ["-""] | join(","))\n"
)
' | sed '/^$/d' || echo "Failed to list Roles"

echo
echo "=== RoleBindings & ClusterRoleBindings that reference these roles ==="

# Build a list of roleRef targets that have the sensitive verbs
SENSITIVE_CLUSTERROLES=$(kubectl get clusterroles -o json \
| jq -r '
.items[]
| select(
(.rules // [])
| map(
(.verbs // []) | map(tostring) | map(ascii_downcase)
| join(" ")
| test("'"$VERBS"")
)
| any
)
| .metadata.name
')

SENSITIVE_ROLES=$(kubectl get roles --all-namespaces -o json \
| jq -r '
.items[]
| select(
(.rules // [])
| map(
(.verbs // []) | map(tostring) | map(ascii_downcase)
| join(" ")
| test("'"$VERBS"")
)
| any
)
| (.metadata.namespace + ":" + .metadata.name)
')

echo "--- ClusterRoleBindings ---"
kubectl get clusterrolebindings -o json \
| jq -r --argjson crNames "$(printf '%s\n' $SENSITIVE_CLUSTERROLES | jq -R . | jq -s .)" '
.items[]
| select(
.roleRef.kind == "ClusterRole"
and (.roleRef.name as $rn | $crNames | index($rn))
)
| "ClusterRoleBinding: \(.metadata.name)\n" +
" roleRef: \(.roleRef.kind)/\(.roleRef.name)\n" +
(
" subjects:\n" +
(
(.subjects // [])
| map(" - kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")")
| join("\n")
)
) + "\n"
' | sed '/^$/d' || echo "Failed to list ClusterRoleBindings"

echo
echo "--- RoleBindings ---"
kubectl get rolebindings --all-namespaces -o json \
| jq -r --argjson rNames "$(printf '%s\n' $SENSITIVE_ROLES | jq -R . | jq -s .)" '
.items[]
| . as $rb
| select(
.roleRef.kind == "Role"
and (
($rb.metadata.namespace + ":" + $rb.roleRef.name) as $key
| $rNames | index($key)
)
)
| "RoleBinding: \(.metadata.namespace)/\(.metadata.name)\n" +
" roleRef: \(.roleRef.kind)/\(.roleRef.name)\n" +
(
" subjects:\n" +
(
(.subjects // [])
| map(" - kind=\(.kind) name=\(.name) namespace=\(.namespace // "-")")
| join("\n")
)
) + "\n"
' | sed '/^$/d' || echo "Failed to list RoleBindings"

echo
echo "=== Interpretation ==="
cat <<'EOF'
Any ClusterRole or Role listed above that:
- is not clearly a Kubernetes system role (e.g. system:*, clusterrole-aggregation-controller, system:masters), and
- grants bind, impersonate, or escalate

should be manually reviewed. Pay particular attention to:

1) Human users or non-system service accounts in the listed RoleBindings/ClusterRoleBindings.
2) Namespaces used by application workloads (non-kube-* namespaces).

These cases typically indicate over-privilege and should be candidates for removing
bind/impersonate/escalate or replacing with more limited permissions.

No changes are made by this script; it only reports state for manual review.
EOF

Output indicating a problem

  • Any ClusterRole: or Role: that is not a known system role and shows Verbs: containing bind, impersonate, or escalate.
  • Any ClusterRoleBinding: or RoleBinding: where:
    • roleRef points to one of those roles, and
    • subjects list non-system users or service accounts (e.g., app service accounts in application namespaces).