JAR error: Operating System Not Supported
gary-archer opened this issue · 5 comments
I am connecting a simple API to a Postgres database using SPIFFE mTLS. I have an end-to-end working solution with your excellent library, but I am having to use a complex workaround when including your library as a dependency. Maybe I am doing something wrong on the build and deployment side of things. I build my API as a fat JAR that includes the Java SPIFFE dependency.
EXAMPLE API
I have produced a minimal API repo with this gradle file:
plugins {
kotlin("jvm") version "1.9.22"
id("com.github.johnrengelman.shadow") version "8.1.1"
}
group = "com.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation("com.sparkjava:spark-core:2.9.4")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.slf4j:slf4j-simple:2.0.9")
runtimeOnly("org.postgresql:postgresql:42.7.2")
runtimeOnly("io.spiffe:java-spiffe-provider:0.8.5")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
tasks.shadowJar {
mergeServiceFiles()
manifest {
attributes["Main-Class"] = "com.example.productsapi.Main"
}
}
RUNTIME ERROR IN K8S
I am running a service mesh deployment in KIND, where SPIRE is wired up to Istio. I build and deploy my example API and it runs OK, yet when I make a REST call and the code first makes a JDBC connection I get this binary level error:
Feb 24, 2024 11:03:14 AM io.spiffe.provider.SpiffeSslSocketFactory <init>
INFO: Creating SpiffeSslSocketFactory
org.postgresql.util.PSQLException: The SSLSocketFactory class provided io.spiffe.provider.SpiffeSslSocketFactory could not be instantiated.
at org.postgresql.core.SocketFactoryFactory.getSslSocketFactory(SocketFactoryFactory.java:68)
at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:34)
at org.postgresql.core.v3.ConnectionFactoryImpl.enableSSL(ConnectionFactoryImpl.java:625)
at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:195)
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:262)
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54)
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:273)
at org.postgresql.Driver.makeConnection(Driver.java:446)
at org.postgresql.Driver.connect(Driver.java:298)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:683)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:253)
at com.example.productsapi.ProductsRepository.getConnection(ProductsRepository.kt:32)
at com.example.productsapi.ProductsRepository.loadProducts(ProductsRepository.kt:14)
at com.example.productsapi.Main$Companion.main$lambda$0(Main.kt:19)
at spark.RouteImpl$1.handle(RouteImpl.java:72)
at spark.http.matching.Routes.execute(Routes.java:61)
at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:134)
at spark.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:50)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1598)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.Server.handle(Server.java:516)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:74)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
at org.postgresql.util.ObjectFactory.instantiate(ObjectFactory.java:66)
at org.postgresql.core.SocketFactoryFactory.getSslSocketFactory(SocketFactoryFactory.java:64)
... 31 more
Caused by: java.lang.IllegalStateException: Operating System is not supported.
at io.spiffe.workloadapi.internal.GrpcManagedChannelFactory.configureNativeSocketChannel(GrpcManagedChannelFactory.java:79)
at io.spiffe.workloadapi.internal.GrpcManagedChannelFactory.createNativeSocketChannel(GrpcManagedChannelFactory.java:56)
at io.spiffe.workloadapi.internal.GrpcManagedChannelFactory.newChannel(GrpcManagedChannelFactory.java:41)
at io.spiffe.workloadapi.DefaultWorkloadApiClient.newClient(DefaultWorkloadApiClient.java:137)
at io.spiffe.workloadapi.DefaultX509Source.createClient(DefaultX509Source.java:180)
at io.spiffe.workloadapi.DefaultX509Source.newSource(DefaultX509Source.java:106)
at io.spiffe.workloadapi.DefaultX509Source.newSource(DefaultX509Source.java:80)
at io.spiffe.provider.SpiffeSslSocketFactory.<init>(SpiffeSslSocketFactory.java:61)
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
... 35 more
This seems to originate from your GrpcManagedChannelFactory class.
THINGS I'VE TRIED
I have tried including libraries differently:
implementation("io.spiffe:java-spiffe-core:0.8.5")
implementation("io.spiffe:java-spiffe-provider:0.8.5")
I am running the KIND cluster on an Ubuntu desktop computer. I have also tried a building my API in the same Debian docker image I am deploying with.
I have also verified that the Apache code behind SystemUtils.IS_OS_LINUX
works OK, by including that library in my API.
CURRENT WORKAROUND
My API and Postgres demo works fine end-to-end if I build your repo in a Docker build:
FROM azul/zulu-openjdk-debian:21-latest
RUN apt-get update && apt-get install -y git
WORKDIR /tmp
RUN git clone https://github.com/spiffe/java-spiffe
WORKDIR /tmp/java-spiffe
RUN git checkout v0.8.5
RUN ./gradlew assemble
Then build my own API with dependencies like this:
dependencies {
implementation("com.sparkjava:spark-core:2.9.4")
implementation("com.google.code.gson:gson:2.10.1")
implementation("org.slf4j:slf4j-simple:2.0.9")
runtimeOnly("org.postgresql:postgresql:42.7.2")
runtimeOnly(files("java-spiffe-provider-0.8.5-all-linux-x86_64.jar"))
But this makes my demo API feel more complicated of course. Is there possibly a binary issue with JAR files deployed to maven central? Or am I doing something wrong? I am no Java guru and I could be misunderstanding something basic. Eg should I be deploying your library to some kind of JDBC folder within my API container?
I was hoping you could take a quick look and give me a hint. Regards.
Hello @gary-archer,
Could it be that the fat JAR is being built on a different operating system than the one it's being run on? The java-spiffe-core
is designed with a build logic that automatically includes OS-specific dependencies. Here's how it works:
// The runtimeOnly grpc-netty dependency module is included in the shadowJar based on the OS.
if (osdetector.os.is('osx')) {
project.ext.osArch = System.getProperty("os.arch")
if ("x86_64".equals(project.ext.osArch)) {
runtimeOnly(project(':java-spiffe-core:grpc-netty-macos'))
} else if ("aarch64".equals(project.ext.osArch)) {
runtimeOnly(project(':java-spiffe-core:grpc-netty-macos-aarch64'))
} else {
throw new GradleException("Unsupported architecture: " + project.ext.osArch)
}
} else {
runtimeOnly(project(':java-spiffe-core:grpc-netty-linux'))
}
This logic ensures that the correct dependency is included based on the detected OS and architecture. If you're encountering issues, please make sure that the build and run environments are consistent regarding OS and architecture. I suggest building the fat JAR of you API within the Dockerfile itself.
Hi Max - thanks for the answer.
My current workaround does what you suggest and it all works fine. I am not blocked or anything.
I wonder if the files published to maven are osx specific. No all-linux-x86_64
files exist there.
Having said that, maybe I need to add this to my gradle file as it says in the README file. I will give that a go later on and report back. Ideally I'd like to use a pre-built tested image rather than building from source.
runtimeOnly group: 'io.spiffe', name: 'grpc-netty-linux', version: '0.8.5'
Hi Max - thanks for the answer.
My current workaround does what you suggest and it all works fine. I am not blocked or anything.
I wonder if the files published to maven are osx specific. No
all-linux-x86_64
files exist there.Having said that, maybe I need to add this to my gradle file as it says in the README file. I will give that a go later on and report back. Ideally I'd like to use a pre-built tested image rather than building from source.
runtimeOnly group: 'io.spiffe', name: 'grpc-netty-linux', version: '0.8.5'
Indeed, you caught an issue here, the published jars are OSX specific. This will be fixed in the next release that will be done in automated fashion using GH actions using an Ubuntu runner, after this PR is merged.
@gary-archer , 0.8.6
was published, please test with that version.
This is working fine now and my example is much simpler, so I am closing this issue. Thanks for the quick fix.