Google Cloud Workload Identities with GitLab

Estimated reading time: 3 minutes

Gerard Samuel Gerard Samuel's profile photo
Original image used on trustedreviews.com

Using JSON keys to authenticate with Google Cloud is highly frowned upon. Unless you have no other option, Google Cloud provides a more secure means of authenticating externally executed code. My use case is for authentication in GitLab pipelines so that I can automate tasks. Think Terraform jobs or updating the files for a website stored in a Google Cloud storage bucket. I will use Google Cloud’s Workload Identity Federation solution and the OIDC (Open ID Connect) protocol in this solution.

To get started, here are a few requirements:

  1. A GitLab project that will contain the pipeline code.
  2. A Google Cloud project with a service to grant access to. I refer to this as the service project below.
  • Create a Google Cloud project specifically for Workload Identity
gcloud projects create workload-id --no-enable-cloud-apis

gcloud config set project workload-id
Note
  • Enable the required APIs for Workload Identity
gcloud services enable \
  cloudresourcemanager.googleapis.com \
  iam.googleapis.com \
  iamcredentials.googleapis.com \
  sts.googleapis.com \
  --project="workload-id"
  • Define a few variables.
WIP_PROJECT_ID=$(gcloud projects describe $(gcloud config get-value core/project) --format="get(projectId)")
WIP_PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value core/project) --format="get(projectNumber)")
GITLAB_GROUP="originaltrini0-codes"
GITLAB_PROJECT_NUMBER="64700261"
WIP_NAME="test-wip"
WIP_PROVIDER_NAME="test-wip-provider"
SERVICE_PROJECT_ID="test-compute-443418"
Note
  • Create a Workload Identity Pool
gcloud iam workload-identity-pools create $WIP_NAME \
  --location="global" \
  --display-name="Test WIP" \
  --description="Test Workload Identity Pool" \
  --project="${WIP_PROJECT_ID}"
  • Create a Workload Identity Provider for GitLab
gcloud iam workload-identity-pools providers create-oidc $WIP_PROVIDER_NAME \
  --location="global" \
  --workload-identity-pool="${WIP_NAME}" \
  --description="Test Workload Identity OIDC Provider" \
  --display-name="Test Workload Identity Provider" \
  --issuer-uri="https://gitlab.com/" \
  --attribute-mapping="attribute.guest_access=assertion.guest_access,\
    attribute.planner_access=assertion.planner_access,\
    attribute.reporter_access=assertion.reporter_access,\
    attribute.developer_access=assertion.developer_access,\
    attribute.maintainer_access=assertion.maintainer_access,\
    attribute.owner_access=assertion.owner_access,\
    attribute.namespace_id=assertion.namespace_id,\
    attribute.namespace_path=assertion.namespace_path,\
    attribute.project_id=assertion.project_id,\
    attribute.project_path=assertion.project_path,\
    attribute.user_id=assertion.user_id,\
    attribute.user_login=assertion.user_login,\
    attribute.user_email=assertion.user_email,\
    attribute.user_access_level=assertion.user_access_level,\
    google.subject=assertion.sub" \
  --attribute-condition="assertion.namespace_id==\"${GITLAB_GROUP}\"" \
  --project="${WIP_PROJECT_ID}"
Note
  • Create a service account for impersonation in the service project
gcloud iam service-accounts create test-svc-acct \
  --display-name="Test Service Account" \
  --project="${SERVICE_PROJECT_ID}"
  • Create a storage bucket in the service project and upload test files to it
gcloud storage buckets create gs://gms-bucket-2024 \
  --project="${SERVICE_PROJECT_ID}"

gcloud storage cp ~/Downloads/*.jpg gs://gms-bucket-2024
  • Get the service project’s service account email address and grant it the Workload Identity User role
SERVICE_ACCOUNT_EMAIL=$(gcloud iam service-accounts list --project="${SERVICE_PROJECT_ID}" --filter="email~test-svc-acct" --format="get(email)")

gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT_EMAIL \
  --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/${WIP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${WIP_NAME}/attribute.project_id/${GITLAB_PROJECT_NUMBER}" \
  --project="${SERVICE_PROJECT_ID}"
  • Grant the service account the roles/storage.objectAdmin role to the storage bucket
gcloud storage buckets add-iam-policy-binding gs://gms-bucket-2024 \
  --member=serviceAccount:$SERVICE_ACCOUNT_EMAIL \
  --role=roles/storage.objectAdmin \
  --project="${SERVICE_PROJECT_ID}"
  • In your GitLab project, commit a .gitlab-ci.yml file with the following content
stages:
  - auth

auth:
  variables:
    GOOGLE_CLOUD_PROJECT: test-compute-443418
    GOOGLE_SERVICE_ACCOUNT_EMAIL: [email protected]
    GOOGLE_WORKLOAD_IDENTITY_PROVIDER: projects/722905479622/locations/global/workloadIdentityPools/test-wip/providers/test-wip-provider
  image: docker.io/google/cloud-sdk:slim
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://iam.googleapis.com/projects/722905479622/locations/global/workloadIdentityPools/test-wip/providers/test-wip-provider
  script:
    - mkdir -p _google_auth
    - echo "${GITLAB_OIDC_TOKEN}" > $CI_PROJECT_DIR/_google_auth/.ci_job_jwt_file
    - gcloud iam workload-identity-pools create-cred-config ${GOOGLE_WORKLOAD_IDENTITY_PROVIDER}
      --service-account="${GOOGLE_SERVICE_ACCOUNT_EMAIL}"
      --service-account-token-lifetime-seconds=600
      --output-file=$CI_PROJECT_DIR/_google_auth/.gcp_temp_cred.json
      --credential-source-file=$CI_PROJECT_DIR/_google_auth/.ci_job_jwt_file
    - gcloud config set project test-compute-443418
    - gcloud auth login --cred-file=$CI_PROJECT_DIR/_google_auth/.gcp_temp_cred.json
    - gcloud storage ls gs://gms-bucket-2024
  stage: auth
  when: manual
  • Manually trigger the job

  • Review the job’s progress. The results show that the GitLab runner can authenticate and list the files in the storage bucket

    Gitlab pipeline results
    Gitlab pipeline results

  • Go to Logs Explorer for the service project, and run the following query

protoPayload.methodName="GenerateAccessToken" AND
protoPayload.authorizationInfo.granted="true"
  • You should find the corresponding log entry similar to this screenshot where the GitLab runner authenticates to Google Cloud
    Authentication logs in Google Cloud Logging
    Authentication logs in Google Cloud Logging

Conclusion

In this article, I demonstrate how to enable and configure a Workload Identity Pool and Provider that uses GitLab’s assertion data to grant access to Google Cloud. For more information about Workload Identity Federation, visit the documentation page.