Secure Your GitHub API Key in GitHub Actions
The Problem
This is what most GitHub Actions codebases look like today. The GITHUB_TOKEN key (ghp_...) lives directly in the process environment. If your repo, your CI logs, or your deployment config leaks — so does your key.
# .github/workflows/release.yml — raw GITHUB_TOKEN in curl
name: Release
on:
push:
tags:
- "v*"
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- name: Create GitHub Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_PAT }}" \
-H "Content-Type: application/json" \
https://api.github.com/repos/${{ github.repository }}/releases \
-d '{
"tag_name": "${{ github.ref_name }}",
"name": "Release ${{ github.ref_name }}",
"draft": false,
"prerelease": false
}'The Fix
Send the request to the KeyVault Edge proxy at https://proxy.keyvaultedge.com with the provider's usual path, your kvt_ token in X-KV-Token and the provider's base URL in X-KV-Target. The proxy refuses requests whose Origin isn't on the token's allowed hosts, then injects your real GITHUB_TOKEN, so it never sits in your app's environment.
# .github/workflows/release.yml — the PAT lives in KeyVault Edge, not in GitHub secrets
name: Release
on:
push:
tags:
- "v*"
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- name: Create GitHub Release via KVE Proxy
env:
KVE_TOKEN: ${{ secrets.KVE_TOKEN }}
run: |
curl -X POST \
-H "X-KV-Token: ${KVE_TOKEN}" \
-H "X-KV-Target: https://api.github.com" \
-H "Origin: https://ci.yourdomain.com" \
-H "Content-Type: application/json" \
https://proxy.keyvaultedge.com/repos/${{ github.repository }}/releases \
-d "{
\"tag_name\": \"${{ github.ref_name }}\",
\"name\": \"Release ${{ github.ref_name }}\",
\"draft\": false,
\"prerelease\": false
}"Setup in 4 steps
Create a KVE token for your GitHub PAT
At keyvaultedge.com, add your fine-grained PAT as a key (GitHub isn't a built-in provider, so add it as a custom provider with base URL https://api.github.com, header Authorization and prefix "Bearer "). Create a token with an allowed host you choose for CI, e.g. ci.yourdomain.com, and send that value as the Origin header. KeyVault Edge does not check IP addresses.
Add KVE_TOKEN as an Actions repository secret
In your GitHub repo, go to Settings → Secrets and variables → Actions → New repository secret. Name it KVE_TOKEN and paste the sanitized token. Remove the GITHUB_PAT secret.
# GitHub CLI
gh secret set KVE_TOKEN --body "kvt_...your_token"
# Then remove the raw PAT secret
gh secret delete GITHUB_PATUpdate your workflow to use the KVE proxy
Replace api.github.com in the URL with proxy.keyvaultedge.com, keep the path, and add the X-KV-Token, X-KV-Target and Origin headers. The JSON body is unchanged. Reference KVE_TOKEN from env, not secrets directly in the curl command.
Scope token permissions in the KVE dashboard
In KeyVault Edge, configure your GitHub token to only allow specific API paths (e.g. /repos/*/releases). Any attempt to call /user, /orgs, or /admin through this token will be blocked at the proxy level.
What this protects, and what it doesn't
- Your real GITHUB_TOKEN stays at the edge, so it can't leak from your repo, logs, bundle or server environment.
- The kvt_ token is still a credential. A copied token can be replayed by anyone who can send the right headers, including a fake Origin from a server. The protection is that each token has its own rate limits, monthly cost cap and endpoint scope, and you can revoke it in one click without rotating the provider key.
- The cost cap is estimated from the usage the provider reports, so a request already in flight can overshoot it slightly.
Plans and limits are on the pricing page.
Start protecting your keys
Your GitHub key is one leaked .env file away from being exploited. With KeyVault Edge the real key stays at the edge, and the token that replaces it is capped, scoped and revocable in one click.