Skip to content

davidmontoyago/pulumi-gcp-github-registry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pulumi-gcp-github-registry

develop

Pulumi Component to setup an artifact registry repository, an OIDC identity provider for Github Actions, and the IAM required to login and push docker images to the registry.

Favors Direct Workload Identity Federation for Github Actions, but supports Workload Identity Federation through a Service Account (CREATE_SERVICE_ACCOUNT=true) for cases when a GSA is required. Both approaches avoid long-lived access credentials. E.g.:

- name: Authenticate to Google Cloud
  uses: google-github-actions/auth@v2
  with:
    project_id: ${{ env.GCP_PROJECT }}
    workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}

See:

Features

  1. Artifact Registry Repository

    • Docker image storage for CI/CD builds
    • Configured with appropriate IAM permissions
    • Region-specific or multi-region deployment
  2. Workload Identity Federation

    • OIDC-based authentication for GitHub Actions
    • Secure token exchange without long-lived credentials
    • Attribute mapping for repository and actor-based access control
  3. IAM Integration

    • Automatic permission assignment for Artifact Registry access
    • Optional service account and binding to workload identity pool
    • Configurable role assignments

Install

go get github.com/davidmontoyago/pulumi-gcp-github-registry

Quickstart

package main

import (
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/davidmontoyago/pulumi-gcp-github-registry/deploy/ci"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        // Load configuration from environment variables
        config, err := ci.LoadConfig()
        if err != nil {
            return err
        }

        // Create CI/CD infrastructure
        ciInfra, err := ci.NewGithubGoogleRegistryStack(ctx, config)
        if err != nil {
            return err
        }

        // Export outputs for GitHub Actions
        ctx.Export("registryURL", ciInfra.RegistryURL)
        ctx.Export("workloadIdentityProviderID", pulumi.ToSecret(ciInfra.OidcProvider.ID()))
        ctx.Export("repositoryWorkloadID", ciInfra.RepositoryPrincipalID)

        return nil
    })
}

Configuration

The component uses environment variables for configuration:

Variable Description Required Default
GCP_PROJECT GCP Project ID Yes -
GCP_REGION GCP Region for resources Yes -
REPOSITORY_LOCATION Artifact Registry location No Value of GCP_REGION
ALLOWED_REPO_URL GitHub repository URL for workload identity access No https://github.com/davidmontoyago/pulumi-gcp-github-registry
REPOSITORY_OWNER GitHub repository owner (username/org) for additional security No -
REPOSITORY_OWNER_ID GitHub repository owner numeric ID (recommended for security) No -
REPOSITORY_ID GitHub repository numeric ID (recommended for security) No -
IDENTITY_POOL_PROVIDER_NAME Workload identity pool provider name (max 32 chars) No github-actions-provider
RESOURCE_PREFIX Prefix for resource names No ci
REPOSITORY_NAME Artifact Registry repository name No registry
CREATE_SERVICE_ACCOUNT Whether to create a GitHub Actions service account No false

GitHub Actions Integration

Setting up Workload Identity Federation

  1. Configure GitHub Actions Secrets

Add the following secrets to your GitHub repository:

# .github/workflows/deploy.yml
env:
  GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
  WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
  1. Authenticate with GCP

Use the google-github-actions/auth action to authenticate:

- name: Google Auth
  id: auth
  uses: google-github-actions/auth@v2
  with:
    project_id: ${{ env.GCP_PROJECT }}
    workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}

- name: Login to Google Artifact Registry
  uses: docker/login-action@v3
  with:
    registry: ${{ env.REGISTRY_URL}}
    username: oauth2accesstoken
    password: ${{ steps.auth.outputs.auth_token }}

- name: Build Docker image
  shell: bash
  run: make image

Complete GitHub Actions Workflow Example

name: Deploy to GCP

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
  WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
  REGISTRY_URL: ${{ secrets.REGISTRY_URL }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write

    steps:
    - uses: actions/checkout@v4

    - name: Google Auth
      id: auth
      uses: google-github-actions/auth@v2
      with:
        project_id: ${{ env.GCP_PROJECT }}
        workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}

    # optionally get setup for gcloud commands
    - name: Set up Cloud SDK
      uses: google-github-actions/setup-gcloud@v2

    - name: Login to Google Artifact Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY_URL }}
        username: oauth2accesstoken
        password: ${{ steps.auth.outputs.auth_token }}

    - name: Build and Push Image
      run: |
        docker build -t ${{ env.REGISTRY_URL }}/app:${{ github.sha }} .
        docker push ${{ env.REGISTRY_URL }}/app:${{ github.sha }}

