Skip to content

Commit 34d23aa

Browse files
committed
Experimental support for GitHub token authentication
This comes from the desire to be more fine-grained in the permissions given to the github management automation. E.g. With GitHub Apps, there's no way to give both the "manage teams" permission without also the "remove org owners" permission. Similarly, the "manage repos" permission also comes with the "remove repos" permission. only what's required. In comparison, with a personal access token of a user, you can give that user specific access levels on the repos it needs access to, or only add it to specific teams it needs to manage. For now, personal access tokens are limited to team member management, because unless you make the user an org admin, you run into problems for pretty much any other functionality.
1 parent ab89941 commit 34d23aa

File tree

13 files changed

+91
-27
lines changed

13 files changed

+91
-27
lines changed

.github/workflows/apply.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
- name: Find sha for plan
3333
id: sha
3434
env:
35+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
3536
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
3637
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
3738
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}
@@ -55,6 +56,7 @@ jobs:
5556
TF_WORKSPACE: ${{ matrix.workspace }}
5657
AWS_ACCESS_KEY_ID: ${{ secrets.RW_AWS_ACCESS_KEY_ID }}
5758
AWS_SECRET_ACCESS_KEY: ${{ secrets.RW_AWS_SECRET_ACCESS_KEY }}
59+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
5860
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
5961
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
6062
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}

.github/workflows/clean.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs:
6060
TF_WORKSPACE_OPT: ${{ matrix.workspace }}
6161
AWS_ACCESS_KEY_ID: ${{ secrets.RW_AWS_ACCESS_KEY_ID }}
6262
AWS_SECRET_ACCESS_KEY: ${{ secrets.RW_AWS_SECRET_ACCESS_KEY }}
63+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
6364
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
6465
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
6566
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}

.github/workflows/cleanup.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
name: Clean Up
3232
runs-on: ubuntu-latest
3333
env:
34+
GITHUB_TOKEN: ${{ secrets.RO_GITHUB_TOKEN }}
3435
GITHUB_APP_ID: ${{ secrets.RO_GITHUB_APP_ID }}
3536
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RO_GITHUB_APP_INSTALLATION_ID_{0}', github.repository_owner)] || secrets.RO_GITHUB_APP_INSTALLATION_ID }}
3637
GITHUB_APP_PEM_FILE: ${{ secrets.RO_GITHUB_APP_PEM_FILE }}

.github/workflows/fix.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ jobs:
135135
steps:
136136
- name: Generate app token
137137
id: token
138+
env:
139+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
140+
if: ${{ ! env.GITHUB_TOKEN }}
138141
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
139142
with:
140143
app_id: ${{ secrets.RW_GITHUB_APP_ID }}
@@ -146,7 +149,7 @@ jobs:
146149
with:
147150
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
148151
ref: ${{ github.event.pull_request.head.sha || github.sha }}
149-
token: ${{ steps.token.outputs.token }}
152+
token: ${{ secrets.RW_GITHUB_TOKEN || steps.token.outputs.token }}
150153
path: head
151154
- name: Checkout
152155
uses: actions/checkout@v4
@@ -184,7 +187,7 @@ jobs:
184187
- if: steps.github-modified.outputs.this == 'true' && github.event_name != 'pull_request_target'
185188
uses: ./base/.github/actions/git-push
186189
env:
187-
GITHUB_TOKEN: ${{ steps.token.outputs.token }}
190+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN || steps.token.outputs.token }}
188191
with:
189192
suffix: fix
190193
working-directory: head

.github/workflows/labels.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ jobs:
2929
name: Sync
3030
runs-on: ubuntu-latest
3131
env:
32+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
3233
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
3334
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', github.repository_owner)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
3435
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}

