nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
108.04k stars 29.82k forks source link

FIPS 140-2 compliancy using existing certified libraries #37072

Open D4V3M0NK opened 3 years ago

D4V3M0NK commented 3 years ago

Is your feature request related to a problem? Please describe. A number of applications that could be FIPS 140-2 compliant simply can't be because the last version of NodeJS that supports being built with certified OpenSSL libraries was v8.x (using the 1.02g libraries). Although I can't find it, I had read a couple of years ago that there was strong consideration of NodeJS v13 being able to be either certified or compliant, but all of the searching that I've done hasn't come up with any mention of that (or any later version) having that capability.

As stated, I'm already using NodeJS v8 in order to run applications that require NodeJS, and so far that has worked out remarkably well considering the age of v8: even newer changes to the upper stack of applications appear to run just fine on this long in the tooth version. However, of course v8 has a number of CVEs that have been addressed in latter versions: but those latter versions can't be compiled using the OpenSSL 1.02g libraries. Added to this is the fact that 1.02g doesn't support TLS1.2+ and with modern browsers no longer supporting TLS1.0/1.1 from March 2021 ... you can see the predicament.

Describe the solution you'd like I'm not a programmer first and foremost, but I've assisted other projects in the same sort of vein, and that is: if I'm running an OS that has gone through FIPS 140-2 certification of the OpenSSL libraries, those other projects have altered their build scripts to be able to compile by linking to the existing libraries already on the system, thereby making their project FIPS compliant. To maintain certification, the OpenSSL libraries can't be re-compiled when on a host, but a program's build script that can be adjusted to compile using those libraries, can be classed as compliant. I'm sure you're aware, the distinction between certified and compliant is significant: the former one has to pony up the finances and go through months worth of work, testing and validation; the latter is free and only (?!) requires the work of altering the build script of the project in question.

Describe alternatives you've considered Are there alternatives to NodeJS? ;) Seriously though, NodeJS has such a solid base and it's reliability is (IMHO) unchallenged, there really isn't another alternative.

For my upper stack of applications I could simply use a newer version of NodeJS, but then they wouldn't be FIPS compliant and that restricts me considerably. I spent months (literally) working through all the versions of NodeJS until I got to the version that could be compiled with the OpenSSL libraries making NodeJS compliant, but AFAIK v8.x was the last version that had this capability. I would love to be told that there's a latter version that can also be used, but I don't believe that to be the case.

FWIW, I'm currently using Ubuntu 16 with the 1.02g OpenSSL libraries but due to the security issues (CVEs etc) I have to look for an alternative. I would love to use Ubuntu 18 and the set of OpenSSL v1.1.1 libraries that Canonical have certified under their Advantage program...

I appreciate the time spent reading through all of this - and keenly look forward to (hopefully good?) news regarding this.

Thank you.

tniessen commented 3 years ago

It is possible to dynamically link against OpenSSL as long as it is a compatible OpenSSL version. We currently use OpenSSL 1.1.1 only, which does not have FIPS certification. The next OpenSSL build with FIPS certification will likely be 3.x, so it is going to be a while longer. I don't think it's realistic to link current Node.js versions against OpenSSL older than 1.1.1.

D4V3M0NK commented 3 years ago

@tniessen thanks for the feedback, especially your comments that OpenSSL 1.1.1 can be used - and that's exactly the version that I'm looking for. My question is: how do you build with that FIPS support, considering the documentation states that it's not an option?

richardlau commented 3 years ago

@tniessen thanks for the feedback, especially your comments that OpenSSL 1.1.1 can be used - and that's exactly the version that I'm looking for. My question is: how do you build with that FIPS support, considering the documentation states that it's not an option?

https://github.com/nodejs/build/issues/2176 has the steps to build for UBI 8.1.

richardlau commented 3 years ago

There's also https://github.com/nodejs/node/pull/36341 which aims to rework some of this.

D4V3M0NK commented 3 years ago

Finally managed to carve out some time to test this. I'm moving forward cautiously as I don't want to mess anything up, but I guess I managed to fall at the first hurdle, attempting this on Ubuntu 18.04LTS:

$ lsb_release -d && uname -r && cat /proc/sys/crypto/fips_enabled && openssl version
Description:    Ubuntu 18.04.5 LTS
4.15.0-1052-fips
1
OpenSSL 1.1.1  11 Sep 2018

$ sudo apt install -y build-essential libssl-dev curl git-core clang

$ git clone https://github.com/nodejs/node.git

$ cd node

$ export OPENSSL_ENABLE_MD5_VERIFY=true

