Getting 403 Forbidden on Google Cloud Run with API key - api-key

I have set up a very simple Node application with Express on Google Cloud Run.
It works great, but when I set it up with "Allow unauthenticated invocations to [service] (y/N)?" to No, I get a 403 Forbidden even though I created an API key and I'm making the calls adding key=[My API key] in the query string, as told in the documentation. My URL ends up looking like
https://service-wodkdj77sba-ew.a.run.app?key=[My API key].
I've tried with restricted (for Google Cloud Run) and unrestricted API keys.
Is there anything I'm missing?

Cloud Run, like many product in GCP, doesn't support API Key authorization. As detailed in your provided link, only a subset of service use API KEY.
It's also mentioned :
API keys do not identify the user or the application making the API request, so you can't restrict access to specific users or service accounts.
Where Cloud Run authentication section specify this here
All Cloud Run services are deployed privately by default, which means that they can't be accessed without providing authentication credentials in the request.
By the way, the Cloud Run expectation and the API Key capabilities aren't compatible.
However, if you want to access to your Cloud Run private service with API Key a workaround exist. You can deploy an Extensible Service Proxy (ESP) on another Cloud Run service. In it, authenticate the API Key and, if it's valid, call the Cloud Run private service with the ServiceAccount of your ESP (which must have roles/run.invoke role).

Related

How do I make my cloud run link more secure?

When I make a GCP cloud run instance anyone can to that link. I am using it for an API for my website. I do not want joe blow opening F12 and following the HTTP requests to my API. I use API keys and stuff however I want my link to be protected to only certain IP addresses or at least lock it behind a username and password. How can I do this with cloud run on GCP?
Cloud Run exposes a public URL by default. You can make it public (let allUsers invoke your endpoint) or restricted (only authorized user can invoke)
The problem with the second solution is that only the Google accounts (Workspace or Gmail) can be added and so restrict your user to use that type of credentials.
So, the solution is to make your API public. You can implement security mechanism in it to software control the authN and authZ of your user, but you aren't protected against DDOS attacks.
Therefore, Cloud Armor enters in the game. You have to create a Load Balancer and to put your Cloud Run service as backend of it. Then activate Cloud Armor. You will be able to check the IP source of the requester, but also to protect your service against attacks.

Using Firebase Auth id tokens to authenticate (multiple) Cloud Run services

Related to Security Cloud Run services for end-users and other services
I'm using:
Firebase Auth to generate id tokens for users with Google, Microsoft, GitHub ... identities
Cloud Endpoints on Cloud Run to invoke (Cloud Run) gRPC services
Firebase Auth users are auth'd by one of my services
Where I'm struggling....
My app provides 1 or more Cloud Run services that the app's users should be able to curl. But authenticating Cloud Run services require per-service id tokens; the id token's audience must use the Cloud Run service URL and the Cloud Run service URL is service-specific.
It seems as though I ought to be able to exchange the Firebase Auth id token for (Google Account) id tokens (with appropriate audiences) that can then be used to invoke the Cloud Run service. The proxy could also run on Cloud Run and it would use my app's auth service to verify whether the id token user should be issued with a Google id token.
Guillaume Blaquire's answer proposes either Coud Endpoints or a proxy similar to what I describe above. However, Cloud Endpoints requires that the backend services be known at deploy time (which these Cloud Run services won't be) and I want to provide the user with the id token so that they can use curl or some other tool to make the auth'd request.
Cloud Run has some compelling documentation for Authenticate (sic.) but I want something between:
Authenticating users -- I have the JWT but I want to receive a Google id token for the Cloud Run service
Authenticating service-to-service which Guillaume's alternative proposal in the answer.
Rather than place your Cloud Run behind Cloud Endpoints, where you have to know the Cloud Run instances ahead of time, you can handle the request and authentication inside the Cloud Run instance itself.
To be able to handle Firebase Authentication tokens inside the Cloud Run instance, they must be setup so that they can be invoked unauthenticated. Then, inside the Cloud Run, it should launch a web server, parse the incoming request (paying attention to the Authorization header - Firebase Auth sample) and then either action or terminate the request.
To achieve this, take a look at this thread for details on how you can handle both HTTP and service-service requests. Alternatively, you could just deploy the Functions Framework image from which that thread's code is based.
If you want cleaner URLs, host multiple endpoints within a single Cloud Run instance and then place that instance behind Cloud Endpoints or you can take a more manual approach via a custom domain using a service like Firebase Hosting.