.github/workflows/plan.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ jobs:
6464
TF_WORKSPACE: ${{ matrix.workspace }}
6565
AWS_ACCESS_KEY_ID: ${{ secrets.RO_AWS_ACCESS_KEY_ID }}
6666
AWS_SECRET_ACCESS_KEY: ${{ secrets.RO_AWS_SECRET_ACCESS_KEY }}
67+
GITHUB_TOKEN: ${{ secrets.RO_GITHUB_TOKEN }}
6768
GITHUB_APP_ID: ${{ secrets.RO_GITHUB_APP_ID }}
6869
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RO_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RO_GITHUB_APP_INSTALLATION_ID }}
6970
GITHUB_APP_PEM_FILE: ${{ secrets.RO_GITHUB_APP_PEM_FILE }}

.github/workflows/sync.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jobs:
5454
TF_WORKSPACE_OPT: ${{ matrix.workspace }}
5555
AWS_ACCESS_KEY_ID: ${{ secrets.RW_AWS_ACCESS_KEY_ID }}
5656
AWS_SECRET_ACCESS_KEY: ${{ secrets.RW_AWS_SECRET_ACCESS_KEY }}
57+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
5758
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
5859
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
5960
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}
@@ -107,6 +108,9 @@ jobs:
107108
steps:
108109
- name: Generate app token
109110
id: token
111+
env:
112+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
113+
if: ${{ ! env.GITHUB_TOKEN }}
110114
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
111115
with:
112116
app_id: ${{ secrets.RW_GITHUB_APP_ID }}
@@ -116,7 +120,7 @@ jobs:
116120
- name: Checkout
117121
uses: actions/checkout@v4
118122
with:
119-
token: ${{ steps.token.outputs.token }}
123+
token: ${{ secrets.RW_GITHUB_TOKEN || steps.token.outputs.token }}
120124
- uses: ./.github/actions/git-config-user
121125
- env:
122126
WORKSPACES: ${{ needs.prepare.outputs.workspaces }}

.github/workflows/update.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
working-directory: scripts
2626
- name: Update PRs
2727
env:
28+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
2829
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
2930
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', matrix.workspace)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
3031
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}

.github/workflows/upgrade.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
with:
1515
ref: inputs.ref
1616
secrets:
17+
RW_GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
1718
GITHUB_APP_ID: ${{ secrets.RW_GITHUB_APP_ID }}
1819
GITHUB_APP_INSTALLATION_ID: ${{ secrets[format('RW_GITHUB_APP_INSTALLATION_ID_{0}', github.repository_owner)] || secrets.RW_GITHUB_APP_INSTALLATION_ID }}
1920
GITHUB_APP_PEM_FILE: ${{ secrets.RW_GITHUB_APP_PEM_FILE }}

.github/workflows/upgrade_reusable.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ on:
99
description: The github-mgmt-template ref to upgrade to
1010
default: master
1111
secrets:
12+
RW_GITHUB_TOKEN:
13+
required: false
1214
GITHUB_APP_ID:
13-
required: true
15+
required: false
1416
GITHUB_APP_INSTALLATION_ID:
15-
required: true
17+
required: false
1618
GITHUB_APP_PEM_FILE:
17-
required: true
19+
required: false
1820

1921
jobs:
2022
upgrade:
@@ -26,6 +28,9 @@ jobs:
2628
steps:
2729
- name: Generate app token
2830
id: token
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN }}
33+
if: ${{ ! env.GITHUB_TOKEN }}
2934
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
3035
with:
3136
app_id: ${{ secrets.GITHUB_APP_ID }}
@@ -42,7 +47,7 @@ jobs:
4247
uses: actions/checkout@v4
4348
with:
4449
path: github-mgmt
45-
token: ${{ steps.token.outputs.token }}
50+
token: ${{ secrets.RW_GITHUB_TOKEN || steps.token.outputs.token }}
4651
- name: Copy files from the template
4752
run: |
4853
for file in $(git ls-files ':!:github/*.yml' ':!:scripts/src/actions/fix-yaml-config.ts' ':!:terraform/*_override.tf' ':!:.github/workflows/*_reusable.yml' ':!:README.md'); do
@@ -57,7 +62,7 @@ jobs:
5762
working-directory: github-mgmt
5863
- uses: ./github-mgmt-template/.github/actions/git-push
5964
env:
60-
GITHUB_TOKEN: ${{ steps.token.outputs.token }}
65+
GITHUB_TOKEN: ${{ secrets.RW_GITHUB_TOKEN || steps.token.outputs.token }}
6166
with:
6267
suffix: upgrade
6368
working-directory: github-mgmt