$ ./configure --shared-openssl --shared-openssl-includes=/usr/include/openssl --openssl-is-fips
Traceback (most recent call last):
  File "./configure", line 21, in <module>
    from distutils.spawn import find_executable
ModuleNotFoundError: No module named 'distutils.spawn'

Am I trying this too early? From https://github.com/nodejs/build/issues/2176 it discusses the use of a UBI, but that's appears to be using RedHat which I don't have access to.

Can anyone provide some insight for me please?

richardlau commented 3 years ago

See https://github.com/nodejs/node/issues/30189 for the ModuleNotFoundError: No module named 'distutils.spawn' error.

UBI is a container (for use in e.g. docker, podman) and doesn't require a RHEL subscription to use.

D4V3M0NK commented 3 years ago

@richardlau thank you for the pointer (and so quickly) ... I'm makeing at the moment...

D4V3M0NK commented 3 years ago

So I've tried this with three branches of node (latest, v14.x and v12.x) and I'm failing on what appears to be the https://github.com/nodejs/node/issues/35213 issue. @mscdex states that the version of OpenSSL to be used has to be the same, or newer, than the version bundled with node, stating that v14.x uses v1.1.1. I do have that version (1.1.1-1ubuntu2.fips.2.1~18.04.7.1) and yet I'm still getting caught. At the very least, v12.x should work shouldn't it?

