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
Remediation
Manual Steps
-
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
{}ornull, Master Authorized Networks (MAN) is effectively disabled. - If
"enabled": trueis present, review thecidrBlocksto ensure they match your intended allowlist.
- If this returns
-
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
kubectlor 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.
- Collect the public IPs or CIDR ranges for:
-
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/0or 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.
- Define explicit IPv4 CIDR ranges (e.g.
-
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, setmaster_authorized_networks_configwith the same CIDRs) and apply, ensuring it matches the gcloud change to avoid drift.
- If MAN is currently disabled or misconfigured, enable/update it (run on any machine with gcloud access):
-
Verify configuration and effective access (on any machine with gcloud and from test clients)
- Re-check the cluster configuration:
Confirm:gcloud container clusters describe CLUSTER_NAME \--zone CLUSTER_ZONE \--format=json | jq '.masterAuthorizedNetworksConfig'
"enabled": true- The
cidrBlockslist matches the approved IP ranges.
- From a machine whose IP is not in the allowlist, attempt:
It should fail with an API access/connection error.kubectl get ns
- From an allowed IP, the same command should succeed.
- Re-check the cluster configuration:
-
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(withHas_Authorized_Networks = NOorYES):
Master Authorized Networks is not effectively protecting the control plane; this fails the intent of CIS GKE 5.6.3. -
MAN_Enabled = ENABLEDandHas_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.