Skip to content

eparon/oidc-secure-api-github-actions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🛡️ Secure internal APIs with GitHub Actions OIDC workload identity

This repository implements a Zero Trust sidecar pattern that replaces static, long-lived API keys with short-lived, identity-based OIDC tokens. By verifying the "Workload Identity" of a GitHub Action runner, your infrastructure can enforce granular access control based on the specific repository, organization, or environment making the request.

📖 Full walkthrough on: How to Secure APIs Using OIDC and GitHub Actions (No Secrets) -- eparon.me

🏛️ Architecture & Concepts

Architecture

This project moves authentication and authorization out of the application and into the infrastructure.

  • Workload Identity: Instead of a secret (e.g. service-account, token), the GitHub Action runner requests a signed JWT from GitHub’s OIDC provider.
  • Sidecar Proxy: An Envoy instance sits in front of the API (NGINX), intercepting all traffic.
  • Identity Verification: Envoy validates the token's signature and uses a custom (Lua) filter to authorize access based on claims like repository or workflow.

💡 Why This Matters

Static secrets in CI/CD are a common attack vector. This approach:

  • Eliminates stored credentials
  • Uses short-lived, scoped tokens
  • Enables Zero Trust patterns in pipelines

⚡ Quick Start

Local testing (via Docker 🐳)

See more on how to get started locally

Kubernetes deployment

kubectl apply -f kubernetes/envoy-config.yaml -n oidc-experiments
kubectl apply -f kubernetes/deployment.yaml -n oidc-experiments
kubectl apply -f kubernetes/service.yaml -n oidc-experiments

📁 Repository Organization

  • kubernetes/ – Kubernetes manifests for deploying Envoy + NGINX
  • local-lab/ – Docker-based environment to simulate OIDC flows locally
  • templates/ – Example GitHub Actions workflows

☸️ Production Deployment (Kubernetes)

The kubernetes folder contains pure Kubernetes manifests. This approach ensures total transparency in how the proxy and application are configured.

Security Hardening:

  • Localhost Isolation: The application container (NGINX) listens only on 127.0.0.1, ensuring no traffic can bypass the Envoy security layer.
  • Automated Verification: Envoy automatically fetches GitHub's public keys (JWKS) to verify token authenticity.
  • Least Privilege: The sidecar only allows requests that match a specific GitHub Organization or Repository.

🧪 The Developer's Lab (Local Verification)

Testing OIDC logic typically requires pushing code to GitHub and waiting for a runner. The Local Lab breaks this cycle by providing a simulating environment for the entire OIDC lifecycle.

Features:

  • Mock IDP: Uses navikt/mock-oauth2-server to act as GitHub's Identity Provider.
  • Fast Iteration: Debug your claim validation logic (e.g. via Lua filters) and Envoy configurations in seconds.
  • Security Testing: Includes a configuration to simulate "Malicious" requests from unauthorized repositories to verify that your 401 - Unauthorized/403 - Forbidden logic is working as expected.

See more about how to run the lab in the corresponding README.

🛡️ Authorization Logic (The Lua Filter)

While the OIDC token proves the request is authentic, the Lua filter handles the authorization. It inspects the claims within the JWT to ensure the caller has the right to access this specific resource:

-- Example: Restrict access to a specific repository
local claims = request_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.jwt_authn")["jwt_payload"]
local repository = claims["repository"]

if repository ~= "your-org/your-repo" then
    request_handle:logInfo("Unauthorized repository access attempt: " .. tostring(repository))
    request_handle:respond({[":status"] = "403"}, "Access Denied: Repository not authorized\n")
end

📖 Deep Dive

This repository is the companion code for the article: How to Secure APIs Using OIDC and GitHub Actions (No Secrets) -- eparon.me

Explore the blog post for a comprehensive guide on:

  • The transition from Static Secrets to Workload Identity.
  • Configuring GitHub Actions permissions for OIDC.
  • The internal mechanics of the Envoy jwt_authn filter.

© 2026 Nontas Rontogiannis. Licensed under the MIT License.

About

🛡️ Secure internal APIs with GitHub Actions OIDC workload identity. Implements a Zero Trust sidecar pattern using Envoy Proxy to replace static secrets with identity-based authorization.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors

Languages