Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Why your RBAC linter misses privilege escalation chains (and how to fix it)

Catching indirect privilege escalation in Kubernetes RBAC with kube-chainsaw

July 7, 2026
Ugo Giordano
Related topics:
DevSecOpsKubernetesOperators
Related products:
Red Hat OpenShift

    If you run kube-linter on your Kubernetes manifests, you probably feel pretty good about your role-based access control (RBAC) setup. It catches wildcard verbs, cluster-admin bindings, and excessive Secret access. But there is a class of vulnerabilities it fundamentally cannot detect: indirect privilege escalation through binding chains.

    This post walks through the problem, shows how an attacker exploits it, and introduces kube-chainsaw, a tool that catches what per-object linters miss by building permission graphs from static manifests.

    The blind spot: Per-object linting

    kube-linter processes each Kubernetes resource independently. When it encounters a cluster role, it checks the rules for wildcards and dangerous verbs. When it encounters a cluster role binding, it checks whether it references cluster-admin. These are useful checks. But they operate in isolation.

    Consider this RBAC setup for a typical Kubernetes operator:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: operator-manager-role
    rules:
      - apiGroups: [""]
        resources: ["secrets"]
        verbs: ["get", "list", "watch", "create", "update"]
      - apiGroups: ["rbac.authorization.k8s.io"]
        resources: ["clusterrolebindings", "clusterroles"]
        verbs: ["create", "patch", "update"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: operator-binding
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: operator-manager-role
    subjects:
      - kind: ServiceAccount
        name: operator-sa
        namespace: operator-system
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: operator-controller
      namespace: operator-system
    spec:
      selector:
        matchLabels:
          app: operator
      template:
        metadata:
          labels:
            app: operator
        spec:
          serviceAccountName: operator-sa
          containers:
            - name: manager
              image: operator:latest

    No wildcards. No cluster-admin. kube-linter reports nothing alarming.

    But trace the chain manually: the deployment runs with operator-sa. That service account is bound cluster-wide to operator-manager-role via a cluster role binding. And that cluster role can create and patch cluster role bindings. In other words, the operator can grant itself, or any other service account, cluster-admin privileges at runtime. It also has read/write access to every Secret in the cluster.

    Flowchart tracing an operator's RBAC path to self-escalation, comparing isolated resource checks with full chain tracing.


    Figure 1: RBAC privilege escalation chain.

    kube-linter cannot see this because it never builds the graph. It checks each resource against a set of rules, but it does not connect deployment to service account to binding to role to permissions.

    How an attacker exploits this

    Here is the scary part: exploiting this does not require any special tools. If an attacker compromises the operator pod (through a container escape, a dependency vulnerability, or a supply chain attack), they inherit the ServiceAccount token automatically. From there:

    1. Verify permissions:

      kubectl auth can-i create clusterrolebindings
      # yes
    2. Grant cluster-admin:

      kubectl create clusterrolebinding pwned-admin \
        --clusterrole=cluster-admin \
        --serviceaccount=operator-system:operator-sa
    3. Full cluster access:

      kubectl get secrets --all-namespaces -o json

    Three commands. No privilege escalation exploit needed, the RBAC configuration already grants the permissions. The operator was one cluster role binding away from full cluster-admin privileges, and it had the ability to create that binding itself.

    kube-chainsaw catches this exact pattern by tracing the chain from the deployment through its service account to the cluster role binding, and flags it with a CRITICAL severity rating.

    This pattern is common in controller-runtime operators. The controller needs create or patch permissions on roles and role bindings for legitimate reconciliation, but the manifests grant it on cluster role bindings as well, often because the role definition was copy-pasted from a broader template.

    The fix: Graph traversal on static manifests

    The core problem is that detecting privilege escalation requires cross-object analysis. A tool needs to see the deployment, look up its service account, find all bindings that reference that service account, resolve the roles those bindings point to, and then evaluate whether the resulting permission set enables escalation.

    As shown in Figure 2, kube-chainsaw follows this exact approach. Instead of checking each resource in isolation, the tool performs the following actions:

    1. Parses all YAML manifests from a directory: cluster roles, roles, bindings, service accounts, pods, deployments, daemon sets, stateful sets, jobs, and cronjobs.
    2. Builds a directed graph: workload to service account to binding to role to verbs or resources.
    3. Traverses the graph to detect 15 categories of privilege escalation and misconfiguration.
    4. Adjusts severity based on binding scope: cluster-wide bindings are HIGH or CRITICAL, namespace-scoped bindings are WARNING, unbound roles are INFO.
    YAML manifests pass through loading, graph building, rule checking, and severity stages to generate console, JSON, or SARIF report outputs.

    Figure 2: kube-chainsaw analysis pipeline.

    The severity model is where graph analysis really pays off. The same cluster role produces different severity levels depending on how it is bound:

    Binding typeSeverityRationale
    ClusterRoleBinding (cluster-wide) with wildcardsCRITICALFull cluster access
    ClusterRoleBinding (cluster-wide) without wildcardsHIGHBroad but scoped access
    RoleBinding (namespace-scoped) with wildcardsHIGHNamespace-wide access
    RoleBinding (namespace-scoped) without wildcardsWARNINGLimited blast radius
    No binding (unbound)INFODormant template

    No per-object linter can make this distinction. It requires correlating the role with its bindings.

    Hands-on walkthrough

    Step 1: Install

    Install the tool:

    go install github.com/ugiordan/kube-chainsaw/cmd/kube-chainsaw@latest

    Or download a binary from the GitHub releases page. Docker and GitHub Action are also available.

    Step 2: Scan your manifests

    Point kube-chainsaw at a directory containing Kubernetes YAML files:

    $ kube-chainsaw config/

    Output:

    === HIGH ===
    
      [KC-006] Secrets access
        File:        config/rbac/role.yaml
        Resource:    ClusterRole/operator-manager-role
        Description: Role "operator-manager-role" grants access to
                     dangerous resource "secrets"
        Remediation: Restrict Secrets access to specific namespaces
                     and only the verbs needed
    
      [KC-010] RBAC modification capability
        File:        config/rbac/role.yaml
        Resource:    ClusterRole/operator-manager-role
        Description: Role "operator-manager-role" grants access to
                     dangerous resource "clusterrolebindings"
        Remediation: Limit RBAC modification to dedicated admin Roles
                     with proper audit
    
      [KC-011] Privilege escalation via Role/Binding modification
        File:        config/rbac/role.yaml
        Resource:    ClusterRole/operator-manager-role
        Description: Role "operator-manager-role" can create/modify
                     Roles or Bindings
        Remediation: Restrict ability to create/modify Roles and
                     Bindings to admin users only
    
    Total: 3 findings [3 HIGH]

    Each finding in the output includes a rule identifier (like KC-006 or KC-011), a human-readable title, the file and resource that triggered it, a description of the issue, and a remediation suggestion. The full list of all 15 rules with YAML examples is in the detection rules reference.

    Compare this with kube-linter on the same manifests:

    $ kube-linter lint config/
    # Only flags: "binding to cluster role that has
    #   [create get list patch update watch] access to [secrets]"
    # Misses: RBAC modification, privilege escalation via
    #   Bindings, workload creation risk

    Step 3: Generate SARIF for GitHub code scanning

    Run the following command to scan your directory and save the results as a Static Analysis Results Interchange Format (SARIF) file:

    $ kube-chainsaw config/ --output results.sarif

    This writes a SARIF 2.1.0 file that integrates directly with GitHub code scanning, GitLab SAST, or any SARIF-compatible security platform. The console report still prints to stdout for human review.

    Step 4: Suppress accepted risks

    Not every finding requires a fix. If the operator genuinely needs cluster-wide Secret access (for example, to manage TLS certificates), create a suppression file:

    # suppressions.yaml
    suppressions:
      - rule_id: KC-006
        resource_name: operator-manager-role
        reason: "Operator manages TLS certificates stored as Secrets"

    Execute the scan using the --suppressions flag to apply your baseline exclusions:

    $ kube-chainsaw config/ --suppressions suppressions.yaml

    Suppressed findings still appear in the output (marked as suppressed) for an audit trail, but they do not affect the exit code. This is important for CI pipelines: the scan passes even when known risks exist, as long as they are documented.

    Step 5: Continuous integration setup

    Add kube-chainsaw to a GitHub Actions workflow:

    - uses: ugiordan/kube-chainsaw@v1
      with:
        paths: config/ deploy/
        fail-on: HIGH
        format: sarif
        output: results.sarif
    
    - uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: results.sarif

    The workflow fails if any unsuppressed finding at HIGH or above is detected. SARIF results appear in the Security tab of the repository.

    What gets detected

    The kube-chainsaw tool runs 15 detection rules organized in three categories. The source code for all rules is in rules.go and analyzer.go.

    The tool scans for risky permissions defined on separate cluster roles and roles, including:

    • Wildcard permissions: verbs: ["*"] or resources: ["*"] that grant unrestricted access.
    • Dangerous verbs: escalate (self-promote RBAC privileges), impersonate (assume another identity), bind (attach roles without restriction).
    • Sensitive resource access: Secrets (credential theft), pods/exec and pods/attach (container hijacking), nodes (node-level compromise), persistent volumes (storage access), cluster roles and cluster role bindings (RBAC modification).
    • Escalation combinations: create, patch, or update permissions on roles, cluster roles, role bindings, or cluster role bindings within the same rule (enables self-granting privileges), or create permissions on pods (enables deploying privileged workloads).

    These checks are apiGroup-aware, so a custom resource definition (CRD) named secrets in the custom.example.com group does not trigger a false positive.

    Workload-to-cluster-admin privilege chains

    These rules trace the full permission path from a workload (pod, deployment, or job) through its service account and bindings to the referenced role.

    One rule flags workloads whose service account is bound to cluster-admin, the most privileged cluster role in Kubernetes. This is the pattern demonstrated in the attack scenario earlier in this post.

    Another rule flags role bindings that reference a cluster role instead of a namespace-scoped role. This is a common misconfiguration where a developer creates a role binding intending namespace-level access but references a cluster role, inadvertently granting the service account unintended permissions.

    Aggregated ClusterRole detection

    The tool detects aggregated cluster roles, which use label selectors to dynamically combine permissions from other cluster roles. Because the effective permissions depend on which cluster roles match the selector at runtime, you cannot fully determine them from static manifests alone.

    Tips and best practices

    Always scope cluster roles to the minimum required resources and verbs. If the operator only needs to read Secrets in its own namespace, use a local role and role binding instead of a cluster-wide cluster role and cluster role binding.

    Never grant create, patch, or update verbs on cluster role bindings unless absolutely required. This is the most dangerous permission an operator can have because it enables self-escalation to cluster-admin.

    Use role bindings instead of cluster role bindings when possible. A role binding referencing a cluster role limits the scope to a single namespace, significantly reducing the blast radius.

    Run kube-chainsaw in your continuous integration (CI) pipeline alongside kube-linter. The tools are complementary. While kube-linter covers CIS benchmark checks, resource limits, SecurityContext configurations, and container best practices, kube-chainsaw focuses on RBAC privilege chain analysis. Together, they provide broad static analysis coverage for your Kubernetes manifests.

    Suppress findings with documented reasons. Every suppression should include a reason field explaining why the risk is accepted. This creates an audit trail and forces a conscious decision about each exception.

    Comparison

    The following table compares kube-chainsaw with other common Kubernetes RBAC analysis tools:

    ToolStatic analysisGraph traversalPrivilege chainsWorkload analysis
    kube-chainsawYesYesYesYes
    kube-linterYesNoNoNo
    KubiScanNo (live cluster)YesYesNo
    rbac-toolNo (live cluster)YesNoNo
    kubectl-who-canNo (live cluster)YesNoNo

    Among the surveyed tools, kube-chainsaw is the only option that performs static graph traversal on YAML manifests to detect privilege escalation chains before deployment, requiring no live cluster.

    Wrap up

    Per-object RBAC linting catches the obvious problems: wildcards, cluster-admin bindings, missing SecurityContext configurations. But the subtle privilege escalation paths—the ones that chain through service accounts, bindings, and roles across multiple manifests—require graph-level analysis.

    kube-chainsaw fills that gap. It runs on static manifests, integrates with CI pipelines, produces SARIF data for security platforms, and supports suppression files for accepted risks. Try it on your operator manifests. The results might surprise you.

    Learn more:

    • kube-chainsaw documentation
    • kube-chainsaw on GitHub
    • Detection rules reference
    • kube-linter on GitHub for complementary Kubernetes manifest linting

    Related Posts

    • 5 anti-patterns that cause Kubernetes operator vulnerabilities

    • Role-based access control behind a proxy in an OAuth access delegation

    • Protect your Kubernetes Operator from OOMKill

    • Blast radius validation: Large and small Red Hat OpenShift nodes

    Recent Posts

    • Visualize your cluster: Manage observability with Red Hat build of Perses

    • Why your RBAC linter misses privilege escalation chains (and how to fix it)

    • Dependency analytics 1.0: AI coding with supply chain security

    • Understanding Argo CD ApplicationSets - Parameters (Part 1)

    • Smarter data generation for faster Speculator training

    What’s up next?

    Kubernetes Operators ebook tile card

    Kubernetes Operators

    Jason Dobies and Joshua Wood
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.