> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Consider Firewalling Gke Worker Nodes

### More Info:

Reduce The Network Attack Surface Of Gke Nodes By Using Firewalls To Restrict Ingress And Egress Traffic

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS GKE

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify node networks, tags, and service accounts**\
           Run on any machine with gcloud access:
           ```bash theme={null}
           PROJECT_ID="<PROJECT_ID>"
           CLUSTER_NAME="<GKE_CLUSTER_NAME>"
           LOCATION="<CLUSTER_LOCATION>"   # e.g. us-central1-b or us-central1

           gcloud container clusters describe "$CLUSTER_NAME" \
             --zone "$LOCATION" \
             --project "$PROJECT_ID" \
             --format="json(network,subnetwork,nodePools)"

           gcloud compute instances list \
             --project "$PROJECT_ID" \
             --filter="metadata.cluster-name=$CLUSTER_NAME" \
             --format="json(name,zone,tags,serviceAccounts,networkInterfaces)"
           ```
           Review which VPC/subnets your nodes use, their network tags, and node service accounts.

        2. **List current firewall rules affecting node networks/tags**
           ```bash theme={null}
           NETWORK_SELF_LINK="<NETWORK_SELF_LINK_FROM_STEP_1>"

           gcloud compute firewall-rules list \
             --project "$PROJECT_ID" \
             --filter="network=$NETWORK_SELF_LINK" \
             --format="json(name,network,direction,priority,allowed,denied,\
           ```

        sourceRanges,sourceTags,sourceServiceAccounts,targetTags,targetServiceAccounts)"

        ````
        Identify rules whose `targetTags` or `targetServiceAccounts` match the nodes, and note overly broad `0.0.0.0/0` or large CIDR ranges and wide `allowed` ports (0-65535, tcp:*, udp:*).

        3. **Determine required ingress/egress for worker nodes**  
        Using GKE documentation and your application requirements, list:
        - Required **ingress** sources: control plane, load balancers, admins, other services.  
        - Required **egress** destinations: control plane, container registry, logging/monitoring, DNS, specific external services.  
        Decide which traffic can be narrowed by:
        - Restricting CIDR ranges
        - Restricting ports/protocols
        - Using `target-tags` / `target-service-accounts` instead of broad targets.

        4. **Design and (optionally) test restrictive firewall rules**  
        For each needed traffic pattern, define a corresponding rule. Example (ingress from control plane to nodes on required ports only):
        ```bash
        gcloud compute firewall-rules create "gke-nodes-ingress-controlplane" \
          --project "$PROJECT_ID" \
          --network "$NETWORK_SELF_LINK" \
          --priority 1000 \
          --direction INGRESS \
          --action ALLOW \
          --target-tags "gke-node" \
          --source-ranges "<CONTROL_PLANE_CIDR>" \
          --rules "tcp:10250,tcp:30000-32767"
        ````

        For egress restriction example:

        ```bash theme={null}
        gcloud compute firewall-rules create "gke-nodes-egress-restricted" \
          --project "$PROJECT_ID" \
          --network "$NETWORK_SELF_LINK" \
          --priority 1000 \
          --direction EGRESS \
          --action ALLOW \
          --target-tags "gke-node" \
          --destination-ranges "<ALLOWED_DEST_CIDR>" \
          --rules "tcp:443,udp:53"
        ```

        Adjust names, CIDRs, tags, ports, and directions according to your design.

        5. **Tighten or remove overly permissive rules**\
           For rules you deem too broad:
           * Edit them in your IaC (if applicable) and redeploy, or:
           * Update or delete directly:
             ```bash theme={null}
             gcloud compute firewall-rules update "<RULE_NAME>" \
               --project "$PROJECT_ID" \
               --source-ranges "<NARROWED_CIDR_RANGES>" \
               --rules "<NARROWED_RULES>"
             ```
             or
             ```bash theme={null}
             gcloud compute firewall-rules delete "<RULE_NAME>" \
               --project "$PROJECT_ID"
             ```
           Make changes incrementally and monitor workload behavior to avoid breaking connectivity.

        6. **Verify node exposure after changes**\
           Re-run:
           ```bash theme={null}
           gcloud compute firewall-rules list \
             --project "$PROJECT_ID" \
             --filter="network=$NETWORK_SELF_LINK" \
             --format="table(name,direction,priority,sourceRanges,destinationRanges,allowed,denied,targetTags,targetServiceAccounts)"
           ```
           Confirm that:
           * Worker-node-related rules have specific `target-tags`/`target-service-accounts`.
           * Ingress and egress rules use the minimal necessary CIDR ranges and ports.
           * No remaining broad `0.0.0.0/0` rules apply to worker nodes unless explicitly justified.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot configure GKE node firewall rules, because they are managed at the Google Cloud VPC / firewall level, not via Kubernetes API objects. Configure firewall rules using Google Cloud (console, `gcloud`, or IaC) as described in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Purpose:
        #   Summarize GKE node networks / tags / service accounts and the
        #   firewall rules that apply to them, so you can manually review
        #   whether worker nodes are sufficiently firewalled.
        #
        # Requirements:
        #   - Run on any machine with:
        #       * gcloud configured (project, auth)
        #       * kubectl configured for the target cluster
        #       * jq installed

        set -euo pipefail

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

        echo "Using project: ${PROJECT_ID}" >&2
        echo >&2

        # 1. Discover node pools / nodes from the cluster via kubectl
        echo "=== Kubernetes nodes (from kubectl) ===" >&2
        kubectl get nodes -o wide
        echo >&2

        NODE_INSTANCE_NAMES=($(kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{" "}{end}'))

        if [[ ${#NODE_INSTANCE_NAMES[@]} -eq 0 ]]; then
          echo "No nodes found via kubectl; exiting." >&2
          exit 0
        fi

        # 2. Map Kubernetes node names to GCE instance selfLinks
        echo "=== Mapping Kubernetes nodes to GCE instances ===" >&2
        kubectl get nodes -o json | jq -r '.items[] |
          {name: .metadata.name, providerID: .spec.providerID} |
          .name + " " + (.providerID // "")' | sed 's|gce://||'
        echo >&2

        # Extract instance names and zones from providerID, if present
        GCE_INSTANCES=()
        GCE_ZONES=()

        while read -r k8s_name provider_id; do
          # provider_id format: projects/PROJECT_ID/zones/ZONE/instances/INSTANCE_NAME
          if [[ -z "${provider_id}" ]]; then
            continue
          fi
          zone="$(awk -F'/' '{print $(NF-3)}' <<< "${provider_id}")"
          inst="$(awk -F'/' '{print $NF}' <<< "${provider_id}")"
          GCE_INSTANCES+=("${inst}")
          GCE_ZONES+=("${zone}")
        done < <(kubectl get nodes -o json | jq -r '.items[] |
          {name: .metadata.name, providerID: .spec.providerID} |
          .name + " " + (.providerID // "")' | sed 's|gce://||')

        if [[ ${#GCE_INSTANCES[@]} -eq 0 ]]; then
          echo "No GCE-backed nodes discovered from providerID; ensure this is a GKE cluster." >&2
          exit 0
        fi

        # Deduplicate zone list
        mapfile -t UNIQUE_ZONES < <(printf "%s\n" "${GCE_ZONES[@]}" | sort -u)

        echo "Discovered zones for nodes: ${UNIQUE_ZONES[*]}" >&2
        echo >&2

        # 3. Gather firewall-relevant properties for each node instance
        echo "=== Node instance firewall-relevant properties ==="
        for zone in "${UNIQUE_ZONES[@]}"; do
          echo
          echo "Zone: ${zone}"
          gcloud compute instances list \
            --project "${PROJECT_ID}" \
            --zones "${zone}" \
            --format 'table(
              name,
              zone.basename(),
              networkInterfaces[0].network.scope(),
              networkInterfaces[0].subnetwork.scope(),
              networkInterfaces[0].networkIP,
              tags.items.list():label=tags,
              serviceAccounts[].email.list():label=serviceAccounts
            )'
        done
        echo

        # 4. List firewall rules that may apply to these nodes
        echo "=== Firewall rules that may apply to worker nodes ==="

        # Get all unique tags and service accounts from the instances we found
        NODE_TAGS=()
        NODE_SAS=()

        for zone in "${UNIQUE_ZONES[@]}"; do
          while IFS= read -r tag; do
            [[ -n "${tag}" ]] && NODE_TAGS+=("${tag}")
          done < <(gcloud compute instances list \
              --project "${PROJECT_ID}" \
              --zones "${zone}" \
              --filter="name=(${GCE_INSTANCES[*]})" \
              --format='flattened(tags.items[])' 2>/dev/null | awk '{print $2}' | sed '/^$/d')

          while IFS= read -r sa; do
            [[ -n "${sa}" ]] && NODE_SAS+=("${sa}")
          done < <(gcloud compute instances list \
              --project "${PROJECT_ID}" \
              --zones "${zone}" \
              --filter="name=(${GCE_INSTANCES[*]})" \
              --format='flattened(serviceAccounts.email)' 2>/dev/null | awk '{print $2}' | sed '/^$/d')
        done

        mapfile -t UNIQUE_TAGS < <(printf "%s\n" "${NODE_TAGS[@]:-}" | sort -u)
        mapfile -t UNIQUE_SAS  < <(printf "%s\n" "${NODE_SAS[@]:-}"  | sort -u)

        echo "Worker node tags: ${UNIQUE_TAGS[*]:-(none)}"
        echo "Worker node service accounts: ${UNIQUE_SAS[*]:-(none)}"
        echo

        echo "--- Relevant firewall rules (by targetTags) ---"
        if [[ ${#UNIQUE_TAGS[@]} -gt 0 ]]; then
          gcloud compute firewall-rules list \
            --project "${PROJECT_ID}" \
            --filter="($(printf 'targetTags:%s OR ' "${UNIQUE_TAGS[@]}" | sed 's/ OR $//'))" \
            --format='table(
              name,
              direction,
              priority,
              disabled,
              network,
              targetTags.list(),
              targetServiceAccounts.list(),
              sourceRanges.list(),
              destinationRanges.list(),
              allowed[].map().firewall_rule().list():label=allowed,
              denied[].map().firewall_rule().list():label=denied
            )'
        else
          echo "No tags found on worker nodes."
        fi

        echo
        echo "--- Relevant firewall rules (by targetServiceAccounts) ---"
        if [[ ${#UNIQUE_SAS[@]} -gt 0 ]]; then
          gcloud compute firewall-rules list \
            --project "${PROJECT_ID}" \
            --filter="($(printf 'targetServiceAccounts:%s OR ' "${UNIQUE_SAS[@]}" | sed 's/ OR $//'))" \
            --format='table(
              name,
              direction,
              priority,
              disabled,
              network,
              targetTags.list(),
              targetServiceAccounts.list(),
              sourceRanges.list(),
              destinationRanges.list(),
              allowed[].map().firewall_rule().list():label=allowed,
              denied[].map().firewall_rule().list():label=denied
            )'
        else
          echo "No service accounts found on worker nodes (unexpected for GKE)."
        fi

        echo
        echo "=== Guidance: what output indicates a problem? ==="
        cat <<'EOF'
        Review the above data looking for:

        1) Nodes with no specific firewall selectors
           - Instances show empty 'tags' AND use generic / shared service accounts.
           - Effect: firewall rules likely apply broadly to the whole network/subnet, not tightly scoped to worker nodes.

        2) Overly permissive ingress
           - Firewall rules with:
               direction: INGRESS
               sourceRanges: 0.0.0.0/0 (or very broad CIDRs)
             combined with:
               allowed: tcp/udp/icmp "all" or many ports
             and targeting worker node tags or service accounts.
           - This indicates a large attack surface for inbound traffic to nodes.

        3) Overly permissive egress
           - Firewall rules with:
               direction: EGRESS
               destinationRanges: 0.0.0.0/0
             and allowing many or all protocols/ports without restriction.
           - This indicates lack of outbound control from worker nodes.

        4) Missing explicit rules
           - If you see that nodes share the same network and subnet as many other workloads,
             but there are few or no firewall rules targeting their specific tags/service accounts,
             then worker node traffic is likely governed only by coarse, shared rules.

        5) Disabled or shadowed rules
           - disabled: true on rules that were intended to restrict node traffic.
           - Very low priority "allow" rules that override more restrictive ones.

        Any of the above patterns suggest that the cluster does not adequately "firewall GKE worker nodes"
        as recommended, and you should tighten or redesign firewall rules using:
          gcloud compute firewall-rules create/patch/delete ...
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://cloud.google.com/vpc/docs/firewalls](https://cloud.google.com/vpc/docs/firewalls)
