Keyless Authorization From GCP to GitHub Actions in GCP Using IdP

Written by arslanbekov | Published 2022/12/02
Tech Story Tags: gcp | github-actions | workload-identity-provider | github | software-development | technology | gcp-to-github-actions | gcp-using-idp

TLDRKeyless authorization from GCP to GitHub Actions in GCP using IdP. How we should auth into GCP from GitHub Actions? What if we use terraform to configure GCP and GitHub Actions for our automation operations? The easiest and not the most secure way — is to create a basic GCP Service Account key (JSON) with specific permission and authorization. This is a great way to start. Let’s see how it works: Using IdP, we create a Workload Identity Pool provider.via the TL;DR App

Let’s talk about keyless authorization from GitHub Actions in GCP using IdP. How should we auth into GCP from GitHub Actions (it doesn't matter if we use our runners or GitHub runners)? What if we use terraform to configure GCP and GitHub Actions for our automation operations? The easiest and not the most secure way — is to create a primary GCP Service Account key (JSON) with specific permission and authorization. This is a great way to start. Let’s see how it works:

It is straightforward, but what if:

  1. The service account key will be compromised?

  2. We want to limit the origins from where the key can be used.

  3. We want to reissue a new service account key automatically.

Google has a great solution — Workload Identity Federation.

Traditionally, applications running outside Google Cloud have used service account keys to access Google Cloud resources. Service account keys are powerful credentials, and can represent a security risk if they are not managed correctly.

With identity federation, you can use Identity and Access Management (IAM) to grant external identities IAM roles, including the ability to impersonate service accounts. This lets you access resources directly, using a short-lived access token, and eliminates the maintenance and security burden associated with service account keys.

You can use workload identity federation with the following:

  • SAML 2.0
  • Any identity provider that supports OpenID Connect (OIDC), such as Microsoft Azure
  • Amazon Web Services (AWS)


Aworkload identity pool provider is an entity that describes a relationship between Google Cloud and an external identity provider, such as the following:

  • AWS
  • Azure Active Directory
  • On-premises Active Directory Federation Services (AD FS)
  • Okta
  • Kubernetes clusters

But let’s look at how to use this keyless authorization paired with GitHub Actions.


On the Google Cloud side

We will perform all the actions using terraform, but you can also use the UI GCP.

First, let’s create Workload Identity Pool:

resource "google_iam_workload_identity_pool" "github_actions" {
  provider                  = google-beta
  project                   = "my-gcp-project"
  workload_identity_pool_id = "github-actions"
  display_name              = "GitHub Actions pool"
  description               = "Workload Identity Pool managed by Terraform"
  disabled                  = false
}

Then we need to create a Workload Identity Pool Provider (many providers can be assigned to one Workload Identity Pool):

resource "google_iam_workload_identity_pool_provider" "github_actions" {
  provider                           = google-beta
  project                            = "my-gcp-project"
  workload_identity_pool_id          = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-actions"
  display_name                       = "GitHub Actions provider"
  description                        = "Workload Identity Pool Provider managed by Terraform"
  attribute_condition                = "attribute.repository_owner==\"arslanbekov\""
  attribute_mapping                  = {
    "google.subject"             = "assertion.sub"
    "attribute.actor"            = "assertion.actor"
    "attribute.aud"              = "assertion.aud"
    "attribute.repository"       = "assertion.repository"
    "attribute.repository_owner" = "assertion.repository_owner"
  }
  oidc {
    allowed_audiences = []
    issuer_uri        = "https://token.actions.githubusercontent.com"
  }
}

Notice the parameter attribute_condition, a condition by which we can prohibit actions that do not fall under our filter.

You can see all available values in the official GitHub documentation.

We also need a service account (don’t worry, we won’t need the keys anywhere); I did not describe it in the terraform code for simplicity; I think that you can easily do it yourself or using CLI gcloud:

gcloud iam service-accounts create SA_NAME \
    --description="DESCRIPTION" \
    --display-name="DISPLAY_NAME"

After we have an email service account, it will need to be given a role. roles/iam.workloadIdentityUser

resource "google_service_account_iam_member" "wif-sa" {
  service_account_id = "projects/my-gcp-project/serviceAccounts/[email protected]"
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/*"
}

Don’t forget to replace my-gcp-project and [email protected]

After applying this terraform in the output, you will have two values (please save them, you will need them later):

output "pool_name" {
  description = "Pool name"
  value       = google_iam_workload_identity_pool.github_actions.name
}
output "provider_name" {
  description = "Provider name"
  value       = google_iam_workload_identity_pool_provider.github_actions.name
}

Example output:

pool_name     = "projects/${redacted_number}/locations/global/workloadIdentityPools/github-actions"
provider_name = "projects/${redacted_number}/locations/global/workloadIdentityPools/github-actions/providers/github-actions"

On the GitHub side

Let’s create 2 GitHub actions secrets:

  1. GCP_WORKLOAD_IDENTITY_PROVIDER_NAME (need to specify the entire line, for example: projects/123456789/locations/global/workloadIdentityPools/github-actions/providers/github-actions)
  2. GCP_WORKLOAD_IDENTITY_SA_EMAIL (service account email that we created earlier and indicated in the terraform)

GitHub workflow example:

name: "Example Workload Identity"

on:
  push:
    branches:
      - "master"

jobs:
  run:
    name: "Workload Identity Job"
    permissions:
      id-token: write
      contents: read
    runs-on: "ubuntu-latest"
    steps:
      - name: "Auth in GCP"
        id: "auth"
        uses: "google-github-actions/auth@v1"
        with:
          token_format: "access_token"
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER_NAME }}
          service_account: ${{ secrets.GCP_WORKLOAD_IDENTITY_SA_EMAIL }}

      - name: "Docker login"
        run: |
          echo '${{ steps.auth.outputs.access_token }}' | docker login -u oauth2accesstoken --password-stdin https://gcr.io

Finally, we can authorize in the docker access_token which will be in the output of the step id: auth.

If you need interaction with gsutils — then this is also available after authorization.

Conclusions

By switching to authorization through a workload identity provider, we can restrict access from specific origins for approval. We also stop caring about key rotation and can control the key's lifetime.

Links

  1. https://github.com/arslanbekov/wif-gcp-terraform (you can find all the code described by terraform in this article in this repository)
  2. https://github.com/arslanbekov/wif-gcp-github-actions (example github-actions auth)

They are also published here.


Written by arslanbekov | Head of SRE @ANNA Money
Published by HackerNoon on 2022/12/02