Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,13 @@
{
"tab": "Integrations",
"groups": [
{
"group": "Workload Identity",
"pages": [
"integrations/workload-identity/overview",
"integrations/workload-identity/github-actions"
]
},
{
"group": "API",
"pages": [
Expand Down
217 changes: 217 additions & 0 deletions docs/integrations/workload-identity/github-actions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
---
title: GitHub Actions
---

This guide explains how to configure Workload Identity for GitHub Actions to authenticate with Bytebase without storing long-lived credentials.

## Step 1: Create a Workload Identity in Bytebase

1. Go to **IAM & Admin** > **Users & Groups**.
2. Click **Add User** in the upper-right corner.
3. Select **Workload Identity** as the Type.
4. Fill in the configuration:

| Field | Description | Example |
|-------|-------------|---------|
| **Name** | Display name for this identity | `GitHub Actions Deploy` |
| **Email** | Unique email for this identity (must end with `@workload.bytebase.com`) | `[email protected]` |
| **Platform** | Select GitHub Actions | `GitHub Actions` |
| **Owner** | GitHub organization or username | `my-org` |
| **Repository** | Repository name | `my-repo` |
| **Branch** | Branch name (use `*` for all branches) | `main` |

5. Click **Confirm** to create the Workload Identity.

## Step 2: Assign Roles

After creating the Workload Identity, assign the `GitOps Service Agent` role to enable automated CI/CD workflows:

1. Go to your project's **Settings** > **Members**.
2. Click **Grant Access**.
3. Enter the Workload Identity email (e.g., `[email protected]`).
4. Select the **GitOps Service Agent** role.
5. Click **Confirm**.

<Tip>

The `GitOps Service Agent` role is designed for automated CI/CD workflows, allowing the identity to create and execute database changes. See [Roles and Permissions](/administration/roles) for details.

</Tip>

## Step 3: Configure GitHub Actions Workflow

In your GitHub Actions workflow, add the following configuration:

### Request OIDC Token

Add `id-token: write` permission and use the `actions/github-script` action to get the token:

```yaml
name: Deploy Database Changes

on:
push:
branches: [main]

permissions:
id-token: write # Required for OIDC token
contents: read

env:
BYTEBASE_URL: https://bytebase.example.com
WORKLOAD_IDENTITY_EMAIL: [email protected]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Get Bytebase Token
id: bytebase-token
uses: actions/github-script@v7
with:
script: |
const token = await core.getIDToken('https://github.com/${{ github.repository_owner }}');
core.setSecret(token);
core.setOutput('token', token);

- name: Exchange for Bytebase API Token
id: exchange
run: |
RESPONSE=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
-H "Content-Type: application/json" \
-d "{\"token\": \"${{ steps.bytebase-token.outputs.token }}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}")

ACCESS_TOKEN=$(echo $RESPONSE | jq -r '.accessToken')
echo "::add-mask::$ACCESS_TOKEN"
echo "access_token=$ACCESS_TOKEN" >> $GITHUB_OUTPUT

- name: Call Bytebase API
run: |
curl -s "${BYTEBASE_URL}/v1/projects" \
-H "Authorization: Bearer ${{ steps.exchange.outputs.access_token }}"
```

## Complete Example

Here's a complete workflow that creates a database change using Workload Identity:

```yaml
name: Database Schema Change

on:
push:
branches: [main]
paths:
- 'migrations/**'

permissions:
id-token: write
contents: read

env:
BYTEBASE_URL: https://bytebase.example.com
WORKLOAD_IDENTITY_EMAIL: [email protected]
PROJECT: projects/my-project

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Get OIDC Token
id: oidc
uses: actions/github-script@v7
with:
script: |
const token = await core.getIDToken('https://github.com/${{ github.repository_owner }}');
core.setSecret(token);
core.setOutput('token', token);

- name: Exchange Token
id: auth
run: |
RESPONSE=$(curl -s -X POST "${BYTEBASE_URL}/v1/auth:exchangeToken" \
-H "Content-Type: application/json" \
-d "{\"token\": \"${{ steps.oidc.outputs.token }}\", \"email\": \"${WORKLOAD_IDENTITY_EMAIL}\"}")

ACCESS_TOKEN=$(echo $RESPONSE | jq -r '.accessToken')
if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
echo "Failed to get access token"
echo $RESPONSE
exit 1
fi

echo "::add-mask::$ACCESS_TOKEN"
echo "access_token=$ACCESS_TOKEN" >> $GITHUB_OUTPUT

- name: Create Plan
id: plan
run: |
# Read migration SQL file
SQL_CONTENT=$(cat migrations/latest.sql | jq -Rs .)

RESPONSE=$(curl -s -X POST "${BYTEBASE_URL}/v1/${PROJECT}/plans" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Migration from GitHub Actions\",
\"steps\": [{
\"specs\": [{
\"changeDatabaseConfig\": {
\"target\": \"instances/prod/databases/mydb\",
\"type\": \"MIGRATE\",
\"sheet\": \"${SQL_CONTENT}\"
}
}]
}]
}")

PLAN_NAME=$(echo $RESPONSE | jq -r '.name')
echo "plan_name=$PLAN_NAME" >> $GITHUB_OUTPUT

- name: Create Issue and Rollout
run: |
# Create issue
curl -s -X POST "${BYTEBASE_URL}/v1/${PROJECT}/issues" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Schema migration\",
\"type\": \"DATABASE_CHANGE\",
\"plan\": \"${{ steps.plan.outputs.plan_name }}\"
}"
```

## Troubleshooting

### Token Exchange Fails

If the token exchange returns an error:

1. **Verify the repository and branch**: Check that your workflow's repository, branch match the configured values in Bytebase.

2. **Check the audience**: Ensure the audience in your `getIDToken()` call matches `https://github.com/{owner}`.

### Permission Denied

If API calls return permission errors:

1. Verify the Workload Identity has the `GitOps Service Agent` role assigned.
2. Check that the Workload Identity is a member of the target project.

### Debug Token Claims

To inspect the OIDC token claims, decode the JWT:

```yaml
- name: Debug Token
run: |
echo "${{ steps.oidc.outputs.token }}" | cut -d. -f2 | base64 -d | jq .
```

This shows the token's claims including `sub`, `aud`, and `iss` that Bytebase validates.
66 changes: 66 additions & 0 deletions docs/integrations/workload-identity/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Overview
---

Workload Identity allows CI/CD pipelines and external services to authenticate with Bytebase using OpenID Connect (OIDC) tokens, eliminating the need to store long-lived credentials as secrets.

## Why Workload Identity?

Traditional CI/CD integration requires storing Service Account keys as secrets in your CI/CD platform. This approach has security risks:

- **Secret sprawl**: Credentials stored in multiple locations increase attack surface
- **No automatic rotation**: Long-lived keys require manual rotation
- **Broad access**: Leaked credentials can be used from anywhere

Workload Identity solves these problems by:

- **No stored secrets**: Workflows request short-lived tokens directly from your CI/CD platform
- **Automatic rotation**: Tokens are generated per-job and expire automatically
- **Scoped access**: Tokens are bound to specific repositories, branches, and workflows

## How It Works

```
┌─────────────────┐ 1. Request OIDC Token ┌─────────────────┐
│ │ ──────────────────────────────► │ │
│ CI/CD Platform │ │ OIDC Provider │
│ (e.g. GitHub) │ ◄────────────────────────────── │ │
│ │ 2. Return JWT Token └─────────────────┘
└────────┬────────┘
│ 3. Exchange Token
│ (JWT + Workload Identity Email)
┌─────────────────┐
│ │ 4. Validate Token
│ Bytebase │ - Verify issuer
│ │ - Check audience
└────────┬────────┘ - Match subject pattern
│ 5. Return Bytebase API Token
┌─────────────────┐
│ CI/CD Workflow │ 6. Call Bytebase API
│ continues... │ with API Token
└─────────────────┘
```

1. Your CI/CD workflow requests an OIDC token from the platform's token endpoint
2. The platform returns a signed JWT containing claims about the workflow (repository, branch, etc.)
3. Your workflow calls Bytebase's `exchangeToken` API with the JWT and Workload Identity email
4. Bytebase validates the token against the configured Workload Identity settings
5. Bytebase returns a short-lived API token (1-hour validity)
6. Your workflow uses this token for subsequent Bytebase API calls

## Supported Platforms

<CardGroup cols={2}>
<Card title="GitHub Actions" icon="github" href="/integrations/workload-identity/github-actions">
Configure OIDC authentication for GitHub Actions workflows
</Card>
</CardGroup>

## Prerequisites

- Bytebase version 3.13.0 or later
- A CI/CD platform that supports OIDC token issuance