Manage Kubernetes RBAC Users With Google Groups For GKE
More Info:
Bind Kubernetes RBAC roles to Google Groups so cluster access is managed centrally through group membership. This simplifies and secures access administration versus per-user bindings.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS GKE
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Review current cluster Google Groups for GKE settings
- On any machine with gcloud configured, run:
gcloud container clusters describe CLUSTER_NAME \--location CONTROL_PLANE_LOCATION \--project PROJECT_ID \--format json | jq '{Enabled: .authenticatorGroupsConfig.enabled, "Security Group": .authenticatorGroupsConfig.securityGroup}'
- Decide whether
EnabledistrueandSecurity Groupis set to the intended parent group (for examplegke-security-groups@YOUR_DOMAIN).
- On any machine with gcloud configured, run:
-
If not enabled, decide on the security group and enable it (control-plane impact)
- Identify or create, in Google Workspace / Cloud Identity, a parent group (e.g.
gke-security-groups@YOUR_DOMAIN) and document its purpose and membership policy (who can be added, who can administer it). - On any machine with gcloud, update the existing cluster:
gcloud container clusters update CLUSTER_NAME \--location CONTROL_PLANE_LOCATION \--project PROJECT_ID \--security-group="gke-security-groups@YOUR_DOMAIN"
- Be aware: updating this may change how users authenticate/authorize; plan and execute during a maintenance window if needed.
- Identify or create, in Google Workspace / Cloud Identity, a parent group (e.g.
-
Inventory existing RBAC bindings that use direct users vs groups
- On any machine with kubectl access:
# ClusterRoleBindings using user subjectskubectl get clusterrolebindings -o json | jq -r '.items[]| select(.subjects != null)| select([.subjects[].kind] | index("User"))| .metadata.name as $n| "CRB: \($n)\n" + (.subjects[] | select(.kind=="User") | " User: \(.name)") + "\n"'# RoleBindings using user subjects across all namespaceskubectl get rolebindings --all-namespaces -o json | jq -r '.items[]| select(.subjects != null)| select([.subjects[].kind] | index("User"))| .metadata.namespace as $ns| .metadata.name as $n| "RB: \($ns)/\($n)\n" + (.subjects[] | select(.kind=="User") | " User: \(.name)") + "\n"'
- Use this output to identify principals that should instead be granted access via Google Groups.
- On any machine with kubectl access:
-
Design target Google Groups-to-RBAC mapping
- For each set of permissions you want to manage centrally (e.g. “cluster-admins”, “namespace-owners”, “read-only”), define a Google Group email that will be used as the RBAC subject (e.g.
gke-cluster-admins@YOUR_DOMAIN). - Map:
- Google Group → Kubernetes (Cluster)Role(s) → Scope (cluster or namespace).
- Ensure membership of these groups is managed through your standard identity governance process.
- For each set of permissions you want to manage centrally (e.g. “cluster-admins”, “namespace-owners”, “read-only”), define a Google Group email that will be used as the RBAC subject (e.g.
-
Implement or update RBAC bindings to use Google Groups
- On any machine with kubectl access, for each intended mapping, create or update bindings referencing the Google Group (subject
kind: Group,name: GROUP_EMAIL). Example (adjust names/namespaces as needed):cat <<'EOF' | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: cluster-admins-gke-groupsubjects:- kind: GroupapiGroup: rbac.authorization.k8s.ioname: gke-cluster-admins@YOUR_DOMAINroleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: cluster-adminEOF - Gradually phase out direct
Usersubjects by removing or editing the existing bindings once you’ve confirmed equivalent group-based access is in place.
- On any machine with kubectl access, for each intended mapping, create or update bindings referencing the Google Group (subject
-
Verify configuration and access behavior
- Re-run the cluster describe command on any machine with gcloud to ensure Google Groups for GKE is enabled and pointing at the correct security group:
gcloud container clusters describe CLUSTER_NAME \--location CONTROL_PLANE_LOCATION \--project PROJECT_ID \--format json | jq '{Enabled: .authenticatorGroupsConfig.enabled, "Security Group": .authenticatorGroupsConfig.securityGroup}'
- On any machine with kubectl access, confirm that RBAC bindings now reference
Groupsubjects (and notUser) for intended access paths:kubectl get clusterrolebindings,rolebindings --all-namespaces -o json | jq -r '.items[]| select(.subjects != null)| .kind + "/" + .metadata.name + " (" + (.metadata.namespace // "cluster-scope") + ")\n" +( .subjects[]| " " + .kind + ": " + .name)' - Optionally, have a test user who is only a member of the appropriate Google Group log in and confirm they receive the expected level of access and no more.
- Re-run the cluster describe command on any machine with gcloud to ensure Google Groups for GKE is enabled and pointing at the correct security group:
Using kubectl
kubectl cannot configure Google Groups for GKE or the cluster’s --security-group setting; those are managed in the GKE control plane via gcloud, console, or IaC. To remediate this finding, follow the guidance in the Manual Steps section, using the cloud provider tools to enable Google Groups for GKE and then create the appropriate RBAC bindings.
Automation
#!/usr/bin/env bash
#
# Audit Google Groups for GKE usage across all GKE clusters in one or more projects.
# Requirements:
# - gcloud, jq
# - You are authenticated and have sufficient IAM permissions.
#
# Usage:
# ./audit-gke-google-groups.sh PROJECT_ID [PROJECT_ID_2 ...]
#
set -euo pipefail
if ! command -v gcloud >/dev/null 2>&1; then
echo "ERROR: gcloud not found in PATH" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "ERROR: jq not found in PATH" >&2
exit 1
fi
if [ "$#" -lt 1 ]; then
echo "Usage: $0 PROJECT_ID [PROJECT_ID_2 ...]" >&2
exit 1
fi
echo "Collecting GKE Google Groups for GKE configuration and RBAC bindings..."
echo
for PROJECT in "$@"; do
echo "======================================================================"
echo "Project: ${PROJECT}"
echo "======================================================================"
MAP_OUTPUT=$(gcloud projects get-iam-policy "${PROJECT}" \
--format='json(bindings)' 2>/dev/null | jq -r '
.bindings[]
| select(.role=="roles/container.admin" or .role=="roles/container.clusterAdmin")
| " IAM: " + .role + " -> " + (.members | join(", "))
' || true)
echo "Project-level IAM holders of container.admin / container.clusterAdmin:"
if [ -z "${MAP_OUTPUT}" ]; then
echo " (none found)"
else
echo "${MAP_OUTPUT}"
fi
echo
CLUSTERS=$(gcloud container clusters list \
--project="${PROJECT}" \
--format='value(name,location)' 2>/dev/null || true)
if [ -z "${CLUSTERS}" ]; then
echo "No GKE clusters found in project ${PROJECT}"
echo
continue
fi
while read -r NAME LOCATION; do
[ -z "${NAME:-}" ] && continue
echo "----------------------------------------------------------------------"
echo "Cluster: ${NAME}"
echo "Location: ${LOCATION}"
echo "----------------------------------------------------------------------"
DESC_JSON=$(gcloud container clusters describe "${NAME}" \
--project "${PROJECT}" \
--location "${LOCATION}" \
--format=json 2>/dev/null || echo "{}")
ENABLED=$(echo "${DESC_JSON}" | jq -r '.authenticatorGroupsConfig.enabled // false')
SEC_GROUP=$(echo "${DESC_JSON}" | jq -r '.authenticatorGroupsConfig.securityGroup // ""')
echo "Google Groups for GKE:"
echo " Enabled: ${ENABLED}"
echo " Security Group: ${SEC_GROUP}"
if [ "${ENABLED}" != "true" ] || [ -z "${SEC_GROUP}" ] || [ "${SEC_GROUP}" = "null" ]; then
echo " STATUS: NOT COMPLIANT with CISGKE 5.8.2 (Google Groups for GKE not fully configured)"
else
echo " STATUS: Google Groups for GKE configured at cluster level"
fi
echo
echo "RBAC ClusterRoleBindings referencing Google Groups (by email-like subjects):"
# This uses kubectl and requires cluster access.
# Any machine with kubectl access and current context pointing to this cluster.
if kubectl config use-context "gke_${PROJECT}_${LOCATION}_${NAME}" >/dev/null 2>&1; then
kubectl get clusterrolebindings.rbac.authorization.k8s.io -o json 2>/dev/null | jq -r '
.items[]
| . as $crb
| $crb.subjects // []
| map(select(.kind=="Group" and (.name | test("@"))))
| select(length > 0)
| $crb.metadata.name as $name
| .[]
| " " + $name + " -> Group: " + .name
' || echo " (none found or error retrieving ClusterRoleBindings)"
echo
echo "RBAC RoleBindings referencing Google Groups (by email-like subjects):"
kubectl get rolebindings.rbac.authorization.k8s.io --all-namespaces -o json 2>/dev/null | jq -r '
.items[]
| . as $rb
| $rb.subjects // []
| map(select(.kind=="Group" and (.name | test("@"))))
| select(length > 0)
| ($rb.metadata.namespace + "/" + $rb.metadata.name) as $name
| .[]
| " " + $name + " -> Group: " + .name
' || echo " (none found or error retrieving RoleBindings)"
else
echo " NOTE: kubectl context gke_${PROJECT}_${LOCATION}_${NAME} not found or unusable."
echo " Skipping RBAC binding inspection for this cluster."
fi
echo
done <<< "${CLUSTERS}"
echo
done
cat <<'EOF'
INTERPRETING RESULTS:
1) Cluster-level Google Groups for GKE configuration:
- Compliant / desired:
Enabled: true
Security Group: gke-security-groups@<your-domain>
and the script prints:
STATUS: Google Groups for GKE configured at cluster level
- Potential problem (requires review):
Enabled: false
or Security Group is empty or "null"
and the script prints:
STATUS: NOT COMPLIANT with CISGKE 5.8.2 ...
This indicates:
* The cluster is not using Google Groups for GKE, OR
* The security group is not set, which does not meet the benchmark guidance.
2) RBAC role bindings:
- Desired pattern:
ClusterRoleBindings and RoleBindings reference Google Groups
(subjects of kind: Group, name looks like an email, e.g. team-admins@domain).
- Problem indicators:
* Few or no Group subjects using email-style names, but many User subjects
(per-user bindings), meaning access is managed per-user instead of via groups.
* Group names that are not your intended Google Groups for GKE (e.g. legacy groups).
This script only reports configuration for review. Enabling Google Groups for GKE
and restructuring RBAC bindings to use groups must be done manually following
the provider documentation and your access-control design.
EOF