Skip to main content

Ensure Master Authorized Networks Is Enabled

More Info:

Enable Master Authorized Networks To Restrict Access To The ClusterS Control Plane (Master Endpoint) To Only An Allowlist (Whitelist) Of Authorized Ips

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS GKE

Triage and Remediation

Remediation

Manual Steps
  1. Gather current configuration (on any machine with gcloud access)

    gcloud container clusters describe CLUSTER_NAME \
    --zone CLUSTER_ZONE \
    --format=json | jq '.masterAuthorizedNetworksConfig'
    • If this returns {} or null, Master Authorized Networks (MAN) is effectively disabled.
    • If "enabled": true is present, review the cidrBlocks to ensure they match your intended allowlist.
  2. Inventory legitimate control‑plane access sources (off-cluster / documentation review)

    • Collect the public IPs or CIDR ranges for:
      • Corporate offices / VPN egress gateways.
      • CI/CD systems that run kubectl or use Kubernetes APIs.
      • Bastion/jump hosts used by administrators.
    • Decide whether direct access from arbitrary developer laptops or the public internet is required; in most cases it should not be.
  3. Decide on the desired allowlist and approve with stakeholders

    • Define explicit IPv4 CIDR ranges (e.g. 203.0.113.10/32, 198.51.100.0/24) that should have API access.
    • Remove any broad ranges such as 0.0.0.0/0 or consumer ISP ranges unless there is a strong, documented justification and compensating controls (e.g. strong VPN, short-lived clusters).
    • Ensure there is at least one path (VPN, bastion, or CI/CD) that operations can use for break‑glass access if needed.
  4. Configure Master Authorized Networks (change via gcloud; IaC users mirror in Terraform/other)

    • If MAN is currently disabled or misconfigured, enable/update it (run on any machine with gcloud access):
      gcloud container clusters update CLUSTER_NAME \
      --zone CLUSTER_ZONE \
      --enable-master-authorized-networks \
      --master-authorized-networks <CIDR_1>,<CIDR_2>,<CIDR_3>
    • Replace <CIDR_1>,<CIDR_2>,<CIDR_3> with the approved list, e.g.:
      gcloud container clusters update CLUSTER_NAME \
      --zone CLUSTER_ZONE \
      --enable-master-authorized-networks \
      --master-authorized-networks 203.0.113.10/32,198.51.100.0/24
    • If using Terraform or another IaC tool, make equivalent changes there (for Terraform google_container_cluster, set master_authorized_networks_config with the same CIDRs) and apply, ensuring it matches the gcloud change to avoid drift.
  5. Verify configuration and effective access (on any machine with gcloud and from test clients)

    • Re-check the cluster configuration:
      gcloud container clusters describe CLUSTER_NAME \
      --zone CLUSTER_ZONE \
      --format=json | jq '.masterAuthorizedNetworksConfig'
      Confirm:
      • "enabled": true
      • The cidrBlocks list matches the approved IP ranges.
    • From a machine whose IP is not in the allowlist, attempt:
      kubectl get ns
      It should fail with an API access/connection error.
    • From an allowed IP, the same command should succeed.
  6. Document and monitor

    • Record the approved CIDR list, justification, and date in your security/change management system.
    • Set up periodic review (e.g., quarterly) and update the allowlist as office/VPN/CI IPs change, repeating steps 1–5 for each review.
Using kubectl

kubectl cannot enable or configure Master Authorized Networks because this setting is part of the managed control plane configuration in your cloud provider (GKE) rather than a Kubernetes API object. To remediate this finding, use the cloud provider console/CLI/IaC as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Report GKE Master Authorized Networks status for all clusters in the project.
# REQUIREMENTS:
# - gcloud CLI installed and authenticated
# - jq installed
#
# RUN ON: any machine with gcloud access to the GCP project.

set -euo pipefail

PROJECT_ID="$(gcloud config get-value project 2>/dev/null || true)"
if [[ -z "${PROJECT_ID}" ]]; then
echo "ERROR: No default project set in gcloud config."
echo "Run: gcloud config set project PROJECT_ID"
exit 1
fi

