idcat is a service for safely delegating GitHub App permissions
It allows authenticated callers to get GitHub App installation access tokens without having direct access to the app’s private signing key. Instead, services and workflows are authenticated with idcat using JWT bearer tokens. Based on the issuer and claims in those tokens, idcat can issue installation tokens with precisely the permissions needed for a particular use case.
This makes it possible to grant applications and services controlled access to GitHub through a GitHub App, while keeping the app’s signing credentials centralised and protected.
- A High-performance, high-availability service written in Rust using Tokio and Axum
- Signing keys can optionally be stored in AWS KMS, making it more difficult for an attacker to get hold of sensitive material
- The centralised configuration setup encourages granting only permissions that are actually needed
- The ability to bridge webhook notifications from GitHub into the NATS messaging service
private-key-directory = "/var/run/secrets/idcat"
[[role]]
name = "kubernetes-default"
audience = "idcat"
issuer = "https://kubernetes.default.svc"
claims = {sub = "system:serviceaccount:default:default"}
[[role]]
name = "github-workflow"
audience = "idcat"
issuer = "https://token.actions.githubusercontent.com"
[[github-app]]
name = "deployments"
app-id = 123456
secret-key = "deployments-private-key.pem"
allowed-roles = ["kubernetes-default"]
[[installation-policy]]
github-app = "deployments"
repositories = ["myorg/alfa", "myorg/beta"]
role = "github-workflow"
required-claims = { repository = "myorg/gamma" }See idcat.toml.example for a fuller configuration with multiple roles and GitHub Apps.
List multiple entries in allowed-roles to allow alternative authentication methods or
role trust requirements for the same GitHub App.
Use [[installation-policy]] to grant a role only for one installed app/repository
combination, with additional required token claims. Set either repository = "owner/name"
or repositories = ["owner/name", "owner/other"]; the request may match any configured
repository pattern. For example, a request for deployments on myorg/alfa can require
the token to satisfy github-workflow and also
carry repository = "myorg/gamma". App-level allowed-roles still grant access to every
repository installation for that GitHub App.
Mount the private keys as files. For example, in Kubernetes this could be a Secret volume mounted at private-key-directory, but idcat only reads files from the filesystem.
kubectl create secret generic idcat \
--from-file=private-key.pem=/path/to/github-app-private-key.pemThe application does not need Kubernetes API permissions to read private keys.
When built with the kms feature, key-source may be set to kms. In that mode,
secret-key selects an AWS KMS alias instead of a filesystem path. Values without
the alias/ prefix are treated as alias names, so secret-key = "deployments"
uses alias/deployments. AWS credentials and region are loaded from the ambient
AWS SDK configuration.
curl -X POST \
-H "Authorization: Bearer $KUBERNETES_JWT" \
http://localhost:8080/installation-token/deployments/github_user/repo_nameThe response body is the GitHub installation token:
ghs_...
To proxy a repository-scoped GitHub API request through an installation token, prefix the GitHub
/repos/{owner}/{repo} path with /proxy/{github-app}. The GitHub app name selects the
configured GitHub App and its allowed roles, while the owner/repo pair comes from the proxied
GitHub API path:
curl -X GET \
-H "Authorization: Bearer $KUBERNETES_JWT" \
http://localhost:8080/proxy/deployments/repos/github_user/repo_name/contents/README.mdcargo run -- --config-file idcat.tomlFor local testing, authentication and role checks can be bypassed:
cargo run -- --config-file idcat.toml --disable-authUse --debug to log detailed installation-token flow steps:
cargo run -- --config-file idcat.toml --debugGitHub App installation IDs are cached in memory after the first lookup. Installation tokens are cached in memory for 50 minutes per GitHub app and repo.
MIT