idcat is a small Axum service that hides the GitHub App authentication dance behind one internal endpoint.
It reads a GitHub App RSA private key from the filesystem, signs a short-lived GitHub App JWT, and exchanges that JWT for a GitHub installation access token.
private-key-directory = "/var/run/secrets/idcat"
[[role]]
name = "kubernetes-default"
audience = "idcat"
issuer = "https://kubernetes.default.svc"
[role.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"
repository = "myorg/alfa"
role = "github-workflow"
[installation-policy.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. 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