云 运行 - 通过服务帐户进行 gRPC 身份验证 - Java
Cloud Run - gRPC authentication through service account - Java
我已经部署到 Google 云 运行(完全托管)gRPC 服务器,选项 "Required Authentication" 设置为 true。
我正在尝试通过 Google 服务帐户对来自我的 gRPC 客户端的调用进行身份验证,但我总是遇到异常。
Exception in thread "main" io.grpc.StatusRuntimeException: UNAUTHENTICATED: HTTP status code 401
下面是我如何创建 gRPC 通道并附加服务帐户。
public GrpcClient(Channel channel) throws IOException {
Credentials credentials = GoogleCredentials.getApplicationDefault();
blockingStub = CalculatorServiceGrpc
.newBlockingStub(channel)
.withCallCredentials(MoreCallCredentials.from(credentials));
}
Obs.: env var GOOGLE_APPLICATION_CREDENTIALS 设置了 SA 的路径,并且 SA 具有 Cloud 运行 Invoker 权限
有什么我遗漏的吗?
从通用 HTTP 客户端调用云 运行 服务器时,设置 GOOGLE_APPLICATION_CREDENTIALS
无效。 (只有当您使用 Google 客户端库调用 Google 的 API 时才有效。)
即使部署到云 运行,gRPC 也只是 HTTP/2,因此在 Service-to-Service Authentication 页面上记录了云 运行 服务的身份验证。简而言之,这涉及:
- 从容器内的元数据服务端点获取 JWT(身份令牌)
- 根据对云 运行 应用程序的请求将其设置为 header,如
Authorization: Bearer [[ID_TOKEN]]
。
在 gRPC 中,header 被称为 "metadata",因此您应该找到等效的 gRPC Java 方法来设置它。 (这可能是一个 per-RPC 选项。)
阅读有关 Go example here 的文章,它基本上向您解释了云 运行 上的 gRPC 服务器 运行 仍然以相同的方式进行身份验证。在这种情况下,还要确保告诉 Java:
- 您需要连接到 domain:443(不是 :80)
- gRPC Java 需要使用机器根 CA 证书来验证云 运行 提供的 TLS 证书的有效性(而不是跳过 TLS 验证)
经过更多研究后,我能够使用 IdTokenCredentials 对请求进行身份验证。结果见下方。
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));
}
我在寻找与 Python 相关的答案时遇到了这个 post。
所以对于那些想使用 Python 客户端解决这个问题的人:
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)`
我已经部署到 Google 云 运行(完全托管)gRPC 服务器,选项 "Required Authentication" 设置为 true。
我正在尝试通过 Google 服务帐户对来自我的 gRPC 客户端的调用进行身份验证,但我总是遇到异常。
Exception in thread "main" io.grpc.StatusRuntimeException: UNAUTHENTICATED: HTTP status code 401
下面是我如何创建 gRPC 通道并附加服务帐户。
public GrpcClient(Channel channel) throws IOException {
Credentials credentials = GoogleCredentials.getApplicationDefault();
blockingStub = CalculatorServiceGrpc
.newBlockingStub(channel)
.withCallCredentials(MoreCallCredentials.from(credentials));
}
Obs.: env var GOOGLE_APPLICATION_CREDENTIALS 设置了 SA 的路径,并且 SA 具有 Cloud 运行 Invoker 权限
有什么我遗漏的吗?
从通用 HTTP 客户端调用云 运行 服务器时,设置 GOOGLE_APPLICATION_CREDENTIALS
无效。 (只有当您使用 Google 客户端库调用 Google 的 API 时才有效。)
即使部署到云 运行,gRPC 也只是 HTTP/2,因此在 Service-to-Service Authentication 页面上记录了云 运行 服务的身份验证。简而言之,这涉及:
- 从容器内的元数据服务端点获取 JWT(身份令牌)
- 根据对云 运行 应用程序的请求将其设置为 header,如
Authorization: Bearer [[ID_TOKEN]]
。
在 gRPC 中,header 被称为 "metadata",因此您应该找到等效的 gRPC Java 方法来设置它。 (这可能是一个 per-RPC 选项。)
阅读有关 Go example here 的文章,它基本上向您解释了云 运行 上的 gRPC 服务器 运行 仍然以相同的方式进行身份验证。在这种情况下,还要确保告诉 Java:
- 您需要连接到 domain:443(不是 :80)
- gRPC Java 需要使用机器根 CA 证书来验证云 运行 提供的 TLS 证书的有效性(而不是跳过 TLS 验证)
经过更多研究后,我能够使用 IdTokenCredentials 对请求进行身份验证。结果见下方。
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));
}
我在寻找与 Python 相关的答案时遇到了这个 post。 所以对于那些想使用 Python 客户端解决这个问题的人:
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)`