"Invalid JWT: Failed audience check" 在 GraalVM 本机图像中使用 Google Api 服务时

"Invalid JWT: Failed audience check" when using Google Api Services in GraalVM native-image

我有一个简单的应用程序,它通过基于 HTTP google-api-services-logging 向 Google 云日志发送消息。我最初使用的是 gRPC cloud-logging 库,但根本无法让它与 GraalVM 一起工作。但不幸的是,我也在为 HTTP 变体而苦苦挣扎。该代码在传统 Java VM 上执行时运行良好,但在 运行 本机映像时在运行时失败。

java.io.IOException: Error getting access token for service account: 400 Bad Request
POST https://oauth2.googleapis.com/token
{"error":"invalid_grant","error_description":"Invalid JWT: Failed audience check."}
    at com.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:444)
    at com.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:157)
    at com.google.auth.oauth2.OAuth2Credentials.getRequestMetadata(OAuth2Credentials.java:145)
    at com.google.auth.oauth2.ServiceAccountCredentials.getRequestMetadata(ServiceAccountCredentials.java:603)
    at com.google.auth.http.HttpCredentialsAdapter.initialize(HttpCredentialsAdapter.java:91)
    at com.google.api.client.http.HttpRequestFactory.buildRequest(HttpRequestFactory.java:88)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.buildHttpRequest(AbstractGoogleClientRequest.java:422)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:541)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:474)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:591)
    ...
Caused by: com.google.api.client.http.HttpResponseException: 400 Bad Request
POST https://oauth2.googleapis.com/token
{"error":"invalid_grant","error_description":"Invalid JWT: Failed audience check."}
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1113)
    at com.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:441)
    ... 35 more

原生-image.properties

Args = \
  --verbose \
  --no-server \
  --no-fallback \
  --static \
  --install-exit-handlers \
  -H:+ReportExceptionStackTraces \
  -H:+TraceClassInitialization \
  -H:+PrintClassInitialization \
  -H:UseMuslC=/musl/ \
  -H:+RemoveSaturatedTypeFlows \
  --enable-https \
  --enable-http \
  --initialize-at-build-time

反射-config.json

[
  {
    "name": "com.google.api.client.json.GenericJson",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.services.logging.v2.model.LogEntry",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.googleapis.GoogleUtils",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  }
]

资源-config.json

{
  "resources": [
    { "pattern": "^.*\.json$" },
    { "pattern": "^.*\.properties$" },
    { "pattern": "^.*\.jks$" }
  ]
}

app.scala

val scopes = util.Arrays.asList(LoggingScopes.CLOUD_PLATFORM_READ_ONLY, LoggingScopes.LOGGING_WRITE)
val credentials  = ServiceAccountCredentials.fromStream("service-account.json").createScoped(scopes)
val logging = new Logging.Builder(
  transport,
  JacksonFactory.getDefaultInstance,
  new HttpCredentialsAdapter(credentials)
).setApplicationName("my-project").build()

Dockerfile

FROM        oracle/graalvm-ce:20.1.0-java11 as builder
...
RUN         gu install native-image
...
RUN         sbt assembly
RUN         native-image -jar /root/target/scala-2.13/graal-test-assembly-0.1.0-SNAPSHOT.jar

FROM        scratch

WORKDIR     /app/

COPY        --from=builder /root/graal-test-assembly-0.1.0-SNAPSHOT /app/my-native-image

CMD         ["/app/my-native-image"]

我怀疑这与加密/SSL 相关的功能有关,但我 运行 无法尝试。

原来生成的JWT token基本是空的,因为序列化到JSON时,字段是通过反射遍历的。将相应的规则添加到 reflect-config.json 解决了该问题并揭示了可以通过配置解决的更多问题。

[
  {
    "name": "com.google.api.client.json.GenericJson",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.json.webtoken.JsonWebToken",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.json.webtoken.JsonWebToken$Header",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.json.webtoken.JsonWebToken$Payload",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.util.GenericData",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.http.UrlEncodedContent",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.json.webtoken.JsonWebSignature$Header",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.json.webtoken.JsonWebSignature",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.services.logging.v2.model.WriteLogEntriesRequest",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.services.logging.v2.model.WriteLogEntriesResponse",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.services.logging.v2.model.LogEntry",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.client.googleapis.json.GoogleJsonError",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  },
  {
    "name": "com.google.api.services.logging.v2.model.MonitoredResource",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  }
]

Google 的 Java 库中反射的滥用是相当痛苦的。他们至少可以做的是将 GraalVM 本机图像配置添加到他们的库中。

您可以找到完整的 reflect-config.json for google cloud logging http here.