GCP external application to app-engine endpoint authentication

We are building a small web-UI using React that will be served up by GCP App-Engine (standard). The UI will display a carousel of images along with some image metadata to our client's employees when they click on a link inside of their internal GIS system. We are looking to authenticate these calls since the App-Engine endpoint will be exposed publicly, and are hoping to use a GCP Service Account private key that will be used by the client to create a time-limited JSON web-token that will give temporary access to the GIS user when they open the web-UI. We are following this GCP documentation. In summary:
We create a new service-account with necessary IAM permissions in GCP along with a key
We share the private key with client which they then use to sign a Json Web Token which is passed in the call to our endpoint when user accesses our web-UI from their GIS system
Call is authenticated by GCP backend (ESP/OpenAPI)
Question: is this a recommended approach for external system accessing GCP resources or is there a better pattern more applicable to this type of situation (external system accessing GCP resource)?
I believe this is the recommended approach for your use case.
According to the official documentation:

How to authenticate google service account credentials

I have a google service account private key (json format) from google console.
How do I create a new client in golang (what google api do I use) and authenticate the credentials I got without setting environment variable?
I would like to provide the Google service account credentials manually in Golang.
I started by passing the json file as byte array:
creds, err := google.CredentialsFromJSON(ctx, blob). Blob is the byte array of the json file.
I can create a client successfully with cloud.google.com/go/secretmanager/apiv1 even after I changed the private key (ouch). So I wonder at what point and how do I authenticate the creds?
Thanks.
Question is unclear. I allow myself to rephrase it a "How do you create a valid credential from a service account's keyfile?"
Well, Google indeed implement a strategy to give an authenticated indentity to the calling process.
At a coarse grain, it indeed look for an environment variable definition called GOOGLE_APPLICATION_CREDENTIAL which value contains a path to a valid SA keyfile. Otherwise it uses the default service account from the piece of compute it runs from (GCE, GKE pod, Appengine, cloud function,...).
Finally you've got an error otherwise.
Working locally, a good practice to set this default credential is to use the cloud SDK with command gcloud auth application-default login. That way the application would act on the behalf of the logged person (you for instance and consume with your permissions and quotas).
Otherwise you could set up the env var manually to point to the service account's keyfile you downloaded.
Now if you run outside from a google cloud environment you can manually build credential unsing a keyfile like you do. The following example is explicit. What you must understand is that the credential you forge is an argument to any Google API client constructor.
Once the API client is built with your credential, you just consume it by calling the methods it exposes. Authentication happens under the hood. Every call to Google API is authenticated with an OAuth2 token which retrieving flow is described here.
You could dig into the source code client of the client API if you need to be convinced, but the nice thing is you don't have to.

Limiting Access to API Gateway (and AWS Lambda) in a package

We have a package that we share with out customers. In the package, we have a chunk of code that does HTTP Request callouts to our central API Gateway. As of now, our API Gateway is open and accepts requests from everywhere, which is not good. I want to limit access to our users who would be using our software. The only solution I have found is using IAM and providing authorization that would require us to include our Access Keys in the package. Our users can install our package in any environment they want and we have no control over that environment. So I think a viable option is to create a generic user policy with minimal access to allow our users to call our API Gateway. However, putting access key in the code doesn't seem like a good idea. Another option is to provider our customers with access keys but that also has overhead. What is a better alternative that is more secure and easy to maintain?
You can use built-in API Gateway API Key functionality when IAM policies aren't possible.
So long as your clients could be on any infrastructure, versus limited to AWS, the API Gateway service provides a generic API key solution, which allows you to restrict client traffic to your API Gateway by enforcing that client requests include API keys. This API key interface is part of their "API Usage Plan" feature.
This document explains how to use the console to set up an API Gateway to enforce that client traffic bears an API key:
To set up API keys, do the following:
Configure API methods to require an API key.
Create or import an API key for the API in a region.
Your clients can implement a "secret storage" solution, in order to avoid putting their API keys into their source code.
For sure it isn't wise for your clients to store their API Keys plain-text inside their source code. Instead, they could use a secret storage solution, to store the API keys outside of their codebase, but still give their applications access to the secret.
This article describes an example solution for secure secret storage (e.g. secure API key storage) which grants an application access to the application secret without putting the unencrypted secret into the source code. It uses Amazon KMS + Cryptex, but the same principle can be applied with other technologies: http://technologyadvice.github.io/lock-up-your-customer-accounts-give-away-the-key/