The problem
I am running an ASP Core app on GCR and need to retrieve the current ServiceAccountCredential
in order to sign urls. The obvious way to do this is:
var credential = GoogleCredential.GetApplicationDefault();
var serviceCred = (ServiceAccountCredential)credential.UnderlyingCredential;
var urlSigner = UrlSigner.FromServiceAccountCredential(serviceCred);
Contrary to my expectations, the UnderlyingCredential
that is returned by GoogleCredential.GetApplicationDefault
is an instance ComputeCredential
(even though the GCR service is running on a service account I created specially for that purpose).
The consequence is that an InvalidCastException
is raised, with the error message: Unable to cast object of type 'Google.Apis.Auth.OAuth2.ComputeCredential' to type 'Google.Apis.Auth.OAuth2.ServiceAccountCredential'
.
The workaround
As a workaround I created a key for the service account, stored it in the secret manager, and am using that instead:
var credential = GoogleCredential.FromJson(credJson);
var serviceCred = (ServiceAccountCredential)credential.UnderlyingCredential;
var urlSigner = UrlSigner.FromServiceAccountCredential(serviceCred);
The solution?
Though the workaround works, I would rather not create a key at all, just to avoid having keys lying around. Is there a proper solution I could use to retrieve the current ServiceAccountCredential
? Or, otherwise, to sign urls using the ComputeCredential
?
CodePudding user response:
Application Default Credentials is a method of obtaining credentials. In your case, the GCR service has a service account assigned to it. The compute features are provided by Compute Engine and this is why you are seeing ComputeEngine as the credential type. These credentials are created by the metadata service attached to the Compute Engine instance.
The metadata service provides tokens - OAuth 2.0 Access and Identity Tokens. The metadata server does not provide the service account private key that is required to sign data (URLs). Therefore, you cannot use credentials created by the metadata server for signing data which Google calls blobs.
You have two options:
Use your method and store an existing service account JSON key file in Secret Manager. This is a secure and recommended method. This method requires adding an IAM role to access secrets stored in Secret Manager. You are correct in that you have an added task to manage the service account JSON key file for both security and deployment.
Use IAM Signing. Since the metadata server does not provide access to the private key, Google provides an API to sign data. This method is also secure and recommended. This method requires adding the IAM permission iam.serviceAccounts.signBlob which is included in roles such as Service Account Token Creator (roles/iam.serviceAccountTokenCreator).
The following API is deprecated. I am including it because there is a good C# example showing how to use the API. A similar method works with the new API. The primary difference between this API and the new API is the addition of delegates which you do not require. Delegates are identities involved with impersonation (delegation).
Method: projects.serviceAccounts.signBlob
The new API: