Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add security considerations for Attribute Conditions #393

Merged
merged 1 commit into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 69 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
1. Create a Workload Identity Pool:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools create "github" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -354,6 +356,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
1. Get the full ID of the Workload Identity **Pool**:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools describe "github" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -368,29 +372,35 @@ These instructions use the [gcloud][gcloud] command-line tool.

1. Create a Workload Identity **Provider** in that pool:

**🛑 CAUTION!** Always add an Attribute Condition to restrict entry into the
Workload Identity Pool. You can further restrict access in IAM Bindings, but
always add a basic condition that restricts admission into the pool. A good
default option is to restrict admission based on your GitHub organization as
demonstrated below. Please see the [security
considerations][security-considerations] for more details.

```sh
# TODO: replace ${PROJECT_ID} and ${GITHUB_ORG} with your values below.

gcloud iam workload-identity-pools providers create-oidc "my-repo" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github" \
--display-name="My GitHub repo Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
sethvargo marked this conversation as resolved.
Show resolved Hide resolved
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
--issuer-uri="https://token.actions.githubusercontent.com"
```

The attribute mappings map claims in the GitHub Actions JWT to assertions
you can make about the request (like the repository or GitHub username of
the principal invoking the GitHub Action). These can be used to further
restrict the authentication using `--attribute-condition` flags.

> [!IMPORTANT]
>
> You must map any claims in the incoming token to attributes before you can
> assert on those attributes in a CEL expression or IAM policy!
> **❗️ IMPORTANT** You must map any claims in the incoming token to
> attributes before you can assert on those attributes in a CEL expression
> or IAM policy!

1. Extract the Workload Identity **Provider** resource name:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools providers describe "my-repo" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -408,12 +418,10 @@ These instructions use the [gcloud][gcloud] command-line tool.
workload_identity_provider: '...' # "projects/123456789/locations/global/workloadIdentityPools/github/providers/my-repo"
```

> [!IMPORTANT]
>
> The `project_id` input is optional, but may be required by downstream
> authentication systems such as the `gcloud` CLI. Unfortunately we cannot
> extract the project ID from the Workload Identity Provider, since it
> requires the project _number_.
> **❗️ IMPORTANT** The `project_id` input is optional, but may be required
> by downstream authentication systems such as the `gcloud` CLI.
> Unfortunately we cannot extract the project ID from the Workload Identity
> Provider, since it requires the project _number_.
>
> It is technically possible to convert a project _number_ into a project
> _ID_, but it requires permissions to call Cloud Resource Manager, and we
Expand All @@ -428,9 +436,14 @@ These instructions use the [gcloud][gcloud] command-line tool.
specific repository a secret in Google Secret Manager.

```sh
# TODO(developer): Update this value to your GitHub repository.
export REPO="username/name" # e.g. "google/chrome"
export WORKLOAD_IDENTITY_POOL_ID="value/from/above" # e.g. "projects/123456789/locations/global/workloadIdentityPools/github"
# TODO: replace ${PROJECT_ID}, ${WORKLOAD_IDENTITY_POOL_ID}, and ${REPO}
# with your values below.
#
# ${REPO} is the full repo name including the parent GitHub organization,
# such as "my-org/my-repo".
#
# ${WORKLOAD_IDENTITY_POOL_ID} is the full pool id, such as
# "projects/123456789/locations/global/workloadIdentityPools/github".

gcloud secrets add-iam-policy-binding "my-secret" \
--project="${PROJECT_ID}" \
Expand Down Expand Up @@ -464,13 +477,17 @@ These instructions use the [gcloud][gcloud] command-line tool.
Service Account, take note of the email address and skip this step.

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam service-accounts create "my-service-account" \
--project "${PROJECT_ID}"
```

1. Create a Workload Identity Pool:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools create "github" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -480,6 +497,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
1. Get the full ID of the Workload Identity **Pool**:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools describe "github" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -494,33 +513,42 @@ These instructions use the [gcloud][gcloud] command-line tool.

1. Create a Workload Identity **Provider** in that pool:

**🛑 CAUTION!** Always add an Attribute Condition to restrict entry into the
Workload Identity Pool. You can further restrict access in IAM Bindings, but
always add a basic condition that restricts admission into the pool. A good
default option is to restrict admission based on your GitHub organization as
demonstrated below. Please see the [security
considerations][security-considerations] for more details.

```sh
# TODO: replace ${PROJECT_ID} and ${GITHUB_ORG} with your values below.

