Skip to main content

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

Manual Steps
  1. List current control plane access configuration

    • On any machine with gcloud and 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: true and cidrBlocks is populated with specific IP ranges, MAN is enabled.
      • If it is absent, enabled: false, or 0.0.0.0/0 is present, the control plane is overly exposed.
  2. 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 kubectl access.
    • Prefer stable ranges: VPN egress IPs, corporate NAT ranges, or bastion hosts over individual, dynamic user IPs.
  3. 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.
  4. 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 gcloud on 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 kubectl and gcloud access to the API endpoint may be blocked.
  5. (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.
  6. 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: true is present.
      • cidrBlocks only contains your approved ranges; no 0.0.0.0/0 or unnecessary broad CIDRs.
    • Capture the output and the business justification for each allowed CIDR in your change records or security documentation for future audits.
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_enabled column:
    • false indicates the control plane endpoint is not restricted by Master Authorized Networks and should be reviewed.
  • Even when master_authorized_networks_enabled is true, review the allowed_cidrs:
    • Very broad ranges like 0.0.0.0/0, ::/0, or organizationally-uncontrolled CIDRs are problematic and should be tightened.
  • Consider endpoint_private:
    • If endpoint_private is false (public endpoint) and master_authorized_networks_enabled is false or has overly broad CIDRs, this is especially high risk.