> ## 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 User Access To Container Image Repositories

### More Info:

Restrict IAM permissions so only required users and service accounts have write access to container image repositories. Broad storage-admin roles allow tampering with image content.

### 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 over-privileged principals at the project level**
           * Run on any machine with `gcloud` access:
             ```bash theme={null}
             PROJECT_ID="my-project-id"

             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 each returned `member`, document which applications, CI pipelines, or teams depend on that access and whether they truly need **write** or **admin** rights to container images.

        2. **Review and adjust project-level IAM for GCR/AR access**
           * If a principal does not require broad storage admin or legacy write roles, update the project IAM policy file instead of editing individual bindings ad hoc.
           * Export the current policy:
             ```bash theme={null}
             gcloud projects get-iam-policy "$PROJECT_ID" > /tmp/${PROJECT_ID}-iam-policy.yaml
             ```
           * Edit `/tmp/${PROJECT_ID}-iam-policy.yaml` and **remove** or narrow any bindings that grant:
             * `roles/storage.admin`
             * `roles/storage.objectAdmin`
             * `roles/storage.objectCreator`
             * `roles/storage.legacyBucketOwner`
             * `roles/storage.legacyBucketWriter`
             * `roles/storage.legacyObjectOwner`\
               where they are not strictly required for image publishing/management.
           * Re-apply the policy:
             ```bash theme={null}
             gcloud projects set-iam-policy "$PROJECT_ID" /tmp/${PROJECT_ID}-iam-policy.yaml
             ```

        3. **Tighten GCR bucket-level permissions (if using GCR)**
           * Determine the GCR bucket name, typically: `artifacts.${PROJECT_ID}.appspot.com`.
           * Show current IAM on the bucket:
             ```bash theme={null}
             BUCKET="gs://artifacts.${PROJECT_ID}.appspot.com"

             gsutil iam get "$BUCKET" > /tmp/${PROJECT_ID}-gcr-bucket-iam.json
             cat /tmp/${PROJECT_ID}-gcr-bucket-iam.json
             ```
           * For any principal that only needs read access, first grant a read-only role if not already present, for example:
             ```bash theme={null}
             gsutil iam ch user:someone@example.com:objectViewer "$BUCKET"
             ```
           * Then remove over-privileged roles from the bucket:
             ```bash theme={null}
             gsutil iam ch -d user:someone@example.com:roles/storage.admin "$BUCKET"
             gsutil iam ch -d user:someone@example.com:roles/storage.objectAdmin "$BUCKET"
             gsutil iam ch -d user:someone@example.com:roles/storage.objectCreator "$BUCKET"
             ```
           * Adjust `user:` to `serviceAccount:` and the role string as appropriate for each principal you identified.

        4. **Restrict Artifact Registry repository IAM (if using AR)**
           * List repositories to identify which hold container images:
             ```bash theme={null}
             gcloud artifacts repositories list \
               --location=us-central1 \
               --project="$PROJECT_ID"
             ```
             (Repeat with other locations as needed.)
           * For each repository that stores container images, get its current IAM policy:
             ```bash theme={null}
             REPO_NAME="my-repo"
             LOCATION="us-central1"

             gcloud artifacts repositories get-iam-policy "$REPO_NAME" \
               --location="$LOCATION" > /tmp/${REPO_NAME}-iam-policy.yaml
             cat /tmp/${REPO_NAME}-iam-policy.yaml
             ```
           * Review bindings and ensure only the minimal required users/service accounts have write or admin roles (for example, CI/CD or image promotion jobs). Remove wide groups or general-purpose accounts that do not need image write access.
           * After editing `/tmp/${REPO_NAME}-iam-policy.yaml`, apply it:
             ```bash theme={null}
             gcloud artifacts repositories set-iam-policy "$REPO_NAME" \
               /tmp/${REPO_NAME}-iam-policy.yaml \
               --location="$LOCATION"
             ```

        5. **Confirm remaining write access is justified and scoped**
           * For each principal that still has write or admin access (project-level, bucket-level, or AR repo-level), document:
             * What pipeline or workload uses it (e.g., image build, promotion, security scanning).
             * Why write access cannot be further reduced (e.g., needs only `artifactregistry.repoAdmin` on a single repo instead of project-level storage admin).
           * Where possible, replace direct user accounts with dedicated service accounts scoped to a single project or repository and used only from CI/CD or automation.

        6. **Re-run the audit to verify reduction of broad roles**
           * Run again on any machine with `gcloud` access:
             ```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 general-purpose users, broad groups, or unrelated service accounts retain these roles.
             * Any remaining use of these roles is explicitly reviewed and accepted as a justified exception.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to change IAM permissions on Google Cloud container image repositories; these settings are managed at the cloud project / Artifact Registry / GCR level via gcloud, gsutil, or IaC. Perform the remediation in the cloud provider configuration instead, following the guidance in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Report broad write-level access to GCR / Artifact Registry used by this GKE cluster.
        # Runs read‑only checks against:
        #   - Project‑level IAM
        #   - GCR buckets (if used)
        #   - Artifact Registry repositories (if used)
        #
        # RUN ON: any machine with gcloud, kubectl and gsutil configured and access to the project.

        set -euo pipefail

        PROJECT_ID="${1:-}"
        REGION_FILTER="${2:-}"   # optional: e.g. "us-" to limit AR scan

        if [[ -z "${PROJECT_ID}" ]]; then
          echo "Usage: $0 <gcp-project-id> [artifact-registry-region-filter]" >&2
          exit 1
        fi

        echo "=== Context ==="
        echo "Project: ${PROJECT_ID}"
        echo "Kubernetes context: $(kubectl config current-context 2>/dev/null || echo 'N/A')"
        echo

        ###############################################################################
        # 1. Identify image registries actually used by this cluster
        ###############################################################################
        echo "=== Step 1: Images used by workloads in the cluster ==="
        echo "[INFO] Collecting images from all namespaces via kubectl..."

        # Requires access to the cluster API
        IMAGES=$(kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{range .spec.initContainers[*]}{.image}{"\n"}{end}{end}' \
          | sort -u)

        if [[ -z "${IMAGES}" ]]; then
          echo "[WARN] No images found via kubectl; cluster may be empty or access restricted."
        else
          echo "[INFO] Unique images in use:"
          echo "${IMAGES}"
        fi
        echo

        GCR_USED=false
        AR_USED=false

        if echo "${IMAGES}" | grep -qE '(^|/)gcr\.io/|\.gcr\.io/'; then
          GCR_USED=true
        fi
        if echo "${IMAGES}" | grep -qE '(^|/)([a-z0-9-]+)-docker\.pkg\.dev/'; then
          AR_USED=true
        fi

        echo "GCR used by cluster: ${GCR_USED}"
        echo "Artifact Registry used by cluster: ${AR_USED}"
        echo

        ###############################################################################
        # 2. Project-level IAM: look for broad storage roles
        ###############################################################################
        echo "=== Step 2: Project-level IAM with broad Storage roles ==="
        echo "[INFO] Checking for roles that can modify GCR/AR content at project scope..."

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

        cat <<'EOF'

        [INTERPRETATION]
        - Any row here indicates a principal with broad write/admin capability on Cloud Storage.
        - If you use GCR (which is backed by a GCS bucket), these roles can enable registry tampering.
        - Pay special attention to:
            - user:* or serviceAccount:* that do not need write access to container images
            - groups with wide membership
            - roles granted to allUsers / allAuthenticatedUsers (critical issue)
        EOF
        echo

        ###############################################################################
        # 3. GCR buckets: bucket-level IAM (if GCR is used)
        ###############################################################################
        if [[ "${GCR_USED}" == "true" ]]; then
          echo "=== Step 3: GCR bucket-level IAM (since GCR is in use) ==="

          # Typical multi-region GCR bucket name:
          GCR_BUCKET="artifacts.${PROJECT_ID}.appspot.com"

          echo "[INFO] Checking IAM policy for GCR bucket: gs://${GCR_BUCKET}"
          if gsutil ls "gs://${GCR_BUCKET}" >/dev/null 2>&1; then
            gsutil iam get "gs://${GCR_BUCKET}" | jq -r '
              .bindings[]
              | select(.role
                  | IN(
                      "roles/storage.admin",
                      "roles/storage.objectAdmin",
                      "roles/storage.objectCreator",
                      "roles/storage.legacyBucketOwner",
                      "roles/storage.legacyBucketWriter",
                      "roles/storage.legacyObjectOwner"
                    ))
              | .role as $r
              | .members[]
              | @tsv "\($r)\t\(.)"
            ' 2>/dev/null || echo "[WARN] Failed to parse IAM policy with jq; raw policy follows:" && gsutil iam get "gs://${GCR_BUCKET}"

            cat <<'EOF'

        [INTERPRETATION]
        - Any principal listed with the roles above has write/admin capability on image blobs.
        - Problematic cases:
            - Principals that do not directly manage images but have storage.admin or objectAdmin
            - allUsers / allAuthenticatedUsers with any of these roles (very high risk)
            - Shared groups used for non-CI purposes
        EOF
          else
            echo "[INFO] GCR bucket gs://${GCR_BUCKET} not found; GCR may not be configured in this form."
          fi
          echo
        else
          echo "=== Step 3: Skipping GCR bucket check (no GCR images detected in workloads) ==="
          echo
        fi

        ###############################################################################
        # 4. Artifact Registry repositories: IAM (if AR is used)
        ###############################################################################
        if [[ "${AR_USED}" == "true" ]]; then
          echo "=== Step 4: Artifact Registry repository IAM (since Artifact Registry is in use) ==="
          echo "[INFO] Listing Artifact Registry repositories in project ${PROJECT_ID}..."
          if [[ -n "${REGION_FILTER}" ]]; then
            REPOS=$(gcloud artifacts repositories list \
              --project "${PROJECT_ID}" \
              --format='value(name)' \
              --filter="location:${REGION_FILTER}" || true)
          else
            REPOS=$(gcloud artifacts repositories list \
              --project "${PROJECT_ID}" \
              --format='value(name)' || true)
          fi

          if [[ -z "${REPOS}" ]]; then
            echo "[INFO] No Artifact Registry repositories found."
          else
            while read -r REPO; do
              [[ -z "${REPO}" ]] && continue
              echo "--- Repository: ${REPO} ---"
              # NOTE: AR IAM roles of concern typically include:
              #   roles/artifactregistry.admin
              #   roles/artifactregistry.repoAdmin
              #   roles/artifactregistry.writer
              gcloud artifacts repositories get-iam-policy "${REPO}" \
                --project "${PROJECT_ID}" \
                --format='table(bindings.members,bindings.role)' \
                | grep -E 'roles/artifactregistry\.admin|roles/artifactregistry\.repoAdmin|roles/artifactregistry\.writer' \
                || echo "[INFO] No broad AR admin/writer roles found on this repo."

              cat <<'EOF'

        [INTERPRETATION PER REPOSITORY]
        - Focus on members with:
            - roles/artifactregistry.admin
            - roles/artifactregistry.repoAdmin
            - roles/artifactregistry.writer
        - Problematic cases:
            - Human users with admin/writer but not directly responsible for publishing images
            - Wide groups (e.g., all developers) that can overwrite or push images
            - Service accounts unrelated to CI/CD or image promotion
        EOF
            done <<< "${REPOS}"
          fi
          echo
        else
          echo "=== Step 4: Skipping Artifact Registry IAM check (no AR images detected in workloads) ==="
          echo
        fi

        ###############################################################################
        # 5. Summary guidance
        ###############################################################################
        cat <<'EOF'
        === How to interpret this report ===
        A configuration is problematic for CIS GKE 5.1.2 when:

        1. Project-level IAM
           - Any principal (user, group, or service account) has one of:
               roles/storage.admin
               roles/storage.objectAdmin
               roles/storage.objectCreator
               roles/storage.legacyBucketOwner
               roles/storage.legacyBucketWriter
               roles/storage.legacyObjectOwner
           - …and does not have a strict, justifiable need to write or administer
             container images (GCR/AR).

        2. GCR bucket IAM
           - The GCR bucket IAM shows the roles above granted to:
               - Unnecessary human users
               - Broad groups (e.g. "developers", "engineering")
               - allUsers or allAuthenticatedUsers
           - These indicate excessive write access to image content.

        3. Artifact Registry IAM
           - Repository policies grant:
               roles/artifactregistry.admin
               roles/artifactregistry.repoAdmin
               roles/artifactregistry.writer
             to principals that are not:
               - CI/CD service accounts that push images
               - Dedicated release automation or image promotion systems

        Use this report as input to a manual review:
        - For each principal with broad access, decide:
            - Do they truly need to push/modify images?
            - Can their access be reduced to read-only or removed?
        - Then adjust IAM using:
            - gsutil iam ch / gcloud projects set-iam-policy for GCR
            - gcloud artifacts repositories set-iam-policy for Artifact Registry

        This script does NOT make any changes; it only surfaces potential issues for review.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