gcloud iam workload-identity-pools providers create-oidc "my-repo" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="github" \
--display-name="My GitHub repo Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
--attribute-condition="assertion.repository_owner == '${GITHUB_ORG}'" \
--issuer-uri="https://token.actions.githubusercontent.com"
```

The attribute mappings map claims in the GitHub Actions JWT to assertions
you can make about the request (like the repository or GitHub username of
the principal invoking the GitHub Action). These can be used to further
restrict the authentication using `--attribute-condition` flags.

> [!IMPORTANT]
>
> You must map any claims in the incoming token to attributes before you can
> assert on those attributes in a CEL expression or IAM policy!**
> **❗️ IMPORTANT** You must map any claims in the incoming token to
> attributes before you can assert on those attributes in a CEL expression
> or IAM policy!

1. Allow authentications from the Workload Identity Pool to your Google Cloud
Service Account.

```sh
# TODO(developer): Update this value to your GitHub repository.
export REPO="username/name" # e.g. "google/chrome"
export WORKLOAD_IDENTITY_POOL_ID="value/from/above" # e.g. "projects/123456789/locations/global/workloadIdentityPools/github"
# TODO: replace ${PROJECT_ID}, ${WORKLOAD_IDENTITY_POOL_ID}, and ${REPO}
# with your values below.
#
# ${REPO} is the full repo name including the parent GitHub organization,
# such as "my-org/my-repo".
#
# ${WORKLOAD_IDENTITY_POOL_ID} is the full pool id, such as
# "projects/123456789/locations/global/workloadIdentityPools/github".

gcloud iam service-accounts add-iam-policy-binding "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
--project="${PROJECT_ID}" \
Expand All @@ -535,6 +563,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
1. Extract the Workload Identity **Provider** resource name:

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam workload-identity-pools providers describe "my-repo" \
--project="${PROJECT_ID}" \
--location="global" \
Expand All @@ -557,6 +587,8 @@ These instructions use the [gcloud][gcloud] command-line tool.
shows granting access to a secret in Google Secret Manager.

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud secrets add-iam-policy-binding "my-secret" \
--project="${PROJECT_ID}" \
--role="roles/secretmanager.secretAccessor" \
Expand Down Expand Up @@ -591,13 +623,17 @@ These instructions use the [gcloud][gcloud] command-line tool.
Service Account, take note of the email address and skip this step.

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam service-accounts create "my-service-account" \
--project "${PROJECT_ID}"
```

1. Create a Service Account Key JSON for the Service Account.

```sh
# TODO: replace ${PROJECT_ID} with your value below.

gcloud iam service-accounts keys create "key.json" \
--iam-account "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com"
```
Expand All @@ -621,3 +657,4 @@ These instructions use the [gcloud][gcloud] command-line tool.
[github-perms]: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#permissions
[map-external]: https://cloud.google.com/iam/docs/access-resources-oidc#impersonate
[wif]: https://cloud.google.com/iam/docs/workload-identity-federation
[security-considerations]: docs/SECURITY_CONSIDERATIONS.md
47 changes: 47 additions & 0 deletions docs/SECURITY_CONSIDERATIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Security Considerations

There are important risks to consider when mapping GitHub Actions OIDC token
claims.


## Use Unique Mapping Values

Many of the claims embedded in the GitHub Actions OIDC token are not guaranteed
to be unique, and tokens issued by other GitHub organizations or repositories
may contain the same values, allowing them to establish an identity. To protect
against this situation, always use an Attribute Condition to restrict access to
tokens issued by your GitHub organization.

```cel
assertion.repository_owner == 'my-github-org'
```

Never use a "*" in an IAM Binding unless you absolutely know what you are doing!


## Use GitHub's Numeric, Immutable Values

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.

To get your numeric organization ID:

```sh
ORG="my-org" # TODO: replace with your org
curl -sfL -H "Accept: application/json" "https://api.github.com/orgs/${ORG}" | jq .id
```

To get your numeric repository ID:

```sh
REPO="my-org/my-repo" # TODO: replace with your full repo including the org
curl -sfL -H "Accept: application/json" "https://api.github.com/repos/${REPO}" | jq .id
```

These can be used in an Attribute Condition:

```cel
assertion.repository_owner_id == 1342004 && assertion.repository_id == 260064828
```

[cybersquatting]: https://en.wikipedia.org/wiki/Cybersquatting
[typosquatting]: https://en.wikipedia.org/wiki/Typosquatting