how can I get my gcloud user creds into a container securely and use them to impersonate a service account when testing locally?
Asked Answered
O

3

5

Starting with this:

  • users have their own google user accounts that are setup locally via gcloud login
  • the application is using the gcp APIs in the usual way- by default it will look for GOOGLE_APPLICATION_CREDENTIALS, GCE roles, service accounts, or use the local users gcloud configured credentials
  • when users run it locally it will use their own user account, when run in gcp it will use a service account
  • The user's account also has access to impersonate the service account. So when running the app locally users first do gcloud config set auth/impersonate_service_account [SA_FULL_EMAIL] and it can be run with the same creds as what will run in the dev environment- without them having to download any keys

Now that works. BUT I also want to make it possible to run the applications locally in containers too. Using docker/docker-compose/minikube/etc how can I make it possible to impersonate a service account?

the container would need access to the gcloud creds and it would need to set impersonation in the session too before the app starts somehow. This must not be done in code- the app should just use the APIs as normal without having to do anything differently.

EDIT: when applications run in dev or prod GCP accounts/projects they run in the context of a service account that has correctly scoped permissions for that specific application. Developer's own user accounts have broad permissions to the dev environment. When running locally its useful to run with the same service account that application runs with in the dev environment instead of the developer's own user account

Oria answered 21/4, 2020 at 14:12 Comment(5)
Edit your question with more details. 1) Are you using the CLI gcloud in the container? 2) If the users already have permissions, why do you want to impersonate a service account. 3) The CLI is used for development, for production environments the CLI credentials should not be used. Instead you should use a service account with the container. 4) If you need to use user credentials, during the authorization phase inside the container, capture the OAuth Access Token and use that for authorization to impersonate.Favored
5) Try restating your question with what you need and not what you think you need. Here I mean what is the authorization design? If users are accessing shared resources, user credentials are usually correct. If the user is logging into your web server and the service is accessing resources, then a service account is usually correct.Favored
feel like i was pretty clear. want to test running an application with the service account credentials when running locally, in a container, without having to generate a static key for that service account by impersonating itOria
If your question was clear, I would not have posted a five-part comment.Favored
i added to original questionOria
O
1

I'm amending my answer.

This only works for the CLI not the SDK: gcloud config set auth/impersonate_service_account [SA_FULL_EMAIL]

For a while the SDK didn't support impersonation like this (afaik) but I recently found now it does with this command: gcloud auth application-default login --impersonate-service-account SERVICE_ACCT_EMAIL. Docs

So this is now possible and all users have to do is:

  1. Mount $HOME/.config/gcloud into the running container
  2. Run gcloud auth login with impersonation on the host

SDK running in the container will find the impersonated creds and everything just works.

Only thing is this is that its annoying and I don't know how to turn it off. The cli lets you do this with a single command but for the SDK you need to login via a browser and authorize it. The only way I have found to turn it off is to re-login without the impersonation flag. Is there just a command to turn on/off impersonation for the SDK without the whole login thing?

Oria answered 11/5, 2021 at 16:15 Comment(1)
I was wrong, this does not work. GOOGLE_APPLICATION_CREDENTIALS must be a fileOria
B
2

The correct way to achieve this is the Secret Manager that is supplied by Google Cloud.

  • Activate Secret Manager API in your Google Cloud account.
  • Create Secret data and relevant payload either using GC UI or sdk.
  • Use the following function in your settings.py with your Project ID.
  • Give permissions for your service account to access Secrets (If it does not already have access)
  • Use the Secret Manager to access the payloads in your code without explicitly exposing the payload.
def access_secret_version(secret_id):
    """
    Access the payload for the given secret version if one exists. The version
    can be a version number as a string (e.g. "5") or an alias (e.g. "latest").
    """
    project_id = PROJECT_ID
    version_id = 1
    # Import the Secret Manager client library.
    from google.cloud import secretmanager_v1beta1 as secretmanager

    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the secret version.
    name = client.secret_version_path(project_id, secret_id, version_id)

    # Access the secret version.
    response = client.access_secret_version(name)

    # Print the secret payload.
    #
    # WARNING: Do not print the secret in a production environment - this
    # snippet is showing how to access the secret material.
    payload = response.payload.data.decode('UTF-8')
    # logging.info('Plaintext: {}'.format(payload))
    logging.info('Secret accessed for  :' + secret_id)
    return payload
Braswell answered 22/4, 2020 at 12:8 Comment(1)
The goal is to avoid static credentials so they don't need to be stored at all (by using impersonation, workload id, etc). The issue is that (unlike aws) the google cli and SDK have totally different auth mechanisms which is stupid. I can impersonate any GSA I have access to with gcloud easily, but the SDK has no way of doing this (without explicitly doing it in code).Oria
O
1

I'm amending my answer.

This only works for the CLI not the SDK: gcloud config set auth/impersonate_service_account [SA_FULL_EMAIL]

For a while the SDK didn't support impersonation like this (afaik) but I recently found now it does with this command: gcloud auth application-default login --impersonate-service-account SERVICE_ACCT_EMAIL. Docs

So this is now possible and all users have to do is:

  1. Mount $HOME/.config/gcloud into the running container
  2. Run gcloud auth login with impersonation on the host

SDK running in the container will find the impersonated creds and everything just works.

Only thing is this is that its annoying and I don't know how to turn it off. The cli lets you do this with a single command but for the SDK you need to login via a browser and authorize it. The only way I have found to turn it off is to re-login without the impersonation flag. Is there just a command to turn on/off impersonation for the SDK without the whole login thing?

Oria answered 11/5, 2021 at 16:15 Comment(1)
I was wrong, this does not work. GOOGLE_APPLICATION_CREDENTIALS must be a fileOria
N
0

It boils down to:

  1. You want your containers to have the same credentials locally and in GCP
  2. You don't want to store secrets locally or remotely
  3. You want the application to be agnostic of where it is run (locally vs in the cloud)

I see two solutions:

Solution 1:

If you want (3), you have to use the "loading credentials" interface that Google uses, i.e. (https://cloud.google.com/docs/authentication/application-default-credentials) via the GOOGLE_APPLICATION_CREDENTIALS env var. This has to be a file so you could, every time you want to test your code:

  1. gcloud iam service-accounts keys create /tempdir/creds.json --iam-account=sa_account + save the key_id
  2. docker run --env GOOGLE_APPLICATION_CREDENTIALS=/creds/creds.json -v /tempdir:/creds
  3. delete the service-account key file locally and in the cloud using the key_id

You could argue that this violates (2), because the key file is created both locally and remotely, but the intention is that they are short lived, and are deleted (both locally and remotely) afterwards.

Solution 2:

Every time you want to test your code:

  1. Create google.auth.credentials.Credentials() locally
  2. Serialize it using .to_json()
  3. Pass is as an env var
  4. In the container, check if the env var exists
  5. If yes, parse it, and initialize google.auth.credentials.Credentials() with it, then to get the right service account use google.auth.impersonated_credentials.Credentials()
  6. If no, use google.auth.default()

This clearly violates (3), but that way you don't store any key files anywhere. Note that you cannot serialiaze google.auth.impersonated_credentials.Credentials() without also including the data in google.auth.credentials.Credentials(), because the google.auth.impersonated_credentials.Credentials() needs the google.auth.credentials.Credentials() to work. The impersonated credentials are like an add-on to the original ones that limit their access rights, not "standalone". So whatever you do, your end user credentials end up entering the container.

Hope this helps.

Nievelt answered 10/3, 2023 at 11:59 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.