docs/SETUP.md

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,15 @@
8787
- [ ] one with read & write policy attached
8888
- [ ] Modify [terraform/terraform_override.tf](terraform/terraform_override.tf) to reflect your AWS setup
8989
90-
## GitHub App
90+
## GitHub API access
91+
92+
There are two possible ways for GitHub API access:
93+
- With GitHub Apps, which has the benefit of not being tied to a GitHub user
94+
- [experimental] With personal access tokens for a GitHub user, which has the benefit of more granular permissions, but is limited in functionality and requires more manual work:
95+
- Only teams and team memberships are supported right now
96+
- The GitHub user must be a team maintainer for any teams it should manage
97+
98+
### GitHub App
9199
92100
*NOTE*: If you already have a GitHub App with required permissions you can skip the app creation step.
93101
@@ -114,18 +122,48 @@
114122
</details>
115123
- [ ] [Install the GitHub Apps](https://docs.github.com/en/developers/apps/managing-github-apps/installing-github-apps) in the GitHub organization for `All repositories`
116124
125+
### Personal access token
126+
127+
- [ ] Create a separate dedicated GitHub account for GitHub Management. It is not recommended to use your personal account.
128+
- [ ] [Create two fine-grained personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) for the dedicated GitHub account - *they are going to be used by terraform and GitHub Actions to authenticate with GitHub*:
129+
- Resource owner: The GitHub Organization
130+
- Expiration: 366 days (you can also [remove the limit](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization#enforcing-a-maximum-lifetime-policy-for-personal-access-tokens)
131+
- Permissions:
132+
<details><summary>read-only</summary>
133+
134+
- `Repository permissions`
135+
- `Contents`: `Read-only`
136+
- `Metadata`: `Read-only`
137+
- `Organization permissions`
138+
- `Members`: `Read-only`
139+
</details>
140+
<details><summary>read & write</summary>
141+
142+
- `Repository permissions`
143+
- `Contents`: `Read & Write`
144+
- `Metadata`: `Read-only`
145+
- `Organization permissions`
146+
- `Members`: `Read & Write`
147+
</details>
148+
- [ ] Switch to an organization owner account and approve the tokens in the organizations settings, under "Personal access tokens > Pending requests"
149+
- [ ] Give the dedicated GitHub account write access to the GitHub Management Repository
150+
117151
## GitHub Repository Secrets
118152
119153
- [ ] [Create encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-an-organization) for the GitHub organization and allow the repository to access them (\*replace `$GITHUB_ORGANIZATION_NAME` with the GitHub organization name) - *these secrets are read by the GitHub Action workflows*
120-
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/apps/$GITHUB_APP_NAME` and copy the `App ID`
121-
- [ ] `RO_GITHUB_APP_ID`
122-
- [ ] `RW_GITHUB_APP_ID`
123-
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/installations`, click `Configure` next to the `$GITHUB_APP_NAME` and copy the numeric suffix from the URL
124-
- [ ] `RO_GITHUB_APP_INSTALLATION_ID` (or `RO_GITHUB_APP_INSTALLATION_ID_$GITHUB_ORGANIZATION_NAME` for organizations other than the repository owner)
125-
- [ ] `RW_GITHUB_APP_INSTALLATION_ID` (or `RW_GITHUB_APP_INSTALLATION_ID_$GITHUB_ORGANIZATION_NAME` for organizations other than the repository owner)
126-
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/apps/$GITHUB_APP_NAME`, click `Generate a private key` and copy the contents of the downloaded PEM file
127-
- [ ] `RO_GITHUB_APP_PEM_FILE`
128-
- [ ] `RW_GITHUB_APP_PEM_FILE`
154+
- If you use a GitHub App:
155+
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/apps/$GITHUB_APP_NAME` and copy the `App ID`
156+
- [ ] `RO_GITHUB_APP_ID`
157+
- [ ] `RW_GITHUB_APP_ID`
158+
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/installations`, click `Configure` next to the `$GITHUB_APP_NAME` and copy the numeric suffix from the URL
159+
- [ ] `RO_GITHUB_APP_INSTALLATION_ID` (or `RO_GITHUB_APP_INSTALLATION_ID_$GITHUB_ORGANIZATION_NAME` for organizations other than the repository owner)
160+
- [ ] `RW_GITHUB_APP_INSTALLATION_ID` (or `RW_GITHUB_APP_INSTALLATION_ID_$GITHUB_ORGANIZATION_NAME` for organizations other than the repository owner)
161+
- [ ] Go to `https://github.com/organizations/$GITHUB_ORGANIZATION_NAME/settings/apps/$GITHUB_APP_NAME`, click `Generate a private key` and copy the contents of the downloaded PEM file
162+
- [ ] `RO_GITHUB_APP_PEM_FILE`
163+
- [ ] `RW_GITHUB_APP_PEM_FILE`
164+
- If you use personal access tokens
165+
- [ ] `RO_GITHUB_TOKEN`
166+
- [ ] `RW_GITHUB_TOKEN`
129167
- [ ] Use the values generated during [AWS](#aws) setup
130168
- [ ] `RO_AWS_ACCESS_KEY_ID`
131169
- [ ] `RW_AWS_ACCESS_KEY_ID`

scripts/src/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default {
44
TF_WORKING_DIR: process.env.TF_WORKING_DIR || '../terraform',
55
FILES_DIR: process.env.FILES_DIR || '../files',
66
GITHUB_DIR: process.env.GITHUB_DIR || '../github',
7+
GITHUB_TOKEN: process.env.GITHUB_TOKEN || '',
78
GITHUB_APP_ID: process.env.GITHUB_APP_ID || '',
89
GITHUB_APP_INSTALLATION_ID: process.env.GITHUB_APP_INSTALLATION_ID || '',
910
GITHUB_APP_PEM_FILE: process.env.GITHUB_APP_PEM_FILE || '',

scripts/src/github.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,21 @@ export class GitHub {
102102
// NOTE: We import these dynamically so that they can be mocked
103103
const {createAppAuth} = await import('@octokit/auth-app')
104104
const {Octokit} = await import('@octokit/rest')
105-
const auth = createAppAuth({
106-
appId: env.GITHUB_APP_ID,
107-
privateKey: env.GITHUB_APP_PEM_FILE
108-
})
109-
const installationAuth = await auth({
110-
type: 'installation',
111-
installationId: env.GITHUB_APP_INSTALLATION_ID
112-
})
105+
let token = env.GITHUB_TOKEN;
106+
if (token == '') {
107+
const auth = createAppAuth({
108+
appId: env.GITHUB_APP_ID,
109+
privateKey: env.GITHUB_APP_PEM_FILE
110+
})
111+
const installationAuth = await auth({
112+
type: 'installation',
113+
installationId: env.GITHUB_APP_INSTALLATION_ID
114+
})
115+
token = installationAuth.token;
116+
}
117+
113118
const client = new (Octokit.plugin(retry, throttling))({
114-
auth: installationAuth.token,
119+
auth: token,
115120
throttle: {
116121
onRateLimit: (
117122
retryAfter: number,

0 commit comments

Comments
 (0)