Latest / v14.x snippet ``` g++ -o /home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto_clienthello.o ../src/node_crypto_clienthello.cc '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D__STDC_FORMAT_MACROS' '-DNODE_ARCH="x64"' '-DNODE_PLATFORM="linux"' '-DNODE_WANT_INTERNALS=1' '-DV8_DEPRECATION_WARNINGS=1' '-DNODE_OPENSSL_SYSTEM_CERT_PATH=""' '-DHAVE_INSPECTOR=1' '-DNODE_ENABLE_LARGE_CODE_PAGES=1' '-D__POSIX__' '-DNODE_USE_V8_PLATFORM=1' '-DNODE_HAVE_I18N_SUPPORT=1' '-DHAVE_OPENSSL=1' '-DNODE_FIPS_MODE' '-DUCONFIG_NO_SERVICE=1' '-DU_ENABLE_DYLOAD=0' '-DU_STATIC_IMPLEMENTATION=1' '-DU_HAVE_STD_STRING=1' '-DUCONFIG_NO_BREAK_ITERATION=0' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_POSIX_C_SOURCE=200112' '-DNGHTTP2_STATICLIB' -I/usr/include/openssl -I../src -I/home/vroot/buildNode/node/out/Release/obj/gen -I/home/vroot/buildNode/node/out/Release/obj/gen/include -I/home/vroot/buildNode/node/out/Release/obj/gen/src -I../deps/histogram/src -I../deps/uvwasi/include -I../deps/v8/include -I../deps/icu-small/source/i18n -I../deps/icu-small/source/common -I../deps/zlib -I../deps/llhttp/include -I../deps/cares/include -I../deps/uv/include -I../deps/nghttp2/lib/includes -I../deps/brotli/c/include -Wall -Wextra -Wno-unused-parameter -pthread -Wall -Wextra -Wno-unused-parameter -m64 -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++1y -MMD -MF /home/vroot/buildNode/node/out/Release/.deps//home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto_clienthello.o.d.raw -c In file included from /usr/include/openssl/x509.h:25:0, from /usr/include/openssl/ssl.h:20, from ../src/node_crypto.h:39, from ../src/node_crypto.cc:22: ../src/node_crypto.cc: In member function ‘virtual bool node::crypto::RSAPSSKeyPairGenerationConfig::Configure(const EVPKeyCtxPointer&)’: ../src/node_crypto.cc:6102:11: error: ‘EVP_PKEY_OP_TYPE_KEYGEN’ was not declared in this scope if (EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), md_) <= 0) ^ ../src/node_crypto.cc:6102:11: note: suggested alternative: ‘EVP_PKEY_OP_TYPE_NOGEN’ ../src/node_crypto.cc: In function ‘void node::crypto::InitCryptoOnce()’: ../src/node_crypto.cc:6858:5: error: ‘OPENSSL_INIT_set_config_filename’ was not declared in this scope OPENSSL_INIT_set_config_filename(settings, conf); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../src/node_crypto.cc:6858:5: note: suggested alternative: ‘OPENSSL_INIT_set_config_appname’ OPENSSL_INIT_set_config_filename(settings, conf); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OPENSSL_INIT_set_config_appname g++ -o /home/vroot/buildNode/node/out/Release/obj.target/libnode/src/tls_wrap.o ../src/tls_wrap.cc '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D__STDC_FORMAT_MACROS' '-DNODE_ARCH="x64"' '-DNODE_PLATFORM="linux"' '-DNODE_WANT_INTERNALS=1' '-DV8_DEPRECATION_WARNINGS=1' '-DNODE_OPENSSL_SYSTEM_CERT_PATH=""' '-DHAVE_INSPECTOR=1' '-DNODE_ENABLE_LARGE_CODE_PAGES=1' '-D__POSIX__' '-DNODE_USE_V8_PLATFORM=1' '-DNODE_HAVE_I18N_SUPPORT=1' '-DHAVE_OPENSSL=1' '-DNODE_FIPS_MODE' '-DUCONFIG_NO_SERVICE=1' '-DU_ENABLE_DYLOAD=0' '-DU_STATIC_IMPLEMENTATION=1' '-DU_HAVE_STD_STRING=1' '-DUCONFIG_NO_BREAK_ITERATION=0' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_POSIX_C_SOURCE=200112' '-DNGHTTP2_STATICLIB' -I/usr/include/openssl -I../src -I/home/vroot/buildNode/node/out/Release/obj/gen -I/home/vroot/buildNode/node/out/Release/obj/gen/include -I/home/vroot/buildNode/node/out/Release/obj/gen/src -I../deps/histogram/src -I../deps/uvwasi/include -I../deps/v8/include -I../deps/icu-small/source/i18n -I../deps/icu-small/source/common -I../deps/zlib -I../deps/llhttp/include -I../deps/cares/include -I../deps/uv/include -I../deps/nghttp2/lib/includes -I../deps/brotli/c/include -Wall -Wextra -Wno-unused-parameter -pthread -Wall -Wextra -Wno-unused-parameter -m64 -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++1y -MMD -MF /home/vroot/buildNode/node/out/Release/.deps//home/vroot/buildNode/node/out/Release/obj.target/libnode/src/tls_wrap.o.d.raw -c libnode.target.mk:343: recipe for target '/home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto.o' failed make[2]: *** [/home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto.o] Error 1 make[2]: *** Waiting for unfinished jobs.... rm e014ff45c4b1291c4b8161570c459e918f066695.intermediate 90cf649cf91580905fe985c1558b332a3148873a.intermediate Makefile:104: recipe for target 'node' failed make[1]: *** [node] Error 2 Makefile:1127: recipe for target 'node-v14.15.6-linux-x64.tar' failed make: *** [node-v14.15.6-linux-x64.tar] Error 2 ```
The v12.x build has an additional warning at the end... ``` g++ -o /home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto_clienthello.o ../src/node_crypto_clienthello.cc '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D__STDC_FORMAT_MACROS' '-DNODE_ARCH="x64"' '-DNODE_PLATFORM="linux"' '-DNODE_WANT_INTERNALS=1' '-DV8_DEPRECATION_WARNINGS=1' '-DNODE_OPENSSL_SYSTEM_CERT_PATH=""' '-DHAVE_INSPECTOR=1' '-DNODE_ENABLE_LARGE_CODE_PAGES=1' '-D__POSIX__' '-DNODE_USE_V8_PLATFORM=1' '-DNODE_HAVE_I18N_SUPPORT=1' '-DNODE_HAVE_SMALL_ICU=1' '-DHAVE_OPENSSL=1' '-DNODE_FIPS_MODE' '-DUCONFIG_NO_SERVICE=1' '-DU_ENABLE_DYLOAD=0' '-DU_STATIC_IMPLEMENTATION=1' '-DU_HAVE_STD_STRING=1' '-DUCONFIG_NO_BREAK_ITERATION=0' '-DHTTP_PARSER_STRICT=0' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_POSIX_C_SOURCE=200112' '-DNGHTTP2_STATICLIB' -I/usr/include/openssl -I../src -I/home/vroot/buildNode/node/out/Release/obj/gen -I/home/vroot/buildNode/node/out/Release/obj/gen/include -I/home/vroot/buildNode/node/out/Release/obj/gen/src -I../deps/histogram/src -I../deps/uvwasi/include -I../deps/v8/include -I../deps/icu-small/source/i18n -I../deps/icu-small/source/common -I../deps/zlib -I../deps/http_parser -I../deps/llhttp/include -I../deps/cares/include -I../deps/uv/include -I../deps/nghttp2/lib/includes -I../deps/brotli/c/include -Wall -Wextra -Wno-unused-parameter -pthread -Wall -Wextra -Wno-unused-parameter -m64 -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++1y -MMD -MF /home/vroot/buildNode/node/out/Release/.deps//home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto_clienthello.o.d.raw -c In file included from /usr/include/openssl/x509.h:25:0, from /usr/include/openssl/ssl.h:20, from ../src/node_crypto.h:38, from ../src/node_crypto.cc:22: ../src/node_crypto.cc: In member function ‘virtual bool node::crypto::RSAPSSKeyPairGenerationConfig::Configure(const EVPKeyCtxPointer&)’: ../src/node_crypto.cc:6128:11: error: ‘EVP_PKEY_OP_TYPE_KEYGEN’ was not declared in this scope if (EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), md_) <= 0) ^ ../src/node_crypto.cc:6128:11: note: suggested alternative: ‘EVP_PKEY_OP_TYPE_NOGEN’ ../src/node_crypto.cc: In function ‘void node::crypto::InitCryptoOnce()’: ../src/node_crypto.cc:6884:5: error: ‘OPENSSL_INIT_set_config_filename’ was not declared in this scope OPENSSL_INIT_set_config_filename(settings, conf); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../src/node_crypto.cc:6884:5: note: suggested alternative: ‘OPENSSL_INIT_set_config_appname’ OPENSSL_INIT_set_config_filename(settings, conf); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OPENSSL_INIT_set_config_appname g++ -o /home/vroot/buildNode/node/out/Release/obj.target/libnode/src/tls_wrap.o ../src/tls_wrap.cc '-DV8_DEPRECATION_WARNINGS' '-DV8_IMMINENT_DEPRECATION_WARNINGS' '-D__STDC_FORMAT_MACROS' '-DNODE_ARCH="x64"' '-DNODE_PLATFORM="linux"' '-DNODE_WANT_INTERNALS=1' '-DV8_DEPRECATION_WARNINGS=1' '-DNODE_OPENSSL_SYSTEM_CERT_PATH=""' '-DHAVE_INSPECTOR=1' '-DNODE_ENABLE_LARGE_CODE_PAGES=1' '-D__POSIX__' '-DNODE_USE_V8_PLATFORM=1' '-DNODE_HAVE_I18N_SUPPORT=1' '-DNODE_HAVE_SMALL_ICU=1' '-DHAVE_OPENSSL=1' '-DNODE_FIPS_MODE' '-DUCONFIG_NO_SERVICE=1' '-DU_ENABLE_DYLOAD=0' '-DU_STATIC_IMPLEMENTATION=1' '-DU_HAVE_STD_STRING=1' '-DUCONFIG_NO_BREAK_ITERATION=0' '-DHTTP_PARSER_STRICT=0' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_POSIX_C_SOURCE=200112' '-DNGHTTP2_STATICLIB' -I/usr/include/openssl -I../src -I/home/vroot/buildNode/node/out/Release/obj/gen -I/home/vroot/buildNode/node/out/Release/obj/gen/include -I/home/vroot/buildNode/node/out/Release/obj/gen/src -I../deps/histogram/src -I../deps/uvwasi/include -I../deps/v8/include -I../deps/icu-small/source/i18n -I../deps/icu-small/source/common -I../deps/zlib -I../deps/http_parser -I../deps/llhttp/include -I../deps/cares/include -I../deps/uv/include -I../deps/nghttp2/lib/includes -I../deps/brotli/c/include -Wall -Wextra -Wno-unused-parameter -pthread -Wall -Wextra -Wno-unused-parameter -m64 -O3 -fno-omit-frame-pointer -fno-rtti -fno-exceptions -std=gnu++1y -MMD -MF /home/vroot/buildNode/node/out/Release/.deps//home/vroot/buildNode/node/out/Release/obj.target/libnode/src/tls_wrap.o.d.raw -c libnode.target.mk:348: recipe for target '/home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto.o' failed make[2]: *** [/home/vroot/buildNode/node/out/Release/obj.target/libnode/src/node_crypto.o] Error 1 make[2]: *** Waiting for unfinished jobs.... ../src/tls_wrap.cc: In static member function ‘static unsigned int node::TLSWrap::PskClientCallback(SSL*, const char*, char*, unsigned int, unsigned char*, unsigned int)’: ../src/tls_wrap.cc:1192:70: warning: ‘static v8::Local v8::String::NewFromUtf8(v8::Isolate*, const char*, v8::String::NewStringType, int)’ is deprecated: Use maybe version [-Wdeprecated-declarations] MaybeLocal maybe_hint = String::NewFromUtf8(isolate, hint); ^ In file included from ../deps/v8/include/v8-internal.h:14:0, from ../deps/v8/include/v8.h:27, from ../src/util.h:31, from ../src/util-inl.h:29, from ../src/aliased_buffer.h:7, from ../src/env.h:27, from ../src/node_crypto.h:30, from ../src/tls_wrap.h:27, from ../src/tls_wrap.cc:22: ../deps/v8/include/v8.h:3032:21: note: declared here Local NewFromUtf8(Isolate* isolate, const char* data, ^ ../deps/v8/include/v8config.h:328:3: note: in definition of macro ‘V8_DEPRECATED’ declarator __attribute__((deprecated(message))) ^~~~~~~~~~ rm ea69077d368c9695b17786cc0274f9a324f9afc1.intermediate e014ff45c4b1291c4b8161570c459e918f066695.intermediate e214d22584df5b3dfc4c1b7b232e8eb7d00c500a.intermediate 90cf649cf91580905fe985c1558b332a3148873a.intermediate Makefile:104: recipe for target 'node' failed make[1]: *** [node] Error 2 Makefile:1132: recipe for target 'node-v12.20.3-linux-x64.tar' failed make: *** [node-v12.20.3-linux-x64.tar] Error 2 ```