echo "Using project: ${PROJECT_ID}"
echo

# List all zones that have GKE clusters in this project
ZONES="$(gcloud container clusters list \
--project "${PROJECT_ID}" \
--format='value(location)' 2>/dev/null | sort -u)"

if [[ -z "${ZONES}" ]]; then
echo "No GKE clusters found in project ${PROJECT_ID}."
exit 0
fi

printf "Cluster,Location,MAN_Enabled,Has_Authorized_Networks,Authorized_CIDRs\n"

# For each cluster, describe and extract masterAuthorizedNetworksConfig
while read -r LOCATION; do
[[ -z "${LOCATION}" ]] && continue

# The same list command will work for both zonal and regional clusters
CLUSTERS_IN_LOC="$(gcloud container clusters list \
--project "${PROJECT_ID}" \
--region "${LOCATION}" \
--format='value(name)' 2>/dev/null || true)"

if [[ -z "${CLUSTERS_IN_LOC}" ]]; then
# Try as zone if region listing failed / empty
CLUSTERS_IN_LOC="$(gcloud container clusters list \
--project "${PROJECT_ID}" \
--zone "${LOCATION}" \
--format='value(name)' 2>/dev/null || true)"
fi

while read -r CLUSTER; do
[[ -z "${CLUSTER}" ]] && continue

# Describe cluster; location can be region or zone, gcloud will handle it
DESC_JSON="$(gcloud container clusters describe "${CLUSTER}" \
--project "${PROJECT_ID}" \
--location "${LOCATION}" \
--format=json 2>/dev/null || true)"

if [[ -z "${DESC_JSON}" ]]; then
printf "%s,%s,ERROR,ERROR,ERROR (failed to describe cluster)\n" \
"${CLUSTER}" "${LOCATION}"
continue
fi

# Extract masterAuthorizedNetworksConfig block
MAN_CFG="$(printf '%s\n' "${DESC_JSON}" | jq -c '.masterAuthorizedNetworksConfig')"

if [[ "${MAN_CFG}" == "null" || "${MAN_CFG}" == "{}" ]]; then
# MAN not configured at all
printf "%s,%s,DISABLED,NO,\n" "${CLUSTER}" "${LOCATION}"
continue
fi

ENABLED="$(printf '%s\n' "${MAN_CFG}" | jq -r '.enabled // "false"')"
# Extract all CIDRs if present
CIDRS="$(printf '%s\n' "${MAN_CFG}" \
| jq -r '.cidrBlocks[]? | .displayName + "=" + .cidrBlock' 2>/dev/null \
| paste -sd'|' -)"

if [[ "${ENABLED}" != "true" ]]; then
printf "%s,%s,DISABLED,YES,%s\n" "${CLUSTER}" "${LOCATION}" "${CIDRS}"
else
if [[ -z "${CIDRS}" ]]; then
# Enabled but no specific networks configured (effectively open)
printf "%s,%s,ENABLED,BUT_NO_CIDRS,\n" "${CLUSTER}" "${LOCATION}"
else
printf "%s,%s,ENABLED,YES,%s\n" "${CLUSTER}" "${LOCATION}" "${CIDRS}"
fi
fi

done <<< "${CLUSTERS_IN_LOC}"

done <<< "${ZONES}"

Interpretation of the output (problem indicators):

  • MAN_Enabled = DISABLED (with Has_Authorized_Networks = NO or YES):
    Master Authorized Networks is not effectively protecting the control plane; this fails the intent of CIS GKE 5.6.3.

  • MAN_Enabled = ENABLED and Has_Authorized_Networks = BUT_NO_CIDRS:
    The feature is enabled but no CIDR blocks are configured; this is functionally insecure and should be reviewed as non-compliant.

Only rows with MAN_Enabled = ENABLED and Has_Authorized_Networks = YES and a tightly scoped Authorized_CIDRs list meet the control; the exact networks should be manually reviewed for appropriateness.

Additional Reading: