> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Minimize Access To Secrets

### More Info:

Roles that grant get, list, or watch on secrets expose sensitive credentials. Access to secrets should be limited to the workloads that genuinely need it.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS AKS

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List all Roles that can access Secrets**
           * Run on: any machine with kubectl access
           ```bash theme={null}
           kubectl get roles --all-namespaces -o json \
           | jq -r '
             .items[]
             | select(
                 .rules[]
                 | select(
                     (.resources // []) | index("secrets")
                   )
               )
             | "\(.metadata.namespace) \(.metadata.name)"' \
           | sort -u
           ```
           This shows all Roles that reference the `secrets` resource in any rule.

        2. **Review exactly which secret permissions each Role has**
           * Run on: any machine with kubectl access\
             For each `<namespace> <role-name>` pair from step 1, inspect in detail:
           ```bash theme={null}
           kubectl get role -n <namespace> <role-name> -o yaml
           ```
           Manually check `.rules` for:
           * `resources: ["secrets"]` or including `secrets`
           * `verbs` containing any of `get`, `list`, `watch` (or `*`)

        3. **Determine if each Role’s secret access is justified**
           * For each Role from step 2, identify which subjects actually use it:
             ```bash theme={null}
             kubectl get rolebinding -n <namespace> -o yaml \
               | yq '.items[] | select(.roleRef.name == "<role-name>") | .subjects'
             ```
           * For each ServiceAccount / user / group listed:
             * Identify the workloads (Pods/Deployments/Jobs, etc.) using that ServiceAccount.
             * Decide whether those workloads truly need to read Kubernetes Secrets (for example, they use `envFrom: secretRef:` or `secret` volumes, or perform runtime secret reads via the API).

        4. **Adjust Roles to least privilege where secret access is not required**
           * Run on: any machine with kubectl access\
             For a Role whose subjects do **not** need to read Secrets, remove `secrets` (or `*`) from its rules, or remove `get`/`list`/`watch` on `secrets`:
           ```bash theme={null}
           kubectl edit role -n <namespace> <role-name>
           ```
           In the editor, update `.rules` so either:
           * `resources` no longer includes `secrets`, or
           * `verbs` no longer includes `get`, `list`, or `watch` for `secrets`.

        5. **Create replacement least-privileged Roles where needed**\
           If a Role is shared and some subjects still require secret access while others do not:
           * Create a new Role without `secrets` access:
             ```bash theme={null}
             kubectl create role <new-role-name> \
               -n <namespace> \
               --verb=<needed-verb-1>,<needed-verb-2> \
               --resource=<needed-resource-1>,<needed-resource-2>
             ```
           * Rebind the subjects that should not have secret access to the new Role:
             ```bash theme={null}
             kubectl edit rolebinding -n <namespace> <rolebinding-name>
             ```
             In `.roleRef.name`, change the Role to `<new-role-name>` for the appropriate subjects, or split RoleBindings so that only the intended subjects keep the original Role with secret access.

        6. **Verify that Roles with secret access are now minimal and intentional**
           * Re-run the discovery query:
             ```bash theme={null}
             kubectl get roles --all-namespaces -o json \
             | jq -r '
               .items[]
               | select(
                   .rules[]
                   | select(
                       (.resources // []) | index("secrets")
                     )
                 )
               | "\(.metadata.namespace) \(.metadata.name)"' \
             | sort -u
             ```
           * For the remaining Roles listed, repeat step 2 quickly to confirm that:
             * Only workloads that genuinely need secret read access are bound.
             * `verbs` for `secrets` are limited to what is strictly necessary, typically only `get` (and rarely `list`/`watch`).
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) List all Roles in all namespaces
        # Run on: any machine with kubectl access
        kubectl get roles --all-namespaces -o wide

        # 2) Show only Roles that reference "secrets" in any rule
        # (quick text filter; may show false positives that still need review)
        kubectl get roles --all-namespaces -o yaml | grep -B4 -A6 "secrets"

        # 3) Detailed inspection of Roles that have "secrets" in their rules
        # This prints full YAML for human review
        kubectl get roles --all-namespaces -o yaml > /tmp/all-roles.yaml
        less /tmp/all-roles.yaml
        ```

        In `/tmp/all-roles.yaml`, look for `rules` entries where:

        ```yaml theme={null}
        rules:
          - apiGroups: [""]
            resources: ["secrets"]
            verbs: ["get", "list", "watch", ...]
        ```

        Indications of a problem (these need human judgment against your workload requirements):

        * `resources` includes `secrets` (or `["*"]`) in combination with:
          * overly broad `verbs`, such as `["get", "list", "watch"]`, `["*"]`, or many verbs where only `get` might be needed, or none at all.
          * overly broad `resourceNames` (omitted or `["*"]`), granting access to all secrets in the namespace instead of specific ones.
          * use in generic/system-wide Roles rather than workload-specific Roles tied to a narrow ServiceAccount.

        Examples that warrant review:

        ```yaml theme={null}
        # Overly broad
        rules:
        - apiGroups: [""]
          resources: ["secrets"]
          verbs: ["get", "list", "watch"]

        # Very high risk
        rules:
        - apiGroups: [""]
          resources: ["*"]
          verbs: ["*"]
        ```

        To focus on one suspicious Role you find in the YAML, use:

        ```bash theme={null}
        # Replace <namespace> and <role-name> with values from the list
        kubectl get role -n <namespace> <role-name> -o yaml
        ```

        Use these outputs to decide, per Role, whether:

        * it truly needs any access to secrets at all, and
        * if so, whether you can scope it down (fewer verbs, specific `resourceNames`, or moving access into a more targeted Role).
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report Roles and ClusterRoles that can access Secrets via get/list/watch
        # Run on: any machine with kubectl access and current-context pointing at the target cluster

        set -euo pipefail

        echo "### Roles with secret access (namespaced) ###"
        echo
        kubectl get roles --all-namespaces -o json \
          | jq -r '
            .items[]
            | {ns: .metadata.namespace, name: .metadata.name, rules: .rules}
            | select(.rules != null)
            | select(
                any(.rules[]?;
                  (.resources? // []) | index("secrets") and
                  (
                    (.verbs? // []) | any(. == "get" or . == "list" or . == "watch" or . == "*")
                  )
                )
              )
            | [
                .ns,
                .name,
                (
                  .rules[]
                  | select(
                      (.resources? // []) | index("secrets") and
                      (
                        (.verbs? // []) | any(. == "get" or . == "list" or . == "watch" or . == "*")
                      )
                    )
                  | "resources=" + ((.resources // []) | join(",")) +
                    " verbs=" + ((.verbs // []) | join(","))
                )
              ]
            | @tsv
          ' 2>/dev/null \
          | awk 'BEGIN { FS="\t"; OFS="\t"; print "NAMESPACE","ROLE","RULE_DETAILS" } { print }'

        echo
        echo "### ClusterRoles with secret access (cluster-wide) ###"
        echo
        kubectl get clusterroles -o json \
          | jq -r '
            .items[]
            | {name: .metadata.name, rules: .rules}
            | select(.rules != null)
            | select(
                any(.rules[]?;
                  (.resources? // []) | index("secrets") and
                  (
                    (.verbs? // []) | any(. == "get" or . == "list" or . == "watch" or . == "*")
                  )
                )
              )
            | [
                .name,
                (
                  .rules[]
                  | select(
                      (.resources? // []) | index("secrets") and
                      (
                        (.verbs? // []) | any(. == "get" or . == "list" or . == "watch" or . == "*")
                      )
                    )
                  | "resources=" + ((.resources // []) | join(",")) +
                    " verbs=" + ((.verbs // []) | join(","))
                )
              ]
            | @tsv
          ' 2>/dev/null \
          | awk 'BEGIN { FS="\t"; OFS="\t"; print "CLUSTERROLE","RULE_DETAILS" } { print }'
        ```

        Explanation of output indicating a problem:

        * Any line listed under either:
          * `### Roles with secret access (namespaced) ###`, or
          * `### ClusterRoles with secret access (cluster-wide) ###`
            shows a Role/ClusterRole that grants `get`, `list`, `watch`, or `*` on the `secrets` resource.
        * These entries must be manually reviewed to determine whether each subject truly requires that level of access; where not required, use `kubectl edit role` / `kubectl edit clusterrole` or manifest updates to remove or narrow these permissions.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
