Search code examples
nettytcnative

Cannot load tcnative on Linux


We have spring boot based app on netty, and recently we've added this piece of code:

SslContextBuilder.forClient()
                .enableOcsp(true)
                .sslProvider(SslProvider.OPENSSL)

For this to work we have added this dependecy:

io.netty:netty-tcnative-boringssl-static:2.0.62.Final

When we want to run it on k8s we got error:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.netty.handler.ssl.SslContext]: Factory method 'sslContext' threw exception with message: failed to load the required native library
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:178) ~[spring-beans-6.1.1.jar:6.1.1]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-6.1.1.jar:6.1.1]
    ... 117 common frames omitted
Caused by: java.lang.UnsatisfiedLinkError: failed to load the required native library
    at io.netty.handler.ssl.OpenSsl.ensureAvailability(OpenSsl.java:616) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
    at io.netty.handler.ssl.SslContext.newClientContextInternal(SslContext.java:835) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
    at io.netty.handler.ssl.SslContextBuilder.build(SslContextBuilder.java:615) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]

with causes:

    Caused by: java.io.FileNotFoundException: META-INF/native/libnetty_tcnative_linux_aarch_64_fedora.so
        at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:186)
        ... 134 common frames omitted
        Suppressed: java.lang.UnsatisfiedLinkError: no netty_tcnative_linux_aarch_64_fedora in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
            at java.base/java.lang.ClassLoader.loadLibrary(Unknown Source)

and

Caused by: java.io.FileNotFoundException: META-INF/native/libnetty_tcnative_aarch_64.so
        at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:186)
        ... 134 common frames omitted
        Suppressed: java.lang.UnsatisfiedLinkError: no netty_tcnative_aarch_64 in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib

and

Caused by: java.io.FileNotFoundException: META-INF/native/libnetty_tcnative.so
        at io.netty.util.internal.NativeLibraryLoader.load(NativeLibraryLoader.java:186)
        ... 134 common frames omitted
        Suppressed: java.lang.UnsatisfiedLinkError: no netty_tcnative in java.library.path: /usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib

Problem seems to be in jar as there is no native library inside called:

META-INF/native/libnetty_tcnative_linux_aarch_64_fedora.so
or
META-INF/native/libnetty_tcnative_aarch_64.so
or
META-INF/native/libnetty_tcnative.so

Even if I downgrade to 2.0.47(meaning: before changes introduces in 2.0.48) the only linux-related native files are:

META-INF/native/libnetty_tcnative_linux_aarch_64.so
META-INF/native/libnetty_tcnative_linux_x86_64.so

But OpenSsl.loadTcNative() requires native libs to be called as in stacktraces above as there is this piece of code:

String staticLibName = "netty_tcnative";

// First, try loading the platform-specific library. Platform-specific
// libraries will be available if using a tcnative uber jar.
if ("linux".equals(os)) {
    Set<String> classifiers = PlatformDependent.normalizedLinuxClassifiers();
    for (String classifier : classifiers) {
        libNames.add(staticLibName + "_" + os + '_' + arch + "_" + classifier);
    }
    // generic arch-dependent library
    libNames.add(staticLibName + "_" + os + '_' + arch);

    // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0).
    // note: should already be included from the classifiers but if not, we use this as an
    //       additional fallback option here
    libNames.add(staticLibName + "_" + os + '_' + arch + "_fedora");
} else {
    libNames.add(staticLibName + "_" + os + '_' + arch);
}
libNames.add(staticLibName + "_" + arch);
libNames.add(staticLibName);

This code is many years old, so this is not introduced recently, and this makes me wondering how this could work for anyone...

I've made a try on my mac(thus different native library name than on linux) and do this trick with reflection:

            Method method = NativeLibraryLoader.class.getDeclaredMethod("getResource", String.class, ClassLoader.class);
            method.setAccessible(true);
            method.invoke(null,"META-INF/native/libnetty_tcnative_osx_aarch_64.jnilib",this.getClass().getClassLoader());

and lib is correctly loaded. But this will not make

SslContextBuilder.forClient()
                    ...
                  .enableOcsp(true)
                    .sslProvider(SslProvider.OPENSSL)
                    .build();

working correctly as wrong names are hardcoded in above mentioned OpenSsl.loadTcNative().

Is this a bug in netty-tcnative-boringssl?

I don't find any corporate-grade solution, renaming libs is no-go for us. Any ideas?


Solution

  • See this PR :https://github.com/apache/cassandra-sidecar/pull/63/files#diff-f70960e5e818dfd8a027e5dcf23a52289302487b8753989d77727b8bab99b01eL59

    // In gradle, we need to specify the classifier for netty-tcnative for every arch/os Combo.