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

# Minimize Cluster Access To Read-Only For Gcr

### More Info:

Scan images being deployed to Amazon EKS for vulnerabilities.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* APRA CPS 234 (Australia)
* BSI C5 (Germany)
* Brazil LGPD
* CCPA / CPRA (California)
* CIS Critical Security Controls v8
* CIS GKE
* 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

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify which identities your cluster nodes use to pull images**
           * On any machine with `gcloud` access, list GKE node service accounts (Workload Identity or node SA) you expect to use for image pulls:
             ```bash theme={null}
             gcloud container clusters describe <cluster-name> \
               --region <region> \
               --project <project-id> \
               --format="yaml(nodeConfig.serviceAccount,workloadIdentityConfig)"
             ```
           * Note each service account email that should have read-only access to Artifact Registry and/or GCR.

        2. **Review project‑level roles that allow image push or overwrite**
           * On any machine with `gcloud` access, check for broad storage roles in the project (these may implicitly allow write access to GCR buckets):
             ```bash theme={null}
             gcloud projects get-iam-policy <project-id> \
               --flatten="bindings[].members" \
               --format='table(bindings.members,bindings.role)' \
               --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner"
             ```
           * For any member that matches a node or workload service account, decide whether it truly needs project‑wide write access. If not, plan to remove or narrow that role via your normal IAM/IaC process.

        3. **For Artifact Registry: ensure only `artifactregistry.reader` on needed repositories**
           * On any machine with `gcloud` access, for each Artifact Registry repo used by the cluster:
             ```bash theme={null}
             gcloud artifacts repositories get-iam-policy <repository> \
               --location=<repository-location>
             ```
           * For each node/workload service account, confirm it has at most:
             * `roles/artifactregistry.reader` on that repo.
           * If you find stronger roles (for example `roles/artifactregistry.writer` or `roles/editor`) that are not strictly required, plan to remove them and instead grant only:
             ```bash theme={null}
             gcloud artifacts repositories add-iam-policy-binding <repository> \
               --location=<repository-location> \
               --member="serviceAccount:<service-account-email>" \
               --role="roles/artifactregistry.reader"
             ```

        4. **For GCR: inspect bucket‑level permissions for write capability**
           * On any machine with `gsutil` access, check IAM on the GCR bucket:
             ```bash theme={null}
             gsutil iam get gs://artifacts.<project-id>.appspot.com
             ```
           * Look for any of these roles on node/workload service accounts:
             * `roles/storage.admin` (or `roles/storage.objectAdmin`, `roles/storage.objectCreator`)
             * Legacy roles: `roles/storage.legacyBucketOwner`, `roles/storage.legacyBucketWriter`, `roles/storage.legacyObjectOwner`
           * If the account needs only to pull images, plan to:
             * Ensure it has `objectViewer`:
               ```bash theme={null}
               gsutil iam ch serviceAccount:<service-account-email>:objectViewer \
                 gs://artifacts.<project-id>.appspot.com
               ```
             * Remove write‑capable roles that are not required:
               ```bash theme={null}
               gsutil iam ch -d serviceAccount:<service-account-email>:<role> \
                 gs://artifacts.<project-id>.appspot.com
               ```

        5. **If write access is inherited from project‑level IAM, update policy carefully**
           * If in step 2 you found that node/workload service accounts are granted storage‑admin‑type roles at the project level and they do not need them, export the policy for review:
             ```bash theme={null}
             gcloud projects get-iam-policy <project-id> --format=yaml > /tmp/<project-id>-policy.yaml
             ```
           * Edit `/tmp/<project-id>-policy.yaml` to remove or narrow only those bindings that are not required for other workloads, following your change‑management process.
           * Re‑apply when you are confident of the impact:
             ```bash theme={null}
             gcloud projects set-iam-policy <project-id> /tmp/<project-id>-policy.yaml
             ```

        6. **Re‑verify that only read‑only access remains**
           * On any machine with `gcloud` access, re‑run:
             ```bash theme={null}
             gcloud projects get-iam-policy <project-id> \
               --flatten="bindings[].members" \
               --format='table(bindings.members,bindings.role)' \
               --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner"
             ```
           * Confirm that no node or workload service accounts used by the GKE cluster appear with these roles, and that for Artifact Registry/GCR they only have read‑only roles needed to pull images.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot change Container Registry or Artifact Registry IAM permissions; this finding must be fixed in Google Cloud IAM / registry configuration using gcloud, gsutil, or IaC. See the Manual Steps section for how to review and adjust project, bucket, and repository permissions to enforce read‑only access.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Audit script for CIS GKE 5.1.3:
        # "Minimize cluster access to read-only for Container Image repositories"
        #
        # REQUIREMENTS:
        # - Run on any machine with:
        #     - gcloud, gsutil, jq, and kubectl installed
        #     - Access to the target GCP project and GKE clusters
        # - You must set or pass the GCP project ID.
        #
        # USAGE:
        #   ./audit_cis_gke_5_1_3.sh <GCP_PROJECT_ID>
        #
        # OUTPUT:
        # - Lists GCP IAM bindings where principals have write/admin-level Storage roles
        #   that could allow pushing/overwriting images in GCR/AR.
        # - You must manually assess which of these principals are used by the GKE
        #   cluster nodes or workloads and whether they should be reduced to read-only.

        set -euo pipefail

        PROJECT_ID="${1:-}"

        if [[ -z "${PROJECT_ID}" ]]; then
          echo "Usage: $0 <GCP_PROJECT_ID>" >&2
          exit 1
        fi

        echo "=== CIS GKE 5.1.3 audit for project: ${PROJECT_ID} ==="
        echo

        ###############################################################################
        # 1. Identify Kubernetes-related service accounts in this project
        ###############################################################################
        echo ">> Discovering Kubernetes-related Google service accounts (GSA)..."
        echo

        # Common patterns:
        # - GKE default node service accounts (if manually created) often include "gke"
        # - Workload Identity GSA names vary; we just list all in the project.
        gcloud iam service-accounts list \
          --project="${PROJECT_ID}" \
          --format="table(email,displayName)" \
          | sed 's/^/    /'
        echo

        ###############################################################################
        # 2. Project-level IAM: find principals with Storage write/admin privileges
        ###############################################################################
        echo ">> Project-level IAM: principals with Storage write/admin-type roles"
        echo "   (These may inherit access to GCR buckets or AR repositories)"
        echo

        gcloud projects get-iam-policy "${PROJECT_ID}" \
          --flatten="bindings[].members" \
          --format='table(bindings.members,bindings.role)' \
          --filter="bindings.role:roles/storage.admin OR bindings.role:roles/storage.objectAdmin OR bindings.role:roles/storage.objectCreator OR bindings.role:roles/storage.legacyBucketOwner OR bindings.role:roles/storage.legacyBucketWriter OR bindings.role:roles/storage.legacyObjectOwner" \
          | sed 's/^/    /'

        echo
        cat <<'EOF'
        Interpretation:
          - Any member here that is a service account used by:
              * GKE node pools (node service accounts), or
              * Workload Identity for pods
            has more than read-only access to GCR/AR via project-level roles.
          - PROBLEM: If a Kubernetes-related service account appears with any of these
            roles, it violates the intent of CIS GKE 5.1.3. It should typically be
            restricted to objectViewer / artifactregistry.reader only.

        EOF

        ###############################################################################
        # 3. GCR: bucket-level IAM for artifacts.<project_id>.appspot.com
        ###############################################################################
        GCR_BUCKET="gs://artifacts.${PROJECT_ID}.appspot.com"

        echo ">> GCR bucket-level IAM for ${GCR_BUCKET}"
        echo

        if gsutil ls "${GCR_BUCKET}" >/dev/null 2>&1; then
          gsutil iam get "${GCR_BUCKET}" | jq '.bindings[] | {role: .role, members: .members}' \
            | sed 's/^/    /'
        else
          echo "    Bucket ${GCR_BUCKET} does not exist or is not accessible."
        fi

        cat <<'EOF'

        Interpretation:
          - Look for roles that allow write/admin capabilities such as:
              roles/storage.admin
              roles/storage.objectAdmin
              roles/storage.objectCreator
              roles/storage.legacyBucketOwner
              roles/storage.legacyBucketWriter
              roles/storage.legacyObjectOwner
          - PROBLEM: Any Kubernetes-related service account (e.g. GKE node SA or
            Workload Identity GSA) appearing with any of the above roles at this
            bucket is overly privileged and can push/overwrite images.

        EOF

        ###############################################################################
        # 4. Artifact Registry: per-repository IAM
        ###############################################################################
        echo ">> Artifact Registry repositories and IAM bindings with elevated roles"
        echo

        # List all AR repositories
        REPOS_JSON="$(gcloud artifacts repositories list \
          --project="${PROJECT_ID}" \
          --location=- \
          --format=json || echo '[]')"

        if [[ "${REPOS_JSON}" == "[]" ]]; then
          echo "    No Artifact Registry repositories found in project ${PROJECT_ID}."
        else
          echo "${REPOS_JSON}" | jq -r '.[] | "\(.name) \(.format) \(.location)"' | while read -r REPO_NAME REPO_FORMAT REPO_LOCATION; do
            echo "  Repository: ${REPO_NAME} (format=${REPO_FORMAT}, location=${REPO_LOCATION})"
            # Extract repository ID from the full resource name projects/PROJECT/locations/LOC/repositories/REPO
            REPO_ID="$(basename "${REPO_NAME}")"

            # Get IAM policy for the specific repository
            gcloud artifacts repositories get-iam-policy "${REPO_ID}" \
              --project="${PROJECT_ID}" \
              --location="${REPO_LOCATION}" \
              --format=json \
              | jq '.bindings[] | select(.role | test("artifactregistry.admin|artifactregistry.writer|storage.admin|storage.objectAdmin|storage.objectCreator|storage.legacyBucketOwner|storage.legacyBucketWriter|storage.legacyObjectOwner"))' \
              | sed 's/^/    /' || true

            echo
          done
        fi

        cat <<'EOF'
        Interpretation:
          - Focus on roles that exceed read-only, such as:
              roles/artifactregistry.admin
              roles/artifactregistry.writer
              roles/storage.admin
              roles/storage.objectAdmin
              roles/storage.objectCreator
              roles/storage.legacyBucketOwner
              roles/storage.legacyBucketWriter
              roles/storage.legacyObjectOwner
          - PROBLEM: If any Kubernetes-related service account (node SA or workload
            GSA) has one of these roles on an Artifact Registry repository, it can
            push/overwrite images, violating the control's intent.

        EOF

        ###############################################################################
        # 5. Map Kubernetes identities to GCP service accounts (for review)
        ###############################################################################
        echo ">> (Optional) Mapping K8s → GCP identities for Workload Identity (for review)"
        echo "   Run from a machine with kubectl configured for this cluster."
        echo

        # This section does NOT change anything; it only helps correlate principals.
        if kubectl get ns >/dev/null 2>&1; then
          echo "  Kubernetes service accounts annotated with GSA (Workload Identity):"
          kubectl get serviceaccounts --all-namespaces -o json \
            | jq -r '
                .items[]
                | {
                    namespace: .metadata.namespace,
                    name: .metadata.name,
                    gsa: (.metadata.annotations["iam.gke.io/gcp-service-account"] // "")
                  }
                | select(.gsa != "")
                | "\(.namespace)/\(.name) -> \(.gsa)"
              ' \
            | sed 's/^/    /'
        else
          echo "    kubectl not configured or cluster not reachable; skipping K8s mapping."
        fi

        cat <<'EOF'

        Interpretation:
          - For each "namespace/serviceaccount -> GSA" mapping above:
              * Check if that GSA appears in the project-level, GCR bucket-level,
                or Artifact Registry IAM outputs with any write/admin-style role.
          - PROBLEM: A mapped GSA that also appears with elevated Storage or
            Artifact Registry roles is a candidate for restriction to read-only
            roles (e.g., objectViewer, artifactregistry.reader).

        FINAL REVIEW:
          - A finding is considered problematic when:
              * A node service account or any GSA used by your workloads
                has more than read-only permissions on:
                  - GCR bucket: artifacts.<project_id>.appspot.com, or
                  - Any Artifact Registry repository.
          - Use this script's output to build a list of:
              K8s SA  →  GSA  →  Roles on GCR/AR
            and then decide which roles should be reduced to:
              - roles/storage.objectViewer (for GCR)
              - roles/artifactregistry.reader (for AR)
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html](https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html)
