google-github-actions / auth

A GitHub Action for authenticating to Google Cloud.
https://cloud.google.com/iam
Apache License 2.0
953 stars 195 forks source link

Help needed on attribute mappings #200

Closed pantelis-karamolegkos closed 2 years ago

pantelis-karamolegkos commented 2 years ago

TL;DR

If applicable, and since @sethvargo I know you are the go-to guy on such issues, I would like to kindly request your support on this

Detailed design

I am trying to wrap my head around this new topic, given that there are still too few examples out there and the documentation is rather obscure.

I am trying to reverse engineer this repo.

What I want to understand is the way we inform GCP that OIDC tokens having specific attributes (i.e coming from specific orgs/repos/branches etc) are only accepted as valid.

I notice that the iam policy is defined as follows:

data "google_iam_policy" "wli_user_ghshr" {
  binding {
    role = "roles/iam.workloadIdentityUser"

    members = [
      "principalSet://iam.googleapis.com/projects/${data.google_project.project.number}/locations/global/workloadIdentityPools/gh-pool/attribute.full/${var.gh_repo}${var.gh_branch}",
    ]
  }
}

then I see that the identity pool provider is also declared like this

resource "google_iam_workload_identity_pool_provider" "provider" {
  provider                           = google-beta
  project                            = var.project_id
  workload_identity_pool_id          = google_iam_workload_identity_pool.gh_pool.workload_identity_pool_id
  workload_identity_pool_provider_id = "gh-provider"
  attribute_mapping                  = {
    "google.subject" = "assertion.sub"
    "attribute.full" = "assertion.repository+assertion.ref"
  }
  oidc {
    allowed_audiences = ["google-wlif"]
    issuer_uri        = "https://token.actions.githubusercontent.com"
  }
}

My question is the following:

does this line in the iam policy declaration

"principalSet://iam.googleapis.com/projects/${data.google_project.project.number}/locations/global/workloadIdentityPools/gh-pool/attribute.full/${var.gh_repo}${var.gh_branch}",

must be aligned with the attribute mapping in the identity pool provider's attribute_mapping field, i.e

attribute_mapping = {
  "google.subject" = "assertion.sub"
  "attribute.full" = "assertion.repository+assertion.ref"
}

i.e. is the attribute.full

"attribute.full" = "assertion.repository+assertion.ref"

is reflected in the last part of the principalSet of the iam policy as follows:

attribute.full/${var.gh_repo}${var.gh_branch}"

?

If so, in the attribute_mapping google.subject field, what's the role of assertion.sub? Does the value of the assertion.sub has to be something specific?

If so, where is this stated / reflected?

Additional information

No response

github-actions[bot] commented 2 years ago

Hi there @pantelis-karamolegkos :wave:!

Thank you for opening an issue. Our team will triage this as soon as we can. Please take a moment to review the troubleshooting steps which lists common error messages and their resolution steps.

GregoireW commented 2 years ago

Hello,

attribute mapping is defined here: https://cloud.google.com/iam/docs/workload-identity-federation#mapping it is stated: A mapping for the 'google.subject' attribute must always be defined. and This value also appears in audit logs.

Just think the google_iam_workload_identity_pool_provider as a transformer from an OIDC token (github token, .. ) (called 'assertion') to an internal representation (a map = a key value list) with a mandatory key: "google.subject".
You can create multiple attributes in this internal representation. It is not a big deal. The assertion from a github actions id_token contains multiple data described here: https://github.com/google-github-actions/auth#github-token-format

Now the iam policy is the other part. it is described here: https://cloud.google.com/iam/docs/workload-identity-federation?hl=en#impersonation. You can see it like : A service account can be impersonated by an external id_token if the internal representation is valid.

From instance, "principalSet://iam.googleapis.com/projects/my-projects/locations/global/workloadIdentityPools/gh-pool/attribute.full/myrepomybranch" means the internal representation must have an attribute called "full" with the value "myrepomybranch" to be able to validate this rule.

To sum up:

You create a pool which contains one or multiple providers (consume id_token), each provider define a rule to create an internal representation. You can set some rule on a service account to be impersonated by something validating an attribute from the internal representation. When you send a request to be this service account with an id_token, GCP will check each provider from the pool to find one validating the token. If there is one, transform the token in an internal representation then check if the internal representation validate one of the principalSet you have set for this service account.

sethvargo commented 2 years ago

Hi @pantelis-karamolegkos - thank you for opening an issue.

You must map any incoming assertions to attributes in order to use them as WIF Attribute Conditions or IAM Service Account impersonation constraints. Attribute Conditions control admission into the pool ("authentication") whereas Service Account impersonation constraints control access ("authorization").

Neither Attribute Conditions nor Service Account impersonation constraints need to use all the mapped assertions. They can use a subset, and you can even mix-and-match.

sub is a special field in JWTs that identifies the principal that is the subject of the JWT. It's up to the JWT creator (e.g. GitHub in this case) to ensure that subject uniquely identifies the authenticating principle.

Hopefully that helps - let me know if you have any other questions!

pantelis-karamolegkos commented 2 years ago

Thank you both for your very enlightening clarifications 👏🏻