Skip to content

Set up continuous deployment

This article describes how you can set up continuous deployment to automate the process of applying configuration changes or performing upgrades.

When you set up continuous deployment, your Git repository becomes the source of truth, and any changes you make in that Git repository are automatically applied to JIT Groups. Using the Git repository as source of truth and deploying changes automatically can have several advantages, including the following:

  • Improved efficiency, because no manual work is required to roll out changes.
  • Improved reliability, because the process is fully automated and repeatable.
  • Improved traceability, because the Git commit log let you can trace all changes.

However, making your Git repository the source of truth also introduces risks and an improperly-secured Git repository can undermine the security of JIT Groups, and the Google Cloud resources that JIT Groups manages access for:

  • Instead of trying to compromise JIT Groups or its Google Cloud project directly, bad actors might attempt to gain access to the Git repository and apply malicious changes to the Git repository.
  • If these malicious changes are applied automatically, they might allow the bad actor to escalate their privileges and compromise the JIT Groups application, or impersonate the privileged service account that JIT Groups uses to provision access.

To help you mitigate these risks, this article describes a continuous deployment setup that uses Cloud Build to implement the following safety measures:

  • Pull instead of push: Instead of letting an external CI/CD system such as GitHub Actions or GitLab push configuration changes to Google Cloud, you let Google Cloud pull changes from your Git repository and apply them locally.

    This approach avoids having to use workload identity federation or service account keys to grant the external CI/CD system privileged access to Google Cloud resources.

  • Require build approvals: All deployments to Google Cloud must be approved manually.

    This additional approval step adds a last layer of defense that can prevent a Git repository compromise affect JIT Groups and your Google Cloud resources.

Before you begin

Before you can configure the automatic deployment, you first need to connect Cloud Build to your Git repository and enable required APIs:

  1. Open Cloud Shell or a local terminal.

    Open Cloud Shell

  2. Authorize gcloud:

    gcloud auth login
    
    You can skip this step if you're using Cloud Shell.

  3. Set an environment variable to contain your project ID:

    gcloud config set project PROJECT_ID
    

    Replace PROJECT_ID with the ID of the project to deploy JIT Groups in.

  4. Enable the App Engine Admin API and Service Usage API:

    gcloud services enable appengine.googleapis.com serviceusage.googleapis.com
    
  5. Connect Cloud Build to your Git repository.

    To connect Cloud Build to a GitHub repository, do the following:

    1. Create a host connection to connect Cloud Build to GitHub.
    2. Link your GitHub repository to let Cloud Build access your repository contents.

    To connect Cloud Build to a GitLab repository, do the following:

    1. Create a host connection to connect Cloud Build to GitLab.
    2. Link your GitLab repository to let Cloud Build access your repository contents.

Deploy automatically

Configure Cloud Build so that it triggers a build and runs terraform apply when your repository's mainline branch (main or master) changes:

  1. Create a service account for Cloud Build:

    DEPLOY_ACCOUNT=$(gcloud iam service-accounts create jitgroups-cloudbuild-deploy \
      --display-name "Service account for deployments " \
      --format "value(email)")
    
  2. Grant the service account the necessary roles to perform deployments:

    PROJECT_ID=$(gcloud config get core/project)
    echo -n \
      roles/viewer \
      roles/logging.logWriter \
      roles/oauthconfig.editor \
      roles/cloudbuild.builds.editor \
      roles/secretmanager.admin \
      roles/resourcemanager.projectIamAdmin \
      roles/appengine.appAdmin \
      roles/storage.objectAdmin \
      roles/iam.serviceAccountUser \
      roles/iam.serviceAccountAdmin \
      | xargs -n 1 gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member "serviceAccount:$DEPLOY_ACCOUNT" \
        --condition None \
        --format "value(etag)" \
        --role
    

    Note

    This service account is now highly-privileged.

  3. In the Cloud Console, go to Cloud Build > Triggers:

    Open Triggers

  4. Click Create trigger and configure the following settings:

    • Name: deploy
    • Region: Select the region in which you created the connection.
    • Event: Push to a branch
    • Source: 2nd-gen
    • Repository: Select the Git repository that contains your Terraform configuration.

      If the right repository isn't listed, verify that you selected the right region.

    • Branch: ^master$ or ^main$, depending on the name of your main branch.

    • Require approval before build executes: enabled
    • Send logs to GitHub: enabled
    • Service account: jitgroups-cloudbuild-deploy
  5. Click Create.

  6. In your Git repository, create a build configuration file:

    touch cloudbuild.yaml
    
  7. Open the file cloudbuild.yaml and paste the following workflow configuration:

    substitutions:
      _JITGROUPS_REF: 'JITGROUPS_REF'
      _JITGROUPS_URL: https://github.com/GoogleCloudPlatform/jit-access.git
    
    steps:
    
    # Clone JIT Groups repository
    - name: 'alpine/git'
      script: |
        git clone $_JITGROUPS_URL --branch $_JITGROUPS_REF target
    
    # Terraform: Init
    - name: 'hashicorp/terraform:1.9'
      script: |
        terraform init -no-color
    
    # Terraform: Plan/Apply
    - name: 'hashicorp/terraform:1.9'
      script: |
        if [ "$TRIGGER_NAME" == "deploy" ]
        then
          terraform apply -no-color -input=false -auto-approve
        else
          terraform plan -no-color -input=false
        fi
      timeout: '600s'
    
    options:
      automapSubstitutions: true
      logging: CLOUD_LOGGING_ONLY
    

    Replace JITGROUPS_REF with the JIT Groups tag or branch that you want to deploy, for example tags/2.0.0 or jitgroups/latest.

  8. Commit your changes and push them to the remote repository:

    git add -A && \
      git commit -m 'Add Cloud Build configuration for deploying automatically' && \
      git push
    
  9. In the Cloud Console, go to Cloud Build > History:

    Open History

    You see a build that's awaiting approval:

    Cloud Build

  10. Click ... > Approve build to approve and start the deployment.

Verify pull requests

Create a second Cloud Build trigger for pull requests and configure it so that it runs terraform plan, but doesn't apply any changes to the project:

  1. Create a service account for Cloud Build:

    VERIFY_ACCOUNT=$(gcloud iam service-accounts create jitgroups-cloudbuild-verify \
      --display-name "Service account for verification " \
      --format "value(email)")
    
  2. Grant the service account the necessary roles to verify deployments:

    PROJECT_ID=$(gcloud config get core/project)
    echo -n \
      roles/viewer \
      roles/logging.logWriter \
      roles/oauthconfig.editor \
      roles/secretmanager.secretAccessor \
      | xargs -n 1 gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member "serviceAccount:$VERIFY_ACCOUNT" \
        --condition None \
        --format "value(etag)" \
        --role 
    gcloud storage buckets add-iam-policy-binding gs://$PROJECT_ID-state \
      --member "serviceAccount:$VERIFY_ACCOUNT" \
      --condition None \
      --format "value(etag)" \
      --role roles/storage.objectUser 
    
  3. In the Cloud Console, go to Cloud Build > Triggers:

    Open Triggers

  4. Click Create trigger and configure the following settings:

    • Name: verify
    • Region: Select the region in which you created the connection.
    • Event: Pull request
    • Source: 2nd-gen
    • Repository: Select the Git repository that contains your Terraform configuration.
    • Branch: ^master$ or ^main$, depending on the name of your main branch.
    • Require approval before build executes: enabled
    • Send logs to GitHub: enabled
    • Service account: jitgroups-cloudbuild-verify
  5. Click Create.