I have this all scripted and working using the shared (certified) library in Ubuntu 16.04 / OpenSSL 1.0.2g but I also had to amend node_version.h's NODE_VERSION_IS_RELEASE from 0 to 1 which I've tested with latest, v14.x and v12.x but it still doesn't get me any further.

My existing script ``` sudo apt install -y build-essential libssl-dev curl git-core clang python3-distutils snap version && make -v && gcc -v && g++ -v && clang -v && python -c 'import sys; print(sys.version_info[:])' && python3 -c 'import sys; print(sys.version_info[:])' #2.48.3+18.04, 4.1, 7.5.0, 7.5.0, 6.0.0, (2, 7, 17, 'final', 0), (3, 6, 9, 'final', 0) git clone https://github.com/nodejs/node.git #git clone -b v12.x https://github.com/nodejs/node.git # this pulls branch: v12.x cd node export OPENSSL_ENABLE_MD5_VERIFY=true ./configure --shared-openssl --shared-openssl-includes=/usr/include/openssl --openssl-is-fips sed -i 's/#define NODE_VERSION_IS_RELEASE 0/#define NODE_VERSION_IS_RELEASE 1/' ./src/node_version.h git config user.email myEmail@address.com git commit -m "Amended node header to release for binary build" ./src/node_version.h CONFIG_FLAGS="--shared-openssl --shared-openssl-includes=/usr/include/openssl --openssl-is-fips" make -j4 binary ```

