> ## 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:

The Kubernetes API stores secrets, which may be service account tokens for the Kubernetes API or credentials used by workloads in the cluster. Access to these secrets should be restricted to the smallest possible group of users to reduce the risk of privilege escalation.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. Identify which subjects currently have broad Secret access
           * Run on any machine with kubectl access:
             ```bash theme={null}
             kubectl get clusterrole,role -A -o yaml | grep -nA5 -B3 "resources:.*secrets"
             ```
             ```bash theme={null}
             kubectl get clusterrolebinding,rolebinding -A -o yaml | grep -nA8 -B3 "roleRef:"
             ```

        2. List the ClusterRoles/Roles that grant `get`, `list`, or `watch` on Secrets
           * Run:
             ```bash theme={null}
             kubectl get clusterrole,role -A -o json \
               | jq -r '.items[]
                 | select(.rules != null)
                 | select([.rules[]
                     | select(.resources != null)
                     | select(.resources|index("secrets"))
                     | select(.verbs|map(.=="get" or .=="list" or .=="watch")|any)
                   ]|length>0)
                 | (.kind + "/" + .metadata.name + " (ns: " + (.metadata.namespace // "cluster-scope") + ")")'
             ```

        3. Review and edit roles to remove or narrow Secret access
           * For each non-system role you decide should not have broad Secret access, edit it:
             ```bash theme={null}
             kubectl edit clusterrole <clusterrole-name>
             ```
             or for a namespace-scoped role:
             ```bash theme={null}
             kubectl edit role <role-name> -n <namespace>
             ```
           * In the opened YAML, locate `rules:` entries with `resources: ["secrets"]` (or including `secrets`) and:
             * Remove the `get`, `list`, and `watch` verbs from `verbs:`, or
             * Remove the entire rule if no longer needed.
           * Save and exit to apply.

        4. Adjust bindings so only intended subjects keep Secret access
           * For roles/clusterroles where some Secret access is still required but only for a limited group, edit bindings:
             ```bash theme={null}
             kubectl edit clusterrolebinding <binding-name>
             ```
             or
             ```bash theme={null}
             kubectl edit rolebinding <binding-name> -n <namespace>
             ```
           * Under `subjects:`, remove users/groups/serviceaccounts that should not be able to access Secrets.
           * Save and exit.

        5. Re-test access for specific identities before global verification
           * For high‑risk groups (for example `system:authenticated` or a CI user), explicitly test:
             ```bash theme={null}
             kubectl auth can-i get,list,watch secrets --all-namespaces --as=<user-or-group>
             ```
           * Confirm it returns `no` for identities that should not have cluster‑wide Secret read access.

        6. Verification (derived from the audit command)
           * Run on any machine with kubectl access:
             ```bash theme={null}
             echo "canGetListWatchSecretsAsSystemAuthenticated: $(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated)"
             ```
           * Confirm the output shows:
             ```text theme={null}
             canGetListWatchSecretsAsSystemAuthenticated: no
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1) Identify which ClusterRoles currently allow get/list/watch on secrets
        # Run on: any machine with kubectl access
        kubectl get clusterrole -o json \
          | jq -r '
            .items[]
            | select(
                .rules[]
                | select(
                    (.resources // []) | index("secrets")
                    and ((.verbs // []) | (.[] | IN("get","list","watch")))
                )
              )
            | .metadata.name
          ' | sort -u

        # 2) Inspect each identified ClusterRole to decide if it truly needs secret read access
        # Replace <clusterrole-name> with each name from the previous command.
        kubectl get clusterrole <clusterrole-name> -o yaml

        # 3) Edit ClusterRoles to remove get/list/watch on secrets where not required
        # Example: remove only the secret-related verbs, keep others.
        # Run once per ClusterRole you’ve decided should not read secrets.

        # This opens your editor; adjust the 'rules' section to drop get/list/watch from
        # any entry where 'resources: ["secrets"]' (or includes "secrets").
        kubectl edit clusterrole <clusterrole-name>

        # 4) For fine-grained control, split secret access into a dedicated ClusterRole (optional)
        # Run on: any machine with kubectl access

        # Create a narrow-scope ClusterRole that allows only what is actually needed;
        # adjust namespaces and subjects before applying.
        cat << 'EOF' > readonly-secrets-narrow-clusterrole.yaml
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRole
        metadata:
          name: readonly-secrets-narrow
        rules:
        - apiGroups: [""]
          resources: ["secrets"]
          verbs: ["get"]   # Remove "list","watch" unless absolutely necessary
        EOF

        kubectl apply -f readonly-secrets-narrow-clusterrole.yaml

        # Example: bind this narrowly to a specific service account instead of broad groups
        cat << 'EOF' > readonly-secrets-narrow-binding.yaml
        apiVersion: rbac.authorization.k8s.io/v1
        kind: RoleBinding
        metadata:
          name: readonly-secrets-narrow-binding
          namespace: default
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: readonly-secrets-narrow
        subjects:
        - kind: ServiceAccount
          name: my-workload-sa
          namespace: default
        EOF

        kubectl apply -f readonly-secrets-narrow-binding.yaml

        # 5) Verification: confirm system:authenticated can no longer get/list/watch secrets cluster-wide
        # Run on: any machine with kubectl access
        kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Minimize access to Secrets for system:authenticated
        #
        # Scope: any machine with kubectl access and current-context pointing to the target cluster.
        # Requirements: kubectl, cluster-admin (or equivalent) privileges.

        set -euo pipefail

        echo "=== Checking current access for system:authenticated to secrets ==="
        CURRENT_ACCESS=$(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated || echo "error")
        echo "canGetListWatchSecretsAsSystemAuthenticated: ${CURRENT_ACCESS}"

        echo "=== Enumerating RBAC bindings granting get/list/watch on secrets to system:authenticated ==="

        # Temporary files
        TMP_CLUSTERROLES=$(mktemp)
        TMP_ROLES=$(mktemp)
        trap 'rm -f "$TMP_CLUSTERROLES" "$TMP_ROLES"' EXIT

        # 1) Find ClusterRoles that grant get/list/watch on secrets
        kubectl get clusterroles -o json > "${TMP_CLUSTERROLES}"

        echo "- ClusterRoles with get/list/watch on secrets:"
        jq -r '
          .items[]
          | select(
              (.rules // [])
              | map(
                  (.resources // [] | index("secrets")) and
                  (.verbs // [] | (index("get") or index("list") or index("watch")))
                )
              | any
            )
          | .metadata.name
        ' "${TMP_CLUSTERROLES}" | sort -u || true

        CLUSTERROLES_WITH_SECRET_ACCESS=$(jq -r '
          .items[]
          | select(
              (.rules // [])
              | map(
                  (.resources // [] | index("secrets")) and
                  (.verbs // [] | (index("get") or index("list") or index("watch")))
                )
              | any
            )
          | .metadata.name
        ' "${TMP_CLUSTERROLES}" | sort -u)

        # 2) Find Roles that grant get/list/watch on secrets
        kubectl get roles --all-namespaces -o json > "${TMP_ROLES}"

        echo "- Namespaced Roles with get/list/watch on secrets:"
        jq -r '
          .items[]
          | select(
              (.rules // [])
              | map(
                  (.resources // [] | index("secrets")) and
                  (.verbs // [] | (index("get") or index("list") or index("watch")))
                )
              | any
            )
          | (.metadata.namespace + "/" + .metadata.name)
        ' "${TMP_ROLES}" | sort -u || true

        ROLES_WITH_SECRET_ACCESS=$(jq -r '
          .items[]
          | select(
              (.rules // [])
              | map(
                  (.resources // [] | index("secrets")) and
                  (.verbs // [] | (index("get") or index("list") or index("watch")))
                )
              | any
            )
          | (.metadata.namespace + "/" + .metadata.name)
        ' "${TMP_ROLES}" | sort -u)

        echo "=== Enumerating bindings that attach those roles to system:authenticated ==="

        echo "- ClusterRoleBindings to system:authenticated that use affected ClusterRoles:"
        kubectl get clusterrolebindings -o json \
          | jq -r --argjson crn "[\"$(echo "${CLUSTERROLES_WITH_SECRET_ACCESS}" | paste -sd '","' -)\"]" '
              .items[]
              | select(
                  (.roleRef.kind == "ClusterRole")
                  and ((.roleRef.name) as $rname | ($crn | index($rname)))
                  and (
                    (.subjects // [])
                    | map(.kind == "Group" and .name == "system:authenticated")
                    | any
                  )
                )
              | .metadata.name
            ' | sort -u || true

        CLUSTERROLEBINDINGS_TO_SYSTEM_AUTH=$(kubectl get clusterrolebindings -o json \
          | jq -r --argjson crn "[\"$(echo "${CLUSTERROLES_WITH_SECRET_ACCESS}" | paste -sd '","' -)\"]" '
              .items[]
              | select(
                  (.roleRef.kind == "ClusterRole")
                  and ((.roleRef.name) as $rname | ($crn | index($rname)))
                  and (
                    (.subjects // [])
                    | map(.kind == "Group" and .name == "system:authenticated")
                    | any
                  )
                )
              | .metadata.name
            ' | sort -u)

        echo "- RoleBindings to system:authenticated that use affected Roles:"
        kubectl get rolebindings --all-namespaces -o json \
          | jq -r --argjson rn "[\"$(echo "${ROLES_WITH_SECRET_ACCESS}" | paste -sd '","' -)\"]" '
              .items[]
              | select(
                  (.roleRef.kind == "Role")
                  and ((.metadata.namespace + "/" + .roleRef.name) as $rname | ($rn | index($rname)))
                  and (
                    (.subjects // [])
                    | map(.kind == "Group" and .name == "system:authenticated")
                    | any
                  )
                )
              | (.metadata.namespace + "/" + .metadata.name)
            ' | sort -u || true

        ROLEBINDINGS_TO_SYSTEM_AUTH=$(kubectl get rolebindings --all-namespaces -o json \
          | jq -r --argjson rn "[\"$(echo "${ROLES_WITH_SECRET_ACCESS}" | paste -sd '","' -)\"]" '
              .items[]
              | select(
                  (.roleRef.kind == "Role")
                  and ((.metadata.namespace + "/" + .roleRef.name) as $rname | ($rn | index($rname)))
                  and (
                    (.subjects // [])
                    | map(.kind == "Group" and .name == "system:authenticated")
                    | any
                  )
                )
              | (.metadata.namespace + "/" + .metadata.name)
            ' | sort -u)

        echo "=== REVIEW REQUIRED (MANUAL CONTROL) ==="
        echo "This control is MANUAL. There is no safe automatic edit of RBAC without human review."
        echo
        echo "1) Review the listed ClusterRoles and Roles and determine:"
        echo "   - Which uses of get/list/watch on secrets are strictly required."
        echo "   - Whether they must apply to the broad group system:authenticated."
        echo
        echo "2) For each binding below that is NOT required, you may remove system:authenticated"
        echo "   from subjects or replace it with a narrower group/service account."
        echo
        echo "ClusterRoleBindings to review (cluster-wide effect):"
        echo "${CLUSTERROLEBINDINGS_TO_SYSTEM_AUTH:-<none>}"
        echo
        echo "RoleBindings to review (namespaced effect):"
        echo "${ROLEBINDINGS_TO_SYSTEM_AUTH:-<none>}"
        echo
        echo "To edit a binding, run for each chosen binding:"
        echo "  # Example: edit ClusterRoleBinding"
        echo "  kubectl edit clusterrolebinding <clusterrolebinding-name>"
        echo
        echo "  # Example: edit RoleBinding in a namespace"
        echo "  kubectl edit rolebinding <rolebinding-name> -n <namespace>"
        echo
        echo "In the opened YAML, locate subjects: and remove or replace entries like:"
        echo "  - kind: Group"
        echo "    name: system:authenticated"
        echo
        read -r -p "Press Enter after you have completed RBAC edits to re-check access, or Ctrl+C to abort... " _

        echo "=== Re-checking access for system:authenticated to secrets after RBAC review ==="
        FINAL_ACCESS=$(kubectl auth can-i get,list,watch secrets --all-namespaces --as=system:authenticated || echo "error")
        echo "canGetListWatchSecretsAsSystemAuthenticated: ${FINAL_ACCESS}"

        echo "=== Completed. ==="
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
