Skip to content

Commit ee1c1b6

Browse files
authored
Add security considerations for Attribute Conditions (#393)
1 parent ec485ac commit ee1c1b6

File tree

2 files changed

+116
-32
lines changed

2 files changed

+116
-32
lines changed

README.md

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
345345
1. Create a Workload Identity Pool:
346346
347347
```sh
348+
# TODO: replace ${PROJECT_ID} with your value below.
349+
348350
gcloud iam workload-identity-pools create "github" \
349351
--project="${PROJECT_ID}" \
350352
--location="global" \
@@ -354,6 +356,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
354356
1. Get the full ID of the Workload Identity **Pool**:
355357
356358
```sh
359+
# TODO: replace ${PROJECT_ID} with your value below.
360+
357361
gcloud iam workload-identity-pools describe "github" \
358362
--project="${PROJECT_ID}" \
359363
--location="global" \
@@ -368,29 +372,35 @@ These instructions use the [gcloud][gcloud] command-line tool.
368372
369373
1. Create a Workload Identity **Provider** in that pool:
370374
375+
**🛑 CAUTION!** Always add an Attribute Condition to restrict entry into the
376+
Workload Identity Pool. You can further restrict access in IAM Bindings, but
377+
always add a basic condition that restricts admission into the pool. A good
378+
default option is to restrict admission based on your GitHub organization as
379+
demonstrated below. Please see the [security
380+
considerations][security-considerations] for more details.
381+
371382
```sh
383+
# TODO: replace ${PROJECT_ID} and ${GITHUB_ORG} with your values below.
384+
372385
gcloud iam workload-identity-pools providers create-oidc "my-repo" \
373386
--project="${PROJECT_ID}" \
374387
--location="global" \
375388
--workload-identity-pool="github" \
376389
--display-name="My GitHub repo Provider" \
377-
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
390+
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
391+
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
378392
--issuer-uri="https://token.actions.githubusercontent.com"
379393
```
380394
381-
The attribute mappings map claims in the GitHub Actions JWT to assertions
382-
you can make about the request (like the repository or GitHub username of
383-
the principal invoking the GitHub Action). These can be used to further
384-
restrict the authentication using `--attribute-condition` flags.
385-
386-
> [!IMPORTANT]
387-
>
388-
> You must map any claims in the incoming token to attributes before you can
389-
> assert on those attributes in a CEL expression or IAM policy!
395+
> **❗️ IMPORTANT** You must map any claims in the incoming token to
396+
> attributes before you can assert on those attributes in a CEL expression
397+
> or IAM policy!
390398
391399
1. Extract the Workload Identity **Provider** resource name:
392400
393401
```sh
402+
# TODO: replace ${PROJECT_ID} with your value below.
403+
394404
gcloud iam workload-identity-pools providers describe "my-repo" \
395405
--project="${PROJECT_ID}" \
396406
--location="global" \
@@ -408,12 +418,10 @@ These instructions use the [gcloud][gcloud] command-line tool.
408418
workload_identity_provider: '...' # "projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo"
409419
```
410420
411-
> [!IMPORTANT]
412-
>
413-
> The `project_id` input is optional, but may be required by downstream
414-
> authentication systems such as the `gcloud` CLI. Unfortunately we cannot
415-
> extract the project ID from the Workload Identity Provider, since it
416-
> requires the project _number_.
421+
> **❗️ IMPORTANT** The `project_id` input is optional, but may be required
422+
> by downstream authentication systems such as the `gcloud` CLI.
423+
> Unfortunately we cannot extract the project ID from the Workload Identity
424+
> Provider, since it requires the project _number_.
417425
>
418426
> It is technically possible to convert a project _number_ into a project
419427
> _ID_, but it requires permissions to call Cloud Resource Manager, and we
@@ -428,9 +436,14 @@ These instructions use the [gcloud][gcloud] command-line tool.
428436
specific repository a secret in Google Secret Manager.
429437
430438
```sh
431-
# TODO(developer): Update this value to your GitHub repository.
432-
export REPO="username/name" # e.g. "google/chrome"
433-
export WORKLOAD_IDENTITY_POOL_ID="value/from/above" # e.g. "projects/123456789/locations/global/workloadIdentityPools/github"
439+
# TODO: replace ${PROJECT_ID}, ${WORKLOAD_IDENTITY_POOL_ID}, and ${REPO}
440+
# with your values below.
441+
#
442+
# ${REPO} is the full repo name including the parent GitHub organization,
443+
# such as "my-org/my-repo".
444+
#
445+
# ${WORKLOAD_IDENTITY_POOL_ID} is the full pool id, such as
446+
# "projects/123456789/locations/global/workloadIdentityPools/github".
434447
435448
gcloud secrets add-iam-policy-binding "my-secret" \
436449
--project="${PROJECT_ID}" \
@@ -464,13 +477,17 @@ These instructions use the [gcloud][gcloud] command-line tool.
464477
Service Account, take note of the email address and skip this step.
465478
466479
```sh
480+
# TODO: replace ${PROJECT_ID} with your value below.
481+
467482
gcloud iam service-accounts create "my-service-account" \
468483
--project "${PROJECT_ID}"
469484
```
470485
471486
1. Create a Workload Identity Pool:
472487
473488
```sh
489+
# TODO: replace ${PROJECT_ID} with your value below.
490+
474491
gcloud iam workload-identity-pools create "github" \
475492
--project="${PROJECT_ID}" \
476493
--location="global" \
@@ -480,6 +497,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
480497
1. Get the full ID of the Workload Identity **Pool**:
481498
482499
```sh
500+
# TODO: replace ${PROJECT_ID} with your value below.
501+
483502
gcloud iam workload-identity-pools describe "github" \
484503
--project="${PROJECT_ID}" \
485504
--location="global" \
@@ -494,33 +513,42 @@ These instructions use the [gcloud][gcloud] command-line tool.
494513
495514
1. Create a Workload Identity **Provider** in that pool:
496515
516+
**🛑 CAUTION!** Always add an Attribute Condition to restrict entry into the
517+
Workload Identity Pool. You can further restrict access in IAM Bindings, but
518+
always add a basic condition that restricts admission into the pool. A good
519+
default option is to restrict admission based on your GitHub organization as
520+
demonstrated below. Please see the [security
521+
considerations][security-considerations] for more details.
522+
497523
```sh
524+
# TODO: replace ${PROJECT_ID} and ${GITHUB_ORG} with your values below.
525+
498526
gcloud iam workload-identity-pools providers create-oidc "my-repo" \
499527
--project="${PROJECT_ID}" \
500528
--location="global" \
501529
--workload-identity-pool="github" \
502530
--display-name="My GitHub repo Provider" \
503-
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
531+
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
532+
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
504533
--issuer-uri="https://token.actions.githubusercontent.com"
505534
```
506535
507-
The attribute mappings map claims in the GitHub Actions JWT to assertions
508-
you can make about the request (like the repository or GitHub username of
509-
the principal invoking the GitHub Action). These can be used to further
510-
restrict the authentication using `--attribute-condition` flags.
511-
512-
> [!IMPORTANT]
513-
>
514-
> You must map any claims in the incoming token to attributes before you can
515-
> assert on those attributes in a CEL expression or IAM policy!**
536+
> **❗️ IMPORTANT** You must map any claims in the incoming token to
537+
> attributes before you can assert on those attributes in a CEL expression
538+
> or IAM policy!
516539
517540
1. Allow authentications from the Workload Identity Pool to your Google Cloud
518541
Service Account.
519542
520543
```sh
521-
# TODO(developer): Update this value to your GitHub repository.
522-
export REPO="username/name" # e.g. "google/chrome"
523-
export WORKLOAD_IDENTITY_POOL_ID="value/from/above" # e.g. "projects/123456789/locations/global/workloadIdentityPools/github"
544+
# TODO: replace ${PROJECT_ID}, ${WORKLOAD_IDENTITY_POOL_ID}, and ${REPO}
545+
# with your values below.
546+
#
547+
# ${REPO} is the full repo name including the parent GitHub organization,
548+
# such as "my-org/my-repo".
549+
#
550+
# ${WORKLOAD_IDENTITY_POOL_ID} is the full pool id, such as
551+
# "projects/123456789/locations/global/workloadIdentityPools/github".
524552
525553
gcloud iam service-accounts add-iam-policy-binding "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
526554
--project="${PROJECT_ID}" \
@@ -535,6 +563,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
535563
1. Extract the Workload Identity **Provider** resource name:
536564
537565
```sh
566+
# TODO: replace ${PROJECT_ID} with your value below.
567+
538568
gcloud iam workload-identity-pools providers describe "my-repo" \
539569
--project="${PROJECT_ID}" \
540570
--location="global" \
@@ -557,6 +587,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
557587
shows granting access to a secret in Google Secret Manager.
558588
559589
```sh
590+
# TODO: replace ${PROJECT_ID} with your value below.
591+
560592
gcloud secrets add-iam-policy-binding "my-secret" \
561593
--project="${PROJECT_ID}" \
562594
--role="roles/secretmanager.secretAccessor" \
@@ -591,13 +623,17 @@ These instructions use the [gcloud][gcloud] command-line tool.
591623
Service Account, take note of the email address and skip this step.
592624
593625
```sh
626+
# TODO: replace ${PROJECT_ID} with your value below.
627+
594628
gcloud iam service-accounts create "my-service-account" \
595629
--project "${PROJECT_ID}"
596630
```
597631
598632
1. Create a Service Account Key JSON for the Service Account.
599633
600634
```sh
635+
# TODO: replace ${PROJECT_ID} with your value below.
636+
601637
gcloud iam service-accounts keys create "key.json" \
602638
--iam-account "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com"
603639
```
@@ -621,3 +657,4 @@ These instructions use the [gcloud][gcloud] command-line tool.
621657
[github-perms]: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#permissions
622658
[map-external]: https://cloud.google.com/iam/docs/access-resources-oidc#impersonate
623659
[wif]: https://cloud.google.com/iam/docs/workload-identity-federation
660+
[security-considerations]: docs/SECURITY_CONSIDERATIONS.md

docs/SECURITY_CONSIDERATIONS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Security Considerations
2+
3+
There are important risks to consider when mapping GitHub Actions OIDC token
4+
claims.
5+
6+
7+
## Use Unique Mapping Values
8+
9+
Many of the claims embedded in the GitHub Actions OIDC token are not guaranteed
10+
to be unique, and tokens issued by other GitHub organizations or repositories
11+
may contain the same values, allowing them to establish an identity. To protect
12+
against this situation, always use an Attribute Condition to restrict access to
13+
tokens issued by your GitHub organization.
14+
15+
```cel
16+
assertion.repository_owner == 'my-github-org'
17+
```
18+
19+
Never use a "*" in an IAM Binding unless you absolutely know what you are doing!
20+
21+
22+
## Use GitHub's Numeric, Immutable Values
23+
24+
Using "name" fields in Attribute Conditions or IAM Bindings like `repository` and `repository_owner` increase the chances of [cybersquatting][] and [typosquatting][] attacks. If you delete your GitHub repository or GitHub organization, someone could claim that same name and establish an identity. To protect against this situation, use the numeric `*_id` fields instead, which GitHub guarantees to be unique and never re-used.
25+
26+
To get your numeric organization ID:
27+
28+
```sh
29+
ORG="my-org" # TODO: replace with your org
30+
curl -sfL -H "Accept: application/json" "https://api.github.com/orgs/${ORG}" | jq .id
31+
```
32+
33+
To get your numeric repository ID:
34+
35+
```sh
36+
REPO="my-org/my-repo" # TODO: replace with your full repo including the org
37+
curl -sfL -H "Accept: application/json" "https://api.github.com/repos/${REPO}" | jq .id
38+
```
39+
40+
These can be used in an Attribute Condition:
41+
42+
```cel
43+
assertion.repository_owner_id == 1342004 && assertion.repository_id == 260064828
44+
```
45+
46+
[cybersquatting]: https://en.wikipedia.org/wiki/Cybersquatting
47+
[typosquatting]: https://en.wikipedia.org/wiki/Typosquatting

0 commit comments

Comments
 (0)