I feel as if I'm incredibly close, but just can't get over this last hurdle?

D4V3M0NK commented 3 years ago

As a follow up to this, I was posed a question this evening and it sparked a separate thought.

If I have a system that has a FIPS compliant OpenSSL module installed upon it and running in FIPS mode, then install NodeJS (which obviously comes with it's own OpenSSL libraries), NodeJS wouldn't be FIPS compliant. However, now that everything is installed on the system, is there any way of altering the running config of NodeJS to use the FIPS module available, rather than those it came with?

I've always assumed that one had to build NodeJS linking to that external library, but you know what they say about "assume" ... :)

mhdawson commented 2 years ago

I know this is long after your question, but just came across this issue.

You can't alter the community Node.js binaries to use the dynamic link library, as you mention you have to rebuild.

Not sure what system you are running on but if it is RHEL, then the RHEL Node.js rpms that you can install already do exactly what you were suggesting -> link against the shared openssl on the RHEL system whichi is FIPs certified.

mhdawson commented 2 years ago

And having said that I believe it may be changing with OpenSSL 3 in 18.x. In that case it may be possible to configured FIPs providers with the existing binary. @richardlau may have more specific info.

Alankarsharma commented 2 years ago

Can we enable FIPS in NodeJS 18 using crypto.setFips, without building NodeJS?

elliot-huffman commented 1 year ago

Can we enable FIPS in NodeJS 18 using crypto.setFips, without building NodeJS?

It will get enabled but throws errors for me. I believe that you still have to build from source, but the cool part is that it is FIPS validated due to OpenSSL 3, just you have to build your own runtime bins :-/

richardlau commented 1 year ago

I've opened a pull request to update the Node.js documentation for enabling FIPS support with OpenSSL 3: https://github.com/nodejs/node/pull/48194

The short answer is that you do not need to rebuild Node.js 18 or later from source, you can use the binaries from nodejs.org. You will however need the OpenSSL 3 FIPS provider, which you will have to obtain separately.