google-github-actions / auth

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

GKE WLI to authenticate as another service account #403

Closed halradaideh closed 7 months ago

halradaideh commented 8 months ago

TL;DR

Pod in GKE has a SA, I would like to authenticate as another service account dynamically via utilizing WLI to impersonate the other account.

Detailed design

in the examples, https://github.com/google-github-actions/auth/blob/main/docs/EXAMPLES.md#workload-identity-federation-through-a-service-account

You can use WLF to authenticate as a service account, I would like to have the same ability to change the pod's identity using its identity which is obtained from WLI, at least for the GitHub action execution.

Using SA impersonation, the KSA/GSA with WLI will have the ability (previously granted with required permission) to impersonate a target SA to dynamically change the identity of the execution.

Additional information

No response

github-actions[bot] commented 8 months ago

Hi there @halradaideh :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.

sethvargo commented 8 months ago

Hi @halradaideh thank you for opening an issue. I'm not sure I understand what you mean by:

I would like to have the same ability to change the pod's identity using its identity which is obtained from WLI, at least for the GitHub action execution.

Can you provide an example? What problem are you trying to solve?

halradaideh commented 8 months ago

i have a case with GitHub Actions

the runner is a pod with a KSA bound to a GSA

I am trying to dynamically change who is accessing GCP resources at execution time so, I am aiming to use SA impersonation in order to allow the pod's KSA/GSA to impersonate another GSA based on the source of the execution

in this way, I need to grant the pod's SA the ability to impersonate others and manage IAM access per team per the team's SA and thus providing the execution access to the team's resources only

sethvargo commented 7 months ago

How is that different from Workload Identity Federation, which does this natively (and auth already supports)?

If you really want an impersonation chain, you can use the delegates option.

halradaideh commented 7 months ago

Sorry but I may be a bit confused

All examples requires workload_identity_provider, I thought there is a default one, but when I checked there is no workloadIdentityPools by default

If the federation example supports what I want, what should I fill workload_identity_provider then?

sethvargo commented 7 months ago

Can you give some concrete examples of what you're trying to do? You have a GKE cluster that is running some pods. Those pods have an identity, and you want to exchange that pod identity for another identity? Why - what are you trying to do? What workload is running on the pod?

halradaideh commented 7 months ago

The workload is a GitHub runner part of GitHub arc I want the execution to have a unique identity at execution time since the runners are shared between teams Then, based on the calculated identity, the execution will exchange the current pod identity to the proper one to limit access to other resources/secrets or stay with the default identity if not needed

regradless of my usecase, if WIF is the way to go, how can I use it without defining new pools/provider in GCP, I want to do the same gcloud do when it applies the impersonation is there a default workload_identity_provider for GCP?

GregoireW commented 7 months ago

It look like your use case is the basic use case for this this action.
GitHub job got a unique id from id_token and you exchange it to a GCP id (can be a SA, or can just be a principalSet). In both case you have to create your own workload identity provider / pool.

At this point, you don't need any (gcp) service account linked to your pod. You only want one if you want to give a base access (read only access to standard service ...)

Impersonation cannot be limited by a policy, if a policy give the permission on 2 services account to be impersonated by your server service account, then everybody executing things on this server will be able to impersonate the 2 service accounts. To limit what people can do, you have to get an external data(here the id_token), and doing so, setup something on GCP (pool / provider).

Now in theory GCP could provide a defaut GitHub pool (it is not done), but it would be a contradiction to the federated service as nobody would be able to put their own additional provider on the pool, nor do any small configuration change on the GitHub provider would be possible.

sethvargo commented 7 months ago

I think multiple ideas are being conflated here. This action (google-github-actions/auth) is for taking GitHub-issued OIDC tokens and exchanging them for Google Cloud identities. Those tokens are signed by GitHub when a job is given id-token: 'write' permissions. With a properly-configured Google Cloud Workload Identity Pool/Provider, that OIDC token can be presented to Google Cloud (via sts.googleapis.com) to get a Google Cloud identity based on attributes in the given token.

If you're already on Google Cloud and you already have the desired identity, you can use Application Default Credentials to get the underlying instance identity credentials; this works on GKE too for KSA identities.

Neither GitHub-managed runners (on github.com) nor self-hosted runners have a specific "identity"; they have a series of claims for which you can map to identities. Those claims include things like the workflow name, caller, repo, and org, for example:

{
  // ...
  "ref": "refs/heads/main",
  "sha": "example-sha",
  "repository": "octo-org/octo-repo",
  "repository_owner": "octo-org",
  "actor_id": "12",
  "repository_visibility": "private",
  "repository_id": "74",
  "repository_owner_id": "65",
  "run_id": "example-run-id",
  "run_number": "10",
  "run_attempt": "2",
  "runner_environment": "github-hosted"
  "actor": "octocat",
  "workflow": "example-workflow",
  //...
}

