K0KEYZERO

Use Case

Secret Management for GitHub Actions

Replace GitHub Actions secrets with vault-backed, policy-controlled credential resolution using KeyZero.

GitHub Actions provides a built-in secrets mechanism: you store key-value pairs in repository or organization settings, and reference them as ${{ secrets.MY_SECRET }} in workflow YAML. This works for simple cases, but as CI/CD pipelines grow in complexity -- especially with AI-assisted workflows -- the limitations become significant.

Problems with GitHub Actions Secrets

Broad Access Scope

GitHub Actions secrets are scoped to the repository or organization level. Every workflow in a repository has access to every repository-level secret. There is no way to say "this secret is only available to the deploy workflow" or "this secret should only be accessible from the main branch" using GitHub's built-in mechanism. Environment-level secrets add some scoping, but environments are coarse-grained and require manual approval gates.

Secrets in Logs

GitHub Actions masks secrets in log output by replacing known values with ***. But this masking is best-effort:

  • Secrets transformed by string operations (base64 encoding, URL encoding, substring extraction) are not masked.
  • Secrets printed by third-party actions or scripts that format output differently may slip through.
  • Multi-line secrets are masked line by line, and partial matches may not trigger masking.

Artifact Leakage

Workflow artifacts, test reports, and coverage files are often uploaded without sanitization. A test that logs a database URL or a debug step that dumps environment variables can embed credentials in artifacts that persist long after the workflow completes.

No Audit Trail for Individual Secrets

GitHub provides audit logs for secret creation and deletion, but not for which workflow run accessed which secret. You know that PROD_DB_PASSWORD was used by the repository, but not whether it was the deploy workflow, the test suite, or a PR from a fork.

How KeyZero Works in GitHub Actions

KeyZero integrates with GitHub Actions through its JWT identity support and kz run CLI. GitHub Actions provides an OIDC token to each workflow run -- KeyZero verifies this token and evaluates policies before resolving secrets.

Step 1: Install KeyZero in Your Workflow

- name: Install KeyZero
  run: |
    curl -fsSL https://get.keyzero.dev | sh
    echo "$HOME/.keyzero/bin" >> $GITHUB_PATH

Step 2: Configure the JWT Issuer

In your KeyZero bundle, configure GitHub Actions as a JWT issuer:

# bundle.yaml
issuers:
  - name: github
    type: github-actions
    issuer_url: "https://token.actions.githubusercontent.com"
    audience: "keyzero"

The github-actions issuer type automatically maps GitHub's OIDC claims to standard identity fields:

GitHub ClaimKeyZero Identity Field
repositoryidentity.service
refidentity.env
workflow_refidentity.workflow
actoridentity.user

Step 3: Write CEL Policies

CEL (Common Expression Language) policies control which workflows can access which secrets:

# bundle.yaml
policies:
  - match: "secret/data/prod/**"
    rules:
      - cel: >
          identity.service == 'myorg/myapp'
          && identity.env == 'refs/heads/main'
        effect: allow
        mode: direct

  - match: "secret/data/staging/**"
    rules:
      - cel: >
          identity.service == 'myorg/myapp'
          && identity.env.startsWith('refs/heads/')
        effect: allow
        mode: direct

  - match: "secret/data/dev/**"
    rules:
      - cel: "identity.service.startsWith('myorg/')"
        effect: allow
        mode: direct

This gives you fine-grained control: production secrets are only available to the main branch of myorg/myapp. Staging secrets are available to any branch. Dev secrets are available to any repo in the org. Learn more about this approach in policy-based access control for machine identities.

Step 4: Use kz run in Your Workflow

name: Deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

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

      - name: Install KeyZero
        run: |
          curl -fsSL https://get.keyzero.dev | sh
          echo "$HOME/.keyzero/bin" >> $GITHUB_PATH

      - name: Deploy with secrets
        run: |
          kz run \
            --bundle ./bundle.yaml \
            --server https://keyzero.internal.myorg.com \
            -- ./deploy.sh
        env:
          KZ_JWT: ${{ steps.auth.outputs.token }}

The deploy.sh script receives environment variables resolved from Vault (or AWS SM, 1Password, etc.) based on the policy evaluation. The secrets never appear in the workflow YAML, and access is controlled per-workflow, per-branch, per-repository. This eliminates the hardcoded secrets problem that plagues many CI/CD pipelines. For backend-specific setup, see the HashiCorp Vault and AWS Secrets Manager integration guides.

Comparison with Native GitHub Secrets

FeatureGitHub Actions SecretsKeyZero
Storage backendGitHubVault, AWS SM, 1Password, GCP SM, etc.
Access scopeRepository / Organization / EnvironmentCEL policy per secret path
Branch restrictionsEnvironment protection rulesCEL expression on identity.env
Audit trailSecret CRUD eventsPer-resolution audit log
Secret rotationManual update in GitHub UIAutomatic via backend
AI workflow supportNo special handlingBlind mode, fetch tool

AI-Assisted CI/CD

If your GitHub Actions workflows use AI agents (e.g., automated code review, AI-powered testing, or LLM-based deployment validation), KeyZero's blind mode prevents credentials from entering agent context windows during CI runs:

- name: AI-assisted review
  run: |
    kz run --blind \
      --bundle ./bundle.yaml \
      -- python ai_review.py

The AI review script can access APIs through opaque tokens. If it logs or outputs credential values, only the opaque tokens appear -- not the real credentials. This is critical for workflows that send CI context to LLM providers.