Skip to content

Commit

Permalink
modified the package files and workflows
Browse files Browse the repository at this point in the history
  • Loading branch information
lepadatu committed Sep 2, 2024
1 parent 9ccc6db commit 2b72e15
Show file tree
Hide file tree
Showing 14 changed files with 1,566 additions and 155 deletions.
14 changes: 11 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ permissions:
contents: write
issues: write
pull-requests: write
id-token: write

jobs:
release:
Expand All @@ -20,6 +21,13 @@ jobs:
with:
persist-credentials: false

- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: eu-west-1
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: release/create-github-app-token-aws

- uses: actions/setup-node@v4
with:
node-version-file: .node-version
Expand All @@ -30,10 +38,10 @@ jobs:
- uses: ./
id: app-token
with:
app-id: ${{ vars.RELEASER_APP_ID }}
private-key: ${{ secrets.RELEASER_APP_PRIVATE_KEY }}
app-id: ${{ secrets.APP_ID }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
# install release dependencies and release
- run: npm install --no-save @semantic-release/git semantic-release-plugin-github-breaking-version-tag
- run: npx semantic-release --debug
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}#
17 changes: 15 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ on:
push:
branches:
- main
- dev
pull_request:
workflow_dispatch:

permissions:
contents: read
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -33,6 +38,14 @@ jobs:
if: github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
steps:
- uses: actions/checkout@v4

- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: test/create-github-app-token-aws