If you want to map those claims to specific identities, you can use Workload Identity Federation (WIF) to establish those rules. For example, if you wanted a rule like:

Any workflow started by "sethvargo" can deploy Cloud Run services

You can create a WIF Pool and Provider that grants the corresponding principalSet:// permissions on the target Google Cloud project:

gcloud projects add-iam-policy-binding "my-project" \
  --role "roles/cloudrun.developer" \
  --member "principalSet://iam.googleapis.com/projects/416438230019/locations/global/workloadIdentityPools/github/attribute.actor/sethvargo"

Then using google-github-actions/auth, you could exchange the GitHub-provided OIDC token with a Google Cloud Federated Identity Token that can be used as a Bearer Token to deploy to Cloud Run

If you go that route, you should ensure that the KSA/GSA has no permissions on Google Cloud, since that identity is not actually used to interact with Google Cloud resources.


I think you might be confusing the Workload Identity for Kubernetes with the per-invocation workflow identity provided by GitHub Actions.

halradaideh commented 7 months ago

Thank you both for the clarification.

@GregoireW I understand that if there were an identity on the Pod it would be able to access any other service account.


@sethvargo Thank you for explaining the WIF, and yes multiple ideas are being conflated here.

What I initially wanted is not related to GitHub-issued OIDC tokens nor the WIF, I simply wanted to exchange the active service account on the pod dynamically based on a mapping I have in mind (map repos to teams), and to achieve that I wanted to use Service Account Impersonation. Clearly, this Auth action is not the action to do what I am thinking of, although it would be nice to add another "auth" method to be the impersonation alongside the WIF and Service Account key this action already provides.

I managed somehow to do what I had in mind, but still, it is a hack (Poor's man impersonation 😆 )

      - name: get SA token for GCP auth
        id: token
        run: |
          #!/bin/bash
          set -x 

          TARGET="SA@PROJECT-infra.iam.gserviceaccount.com"

          TOKEN_URL="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${TARGET}:generateAccessToken"
          SCOPE_URL="https://www.googleapis.com/auth/cloud-platform"

          SOURCE=$(curl -s -H  "Metadata-Flavor: Google"   "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email")

          TOKEN_REQUEST='{
            "delegates": ["'$SOURCE'"],
            "scope": "'$SCOPE_URL'"
          }'

          ACCESS_TOKEN=$(curl -s -H  "Metadata-Flavor: Google"   "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"   | jq -r .access_token)
          curl -s -X POST -H "Content-Type: application/json" \
               -H "Authorization: Bearer $ACCESS_TOKEN" \
               -d "$TOKEN_REQUEST" \
               "$TOKEN_URL" > /tmp/token.json

          file=$(find * | grep gha-kubeconfig)
          DATA=$(cat /tmp/token.json | jq .accessToken)
          sed -i "s/token: .*/token: $DATA/g" $file

so at the end, if the impersonation logic is not considered part of the "auth" action, I think it should be a separated action.


anyway, in the WIF you provided, you mapped the principalSet as the user to a role, is it possible to grant it what another SA have (impersonate a GCP SA)? I know I can make my mapping to map to a custom role to include what the SA have, but the idea I am trying to achieve is to make the workflow execution impersonate/impose a service account with what that service account has access to, thus have dynamic identity for the execution and manage access on service account level rather than doing it on the WIF

sethvargo commented 7 months ago

Hi @halradaideh I still think you're misunderstanding how WIF works. The underlying configuration for the code snippet you provided indicates that the KSA has permissions to impersonate the superset of all service accounts jobs might need; that means an insider or an attacker can get access to arbitrary identities, regardless of the source job or attributes.

I simply wanted to exchange the active service account on the pod dynamically based on a mapping I have in mind (map repos to teams), and to achieve that I wanted to use Service Account Impersonation.

This certainly sounds like you're trying to use GitHub Actions + OIDC as it's intended to be used...

...is it possible to grant it what another SA have (impersonate a GCP SA)?

Yes, that's Workload Identity Federation through a Service Account as documented the README. The auth action will take care of the impersonation when it generates an access token, but it cannot configure downstream things to use impersonation.

In general, we recommend using Direct Workload Identity Federation instead of relying on impersonation, since impersonation introduces multiple layers of indirection.

halradaideh commented 7 months ago

Yes, I am aware of the security risk as mentioned in the previous comment, it is not managed by the user another step generates the mapping, but yes anyone can get access if they know what is being made underneath.

WIF seems the logical way to go.

Thank you all for your time and explanation.