nodejs / help

:sparkles: Need help with Node.js? File an Issue here. :rocket:
1.48k stars 281 forks source link

How to build native addon against their own version of OpenSSL - Ignoring Node.js shipped OpenSSL #1724

Closed JCMais closed 2 years ago

JCMais commented 5 years ago

I've opened a similar issue on node-gyp (https://github.com/nodejs/node-gyp/issues/1559), and there are more information there, but since there was no solution posted, I'm also opening another issue here to have more chances of a definitive solution / guide.

Currently building a native addon with a version of OpenSSL independent of the one shipped with Node.js is very hard, at least to me with basic knowledge on linking / libraries in C++ and / or using gyp files.

Looks like the shipped symbols always take precedence, no matter what I do, which causes all kinds of weird errors and segmentation faults.

There are many other issues about this topic, most are closed without a real solution, and the following wiki page is outdated: https://github.com/nodejs/node-gyp/wiki/Linking-to-OpenSSL

hsgreen commented 5 years ago

This is directly related to this issue: https://github.com/nodejs/node/issues/24964

JCMais commented 5 years ago

Bumping this, I still have not found a correct solution to that issue, and I'm still looking into one

JCMais commented 5 years ago

This is still an issue. Just found another problem:

Let's imagine that an addon dependency (shared library, in my case it's libcurl/libssh) was built with OpenSSL 1.1.x (static linked in this case), when running the addon, that dependency will receive the OpenSSL version Node.js exports, instead of the one it was built with.

And so by building this addon against Node.js v8, which is built with OpenSSL 1.0.x, it will end in a segmentation fault.

gireeshpunathil commented 5 years ago

@JCMais - trying to understand the issue better: is the requirement to be able to build a native addon that links against system supplied openssl, while node itself was built with resident openssl?

JCMais commented 5 years ago

hey @gireeshpunathil, basically yes.

The requirement is to build a native addon using their own version of OpenSSL, which can differ from the one used by Node.js itself.

My use case was building an addon that depends on libcurl, which in turn depends on OpenSSL.

gireeshpunathil commented 5 years ago

thanks @JCMais for the clarification.

I guess it is not going to be feasible? As any standard SSL implementations should export the public interfaces, the executing code (node app + native addon) at run time face symbol resolution conflicts between the two, and depend on the prevalent linker flags etc. this conflict is going to manifest in different manner.

if your requirement is to build a native addon using their own version of OpenSSL without strict requirement on the one used by Node.js itself, then you can build Node.js from source with --shared-openssl , --shared-openssl-includes, --shared-openssl-libname and --shared-openssl-libpath passed to the configure where the custom library is the same that will be used by the native addon I guess.

pinging @sam-github to see if my understanding is correct.

gireeshpunathil commented 5 years ago

most OS's provide runtime linking option that helps overriding symbols that can be used as a hack here, but its reliability is questionable - the app that has started with the in-built routines and assuming a specific program state, will be switched to another set of routines when the addon is loaded - can cause unpredictable results IMO.

sam-github commented 5 years ago

@JCMais I don't know if I can help you here.

An addon is dynamically loaded into node.js. In a sense, it becomes PART of the node.js executable. It is not easy for executables written in C/C++ to depend on two different versions of a single library. This isn't node specific, and may be flat out impossible, which may be why you aren't finding any solution.

That said, I believe that this may be possible if your addon:

  1. Doesn't use anything from node.js's OpenSSL (you may need to be very careful that you don't accidentally compile against node's openssl headers)
  2. Statically links your openssl into the addon. This might possibly result in an addon that doesn't need any dynamic resolution, so doesn't get any symbols resolved to node's openssl.

But full disclosure, I haven't tried, so YMMV.

If we can step back a bit, I might be able to help if you described why you are trying to do this. What is it you are trying to do that led to this attempt to use two versions of OpenSSL simultaneously? Gireesh's suggestion to build a custom node so your addon and node share the same openssl seems very possible, though still begs the question "what is it you are trying to do".

hsgreen commented 5 years ago

@sam-github Take a read on the linked issue https://github.com/nodejs/node/issues/24964 It answers your questions as to why what you suggest won't work.

TLDR; Even if you statically link openssl into your add-on, when the add-on is loaded into Node if there are symbols that are the same between openssl versions and happen to be in the Node process, they get used over what was linked in the add-on. It gets even worse when your add-on relies on 3rd party libraries that use openssl (see discussion of curl).

The prebuilt node distributions basically have created this mess by statically linking to a version of openssl that is not standard in the target OS for the prebuilt distributions.

sam-github commented 5 years ago

Got it. Moving conversation to #24964

hadronized commented 4 years ago

Hi,

I’m facing a very very similar issue, but on Linux with HMAC code. @hsgreen have you found a way to prevent that node overriding? I’m statically linking a C++ library to some OpenSSL 1.0.1e that gets loaded by the node app.

peschue commented 4 years ago

I solved a similar issue by building node.js manually from source with

./configure --shared-openssl --shared-openssl-includes=/usr/include/ --shared-openssl-libpath=/usr/lib/x86_64-linux-gnu/

I guess if you also do this against the same version that your C++ code uses, it will work.

JCMais commented 4 years ago

I solved a similar issue by building node.js manually from source with

./configure --shared-openssl --shared-openssl-includes=/usr/include/ --shared-openssl-libpath=/usr/lib/x86_64-linux-gnu/

I guess if you also do this against the same version that your C++ code uses, it will work.

This works but if you are building an Node.js addon to be installed via NPM it's not feasible.


At the end of the day what worked was to always use the exact OpenSSL version used on Node.js while building the addon. From what I could see this still creates duplicate symbols on the final binary, but atleast there is no ABI conflict.

As I'm building prebuilt binaries for the addon, the binaries must use the same OpenSSL version than the one used by the Node.js version they are targetting. If the user is installing the package building from source they must have a libcurl built with the same OpenSSL version than the one used by their current Node.js version, I made this clear in the README.md of the project, but this is not that great since most people do not read that before installing a package.

PoojaDurgad commented 4 years ago

@phaazon , were you able to follow @peschue' s suugestion? is it still an pending?

celesteking commented 4 years ago

This is a serious issue and it just DOESNT work. There are at least 2 libraries where it's present.

  1. grpc
  2. node-webcrypto-ossl

Try an EL7 with rh-nodejs12 from SCL, you'll see what happens. SCL team did a great job of not going nuts and actually doing the sane thing -- which is linking against shared OS supplied openssl lib. Why would you bundle your own version of openssl guys? What happens if there's a secvuln in this bundled lib, and I guess your willingness to go that far means there are tons of these libs, who's gonna cover it? There are lots of examples when such software isn't updated (e.g., 3 years from now for node 12, you/SCL will stop updating it) while OS is still on extended maintenance -- and would get sec fixes, including openssl sec patches.

You should've gotten someone from "big 3" on your team, centos, ubuntu, oracle, suse, those guys, who really know their shit, not some amateur students.

celesteking commented 4 years ago

Alright, circling back on this. 1) has been resolved by repo authors and 2) seems to have some kind of configuration problem.

Nevertheless, my complaint is still valid. The proper and ultimate fix is an easy one --- just don't supply any openssl code when nodejs has been compiled with shared openssl. This is how I worked around problem in 2): rm -rf ~/.cache/node-gyp/12.18.2/include/node/openssl in the middle of compilation, then restarting the compile again. This wouldn't have been needed if node-gyp hadn't been pulling openssl stuff for some unknown reason.