> ## 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:

Apply firewall rules that restrict traffic to and from GKE worker nodes based on tags, service accounts, and CIDR ranges. Tight firewalling limits lateral movement and unwanted access.

### 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 all node pools and instance groups**
           * On any machine with gcloud configured, list GKE node pools and note the node locations:
             ```bash theme={null}
             gcloud container clusters describe CLUSTER_NAME \
               --zone ZONE \
               --project PROJECT_ID \
               --format="json(nodePools[].{name:name,config:config})"
             ```
           * List managed instance groups backing the node pools:
             ```bash theme={null}
             gcloud compute instance-groups managed list \
               --project PROJECT_ID \
               --format="table(name,zone,baseInstanceName)"
             ```

        2. **Gather current worker node tags, service accounts, and networks**
           * For each node pool MIG/instance name pattern, describe a representative node to see its tags, service accounts, and network:
             ```bash theme={null}
             gcloud compute instances list \
               --project PROJECT_ID \
               --filter="name~'BASE_INSTANCE_NAME'" \
               --format="table(name,zone)"  # pick an instance name/zone

             gcloud compute instances describe INSTANCE_NAME \
               --zone ZONE \
               --project PROJECT_ID \
               --format=json | jq '{name:.name, tags:.tags.items, serviceAccounts:[.serviceAccounts[].email], networks:[.networkInterfaces[].network], subnets:[.networkInterfaces[].subnetwork]}'
             ```

        3. **Inventory existing firewall rules affecting worker nodes**
           * List all firewall rules in the relevant VPC network(s):
             ```bash theme={null}
             gcloud compute networks list --project PROJECT_ID \
               --format="table(name,autoCreateSubnetworks)"

             gcloud compute firewall-rules list \
               --project PROJECT_ID \
               --format="table(name,network,direction,priority,sourceRanges,targetTags,targetServiceAccounts,allowed,denied)"
             ```
           * For each worker node tag or service account from step 2, filter rules that apply:
             ```bash theme={null}
             gcloud compute firewall-rules list \
               --project PROJECT_ID \
               --filter="targetTags:list(TAG1,TAG2)" \
               --format="table(name,direction,sourceRanges,allowed,denied)"

             gcloud compute firewall-rules list \
               --project PROJECT_ID \
               --filter="targetServiceAccounts:SA_EMAIL" \
               --format="table(name,direction,sourceRanges,allowed,denied)"
             ```

        4. **Decide the minimal required traffic**
           * Review which sources legitimately need to reach worker nodes (e.g., control-plane IPs, load balancer health checks, CI/CD CIDRs, on-prem CIDRs) and which egress from nodes is required (e.g., Google APIs, container registries, specific partner services).
           * Document allowed CIDR ranges, ports/protocols, and whether scoping by target tags or target service accounts is preferable for each node pool.

        5. **Create or tighten firewall rules based on the decision**
           * For each required traffic pattern, create least-privilege rules scoped by tags or service accounts (example template; replace placeholders):
             ```bash theme={null}
             gcloud compute firewall-rules create WORKER-INGRESS-ALLOWED \
               --project PROJECT_ID \
               --network VPC_NETWORK_NAME \
               --priority 1000 \
               --direction INGRESS \
               --action ALLOW \
               --target-tags WORKER_NODE_TAG \
               --target-service-accounts WORKER_SA_EMAIL \
               --source-ranges SOURCE_CIDR_1,SOURCE_CIDR_2 \
               --rules tcp:443,tcp:10250

             gcloud compute firewall-rules create WORKER-EGRESS-ALLOWED \
               --project PROJECT_ID \
               --network VPC_NETWORK_NAME \
               --priority 1000 \
               --direction EGRESS \
               --action ALLOW \
               --target-tags WORKER_NODE_TAG \
               --target-service-accounts WORKER_SA_EMAIL \
               --destination-ranges DEST_CIDR_1,DEST_CIDR_2 \
               --rules tcp:443
             ```
           * Remove or lower-priority broad rules that unnecessarily allow `0.0.0.0/0` or wide port ranges to worker node tags/service accounts, ensuring you do not block required control-plane or health-check traffic.

        6. **Verify and re-assess exposure**
           * Re-list firewall rules and confirm workers are only covered by appropriately scoped rules:
             ```bash theme={null}
             gcloud compute firewall-rules list \
               --project PROJECT_ID \
               --filter="targetTags:list(WORKER_NODE_TAG) OR targetServiceAccounts:WORKER_SA_EMAIL" \
               --format="table(name,direction,sourceRanges,destinationRanges,allowed,denied,priority)"
             ```
           * Optionally, perform connectivity tests from expected sources and from disallowed networks to confirm access is correctly restricted.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot configure Google Cloud firewall rules or VPC networking; those changes must be made in the GCP console, via `gcloud`, or through IaC (e.g., Terraform) at the cloud provider level. Refer to the Manual Steps section for guidance on reviewing and implementing appropriate firewall rules for GKE worker nodes.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        set -euo pipefail

        # REQUIREMENTS:
        # - Runs on: any machine with gcloud, kubectl, and jq configured
        # - Auth: gcloud auth logged in with permissions to list GKE clusters, nodes, and firewall rules

        PROJECT_ID="my-project-id"   # <-- set explicitly
        REGION=""                    # optional: set to restrict to a region, else all regions in project

        ################################################################################
        # Helper: list GKE clusters (optionally by region)
        ################################################################################
        if [[ -n "${REGION}" ]]; then
          CLUSTERS_JSON=$(gcloud container clusters list \
            --project "${PROJECT_ID}" \
            --region "${REGION}" \
            --format=json)
        else
          CLUSTERS_JSON=$(gcloud container clusters list \
            --project "${PROJECT_ID}" \
            --format=json)
        fi

        echo "===== GKE Node Firewall Review Report (project: ${PROJECT_ID}) ====="
        echo

        echo "${CLUSTERS_JSON}" | jq -r '.[] | [.name, .location, .network, .subnetwork] | @tsv' | \
        while IFS=$'\t' read -r CLUSTER LOCATION NETWORK SUBNETWORK; do
          echo "-------------------------------------------------------------------------------"
          echo "Cluster: ${CLUSTER}  Location: ${LOCATION}"
          echo "VPC Network: ${NETWORK}"
          echo "Subnetwork: ${SUBNETWORK}"
          echo

          # Configure kubectl for this cluster
          gcloud container clusters get-credentials "${CLUSTER}" \
            --project "${PROJECT_ID}" \
            --location "${LOCATION}" >/dev/null 2>&1

          ##############################################################################
          # 1. List node instances, their tags, service accounts, and networks
          ##############################################################################
          echo "[1] Node instances, tags, service accounts, and networks"
          NODE_NAMES=$(kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{" "}{end}')

          if [[ -z "${NODE_NAMES}" ]]; then
            echo "  No nodes found in cluster."
            echo
            continue
          fi

          for NODE in ${NODE_NAMES}; do
            # Get underlying GCE instance name and zone from node labels
            INSTANCE_NAME=$(kubectl get node "${NODE}" -o jsonpath='{.metadata.name}')
            INSTANCE_ZONE=$(kubectl get node "${NODE}" \
              -o jsonpath='{.metadata.labels."topology\.gke\.io/zone"}')

            if [[ -z "${INSTANCE_ZONE}" ]]; then
              # Fallback to old label
              INSTANCE_ZONE=$(kubectl get node "${NODE}" \
                -o jsonpath='{.metadata.labels."topology\.kubernetes\.io/zone"}')
            fi

            echo "  Node: ${NODE}"
            echo "    GCE instance: ${INSTANCE_NAME}"
            echo "    Zone: ${INSTANCE_ZONE}"

            INSTANCE_JSON=$(gcloud compute instances describe "${INSTANCE_NAME}" \
              --zone "${INSTANCE_ZONE}" \
              --project "${PROJECT_ID}" \
              --format=json)

            echo "${INSTANCE_JSON}" | jq -r '
              "    Tags: " + ( .tags.items // [] | join(",") | if . == "" then "(none)" else . end ) + "\n" +
              "    Service Accounts: " + ( .serviceAccounts[].email // "" ) + "\n" +
              "    Network(s): " + ( .networkInterfaces[].network // "" ) + "\n" +
              "    Subnetwork(s): " + ( .networkInterfaces[].subnetwork // "" )
            '
            echo
          done

          ##############################################################################
          # 2. List firewall rules that could apply to these node instances
          ##############################################################################
          echo "[2] Firewall rules potentially applying to cluster nodes"
          echo "  (filtered by network: ${NETWORK})"
          echo

          # Get all unique tags and service accounts for nodes in this cluster
          TMP_NODE_INFO=$(mktemp)
          for NODE in ${NODE_NAMES}; do
            INSTANCE_NAME=$(kubectl get node "${NODE}" -o jsonpath='{.metadata.name}')
            INSTANCE_ZONE=$(kubectl get node "${NODE}" \
              -o jsonpath='{.metadata.labels."topology\.gke\.io/zone"}')
            if [[ -z "${INSTANCE_ZONE}" ]]; then
              INSTANCE_ZONE=$(kubectl get node "${NODE}" \
                -o jsonpath='{.metadata.labels."topology\.kubernetes\.io/zone"}')
            fi

            gcloud compute instances describe "${INSTANCE_NAME}" \
              --zone "${INSTANCE_ZONE}" \
              --project "${PROJECT_ID}" \
              --format=json >> "${TMP_NODE_INFO}"
          done

          NODE_TAGS=$(jq -r '.tags.items[]? // empty' "${TMP_NODE_INFO}" | sort -u)
          NODE_SAS=$(jq -r '.serviceAccounts[].email? // empty' "${TMP_NODE_INFO}" | sort -u)

          echo "  Node tags:"
          if [[ -n "${NODE_TAGS}" ]]; then
            echo "${NODE_TAGS}" | sed 's/^/    - /'
          else
            echo "    - (none)"
          fi

          echo "  Node service accounts:"
          if [[ -n "${NODE_SAS}" ]]; then
            echo "${NODE_SAS}" | sed 's/^/    - /'
          else
            echo "    - (none)"
          fi
          echo

          # List all firewall rules on this network
          FW_JSON=$(gcloud compute firewall-rules list \
            --project "${PROJECT_ID}" \
            --filter="network:${NETWORK}" \
            --format=json)

          echo "  Firewall rules on network ${NETWORK}:"
          echo "${FW_JSON}" | jq -r '
            .[] |
            "  - Name: " + .name + "\n" +
            "    Direction: " + .direction + "\n" +
            "    Priority: " + ( .priority | tostring ) + "\n" +
            "    Action: " + .allowed[0].IPProtocol? // .denied[0].IPProtocol? // "N/A" + "\n" +
            "    Disabled: " + ( .disabled | tostring ) + "\n" +
            "    Target tags: " + ( .targetTags // [] | join(",") ) + "\n" +
            "    Target service accounts: " + ( .targetServiceAccounts // [] | join(",") ) + "\n" +
            "    Source ranges: " + ( .sourceRanges // [] | join(",") ) + "\n" +
            "    Source tags: " + ( .sourceTags // [] | join(",") ) + "\n" +
            "    Source service accounts: " + ( .sourceServiceAccounts // [] | join(",") ) + "\n" +
            "    Destination ranges: " + ( .destinationRanges // [] | join(",") ) + "\n" +
            "    Allowed: " + ( .allowed // [] | map(.IPProtocol + ":" + ((.ports // []) | join(","))) | join(";") ) + "\n" +
            "    Denied: " + ( .denied // [] | map(.IPProtocol + ":" + ((.ports // []) | join(","))) | join(";") ) + "\n"
          '

          rm -f "${TMP_NODE_INFO}"
          echo
        done

        echo "===== END OF REPORT ====="
        ```

        **How to interpret the output (what indicates a problem)**

        Review the report for each cluster and flag cases like:

        * Nodes with tags or service accounts that are not used by any **restrictive** firewall rules (no `targetTags` / `targetServiceAccounts` match), meaning they rely only on wide-open network rules.
        * Firewall rules with:
          * Very broad `sourceRanges` (e.g. `0.0.0.0/0` or full private ranges) reaching node tags/service accounts.
          * No `targetTags` or `targetServiceAccounts` (applies to all instances in the network).
          * Allowed protocols/ports that are unnecessary or very broad (e.g. `tcp:1-65535`, `all`).
        * Absence of dedicated rules that:
          * Limit inbound access to nodes only from known admin/source CIDRs or specific service accounts/tags.
          * Limit east‑west (lateral) traffic between node pools, environments, or workloads where isolation is desired.

        Use these findings to manually design tighter firewall rules with `gcloud compute firewall-rules create` as per the benchmark remediation.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
