Skip to main content

Limit Use Of The Bind, Impersonate And Escalate Permissions

More Info:

The bind, impersonate and escalate verbs allow a subject to acquire additional privileges beyond those directly granted. Their use should be tightly restricted.

Risk Level

High

Address

Security

Compliance Standards

  • CIS GKE

Triage and Remediation

Remediation

Manual Steps
  1. List all roles/clusterroles using bind/impersonate/escalate

    • Run on: any machine with kubectl access
    • Command (given):
      kubectl get clusterrole,role -A -o json | jq -r '
      def risky_verbs: ["bind","impersonate","escalate"];

      .items[]
      | . as $r
      | [ $r.rules[]?
      | select(
      (.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
      )
      | {
      resources: ((.resources // ["*"]) | join(",")),
      verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(","))
      }
      ] as $matches
      | select($matches | length > 0)
      | [
      $r.kind,
      $r.metadata.name,
      ($r.metadata.namespace // "cluster-wide"),
      ($matches | map(.resources) | unique | join(",")),
      ($matches | map(.verbs) | unique | join(","))
      ] | @tsv
      ' | awk -F'\t' 'BEGIN{
      printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS"
      print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------"
      }{
      printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5
      }'
  2. For each risky Role/ClusterRole, identify who uses it and why

    • Run on: any machine with kubectl access
    • For a ClusterRole:
      CLUSTERROLE_NAME="<name-from-output>"
      kubectl get clusterrolebinding -A \
      --field-selector metadata.name=${CLUSTERROLE_NAME} -o yaml
      kubectl get clusterrolebinding -A -o json | jq -r \
      --arg cr "${CLUSTERROLE_NAME}" '
      .items[] | select(.roleRef.kind=="ClusterRole" and .roleRef.name==$cr)
      | {name:.metadata.name, namespace:(.metadata.namespace//"cluster-wide"), subjects:.subjects}'
    • For a namespaced Role:
      ROLE_NAME="<name-from-output>"
      ROLE_NAMESPACE="<namespace-from-output>"
      kubectl get rolebinding -n "${ROLE_NAMESPACE}" -o json | jq -r \
      --arg rn "${ROLE_NAME}" '
      .items[] | select(.roleRef.kind=="Role" and .roleRef.name==$rn)
      | {name:.metadata.name, subjects:.subjects}'
    • Use this to decide if the subject truly needs bind/impersonate/escalate or could operate with lesser privileges (e.g., read/update only).
  3. Narrow or remove risky verbs where not strictly required

    • Run on: any machine with kubectl access
    • Fetch the role for editing:
      # ClusterRole
      kubectl get clusterrole "<name-from-output>" -o yaml > /tmp/clusterrole-risky.yaml

      # OR namespaced Role
      kubectl get role "<name-from-output>" -n "<namespace-from-output>" -o yaml > /tmp/role-risky.yaml
    • In the YAML file, for each rule containing bind, impersonate, or escalate:
      • Remove those verbs entirely when they are not required, or
      • Move them into a more restricted role intended only for a small set of trusted subjects.
    • Apply the edited role:
      # ClusterRole
      kubectl apply -f /tmp/clusterrole-risky.yaml

      # OR namespaced Role
      kubectl apply -f /tmp/role-risky.yaml
  4. Where risky verbs are necessary, scope access as tightly as possible

    • Run on: any machine with kubectl access
    • In the same YAML files:
      • Replace resources: ["*"] or broad resource sets with the minimum concrete resources (e.g., ["roles","rolebindings"] instead of ["*"]).
      • If feasible, separate duties:
        • One role with normal verbs (get/list/watch/create/update/delete) for day‑to‑day tasks.
        • A distinct, rarely used, strongly controlled role with bind/impersonate/escalate.
    • Re‑apply with kubectl apply -f as in step 3.
  5. Tighten bindings to trusted subjects only

    • Run on: any machine with kubectl access
    • For each RoleBinding/ClusterRoleBinding referencing a role that still uses any of these verbs:
      • Export, edit, and re‑apply:
        # ClusterRoleBinding
        kubectl get clusterrolebinding "<binding-name>" -o yaml > /tmp/crb-risky.yaml

        # RoleBinding
        kubectl get rolebinding "<binding-name>" -n "<namespace>" -o yaml > /tmp/rb-risky.yaml
      • In the YAML, remove untrusted/unused subjects from subjects: and keep only tightly controlled identities (e.g., a break‑glass admin group).
      • Apply:
        kubectl apply -f /tmp/crb-risky.yaml
        # or
        kubectl apply -f /tmp/rb-risky.yaml
  6. Re‑run the audit to verify and document accepted exceptions

    • Run on: any machine with kubectl access
    • Verification command (same as step 1):
      kubectl get clusterrole,role -A -o json | jq -r '
      def risky_verbs: ["bind","impersonate","escalate"];

      .items[]
      | . as $r
      | [ $r.rules[]?
      | select(
      (.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
      )
      | {
      resources: ((.resources // ["*"]) | join(",")),
      verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(","))
      }
      ] as $matches
      | select($matches | length > 0)
      | [
      $r.kind,
      $r.metadata.name,
      ($r.metadata.namespace // "cluster-wide"),
      ($matches | map(.resources) | unique | join(",")),
      ($matches | map(.verbs) | unique | join(","))
      ] | @tsv
      ' | awk -F'\t' 'BEGIN{
      printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS"
      print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------"
      }{
      printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5
      }'
    • For any remaining entries, explicitly document why the risky verb is required, who owns it, and how access is controlled (break‑glass, approval workflow, etc.).
Using kubectl
# 1) List all Roles/ClusterRoles that use bind/impersonate/escalate (from any machine with kubectl access)
kubectl get clusterrole,role -A -o json | jq -r '
def risky_verbs: ["bind","impersonate","escalate"];

.items[]
| . as $r
| [ $r.rules[]?
| select(
(.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
)
| {
resources: ((.resources // ["*"]) | join(",")),
verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(","))
}
] as $matches
| select($matches | length > 0)
| [
$r.kind,
$r.metadata.name,
($r.metadata.namespace // "cluster-wide"),
($matches | map(.resources) | unique | join(",")),
($matches | map(.verbs) | unique | join(","))
] | @tsv
' | awk -F'\t' 'BEGIN{
printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS"
print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------"
}{
printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5
}'

Problem indication:

  • Any line in this table is a role that uses at least one of: bind, impersonate, or escalate.
  • Treat these as potentially risky and review each role for necessity and scope.
# 2) For each suspicious ClusterRole, inspect full rules
# Example for a cluster role named "example-clusterrole"
kubectl get clusterrole example-clusterrole -o yaml

Problem indication in the YAML:

  • Rules where verbs includes bind, impersonate, or escalate, especially when:
    • resources: ["*"] or very broad resources are present.
    • apiGroups: ["*"] or cross-namespace/global impact is visible.
    • There is no clear, tightly scoped operational need.
# 3) For each suspicious namespaced Role, inspect full rules
# Example for a role named "example-role" in namespace "example-namespace"
kubectl get role example-role -n example-namespace -o yaml

Same problem indicators as above, but scoped to a namespace.

# 4) See who is bound to a given risky ClusterRole
# Example for "example-clusterrole"
kubectl get clusterrolebinding -o wide | grep 'example-clusterrole' || true
kubectl get clusterrolebinding -o yaml | grep -A5 'name: example-clusterrole'

Problem indication:

  • ClusterRoleBinding subjects that are:
    • Broad groups (e.g., system:authenticated, system:serviceaccounts, wildcard-like SSO groups).
    • Service accounts in default or shared namespaces that many workloads can use.
    • Human users/groups that do not need role-binding, impersonation, or escalation capabilities.
# 5) For a risky namespaced Role, see who is bound to it
# Example role "example-role" in "example-namespace"
kubectl get rolebinding -n example-namespace -o wide | grep 'example-role' || true
kubectl get rolebinding -n example-namespace -o yaml | grep -A5 'name: example-role'

Problem indication:

  • RoleBindings that grant these verbs to:
    • Many service accounts or all service accounts in a namespace.
    • Shared or default service accounts.
    • Broad user groups where only a few operators should have the capability.
# 6) Verify after any changes (re-run audit)
kubectl get clusterrole,role -A -o json | jq -r '
def risky_verbs: ["bind","impersonate","escalate"];

.items[]
| . as $r
| [ $r.rules[]?
| select(
(.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
)
| {
resources: ((.resources // ["*"]) | join(",")),
verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(","))
}
] as $matches
| select($matches | length > 0)
| [
$r.kind,
$r.metadata.name,
($r.metadata.namespace // "cluster-wide"),
($matches | map(.resources) | unique | join(",")),
($matches | map(.verbs) | unique | join(","))
] | @tsv
' | awk -F'\t' 'BEGIN{
printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS"
print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------"
}{
printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5
}'

Verification indication:

  • You must still manually assess necessity, but a reduction in rows or narrowed resources/subjects shows progress in limiting bind, impersonate, and escalate usage.
Automation
#!/usr/bin/env bash
# Report ClusterRoles/Roles using bind, impersonate, or escalate

set -euo pipefail

# REQUIREMENT: run on any machine with kubectl access and correct KUBECONFIG

echo "[INFO] Scanning ClusterRoles and Roles for bind/impersonate/escalate verbs..."
echo

kubectl get clusterrole,role -A -o json | jq -r '
def risky_verbs: ["bind","impersonate","escalate"];

.items[]
| . as $r
| [ $r.rules[]?
| select(
(.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
)
| {
resources: ((.resources // ["*"]) | join(",")),
verbs: ((.verbs // []) | map(select(IN("bind","impersonate","escalate"))) | join(","))
}
] as $matches
| select($matches | length > 0)
| [
$r.kind,
$r.metadata.name,
($r.metadata.namespace // "cluster-wide"),
($matches | map(.resources) | unique | join(",")),
($matches | map(.verbs) | unique | join(","))
] | @tsv
' | awk -F'\t' 'BEGIN{
printf "%-15s %-40s %-20s %-40s %-30s\n","KIND","NAME","NAMESPACE","RESOURCES","VERBS"
print "--------------- ---------------------------------------- -------------------- ---------------------------------------- ------------------------------"
}{
printf "%-15s %-40s %-20s %-40s %-30s\n",$1,$2,$3,$4,$5
}'

echo
echo "[INFO] Detailed JSON for review (who can use these roles):"
echo

# For each risky role, show bindings and subject details
kubectl get clusterrole,role -A -o json | jq -r '
def risky_verbs: ["bind","impersonate","escalate"];

.items[]
| . as $r
| [ $r.rules[]?
| select(
(.verbs // [] | any(. as $v | $v == "bind" or $v == "impersonate" or $v == "escalate"))
)
] as $matches
| select($matches | length > 0)
| {
kind: .kind,
name: .metadata.name,
namespace: (.metadata.namespace // "cluster-wide"),
rules: $matches
}
' | jq -s '
. as $risky
| {
riskyRoles: $risky,
roleBindings: (
[
(kubectl get rolebinding -A -o json | .items[] | . as $b | {
bindingKind: "RoleBinding",
bindingName: .metadata.name,
bindingNamespace: .metadata.namespace,
roleRefKind: .roleRef.kind,
roleRefName: .roleRef.name,
subjects: (.subjects // [])
}),
(kubectl get clusterrolebinding -o json | .items[] | . as $b | {
bindingKind: "ClusterRoleBinding",
bindingName: .metadata.name,
bindingNamespace: "cluster-wide",
roleRefKind: .roleRef.kind,
roleRefName: .roleRef.name,
subjects: (.subjects // [])
})
] | add
)
}
| .riskyRoles[]
| . as $rr
| {
kind: $rr.kind,
name: $rr.name,
namespace: $rr.namespace,
rules: $rr.rules,
bindings: (
. as $dummy
| $ENV.roleBindings
)
}
' roleBindings="$(kubectl get rolebinding -A -o json; kubectl get clusterrolebinding -o json)" 2>/dev/null || {
echo "[WARN] Could not build detailed role-to-subject mapping; inspect bindings manually."
}

How to interpret the output

  • The first table lists every Role or ClusterRole that uses any of the risky verbs in VERBS (one or more of bind, impersonate, escalate) and which RESOURCES they apply to.
  • Any line in this table indicates a potential problem and must be manually reviewed:
    • Confirm whether that role truly needs the listed verb(s).
    • If not strictly required, plan to remove the verb(s) from that role’s rules.
  • The JSON section (if it succeeds) is for deeper analysis: it links each risky role to bindings and subjects. Any subject bound to these roles is a candidate for privilege reduction.