Skip to content

open-security-tools/ost-environment-gate

Repository files navigation

ost-environment-gate

GitHub's environments allow adding required reviewers as a deployment protection rule. When applied to a release environment, this allows for 2-factor release workflows where a second team member must approve the workflow before it can access the release secrets.

Unfortunately GitHub applies the deployment protection to every job that runs in the workflow. If a release process has multiple steps, then each step needs to be approved as it starts.

To work around this issue, we make use of the deployment_protection_rule webhook which a GitHub App can subscribe to. The GitHub App can then be used to approve or deny deployments to an environment. The human approval in a 2-factor release workflow is retained by having two environments.

  1. release-gate: This requires approval by a human
  2. release: This requires approval from the GitHub App

The GitHub App has a simple purpose: approve the release deployment if the release-gate deployment was approved.

GitHub Actions workflow

A minimal workflow would look like this:

name: Release

on:
  workflow_dispatch:
    inputs:
      version:
        required: true
        type: string

permissions: {}

jobs:
  release-gate:
    name: release-gate
    runs-on: ubuntu-latest
    environment: release-gate
    steps:
      - run: echo "Release approved"

  release:
    name: Publish release
    runs-on: ubuntu-latest
    needs: [release-gate]
    environment: release
    permissions:
      contents: write
    steps:
      - run: echo "Use a secret from release!"

GitHub App

The minimal manifest for the GitHub App is:

{
  "name": "ost-environment-gate",
  "url": "https://github.com/open-security-tools/ost-environment-gate/",
  "public": false,
  "hook_attributes": {
    "url": "https://example.execute-api.us-east-2.amazonaws.com/github/webhook",
    "active": true
  },
  "default_permissions": {
    "actions": "read",
    "deployments": "write"
  },
  "default_events": [
    "deployment_protection_rule"
  ]
}

The GitHub App requires the minimum permissions to perform this action.

Webhook

The webhook API is implemented in Rust and deployed as a Lambda via AWS SAM.

The GitHub App ID and webhook secret are stored in AWS SSM Parameter Store. The private key is stored in AWS Secrets Manager.

The webhook lifecycle is roughly:

  1. Receive an event from GitHub
  2. Validate the event is authentic using the webhook secret
  3. Discard non-deployment_protection_rule events
  4. Use the private key to mint a JWT
  5. Exchange the JWT for a GitHub access token (POST /app/installations/{id}/access_tokens)
  6. Extract the workflow run id from the event
  7. Validate that the workflow run comes from the expected workflow file (GET /repos/{owner}/{repo}/actions/runs/{run_id})
  8. Read the workflow jobs for the run (GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs)
  9. Find the release gate job and check that it succeeded
  10. Approve or deny the deployment (POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule)
  11. Return an HTTP 204

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors