{"id":13471551,"url":"https://github.com/martinbaillie/vault-plugin-secrets-github","last_synced_at":"2025-05-16T12:12:17.402Z","repository":{"id":36428870,"uuid":"223704264","full_name":"martinbaillie/vault-plugin-secrets-github","owner":"martinbaillie","description":"Create ephemeral, finely-scoped @github access tokens using @hashicorp Vault.","archived":false,"fork":false,"pushed_at":"2025-03-15T13:35:41.000Z","size":659,"stargazers_count":282,"open_issues_count":8,"forks_count":26,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T08:38:34.581Z","etag":null,"topics":["github","github-apps","hashicorp","hashicorp-vault"],"latest_commit_sha":null,"homepage":"https://martin.baillie.id/wrote/ephemeral-github-tokens-via-hashicorp-vault/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martinbaillie.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"martinbaillie"}},"created_at":"2019-11-24T06:49:28.000Z","updated_at":"2025-04-09T21:45:05.000Z","dependencies_parsed_at":"2024-01-15T15:46:05.171Z","dependency_job_id":"0c4c9c70-80ad-4cd1-ae09-e8be738b4951","html_url":"https://github.com/martinbaillie/vault-plugin-secrets-github","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinbaillie%2Fvault-plugin-secrets-github","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinbaillie%2Fvault-plugin-secrets-github/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinbaillie%2Fvault-plugin-secrets-github/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinbaillie%2Fvault-plugin-secrets-github/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martinbaillie","download_url":"https://codeload.github.com/martinbaillie/vault-plugin-secrets-github/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254527099,"owners_count":22085919,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github","github-apps","hashicorp","hashicorp-vault"],"created_at":"2024-07-31T16:00:46.527Z","updated_at":"2025-05-16T12:12:17.383Z","avatar_url":"https://github.com/martinbaillie.png","language":"Go","funding_links":["https://github.com/sponsors/martinbaillie"],"categories":["Go"],"sub_categories":[],"readme":"[[https://github.com/martinbaillie/vault-plugin-secrets-github/actions][https://github.com/martinbaillie/vault-plugin-secrets-github/actions/workflows/test.yml/badge.svg]]\n[[https://codecov.io/gh/martinbaillie/vault-plugin-secrets-github][https://codecov.io/gh/martinbaillie/vault-plugin-secrets-github/branch/master/graph/badge.svg]]\n[[https://godoc.org/github.com/martinbaillie/vault-plugin-secrets-github][https://godoc.org/github.com/martinbaillie/vault-plugin-secrets-github?status.svg]]\n[[https://goreportcard.com/report/github.com/martinbaillie/vault-plugin-secrets-github][https://goreportcard.com/badge/github.com/martinbaillie/vault-plugin-secrets-github?status.svg]]\n[[https://github.com/martinbaillie/vault-plugin-secrets-github/releases/latest][https://img.shields.io/github/release/martinbaillie/vault-plugin-secrets-github.svg]]\n\n* Vault Plugin Secrets GitHub                                :TOC_2_gh:noexport:\n- [[#about][About]]\n  - [[#why][Why?]]\n  - [[#what][What?]]\n  - [[#how][How?]]\n- [[#installation][Installation]]\n  - [[#from-release][From release]]\n  - [[#from-source][From source]]\n  - [[#setup-github][Setup (GitHub)]]\n  - [[#setup-vault][Setup (Vault)]]\n- [[#api][API]]\n  - [[#token][Token]]\n  - [[#permission-sets][Permission sets]]\n  - [[#config][Config]]\n  - [[#metrics][Metrics]]\n  - [[#info][Info]]\n- [[#development][Development]]\n  - [[#tests][Tests]]\n- [[#security][Security]]\n\n* About\nAre you using HashiCorp Vault and GitHub in your organisation? Do you want\nephemeral, finely-scoped GitHub tokens? If so, this plugin might be for you.\n\n#+begin_quote\nUPDATE: The plugin was recently demoed at HashiCorp's [[https://www.youtube.com/watch?v=JuzBolDyGdg\u0026t=17308s][Hashitalks 2021]].\n#+end_quote\n\n** Why?\nPerforming automation against GitHub APIs often neccessitates the creation of\n[[https://help.github.com/en/github/extending-github/git-automation-with-oauth-tokens][OAuth Tokens]]. These tokens are tied to a user account, have /very/\n[[https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/#available-scopes][coarsely-scoped permissions]] and do not expire.\n\nAs an organisation owner this likely means your automation-savvy users have\ncreated personal access tokens with powerful permissions which are being neither\nrotated nor deleted.\n\nYou will also commonly have wasted at least one of your GitHub seats on a\n[[https://help.github.com/en/github/getting-started-with-github/types-of-github-accounts#personal-user-accounts][robot/machine user]] for CI/CD purposes. These users share a similar access token\nand SSH key story as your human users do, and additonally need their credentials\nmanaged and rotated for them (arguably made more awkward when authenticating\nthrough an IdP).\n\n[[https://developer.github.com/apps/building-github-apps/][GitHub Apps]] offer a better approach to this automation problem:\n\n- They do not consume a seat (license) nor need credential management.\n- They have /much/ finer-grained [[https://developer.github.com/v3/apps/permissions/][permissions]] available to the access tokens.\n- The tokens they issue expire after one hour.\n\nHowever, GitHub Apps require the management of at least one private key which is\nneeded to mint the JWTs used for the [[https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation][App installation authentication]] token\nrequest flow.\n\n** What?\nThis plugin allows you to take advantage of your existing Vault deployment's\ndurable storage backend to protect the GitHub App private key, and your enabled\nVault authN/Z mechanisms and highly-available API to perform the GitHub App\ntoken request flow on behalf of your users.\n\n** How?\nThe Vault plugin acts as an intermediary for a GitHub App that you install into\nyour organisation:\n\n#+ATTR_ORG: :width 1024\n#+CAPTION: Logical Architecture\n[[file:vault_github_plugin.png]]\n\nUsers login using your existing Vault auth backend(s) and, Vault RBAC\npermitting, can request GitHub tokens from the plugin. The plugin, in turn,\nauthenticates with the GitHub App and requests a token on behalf of the user.\nThis flow is better explained in the sequence diagram below:\n\n#+CAPTION: Sequence Diagram\n[[https://mermaid-js.github.io/mermaid-live-editor/#/view/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgcGFydGljaXBhbnQgVSBhcyBVc2VyXG4gICAgcGFydGljaXBhbnQgViBhcyBWYXVsdFxuICAgIHBhcnRpY2lwYW50IEcgYXMgR2l0SHViXG4gICAgTm90ZSBvdmVyIFU6IEh1bWFuLCBDSS9DRCBhZ2VudCBldGMuXG4gICAgVS0+PlY6IFBPU1QgeW91ci52YXVsdC5vcmcvdjEvYXV0aFxuICAgIGFjdGl2YXRlIFZcbiAgICBOb3RlIG92ZXIgVjogU1NPLCBBRCwgQ2xvdWQgSUFNIGV0Yy5cbiAgICBWLS0+PlU6IE9LXG4gICAgZGVhY3RpdmF0ZSBWXG4gICAgTm90ZSByaWdodCBvZiBVOiBSZXF1ZXN0IGEgR2l0SHViIHRva2VuXG4gICAgYWx0XG4gICAgVS0+PlY6IFBPU1QgeW91ci52YXVsdC5vcmcvdjEvZ2l0aHViL3Rva2VuXG4gICAgZWxzZSByZXBvczogWzEyMyw0NTZdLCBwZXJtczogXCJpc3N1ZXM6d3JpdGVcIlxuICAgIFUtPj5WOiBQT1NUIHlvdXIudmF1bHQub3JnL3YxL2dpdGh1Yi90b2tlblxuICAgIGVuZFxuICAgIGFjdGl2YXRlIFZcbiAgICBOb3RlIG92ZXIgVjogTWludCBKV1QgdXNpbmcgUHJpdmF0ZSBLZXlcbiAgICBWLT4+VjogR2l0SHViIEpXVCAoZXhwOiAxMG0pXG4gICAgVi0+Pkc6IFBPU1QgYXBpLmdpdGh1Yi5jb20vLi4uL2FjY2Vzc190b2tlbnNcbiAgICBHLS0+PlY6IHRva2VuOiBbIFwidjEuMTIzNDU2Nzg5Li4uXCIgZXhwOiBcIjFoXCIgXVxuICAgIFYtPj5WOiBSZWNvcmQgbWV0cmljcywgbG9nc1xuICAgIFYtLT4+VTogWyB0b2tlbjogXCJ2MS4xMjM0NTY3ODkuLi5cIiBleHA6IFwiMWhcIiBdXG4gICAgZGVhY3RpdmF0ZSBWXG4gICAgTm90ZSBvdmVyIFU6IFVzZSBhY2Nlc3MgdG9rZW4gb24gR2l0SHViXG4gICAgYWx0IE9wZXJhdGUgb24gcmVwb3N0b3JpZXMgYWNjZXNzIHRva2VuIGhhcyBwZXJtaXNzaW9ucyBvblxuICAgIFUtPj5HOiAkIGdpdCBjbG9uZSBodHRwczovL3gtYWNjZXNzLXRva2VuOnYxLjEyMzQ1Njc4OS4uLkBnaXRodWIuY29tL29yZy9yZXBvLmdpdFxuICAgIGVsc2UgUGVyZm9ybSBBUEkgcmVxdWVzdHMgYWdhaW5zdCByZXNvdXJjZXMgYWNjZXNzIHRva2VuIGhhcyBwZXJtaXNzaW9ucyBvblxuICAgIFUtPj5HOiAkIGN1cmwgLUggXCJBdXRob3JpemF0aW9uOiBCZWFyZXIgdjEuMTIzNDU2Nzg5Li4uXCIgaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXNvdXJjZVxuICAgIGVuZCIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0Iiwic2VxdWVuY2UiOnsibWlycm9yQWN0b3JzIjpmYWxzZSwiYWN0b3JNYXJnaW4iOjEyMCwibm90ZU1hcmdpbiI6MTAsIm1lc3NhZ2VNYXJnaW4iOjMwLCJib3hUZXh0TWFyZ2luIjoxLCJoZWlnaHQiOjMwLCJ3aWR0aCI6MjAwfX19][https://mermaid.ink/img/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgcGFydGljaXBhbnQgVSBhcyBVc2VyXG4gICAgcGFydGljaXBhbnQgViBhcyBWYXVsdFxuICAgIHBhcnRpY2lwYW50IEcgYXMgR2l0SHViXG4gICAgTm90ZSBvdmVyIFU6IEh1bWFuLCBDSS9DRCBhZ2VudCBldGMuXG4gICAgVS0+PlY6IFBPU1QgeW91ci52YXVsdC5vcmcvdjEvYXV0aFxuICAgIGFjdGl2YXRlIFZcbiAgICBOb3RlIG92ZXIgVjogU1NPLCBBRCwgQ2xvdWQgSUFNIGV0Yy5cbiAgICBWLS0+PlU6IE9LXG4gICAgZGVhY3RpdmF0ZSBWXG4gICAgTm90ZSByaWdodCBvZiBVOiBSZXF1ZXN0IGEgR2l0SHViIHRva2VuXG4gICAgYWx0XG4gICAgVS0+PlY6IFBPU1QgeW91ci52YXVsdC5vcmcvdjEvZ2l0aHViL3Rva2VuXG4gICAgZWxzZSByZXBvczogWzEyMyw0NTZdLCBwZXJtczogXCJpc3N1ZXM6d3JpdGVcIlxuICAgIFUtPj5WOiBQT1NUIHlvdXIudmF1bHQub3JnL3YxL2dpdGh1Yi90b2tlblxuICAgIGVuZFxuICAgIGFjdGl2YXRlIFZcbiAgICBOb3RlIG92ZXIgVjogTWludCBKV1QgdXNpbmcgUHJpdmF0ZSBLZXlcbiAgICBWLT4+VjogR2l0SHViIEpXVCAoZXhwOiAxMG0pXG4gICAgVi0+Pkc6IFBPU1QgYXBpLmdpdGh1Yi5jb20vLi4uL2FjY2Vzc190b2tlbnNcbiAgICBHLS0+PlY6IHRva2VuOiBbIFwidjEuMTIzNDU2Nzg5Li4uXCIgZXhwOiBcIjFoXCIgXVxuICAgIFYtPj5WOiBSZWNvcmQgbWV0cmljcywgbG9nc1xuICAgIFYtLT4+VTogWyB0b2tlbjogXCJ2MS4xMjM0NTY3ODkuLi5cIiBleHA6IFwiMWhcIiBdXG4gICAgZGVhY3RpdmF0ZSBWXG4gICAgTm90ZSBvdmVyIFU6IFVzZSBhY2Nlc3MgdG9rZW4gb24gR2l0SHViXG4gICAgYWx0IE9wZXJhdGUgb24gcmVwb3N0b3JpZXMgYWNjZXNzIHRva2VuIGhhcyBwZXJtaXNzaW9ucyBvblxuICAgIFUtPj5HOiAkIGdpdCBjbG9uZSBodHRwczovL3gtYWNjZXNzLXRva2VuOnYxLjEyMzQ1Njc4OS4uLkBnaXRodWIuY29tL29yZy9yZXBvLmdpdFxuICAgIGVsc2UgUGVyZm9ybSBBUEkgcmVxdWVzdHMgYWdhaW5zdCByZXNvdXJjZXMgYWNjZXNzIHRva2VuIGhhcyBwZXJtaXNzaW9ucyBvblxuICAgIFUtPj5HOiAkIGN1cmwgLUggXCJBdXRob3JpemF0aW9uOiBCZWFyZXIgdjEuMTIzNDU2Nzg5Li4uXCIgaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXNvdXJjZVxuICAgIGVuZCIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0Iiwic2VxdWVuY2UiOnsibWlycm9yQWN0b3JzIjpmYWxzZSwiYWN0b3JNYXJnaW4iOjEyMCwibm90ZU1hcmdpbiI6MTAsIm1lc3NhZ2VNYXJnaW4iOjMwLCJib3hUZXh0TWFyZ2luIjoxLCJoZWlnaHQiOjMwLCJ3aWR0aCI6MjAwfX19?nothing.svg]]\n\n#+BEGIN_COMMENT Mermaid source\nsequenceDiagram\n    participant U as User\n    participant V as Vault\n    participant G as GitHub\n    Note over U: Human, CI/CD agent etc.\n    U-\u003e\u003eV: POST your.vault.org/v1/auth\n    activate V\n    Note over V: SSO, AD, Cloud IAM etc.\n    V--\u003e\u003eU: OK\n    deactivate V\n    Note right of U: Request a GitHub token\n    alt\n    U-\u003e\u003eV: POST your.vault.org/v1/github/token\n    else repos: [123,456], perms: \"issues:write\"\n    U-\u003e\u003eV: POST your.vault.org/v1/github/token\n    end\n    activate V\n    Note over V: Mint JWT using Private Key\n    V-\u003e\u003eV: GitHub JWT (exp: 10m)\n    V-\u003e\u003eG: POST api.github.com/.../access_tokens\n    G--\u003e\u003eV: token: [ \"v1.123456789...\" exp: \"1h\" ]\n    V-\u003e\u003eV: Record metrics, logs\n    V--\u003e\u003eU: [ token: \"v1.123456789...\" exp: \"1h\" ]\n    deactivate V\n    Note over U: Use access token on GitHub\n    alt Operate on repostories access token has permissions on\n    U-\u003e\u003eG: $ git clone https://x-access-token:v1.123456789...@github.com/org/repo.git\n    else Perform API requests against resources access token has permissions on\n    U-\u003e\u003eG: $ curl -H \"Authorization: Bearer v1.123456789...\" https://api.github.com/resource\n    end\n#+END_COMMENT\n#+BEGIN_COMMENT Mermaid style\n{\n  \"theme\": \"default\",\n  \"sequence\": {\n  \"mirrorActors\": false,\n  \"actorMargin\": 120,\n  \"noteMargin\": 10,\n  \"messageMargin\": 30,\n  \"boxTextMargin\": 1,\n  \"height\": 30,\n  \"width\": 200\n  }\n}\n#+END_COMMENT\n\n* Installation\nTo begin plugin installation, either download a [[https://github.com/martinbaillie/vault-plugin-secrets-github/releases][release]] or build\nfrom source for your chosen OS and architecture.\n\n** From release\nAlways download the latest stable release from the [[https://github.com/martinbaillie/vault-plugin-secrets-github/releases][releases]] section.\n\n*** Verify\n\nYou can and should verify the authenticity and integrity of the plugin you\ndownloaded. All released binaries are hashed and the resulting sums are signed\nby my GPG key.\n\n#+BEGIN_SRC shell\n# Import my key.\ncurl -sS https://github.com/martinbaillie.gpg | gpg --import -\n\n# Verify the authenticity.\ngpg --verify SHA256SUMS.sig SHA256SUMS\n\n# Verify the integrity.\nshasum -a 256 -c SHA256SUMS\n#+END_SRC\n\n** From source\n#+BEGIN_QUOTE\nNOTE: You will need at least a [[https://golang.org/dl/][Go 1.22+ toolchain]] to build this plugin from\nsource. Ideally you will also be in the project's Nix shell. See [[Development][Development]] for\nmore.\n#+END_QUOTE\n\n1. Either download the source zip/tar.gz of the latest release from the [[./releases][releases]]\n   section and uncompress, or shallow clone to the target release tag as in\n   below:\n\n    #+BEGIN_SRC shell\n    git clone --depth 1 -b \u003ctarget_release_tag\u003e \\\n        https://github.com/martinbaillie/vault-plugin-secrets-github.git\n    #+END_SRC\n\n2. Build for your target OS and architecture.\n\n    #+BEGIN_SRC shell\n      # Recommended for accurate release-like reproduction.\n      goreleaser build --single-target # Your current OS/Arch.\n      GOOS=darwin GOARCH=amd64 goreleaser build --single-target # Another supported OS/Arch.\n      goreleaser build # All supported OS/Arch combinations.\n      # Alternatively,\n      go build\n      # or\n      nix build\n    #+END_SRC\n\n** Setup (GitHub)\n#+BEGIN_QUOTE\nNOTE: You will need access to admin of your GitHub or GitHub Enterprise\norganisation to continue with the one-time setup. You can of course use this\nplugin on your personal account if so desired.\n#+END_QUOTE\n\n1. Sign in as an org admin and begin creating a new [[https://github.com/settings/apps/new][GitHub App.]]\n\n2. Choose any unique name, and homepage / webhook URLs (these can be anything;\n   they are not required by the plugin).\n   #+BEGIN_QUOTE\n    NOTE: You may wish to take advantage of having GitHub call a webhook URL you\n    own each time the App is used for auditing purposes.\n    #+END_QUOTE\n\n3. Carefully choose the permissions the App will have access to. This is the\n   superset of permissions. You will have the option of further restricting\n   access to all or some repositories when you install an instance of the App,\n   and users of this plugin will be able to even further restrict access in\n   their individual token requests.\n\n4. Decide if you want the app to be installable to other accounts. Usually\n   you just want the one you are signed into.\n\n5. Create the App. On the next screen GitHub will prompt you to create a *Private\n   Key*. Do so, and save it somewhere safe (this is the key that will be\n   ultimately lodged into the plugin configuration).\n\n6. Note the *App ID* at the top of this page as well.\n    #+BEGIN_QUOTE\n    NOTE: Get the App ID anytime: Settings \u003e Developer \u003e settings \u003e GitHub App \u003e\n    About item.\n    #+END_QUOTE\n\n7. Click Install App from the LHS. You will be taken to the account installation\n   pages where you can confirm the app was installed.\n\n8. (OPTIONAL as of v1.3.0) Note the *Installation ID* from the URL of this page (usually:\n   https://github.com/settings/installations/\u003cinstallation id\u003e) if you wish to\n   configure using the installation ID directly.\n    #+BEGIN_QUOTE\n    NOTE: Get the Installation ID anytime: Settings \u003e Developer \u003e settings \u003e\n    GitHub Apps \u003e Advanced \u003e Payload in Request tab.\n    #+END_QUOTE\n\n** Setup (Vault)\nUsing the information noted from the previous step (Private Key, App ID and\noptionally the Installation ID), you are ready to move on to setting up the\nVault plugin.\n\n1. Move the desired plugin binary into your Vault's configured\n   =plugin_directory=.\n\n    #+BEGIN_SRC shell\n    mv vault-plugin-secrets-github-\u003cos\u003e-\u003carch\u003e \u003cplugin_directory\u003e/vault-plugin-secrets-github\n    #+END_SRC\n\n2. (OPTIONAL) Allow [[https://linux.die.net/man/2/mlock][ =mlock()= ]] capabilities for the plugin binary. Memory locking\n   is available to most UNIX-like OSes; the example below is for Linux.\n\n    #+BEGIN_SRC shell\n    setcap cap_ipc_lock=+ep \u003cplugin_directory\u003e/vault-plugin-secrets-github\n    #+END_SRC\n\n3. (OPTIONAL) Calculate the SHA256 sum of the plugin and register it in Vault's\n   plugin catalog. If you are downloading the pre-compiled binary, it is highly\n   recommended that you use the published SHA256SUMS file.\n\n    #+BEGIN_QUOTE\n    NOTE: The rest of these commands assume you have a valid VAULT_TOKEN and\n    VAULT_API environment variables.\n    #+END_QUOTE\n\n    #+BEGIN_SRC shell\n    # If using a pre-compiled binary:\n    SHA256SUM=$(grep \u003cdownloaded_binary\u003e SHA256SUMS | cut -d' ' -f1)\n    # If building from source:\n    SHA256SUM=$(shasum -a 256 \u003ccompiled_binary\u003e | cut -d' ' -f1)\n\n    vault write sys/plugins/catalog/secret/vault-plugin-secrets-github \\\n        sha_256=${SHA256SUM} command=vault-plugin-secrets-github\n    #+END_SRC\n\n4. Mount the secrets engine, choosing a prefix path (recommendation: =github=).\n\n    #+BEGIN_SRC shell\n    vault secrets enable -path=github -plugin-name=vault-plugin-secrets-github plugin\n    #+END_SRC\n\n5. Configure the plugin with the details noted from the previous section.\n\n   #+BEGIN_SRC shell\n     # Write the configuration\n     vault write /github/config app_id=\u003capp_id\u003e prv_key=@\u003cprivate_key_file\u003e\n\n     # (OPTIONAL) Exclude repository metadata from token responses (reduces memory footprint).\n     vault write /github/config exclude_repository_metadata=true\n\n     # (OPTIONAL) Confirm the configuration landed as you expected.\n     vault read /github/config\n\n     # (OPTIONAL) Test a token creation.\n     vault read /github/token installation_id=\u003cinstallation_id\u003e\n     vault read /github/token org_name=\u003corg_name\u003e # Installation ID discovered from Org.\n   #+END_SRC\n\n6. (OPTIONAL) Use Vault policy to constrain user capabilities on the GitHub\n   endpoints. Example:\n\n   #+BEGIN_SRC shell\n# Create a restrictive policy that only permits GitHub tokens that can write\n# pull requests to a single repository.\nvault policy write github-only-prs -\u003c\u003cEOF\npath \"github/token\" {\n  capabilities = [\"update\"]\n  required_parameters = [\"installation_id\",\"permissions\",\"repository_ids\"]\n  allowed_parameters = {\n    \"installation_id\" = [\"987654\"]\n    \"repository_ids\" = [\"69857131\"]\n    \"permissions\"= [\"pull_requests=write\"]\n  }\n}\nEOF\n\n# Create and login as an example user with the policy attached.\nvault auth enable userpass\nvault write auth/userpass/users/martin password=baillie policies=\"github-only-prs\"\nvault login -method=userpass username=martin password=baillie\n\n# Test the efficacy of the policy.\n# Successfully creates token:\nvault write /github/token installation_id=987654 repository_ids=69857131 permissions=pull_requests=write\n# Permission denied:\nvault write -f /github/token\nvault write /github/token installation_id=987654 permissions=pull_requests=write\nvault write /github/token installation_id=987654 repository_ids=69857131 permissions=pull_requests=read\nvault write /github/token installation_id=987654 repository_ids=69857131 permissions=metadata=read\nvault write /github/token installation_id=987654 repository_ids=123 permissions=pull_requests=write\nvault write /github/token installation_id=987654 repository_ids=69857131\nvault write /github/token installation_id=123456 repository_ids=69857131\n   #+END_SRC\n\n* API\nEach plugin path is documented using Vault's own help framework. To find out\nmore information about any path, use =vault path-help=. For brevity, the API is\nalso documented below.\n\n** Token\nInstruct the plugin to create an installation access token against the configured GitHub App.\n\n| Method | Path   | Produces         |\n|--------+--------+------------------|\n| GET    | /token | application/json |\n| POST   | /token | application/json |\n| PUT    | /token | application/json |\n\n*** Parameters\n#+begin_quote\nNOTE: Only one of =installation_id= or =org_name= is required. If only =org_name= is\nprovided, an additional lookup against the GitHub instance is performed _per\ntoken creation_ to discover the =installation_id=. If both are provided,\n=installation_id= takes precedence to avoid the additional round trip. Also note\nthat no caching is performed so for high traffic use cases, favour\n=installation_id=.\n\nAll other parameters are optional. Omitting them results in a token that has\naccess to all of the repositories and permissions that the GitHub App\ninstallation has.\n\nWhen crafting Vault policy, hyper security sensitive organisations may wish to\nfavour =repository_ids= (GitHub repository IDs are immutable) instead of\n=repositories= (GitHub repository names are mutable).\n#+end_quote\n\n- =installation_id= (int64) — the ID of the app installation.\n- =org_name= (string) — the organisation name.\n- =repositories= ([]string) — a list of the names of the repositories within the\n  organisation that the installation token can access.\n- =repository_ids= ([]int64) — a list of the IDs of the repositories that the\n  installation token can access. See this [[https://stackoverflow.com/a/47223479][StackOverflow post]] for the quickest\n  way to find a repository ID.\n- =permissions= (map[string]string) — a key value map of permission names to\n  their access type (read or write). See [[https://developer.github.com/v3/apps/permissions][GitHub's documentation]] on permission\n  names and access types.\n\n*** Examples\n#+BEGIN_SRC shell\n# Create a token with all the superset of all permissions and repositories that\n# the GitHub App installation has access to.\nvault read /github/token\n\n# Create a token with read permissions on the packages and write permissions on\n# the pull requests of repositories named \"demo-repo\" and ID 123.\nvault write /github/token \\\n  installation_id=456 \\\n\trepository_ids=123 \\\n\trepositories=demo-repo \\\n\tpermissions=packages=read \\\n\tpermissions=pull_requests=write\n\n# Create a token with all permissions but only on the \"demo-repo\" repository.\nvault write /github/token installation_id=456 repository_ids=123 repository_ids=456\n\n# Create a token with all permissions but only on repositories 123 and 456.\nvault write /github/token installation_id=456 repository_ids=123 repository_ids=456\n\n# Create a token with write access to pull requests using read / GET.\nvault write /github/token permissions=pull_requests=write\n\n# Create a token with read access to metadata and write access to pull requests\n# on repositories \"demo-repo\", 123 and 456 only.\n# NOTE: Uses a Vault CLI JSON heredoc to submit the complex map type.\nvault write /github/token - \u003c\u003cEOF\n{\n\"installation_id\": 456,\n\"repositories\": [\"demo-repo\"],\n\"repository_ids\": [123,456],\n\"permissions\": {\"metadata\": \"read\", \"pull_requests\": \"write\"}\n}\nEOF\n#+END_SRC\n\n#+BEGIN_QUOTE\nNOTE: a 422 response usually indicates you have requested repositories IDs or\npermissions that your GitHub App install does not have access to.\n#+END_QUOTE\n*** Revocation\nIt is possible to revoke tokens in your configured GitHub using Vault\nconstructs. For example:\n#+begin_src shell\n# Revoke an individual token with just the lease ID.\nvault lease revoke \u003clease_id previously received from token endpoint\u003e\n# List currently active lease IDs.\nvault list sys/leases/lookup/github/token\n# Revoke all tokens currently leased by Vault.\nvault lease revoke -prefix github/token\n#+end_src\n\n#+begin_quote\nNOTE: the previous commands presume your plugin is mounted at =/github=.\n#+end_quote\n\nAlternatively, you can go directly to your GitHub with the token in hand:\n#+begin_src shell\ncurl -H \"Authorization: Bearer ${GITHUB_TOKEN}\" \\\n\t-X DELETE https://api.mygithub.com/installation/token\n#+end_src\n\n** Permission sets\nInstruct the plugin to create a specific permission set.\n\n| Method | Path                      | Produces         |\n|--------+---------------------------+------------------|\n| GET    | /permissionset/\u003cname\u003e     | application/json |\n| POST   | /permissionset/\u003cname\u003e     | application/json |\n| PUT    | /permissionset/\u003cname\u003e     | application/json |\n| DELETE | /permissionset/\u003cname\u003e     | application/json |\n| GET    | /permissionsets?list=true | application/json |\n\n*** Parameters\n#+begin_quote\nNOTE: Only one of =installation_id= or =org_name= is required. If only =org_name= is\nprovided, an additional lookup against the GitHub instance is performed _per\ntoken creation_ to discover the =installation_id=. If both are provided,\n=installation_id= takes precedence to avoid the additional round trip. Also note\nthat no caching is performed so for high traffic use cases, favour\n=installation_id=.\n\nAll other parameters are optional. Omitting them results in a token that has\naccess to all of the repositories and permissions that the GitHub App\ninstallation has.\n\nWhen crafting Vault policy, hyper security sensitive organisations may wish to\nfavour =repository_ids= (GitHub repository IDs are immutable) instead of\n=repositories= (GitHub repository names are mutable).\n#+end_quote\n\n- =installation_id= (int64) — the ID of the app installation.\n- =org_name= (string) — the organisation name.\n- =repositories= ([]string) — a list of the names of the repositories within the\n  organisation that the installation token can access.\n- =repository_ids= ([]int64) — a list of the IDs of the repositories that the\n  installation token can access. See this [[https://stackoverflow.com/a/47223479][StackOverflow post]] for the quickest\n  way to find a repository ID.\n- =permissions= (map[string]string) — a key value map of permission names to\n  their access type (read or write). See [[https://developer.github.com/v3/apps/permissions][GitHub's documentation]] on permission\n  names and access types.\n\n*** Request a token from a permission set\nSimilar to the [[#token][token]] flow in the previous section, you can instruct the plugin\nto create an installation access token by using a permission set name. The token\nreturned will be constrained by that pre-configured permission set.\n\n| Method | Path          | Produces         |\n|--------+---------------+------------------|\n| GET    | /token/\u003cname\u003e | application/json |\n| POST   | /token/\u003cname\u003e | application/json |\n| PUT    | /token/\u003cname\u003e | application/json |\n\n*** Examples\n#+BEGIN_SRC shell\n# Configure a permission set that only allows metadata reads and PR writes\n# against three repositories.\nvault write /github/permissionset/demo-set - \u003c\u003cEOF\n{\n\"installation_id\": 987,\n\"repositories\": [\"demo-repo\"],\n\"repository_ids\": [123,456],\n\"permissions\": {\"metadata\": \"read\", \"pull_requests\": \"write\"}\n}\nEOF\n# Or\nvault write /github/permissionset/demo-set \\\n\tinstallation_id=987 \\\n\trepositories=demo-repo \\\n\trepository_ids=123 \\\n\trepository_ids=456 \\\n\tpermissions=pull_requests=read \\\n\tpermissions=metadata=read\n\n# Read the permission set configuration.\nvault read /github/permissionset/demo-set\n\n# List all permission sets.\nvault list /github/permissionsets\n\n# Create a token automatically constrained by the permission set.\nvault read /github/token/demo-set\n\n# Delete an existing permission set.\nvault delete /github/permissionset/demo-set\n#+END_SRC\n\n** Config\nGeneral CRUD operations against the configuration of the plugin.\n\n| Method | Path    | Produces         |\n|--------+---------+------------------|\n| POST   | /config | application/json |\n| GET    | /config | application/json |\n| PUT    | /config | application/json |\n| DELETE | /config | application/json |\n\n*** Parameters\n- =app_id= (int64) — the Application ID of the GitHub App.\n- =prv_key= (string) — a private key configured in the GitHub App. This private key must be in PEM PKCS#1 RSAPrivateKey format. It is not returned with read requests for security reasons but its presence or lack thereof is indicated.\n- =base_url= (string) — the base URL for API requests (defaults to the public GitHub API).\n- =exclude_repository_metadata= (bool) — reduce the verbose `repositories` array in GitHub token responses to a simple list of repository names. This significantly reduces the memory required by the plugin when used at scale.\n\n*** Examples\n#+BEGIN_SRC shell\n  # Write the plugin configuration using the default base URL, and reading the key from a file.\n  vault write /github/config app_id=123 prv_key=@key.pem\n\n  # Read the plugin configuration.\n  vault read /github/config\n\n  # Update the plugin configuration to a GitHub Enterprise base URL.\n  vault write /github/config base_url=\"https://api.mygithub.org\"\n\n  # Significantly reduce memory consumed per token.\n  vault write /github/config exclude_repository_metadata=true\n\n  # Delete the plugin configuration.\n  vault delete /github/config\n#+END_SRC\n\n** Metrics\nPrometheus/OpenMetrics formatted metrics exposition.\n\n| Method | Path     | Produces    |\n|--------+----------+-------------|\n| GET    | /metrics | text/plain  |\n\n*** Metrics\nIn addition to standard Go metrics, the following custom metrics are exposed:\n- =vault_github_token_request_duration_seconds= — a summary of token request latency and status.\n- =vault_github_token_revocation_request_duration_seconds= — a summary of token revocation request latency and status.\n- =vault_github_token_build_info= — a constant with useful build information.\n\n*** Sample Dashboard\nA sample dashboard is [[dashboard.json][provided]].\n#+CAPTION: Sample Dashboard\n[[dashboard.png][dashboard.png]]\n\n** Info\nInformation about the GitHub secrets plugin, such as the plugin version, VCS\ndetail and where to get help.\n\n| Method | Path  | Produces         |\n|--------+-------+------------------|\n| GET    | /info | application/json |\n\n* Development\nThis plugin is written using modern [[https://golang.org/dl/][Go]] (1.22+). Its project infrastructure\nsupports Linux and macOS aarch64/amd64 platforms and is handled by [[https://nixos.org/][Nix]].\n\nNix is the only pre-requisite you need to have installed. If you do not already\nhave or wish to install =nix=, consider the =nixos/nix= container image.\n\nThe Nix [[./flake.nix][flake]] at the project root provides the Go toolchain as well as any\nancillary tooling needed. If you use [[https://direnv.net/docs/installation.html][direnv]] (recommended) you will automatically\nbe in a usable project shell after trusting it once with =direnv allow=.\n\nOtherwise you will need to enter a Nix shell manually:\n#+begin_src shell\n  nix develop\n#+end_src\n\nOnce in the Nix shell you should be presented with the following:\n#+begin_example\nvault-plugin-secrets-github\nmenu                              - available commands\n#+end_example\n\nProceed to browse the =menu= or learn how to test the project below.\n\n** Tests\nThis plugin is comprehensively tested by both unit and acceptance tests. Pull\nrequests that do not maintain an [[https://codecov.io/gh/martinbaillie/vault-plugin-secrets-github][\u003e90% coverage]] will *not* be accepted.\n\n#+BEGIN_SRC shell\n  # View the developer shell menu.\n  menu\n\n  # Run linting.\n  lint\n\n  # Run CI-grade linting.\n  env CI=true lint\n\n  # Run unit tests.\n  unit\n\n  # Run unit and acceptance tests, integrating against a local Vault and stubbed\n  # GitHub API.\n  integration\n\n  # Run integration tests with debug logging.\n  env DEBUG=true integration\n\n  # Run unit and acceptance tests, integrating against a local Vault and the real\n  # GitHub API against your own GitHub App installation (you can also run these\n  # tests against a GitHub Enterprise deployment). Setting `BASE_URL` causes the\n  # tests to call against a real API.\n  # NOTE: Keep in mind this test configuration will create real tokens.\n  env \\\n      BASE_URL=https://api.github.com \\\n      APP_ID=\u003cyour application id\u003e \\\n      ORG_NAME=\u003corg_name\u003e \\\n      INSTALLATION_ID=\u003cinstallation_id\u003e \\\n      PRV_KEY=\"$(cat /path/to/your/app/prv_key_file)\" integration\n#+END_SRC\n\n* Security\nHashiCorp and GitHub take their security seriously. If you believe you have\nfound a security issue with either through using this plugin, do not open an\nissue here. Responsibly disclose by getting in touch with [[mailto:security@hashicorp.com][HashiCorp]] or [[https://hackerone.com/github][GitHub]]\nsecurity teams respectively.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinbaillie%2Fvault-plugin-secrets-github","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartinbaillie%2Fvault-plugin-secrets-github","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinbaillie%2Fvault-plugin-secrets-github/lists"}