Flask web app on Cloud Run – google.auth.exceptions.DefaultCredentialsError:

Question:

I’m hosting a Flask web app on Cloud Run. I’m also using Secret Manager to store Service Account keys. (I previously downloaded a JSON file with the keys)

In my code, I’m accessing the payload then using os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = payload to authenticate. When I deploy the app and try to visit the page, I get an Internal Service Error. Reviewing the logs, I see:

File "/usr/local/lib/python3.10/site-packages/google/auth/_default.py", line 121, in load_credentials_from_file
    raise exceptions.DefaultCredentialsError(
google.auth.exceptions.DefaultCredentialsError: File {"

I can access the secret through gcloud just fine with: gcloud secrets versions access 1 --secret="<secret_id>" while acting as the Service Account.

Here is my Python code:

# Grabbing keys from Secret Manager
def access_secret_version():

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

    # Build the resource name of the secret version.
    name = "projects/{project_id}/secrets/{secret_id}/versions/1"

    # Access the secret version.
    response = client.access_secret_version(request={"name": name})

    payload = response.payload.data.decode("UTF-8")
    return payload

@app.route('/page/page_two')
def some_random_func():

    # New way
    payload = access_secret_version() # <---- calling the payload
   
    os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = payload


    # Old way
    os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "service-account-keys.json"

I’m not technically accessing a JSON file like I was before. The payload variable is storing entire key. Is this why it’s not working?

Asked By: scarecrow

||

Answers:

Your approach is incorrect.

When you run on a Google compute service like Cloud Run, the code runs under the identity of the compute service.

In this case, by default, Cloud Run uses the Compute Engine default service account but, it’s good practice to create a Service Account for your service and specify it when you deploy it to Cloud Run (see Service accounts).

This mechanism is one of the "legs" of Application Default Credentials when your code is running on Google Cloud, you don’t specify the environment variable (you also don’t need to create a key) and Cloud Run service acquires the credentials from the Metadata service:

import google.auth

credentials, project_id = google.auth.default()

See google.auth package

It is bad practice to define|set an environment variable within code. By their nature, environment variables should be provided by the environment. Doing this with APPLICATION_DEFAULT_CREDENTIALS means that your code always sets this value when it should only do this when the code is running off Google Cloud.

For completeness, if you need to create Credentials from a JSON string rather than from a file contain a JSON string, you can use from_service_account_info (see google.oauth2.service_account)

Answered By: DazWilkin
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.