1
votes

I've deployed to Google Cloud Run (fully managed) a gRPC server with the option "Required Authentication" set to true.

I'm trying to authenticate the calls from my gRPC client through a Google Service Account, however I'm always getting below exception.

Exception in thread "main" io.grpc.StatusRuntimeException: UNAUTHENTICATED: HTTP status code 401

Below is how I'm creating the gRPC channel and attaching the service account.

public GrpcClient(Channel channel) throws IOException {
    Credentials credentials = GoogleCredentials.getApplicationDefault();

    blockingStub = CalculatorServiceGrpc
            .newBlockingStub(channel)
            .withCallCredentials(MoreCallCredentials.from(credentials));
}

Obs.: env var GOOGLE_APPLICATION_CREDENTIALS is set with the path of the SA, and the SA has Cloud Run Invoker privilege

Is there anything that I'm missing?

3

3 Answers

2
votes

When calling a Cloud Run server from a generic HTTP client, setting GOOGLE_APPLICATION_CREDENTIALS doesn't have an effect. (That only works when you call Google’s APIs with a Google client library.)

Even when deployed to Cloud Run, gRPC is just HTTP/2, so authenticating to a Cloud Run service is documented at the Service-to-Service Authentication page. In a nutshell this involves:

  • getting a JWT (identity token) from metadata service endpoint inside the container
  • setting it as a header on the request to the Cloud Run app, as Authorization: Bearer [[ID_TOKEN]].

In gRPC, headers are called "metadata", so you should find the equivalent gRPC Java method to set that. (It probably is a per-RPC option.)

Read about a Go example here, it basically explains you that gRPC servers running on Cloud Run still authenticate the same way. In this case, also make sure to tell Java:

  • you need to connect to domain:443 (not :80)
  • gRPC Java needs to use machine root CA certificates to verify validity of TLS certificate presented by Cloud Run (as opposed to skipping TLS verification)
1
votes

After some more research I was able to authenticate the requests using IdTokenCredentials. See below the result.

public GrpcClient(Channel channel) throws IOException {
    ServiceAccountCredentials saCreds = ServiceAccountCredentials
            .fromStream(new FileInputStream("path\to\sa"));

    IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder().setIdTokenProvider(saCreds)
            .setTargetAudience("https://{projectId}-{hash}-uc.a.run.app").build();

    blockingStub = CalculatorServiceGrpc
            .newBlockingStub(channel)
            .withCallCredentials(MoreCallCredentials.from(tokenCredential));
}
0
votes

I came across this post looking for a Python-related answer. So for those who want to solve this problem using a Python client:

import google.oauth2
import google.oauth2.id_token
import google.auth
import google.auth.transport
import google.auth.transport.requests

TARGET_CHANNEL = "your-app-name.run.app:443"

token = google.oauth2.id_token.fetch_id_token(google.auth.transport.requests.Request(), TARGET_CHANNEL)
call_cred = grpc.access_token_call_credentials(auth.get_identification_token(
        "https://" + TARGET_CHANNEL.strip(':443')))
channel_cred = grpc.composite_channel_credentials(grpc.ssl_channel_credentials(), call_cred)
channel = grpc.secure_channel(TARGET_CHANNEL, credentials=channel_cred)`