Security Features

  • Workload Identity Federation: Eliminates the need for long-lived service account keys
  • Least Privilege Access: Service account has minimal required permissions
  • Repository Scoping: OIDC provider can be configured to restrict access to specific repositories
  • Repository Owner Constraints: Additional security through owner username and numeric ID validation
  • Audit Logging: All operations are logged in GCP Cloud Audit Logs

Repository Scoping

This component implements security best practices for Workload Identity Federation by restricting OIDC authentication to specific GitHub repositories.

See:

Security Configuration

The OIDC provider is configured with multiple security layers:

1. Repository-Specific Access Control

AttributeCondition: pulumi.String("attribute.repository == \"my-org/my-repo\""),

This ensures that only the specified repository can authenticate with the workload identity pool. Any attempt from other repositories will be rejected.

2. Audience Validation

The token audience claim will be validated in GCP against the full name of the OIDC pool provider.

See:

3. Comprehensive Attribute Mapping

The provider maps GitHub Actions context to GCP attributes for fine-grained control:

GitHub Attribute GCP Attribute Description
assertion.sub google.subject Unique identifier for the workflow run
assertion.actor attribute.actor GitHub username of the actor
assertion.repository attribute.repository Repository name (e.g., owner/repo)
assertion.repository_owner attribute.repository_owner Repository owner (username/org)
assertion.repository_owner_id attribute.repository_owner_id Repository owner numeric ID
assertion.repository_id attribute.repository_id Repository numeric ID
assertion.ref attribute.ref Branch or tag reference
assertion.sha attribute.sha Commit SHA
assertion.workflow attribute.workflow Workflow name
assertion.head_ref attribute.head_ref PR head reference
assertion.base_ref attribute.base_ref PR base reference

Security Benefits

  • Repository Isolation: Prevents cross-repository access
  • Audit Trail: All authentication attempts are logged with repository context
  • No Credential Exposure: Eliminates the risk of leaked service account keys
  • Automatic Rotation: GitHub Actions tokens are automatically rotated
  • Least Privilege: Service account only has necessary permissions

Verification

You can verify the repository scoping is working by:

  1. Checking GCP Cloud Audit Logs for authentication events
  2. Testing from unauthorized repositories (should be rejected)
  3. Monitoring the google.subject attribute in logs to ensure proper mapping

Outputs

The component exports the following values for use in CI/CD pipelines:

  • registryURL: The full URL of the Artifact Registry repository
  • serviceAccountEmail: The email of the GitHub Actions service account
  • workloadIdentityPoolID: The ID of the workload identity pool (marked as secret)
  • workloadIdentityProviderID: The full provider ID for GitHub Actions authentication (marked as secret)
  • workloadIdentityProviderCondition: The attribute condition used for repository scoping

Security Note for Exported Values

The workloadIdentityPoolId and workloadIdentityProviderId are marked as secrets in Pulumi state:

  • Encrypted in state: These values are encrypted when stored in Pulumi state files
  • Masked in logs: Values are displayed as [secret] in Pulumi CLI output
  • Secure retrieval: Use pulumi stack output --show-secrets to view the actual values

Example output:

$ pulumi stack output
Current stack outputs (1):
    OUTPUTS
    workloadIdentityPoolID     [secret]
    workloadIdentityProviderID [secret]
    registryURL                us-docker.pkg.dev/my-project/registry

$ pulumi stack output --show-secrets
Current stack outputs (1):
    OUTPUTS
    workloadIdentityPoolID     projects/123456789/locations/global/workloadIdentityPools/ci-github-actions-pool
    workloadIdentityProviderID projects/123456789/locations/global/workloadIdentityPools/ci-github-actions-pool/providers/ci-github-actions-provider
    registryURL                us-docker.pkg.dev/my-project/registry

About

Setup GCP Artifact Registry with OIDC auth for Github Actions

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published