I searched a lot how to authenticate/authorize Google's client libraries and it seems no one agrees how to do it.
Some people states that I should create a service account, create a key out from it and give that key to each developer that wants to act as this service account. I hate this solution because it leaks the identity of the service account to multiple person.
Others mentioned that you simply log in with the Cloud SDK and ADC (Application Default Credentials) by doing:
$ gcloud auth application-default login
Then, libraries like google-cloud-storage
will load credentials tied to my user from the ADC.
It's better but still not good for me as this require to go to IAM
and give every developer (or a group) the permissions required for the application to run. Moreover, if the developer runs many applications locally for testing purposes (e.g. microservices), the list of permissions required will probably be very long. Also it will be hard to understand why we gave such permissions after some time.
The last approach I encountered is service account impersonation. This resolve the fact of exposing private keys to developers, and let us define the permission required by an application, let's say A once, associate them to a service account and say:
Hey, let Julien act as the service account used for application A.
Here's a snippet of how to impersonate a principal:
from google.auth import impersonated_credentials
from google.auth import default
from google.cloud import storage
target_scopes = ['https://www.googleapis.com/auth/devstorage.read_only']
credentials, project = default(scopes=target_scopes)
final_credentials = impersonated_credentials.Credentials(
source_credentials=credentials,
target_principal="[email protected]",
target_scopes=target_scopes
)
client = storage.Client(credentials=final_credentials)
print(next(client.list_buckets()))
If you want to try this yourself, you need to create the service account you want to impersonate (here [email protected]) and grant your user the role
Service Account Token Creator
from the service account permission tab.
My only concern is that it would require me to wrap all Google Cloud client libraries I want to use with something that checks if I am running my app locally:
from google.auth import impersonated_credentials
from google.auth import default
from google.cloud import storage
target_scopes = ['https://www.googleapis.com/auth/devstorage.read_only']
credentials, project = default(scopes=target_scopes)
if env := os.getenv("RUNNING_ENVIRONMENT") == "local":
credentials = impersonated_credentials.Credentials(
source_credentials=credentials,
target_principal=os.environ["TARGET_PRINCIPAL"],
target_scopes=target_scopes
)
client = storage.Client(credentials=credentials)
print(next(client.list_buckets()))
Also, I have to define the scopes (I think it's the oauth2 access scopes?) I am using, which is pretty annoying.
My question is: I am I going the right direction? Do I overthink all of that? Is there any easier way to achieve all of that?
Here's some of the source I used:
This topic is discussed here.
I've made a first proposition here to support this enhancement.
The feature has been implemented! See here for details.
You can use a new gcloud feature and impersonate your local credential like that:
gcloud auth application-default login --impersonate-service-account=<SA email>
It's a new feature. Being a Java and Golang developer, I checked and tested the Java client library and it already supports this authentication mode. However, it's not yet the case in Go. And I submitted a pull request to add it into the go client library.
I quickly checked in Python, and it seems implemented. Have a try on one of the latest versions (released after August 3rd 2021) and let me know!!
Note: A few is aware of your use case. I'm happy not to be alone in this case :)