- uses: actions/setup-node@v4
with:
node-version: 20
Expand All @@ -42,8 +55,8 @@ jobs:
- uses: ./ # Uses the action in the root directory
id: test
with:
app-id: ${{ vars.TEST_APP_ID }}
private-key: ${{ secrets.TEST_APP_PRIVATE_KEY }}
app-id: ${{ secrets.APP_ID }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
- uses: octokit/request-action@v2.x
id: get-repository
env:
Expand Down
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ The MIT License (MIT)

Copyright (c) 2023 Gregor Martynus
Copyright (c) 2023 Parker Brown
Copyright (c) 2024 Cristian Lepadatu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
110 changes: 77 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

[![test](https://github.com/actions/create-github-app-token/actions/workflows/test.yml/badge.svg)](https://github.com/actions/create-github-app-token/actions/workflows/test.yml)

GitHub Action for creating a GitHub App installation access token.
GitHub Action for creating a GitHub App installation access token using AWS KMS in order to safely store the GitHub repositry private key.

## Usage

In order to use this action, you need to:

1. [Register new GitHub App](https://docs.github.com/apps/creating-github-apps/setting-up-a-github-app/creating-a-github-app)
2. [Store the App's ID in your repository environment variables](https://docs.github.com/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows) (example: `APP_ID`)
3. [Store the App's private key in your repository secrets](https://docs.github.com/actions/security-guides/encrypted-secrets?tool=webui#creating-encrypted-secrets-for-a-repository) (example: `PRIVATE_KEY`)
2. [Store the App's ID in your repository environment variable](https://docs.github.com/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows) or [secret](https://docs.github.com/actions/security-guides/encrypted-secrets?tool=webui#creating-encrypted-secrets-for-a-repository) (example: `APP_ID`)
3. [Import the App's private key in your AWS Account KMS service, under customer-managed keys of type assymetric, sign-verify](https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-create-cmk.html)
4. [Store the above KMS Key ID as a repository secret](https://docs.github.com/actions/security-guides/encrypted-secrets?tool=webui#creating-encrypted-secrets-for-a-repository) (example `KMS_KEY_ID`). Once stored in AWS KMS, the GitHub private key can no longer be retieved from AWS. AWS API can only by asked to sign/verify using the respective key. This substantially improves the security posture, because the key is no longer accessible.
5. [Store the AWS role to be assumed by the action as a repository secret](https://docs.github.com/actions/security-guides/encrypted-secrets?tool=webui#creating-encrypted-secrets-for-a-repository) (example `ROLE_TO_ASSUME`)
6. [Store the AWS session name as an environment_variable](https://docs.github.com/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows) (example `ROLE_SESSION_NAME`)
7. [Store the AWS region name as an environment_variable](https://docs.github.com/actions/learn-github-actions/variables#defining-configuration-variables-for-multiple-workflows) (example `AWS_REGION`)

> [!IMPORTANT]
> An installation access token expires after 1 hour. Please [see this comment](https://github.com/actions/create-github-app-token/issues/121#issuecomment-2043214796) for alternative approaches if you have long-running processes.
Expand All @@ -28,11 +32,17 @@ jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
- uses: ./actions/staging-tests
with:
token: ${{ steps.app-token.outputs.token }}
Expand All @@ -47,12 +57,18 @@ jobs:
auto-format:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
# required
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
- uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
Expand All @@ -73,12 +89,18 @@ jobs:
auto-format:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
# required
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
Expand All @@ -98,12 +120,18 @@ jobs:
auto-format:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
# required
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
- name: Get GitHub App User ID
id: get-user-id
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -135,11 +163,17 @@ jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
owner: ${{ github.repository_owner }}
- uses: peter-evans/create-or-update-comment@v3
with:
Expand All @@ -157,11 +191,17 @@ jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
owner: ${{ github.repository_owner }}
repositories: "repo1,repo2"
- uses: peter-evans/create-or-update-comment@v3
Expand All @@ -180,11 +220,17 @@ jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
owner: another-owner
- uses: peter-evans/create-or-update-comment@v3
with:
Expand Down Expand Up @@ -221,11 +267,17 @@ jobs:
owners-and-repos: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
owner: ${{ matrix.owners-and-repos.owner }}
repositories: ${{ join(matrix.owners-and-repos.repos) }}
- uses: octokit/request-action@v2.x
Expand All @@ -249,12 +301,18 @@ jobs:
runs-on: self-hosted
steps:
- name: AWS Login
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
role-session-name: ${{ vars.ROLE_SESSION_NAME }}
- name: Create GitHub App token
id: create_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.GHES_APP_ID }}
private-key: ${{ secrets.GHES_APP_PRIVATE_KEY }}
kms-key-id: ${{ secrets.KMS_KEY_ID }}
owner: ${{ vars.GHES_INSTALLATION_ORG }}
github-api-url: ${{ vars.GITHUB_API_URL }}
Expand All @@ -274,27 +332,9 @@ jobs:

**Required:** GitHub App ID.

### `private-key`

**Required:** GitHub App private key. Escaped newlines (`\\n`) will be automatically replaced with actual newlines.
### `kms-key-id`

Some other actions may require the private key to be Base64 encoded. To avoid recreating a new secret, it can be decoded on the fly, but it needs to be managed securely. Here is an example of how this can be achieved:

```yaml
steps:
- name: Decode the GitHub App Private Key
id: decode
run: |
private_key=$(echo "${{ secrets.PRIVATE_KEY }}" | base64 -d | awk 'BEGIN {ORS="\\n"} {print}' | head -c -2) &> /dev/null
echo "::add-mask::$private_key"
echo "private-key=$private_key" >> "$GITHUB_OUTPUT"
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ steps.decode.outputs.private-key }}
```
**Required:** AWS KMS Key ID that is imported from GitHub.

### `owner`

Expand Down Expand Up @@ -331,7 +371,11 @@ GitHub App slug.

## How it works

The action creates an installation access token using [the `POST /app/installations/{installation_id}/access_tokens` endpoint](https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app). By default,
The action creates an installation access token using [the `POST /app/installations/{installation_id}/access_tokens` endpoint](https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app).

The action uses the GitHub private key stored in AWS KMS so sign a JWT token and uses this token subsequently for autheticating each GitHub API call, including the one above. Once stored in AWS KMS, the GitHub private key can no longer be retieved from AWS. AWS API can only by asked to sign/verify using the respective key. This substantially improves the security posture, because the action will no longer access the private key anymore, but ask AWS API to sign/verify instead.

By default,

1. The token is scoped to the current repository or `repositories` if set.
2. The token inherits all the installation's permissions.
Expand Down
18 changes: 9 additions & 9 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: "Create GitHub App Token"
description: "GitHub Action for creating a GitHub App installation access token"
author: "Gregor Martynus and Parker Brown"
name: "Create GitHub App Token using AWS KMS"
description: "GitHub Action for creating a GitHub App installation access token using AWS KMS"
author: "Cristian Lepadatu, Gregor Martynus and Parker Brown"
branding:
icon: "lock"
color: "gray-dark"
Expand All @@ -12,13 +12,13 @@ inputs:
description: "GitHub App ID"
required: false
deprecationMessage: "'app_id' is deprecated and will be removed in a future version. Use 'app-id' instead."
private-key:
description: "GitHub App private key"
required: false # TODO: When 'private_key' is removed, make 'private-key' required
private_key:
description: "GitHub App private key"
kms-key-id:
description: AWS KMS Key ID"
required: false # TODO: When 'kms_key_id' is removed, make 'kms-key-id' required
kms_key_id:
description: "AWS KMS Key ID"
required: false
deprecationMessage: "'private_key' is deprecated and will be removed in a future version. Use 'private-key' instead."
deprecationMessage: "'kms_key_id' is deprecated and will be removed in a future version. Use 'kms-key-id' instead."
owner:
description: "The owner of the GitHub App installation (defaults to current repository owner)"
required: false
Expand Down
Loading

0 comments on commit 2b72e15

Please sign in to comment.