Restrict Access To The Control Plane Endpoint
More Info:
Enable Master Authorized Networks to restrict access to the clusters control plane (master endpoint) to an allowlist of authorized IP addresses.
Risk Level
High
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS OKE
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
List current control plane access configuration
- On any machine with
gcloudand the correct project/permissions:gcloud container clusters describe CLUSTER_NAME \--region=CLUSTER_REGION \--project=PROJECT_ID \--format="yaml(name,endpoint,masterAuthorizedNetworksConfig,privateClusterConfig)" - Review
masterAuthorizedNetworksConfig:- If
enabled: trueandcidrBlocksis populated with specific IP ranges, MAN is enabled. - If it is absent,
enabled: false, or0.0.0.0/0is present, the control plane is overly exposed.
- If
- On any machine with
-
Identify legitimate admin and automation source IPs
- Collect the public IPs (or ranges) used by:
- Platform/cluster administrators.
- CI/CD systems, bastion hosts, VPNs, and on‑prem connectivity endpoints that need
kubectlaccess.
- Prefer stable ranges: VPN egress IPs, corporate NAT ranges, or bastion hosts over individual, dynamic user IPs.
- Collect the public IPs (or ranges) used by:
-
Decide the target allowed IP ranges
- Normalize and consolidate IPs into the smallest sensible set of CIDR blocks (e.g.
203.0.113.10/32,198.51.100.0/24). - Explicitly decide whether any broad ranges (e.g.
0.0.0.0/0,/16) are truly required; in most cases they should be removed. - Confirm with stakeholders that all operationally required access is covered by the finalized allowlist.
- Normalize and consolidate IPs into the smallest sensible set of CIDR blocks (e.g.
-
Configure / tighten Master Authorized Networks
- Using the Google Cloud Console:
- Go to: Kubernetes Engine → Clusters → select the cluster.
- Edit the cluster → Networking / Security section.
- Enable “Master Authorized Networks” (if disabled).
- Add each approved CIDR block; remove any unapproved or overly broad ranges.
- Save and apply changes (this may trigger a control plane update operation).
- Or using
gcloudon any machine with access:gcloud container clusters update CLUSTER_NAME \--region=CLUSTER_REGION \--project=PROJECT_ID \--enable-master-authorized-networks \--master-authorized-networks=203.0.113.10/32,198.51.100.0/24 - Be careful not to exclude your own current IP; otherwise, further
kubectlandgcloudaccess to the API endpoint may be blocked.
- Using the Google Cloud Console:
-
(Optional) Combine with private control plane if feasible
- If your environment supports it and it fits your network design, review whether the cluster is (or should be) a private cluster, limiting the control plane to internal addresses:
gcloud container clusters describe CLUSTER_NAME \--region=CLUSTER_REGION \--project=PROJECT_ID \--format="yaml(privateClusterConfig)"
- Decide, with your network/security teams, whether converting to or using a private cluster plus MAN further improves your exposure posture.
- If your environment supports it and it fits your network design, review whether the cluster is (or should be) a private cluster, limiting the control plane to internal addresses:
-
Verify remediation and document rationale
- On any machine with
gcloud:gcloud container clusters describe CLUSTER_NAME \--region=CLUSTER_REGION \--project=PROJECT_ID \--format="yaml(masterAuthorizedNetworksConfig)" - Confirm:
enabled: trueis present.cidrBlocksonly contains your approved ranges; no0.0.0.0/0or unnecessary broad CIDRs.
- Capture the output and the business justification for each allowed CIDR in your change records or security documentation for future audits.
- On any machine with
Using kubectl
kubectl cannot configure the control plane endpoint or Master Authorized Networks; this setting is managed at the cloud provider / managed control plane level (console, provider CLI, or IaC). Refer to the Manual Steps section for how to review and change this configuration in your provider.
Automation
#!/usr/bin/env bash
#
# audit-gke-master-authorized-networks.sh
#
# Purpose:
# Report whether Master Authorized Networks are enabled for a set of GKE clusters
# and list the allowed CIDR ranges where enabled.
#
# Requirements:
# - gcloud CLI installed and authenticated
# - jq installed
#
# Scope:
# - This runs from any machine with gcloud access to the relevant project(s).
#
# Usage:
# # Single project, all regions/zones:
# ./audit-gke-master-authorized-networks.sh PROJECT_ID
#
# # Multiple projects:
# ./audit-gke-master-authorized-networks.sh PROJECT_ID_1 PROJECT_ID_2 ...
#
set -euo pipefail
if ! command -v gcloud >/dev/null 2>&1; then
echo "ERROR: gcloud CLI 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
exit 1
fi
echo "timestamp,project,location,cluster_name,endpoint_private,master_authorized_networks_enabled,allowed_cidrs"
for PROJECT in "$@"; do
# List all clusters (regional and zonal) in the project
# Output in JSON to be parsed reliably
CLUSTERS_JSON=$(gcloud container clusters list \
--project="${PROJECT}" \
--format=json)
# If no clusters, continue
if [ "$(echo "${CLUSTERS_JSON}" | jq 'length')" -eq 0 ]; then
continue
fi
echo "${CLUSTERS_JSON}" | jq -c '.[]' | while read -r cluster; do
NAME=$(echo "${cluster}" | jq -r '.name')
LOCATION=$(echo "${cluster}" | jq -r '.location')
# Get full description including masterAuthorizedNetworksConfig
DESC_JSON=$(gcloud container clusters describe "${NAME}" \
--project="${PROJECT}" \
--region="${LOCATION}" \
--format=json 2>/dev/null || \
gcloud container clusters describe "${NAME}" \
--project="${PROJECT}" \
--zone="${LOCATION}" \
--format=json 2>/dev/null || echo "{}")
# Detect whether private endpoint is enabled (private cluster)
ENDPOINT_PRIVATE=$(echo "${DESC_JSON}" | jq -r '.privateClusterConfig.enablePrivateEndpoint // false')
# Detect whether Master Authorized Networks is enabled
MAN_ENABLED=$(echo "${DESC_JSON}" | jq -r '.masterAuthorizedNetworksConfig.enabled // false')
# Collect all CIDR blocks (if enabled)
CIDRS=$(echo "${DESC_JSON}" \
| jq -r '[.masterAuthorizedNetworksConfig.cidrBlocks[]?.cidrBlock] | join(";")')
# If MAN is disabled, set CIDRS to empty string for clarity
if [ "${MAN_ENABLED}" != "true" ]; then
CIDRS=""
fi
printf "%s,%s,%s,%s,%s,%s,%s\n" \
"$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
"${PROJECT}" \
"${LOCATION}" \
"${NAME}" \
"${ENDPOINT_PRIVATE}" \
"${MAN_ENABLED}" \
"${CIDRS}"
done
done
Explanation of problematic output:
- Focus on the
master_authorized_networks_enabledcolumn:falseindicates the control plane endpoint is not restricted by Master Authorized Networks and should be reviewed.
- Even when
master_authorized_networks_enabledistrue, review theallowed_cidrs:- Very broad ranges like
0.0.0.0/0,::/0, or organizationally-uncontrolled CIDRs are problematic and should be tightened.
- Very broad ranges like
- Consider
endpoint_private:- If
endpoint_privateisfalse(public endpoint) andmaster_authorized_networks_enabledisfalseor has overly broad CIDRs, this is especially high risk.
- If