Skip to content

Commit fd15768

Browse files
authored
docs: OIDC-preferred + GitHub App token recommendations (Phases 2 + 3) (#25)
Closes #8 and #9. Documentation-only; no code change. ## Phase 2 (#8) — OIDC for AWS credentials README's 'How to start' step 1 now leads with GitHub OIDC as the preferred path and relegates static IAM access keys to a 'legacy' Option B. Rationale: static keys don't rotate, live in GitHub secrets indefinitely (permanent attack surface), and can't be scoped to a specific repo/branch/environment. OIDC issues short-lived STS tokens per run, scoped by repo/branch/environment. Includes: - A Terraform example for the trust-relationship IAM role with a repo-scoped 'sub' StringLike condition. - The minimum permissions policy (unchanged — attaches to role or user). - A workflow snippet showing 'permissions: id-token: write' and 'role-to-assume' instead of access-key secrets. No changes to the action's code — it already reads AWS creds from env, which configure-aws-credentials@v6 populates identically under both paths. ## Phase 3 (#9) — GitHub token type preferences README's 'How to start' step 2 replaced. Three token options ordered by preference: - A (preferred): GitHub App installation token via actions/create-github-app-token. No human identity, short-lived, minimal permission (Repository Administration: read/write). - B: fine-grained PAT scoped to specific repos with just Repository Administration. Better than classic PAT but still tied to a human. - C (deprecated): classic PAT with 'repo' scope. Over-permissive and human-tied; kept in docs as a fallback for environments that don't allow Apps or fine-grained PATs. No code change — the action accepts any token that has permission to manage self-hosted runners on the target repo. Docs change only. ## Not included in this PR - Phase 2's optional 'role-to-assume' input on the action itself (so consumers don't need the separate configure-aws-credentials step). Deferred — the current dual-step pattern is standard and works fine. Convenience feature, not urgency. Signed-off-by: yuriyryabikov <22548029+kurok@users.noreply.github.com>
1 parent 6bb148b commit fd15768

1 file changed

Lines changed: 93 additions & 6 deletions

File tree

README.md

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,65 @@ EC2 self-hosted runner will handle everything else so that you will pay for it t
6464

6565
Use the following steps to prepare your workflow for running on your EC2 self-hosted runner:
6666

67-
**1. Prepare IAM user with AWS access keys**
67+
**1. Configure AWS credentials (OIDC preferred)**
68+
69+
This action reads AWS credentials from the environment. Two paths — pick one.
70+
71+
**Option A (preferred): GitHub OIDC.** No long-lived static keys in your GitHub secrets. A short-lived STS token is minted per workflow run, scoped to the exact repo / branch / environment.
72+
73+
1. Create an OIDC provider for GitHub in your AWS account (one-time per account). The thumbprint is `6938fd4d98bab03faadb97b34396831e3780aea1` as of this writing.
74+
2. Create an IAM role with a trust relationship to `token.actions.githubusercontent.com`:
75+
76+
```hcl
77+
# Terraform
78+
resource "aws_iam_role" "github_runner" {
79+
name = "github-runner"
80+
assume_role_policy = jsonencode({
81+
Version = "2012-10-17"
82+
Statement = [{
83+
Effect = "Allow"
84+
Principal = { Federated = "arn:aws:iam::<account>:oidc-provider/token.actions.githubusercontent.com" }
85+
Action = "sts:AssumeRoleWithWebIdentity"
86+
Condition = {
87+
StringEquals = {
88+
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
89+
}
90+
StringLike = {
91+
"token.actions.githubusercontent.com:sub" = "repo:<org>/<repo>:*"
92+
}
93+
}
94+
}]
95+
})
96+
}
97+
```
98+
99+
3. Attach the least-privilege permissions policy below to that role.
100+
4. In the workflow, grant OIDC permission to the job and assume the role via `aws-actions/configure-aws-credentials` without any access-key secrets:
101+
102+
```yaml
103+
permissions:
104+
id-token: write # required for OIDC
105+
contents: read
106+
steps:
107+
- uses: aws-actions/configure-aws-credentials@<sha>
108+
with:
109+
role-to-assume: arn:aws:iam::<account>:role/github-runner
110+
aws-region: <region>
111+
- uses: namecheap/ec2-github-runner@<sha>
112+
with:
113+
mode: start
114+
# ...
115+
```
68116

69-
1. Create new AWS access keys for the new or an existing IAM user with the following least-privilege minimum required permissions:
117+
**Option B (legacy): static IAM access keys.** Only use this if OIDC isn't available (e.g., restricted AWS Organization SCPs). The keys rotate manually and live in GitHub secrets indefinitely — a permanent attack surface.
118+
119+
1. Create an IAM user with the same permissions policy below.
120+
2. Generate an access key pair for the user; store as `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` secrets.
121+
3. Use `aws-actions/configure-aws-credentials` with those secrets.
122+
123+
**Permissions policy (both paths)**
124+
125+
1. Attach the following least-privilege minimum required permissions to the role (Option A) or user (Option B):
70126

71127
```
72128
{
@@ -136,11 +192,42 @@ Use the following steps to prepare your workflow for running on your EC2 self-ho
136192
2. Add the keys to GitHub secrets.
137193
3. Use the [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) action to set up the keys as environment variables.
138194

139-
**2. Prepare GitHub personal access token**
195+
**2. Prepare the GitHub token**
196+
197+
The action's `github-token` input needs permission to manage self-hosted runners on the target repo — specifically it hits `POST /repos/:owner/:repo/actions/runners/registration-token` and `DELETE /repos/:owner/:repo/actions/runners/:id`. Three token types work; pick the lowest-privilege one your setup supports.
198+
199+
**Option A (preferred): GitHub App installation token.** No human identity, no long-lived secret.
200+
201+
1. Create a GitHub App in your org with the permissions below. Grant it installation on the target repo.
202+
2. In the workflow, mint a short-lived installation token via `actions/create-github-app-token@<sha>` and pass its output to this action's `github-token` input.
203+
204+
```yaml
205+
- uses: actions/create-github-app-token@<sha>
206+
id: app-token
207+
with:
208+
app-id: ${{ vars.RUNNER_APP_ID }}
209+
private-key: ${{ secrets.RUNNER_APP_PRIVATE_KEY }}
210+
- uses: namecheap/ec2-github-runner@<sha>
211+
with:
212+
mode: start
213+
github-token: ${{ steps.app-token.outputs.token }}
214+
# ...
215+
```
216+
217+
**Minimum permissions for the App:**
218+
- Repository — **Administration**: Read and write.
219+
220+
**Option B: fine-grained personal access token.** Scoped to specific repos, per-resource permissions. Expires. Better than a classic PAT, worse than an App because it's tied to a human identity.
221+
222+
1. GitHub → Settings → Developer settings → Fine-grained tokens → Generate new.
223+
2. Resource owner: your org. Repositories: only the repos where this action runs.
224+
3. Repository permissions: **Administration: Read and write**. Nothing else.
225+
4. Store as a GitHub secret; pass via `github-token`.
226+
227+
**Option C (deprecated): classic personal access token.** Grants repo-wide permissions far broader than this action needs. Tied to a human identity — CI breaks when the person leaves the org. Only use this if neither of the above is available.
140228

141-
1. Create a new GitHub personal access token with the `repo` scope.
142-
The action will use the token for self-hosted runners management in the GitHub account on the repository level.
143-
2. Add the token to GitHub secrets.
229+
1. Scope: `repo` (necessary evil — finer-grained scopes don't exist on classic PATs).
230+
2. Store as a GitHub secret; pass via `github-token`.
144231

145232
**3. Prepare EC2 image**
146233

0 commit comments

Comments
 (0)