Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
build,src,test,doc: enable FIPS for OpenSSL 3.0
This commit enables FIPS when Node.js is dynamically linking against
quictls/openssl-3.0.

BUILDING.md has been updated with instructions to configure and build
quictls/openssl 3.0.0-alpha-15 and includes a couple of work-arounds
which I believe are fixed in alpha-16 and can be removed when alpha-16
is available. The information might be a little too detailed/verbose
but I thought it would be helpful to at least initially include all the
steps.
  • Loading branch information
danbev committed May 12, 2021
commit d6649514bf83eefcb43a144a47203db39ab735a8
151 changes: 150 additions & 1 deletion BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,156 @@ as `deps/icu` (You'll have: `deps/icu/source/...`)

## Building Node.js with FIPS-compliant OpenSSL

The current version of Node.js does not support FIPS.
The current version of Node.js does not support FIPS when statically linking
(the default) with OpenSSL 1.1.1 but for dynamically linking it is possible
to enable FIPS using the configuration flag `--openssl-is-fips`.

### Configuring and Building quictls/openssl for FIPS
Comment thread
danbev marked this conversation as resolved.
Outdated

For quictls/openssl 3.0 it is possible to enable FIPS when dynamically linking.
Node.js currently uses openssl-3.0.0-alpha15+quic which can be configured as
follows:
```console
$ git clone git@github.com:quictls/openssl.git
$ cd openssl
$ ./config -Werror --strict-warnings --debug --prefix=/path/to/install/dir/ shared enable-fips linux-x86_64
Comment thread
danbev marked this conversation as resolved.
Outdated
```
This can be compiled using:
```console
$ make -j8
$ make install_ssldirs
```
Next set the `PATH` environment variable to point to the `openssl` executable
in the `apps` directory:
```console
$ export PATH=./apps:$PATH
```
Now that should be enough to run the target `install_fips` but there seems to
be an error with alpha-15 in regards to this target (this should be fixed in
alpha-16). This can be worked around by removing the last option of the openssl
fipsinstall command so that is looks like this:
<!--lint disable no-tabs-->
```console
install_fips: install_sw
@$(ECHO) "*** Installing FIPS module configuration"
@$(ECHO) "fipsinstall $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME).cnf"
@openssl fipsinstall -module $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME) \
-out $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME).cnf
```
<!--lint disable no-tabs-->
With those changes it should be possible to run the `install_fips` target:
```console
$ make install_fips
```

After the FIPS module and configuration file have been installed by the above
instructions we also need to update `/path/to/install/dir/ssl/openssl.cnf` to
use the generated FIPS configuration file (`fips.so.cnf`):
```text
.include /path/to/install/dir/lib/ossl-modules/fips.so.cnf
Comment thread
danbev marked this conversation as resolved.
Outdated

# List of providers to load
[provider_sect]
default = default_sect
# The fips section name should match the section name inside the
# included fips.so.cnf.
fips = fips_sect

[default_sect]
activate = 1
```

In the above case OpenSSL is not installed in the default location so two
environment variables need to be set, `OPENSSL_CONF`, and `OPENSSL_MODULES`
which should point to the OpenSSL configuration file and the directory where
OpenSSL modules are located:
```console
$ export OPENSSL_CONF=/path/to/install/dir/ssl/openssl.cnf
$ export OPENSSL_MODULES=/path/to/install/dir/lib/ossl-modules
```

Node.js can then be configured to enable FIPS:
```console
$ ./configure --shared-openssl --shared-openssl-libpath=/path/to/install/dir/lib --shared-openssl-includes=/path/to/install/dir/include --shared-openssl-libname=crypto,ssl --openssl-is-fips
$ export LD_LIBRARY_PATH=/path/to/install/dir/lib
$ make -j8
```

Verify the produced executable:
```console
$ ldd ./node
linux-vdso.so.1 (0x00007ffd7917b000)
libcrypto.so.81.3 => /path/to/install/dir/lib/libcrypto.so.81.3 (0x00007fd911321000)
libssl.so.81.3 => /path/to/install/dir/lib/libssl.so.81.3 (0x00007fd91125e000)
libdl.so.2 => /usr/lib64/libdl.so.2 (0x00007fd911232000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fd911039000)
libm.so.6 => /usr/lib64/libm.so.6 (0x00007fd910ef3000)
libgcc_s.so.1 => /usr/lib64/libgcc_s.so.1 (0x00007fd910ed9000)
libpthread.so.0 => /usr/lib64/libpthread.so.0 (0x00007fd910eb5000)
libc.so.6 => /usr/lib64/libc.so.6 (0x00007fd910cec000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd9117f2000)
```
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also run process.report.getReport() in Node.js and look at the sharedObjects section to see loaded libraries.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a note about using it. I think it might be useful to keep the ldd command just as a way to make sure the libraries can be found or node will note be able to run.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for keeping the ldd command. I'm pointing out an alternative... we could possibly also use the report in tests in the future as additional verification.

If the `ldd` command says that `libcrypto` cannot be found one needs to set
`LD_LIBRARY_PATH` to point to the directory used above for
`--shared-openssl-libpath` (see previous step).

Verify the OpenSSL version:
```console
$ ./node -p process.versions.openssl
3.0.0-alpha15+quic
```

Verify that FIPS is available:
```console
$ ./node -p 'process.config.variables.openssl_is_fips'
true
$ ./node --enable-fips -p 'crypto.getFips()'
1
```

FIPS support can then be enable via the OpenSSL configuration file or
using `--enable-fips` or `--force-fips` command line options to the Node.js
executable. See sections
[Enabling FIPS using Node.js options](#enabling-fips-using-node.js-options) and
[Enabling FIPS using OpenSSL config](#enabling-fips-using-openssl-config) below.

### Enabling FIPS using Node.js options
This is done using one of the Node.js options `--enable-fips` or
`--force-fips`, for example:
```console
$ node --enable-fips -p 'crypto.getFips()'
```

### Enabling FIPS using OpenSSL config
This example show that using OpenSSL's configuration file, FIPS can be enabled
without specifying the `--enable-fips` or `--force-fips` options by setting
`default_properties = fips=yes` in the FIPS configuration file. See
[link](https://github.com/openssl/openssl/blob/master/README-FIPS.md#loading-the-fips-module-at-the-same-time-as-other-providers)
for details.

For this to work the OpenSSL configuration file (default openssl.cnf) needs to
be updated. The following shows an example:
```console
openssl_conf = openssl_init

.include /path/to/install/dir/lib/ossl-modules/fips.so.cnf

[openssl_init]
providers = prov
alg_section = algorithm_sect

[prov]
fips = fips_sect
default = default_sect

[default_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes
```
After this change Node.js can be run without the `--enable-fips` or `--force-fips`
options.

## Building Node.js with external core modules

Expand Down
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
'v8_base': '<(PRODUCT_DIR)/obj.target/tools/v8_gypfiles/libv8_snapshot.a',
}],
['openssl_fips != ""', {
'openssl_product': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)',
'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)',
}, {
'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)',
}],
Expand Down
6 changes: 6 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,12 @@ def without_ssl_error(option):
if options.openssl_fips or options.openssl_fips == '':
error('FIPS is not supported in this version of Node.js')

if options.openssl_is_fips and not options.shared_openssl:
error('--openssl-is-fips is only available with --shared-openssl')

if options.openssl_is_fips:
o['defines'] += ['OPENSSL_FIPS']

if options.shared_openssl:
variables['openssl_quic'] = b(getsharedopensslhasquic.get_has_quic(options.__dict__['shared_openssl_includes']))

Expand Down
13 changes: 13 additions & 0 deletions src/crypto/crypto_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

#include "math.h"

#ifdef OPENSSL_FIPS
#include "openssl/provider.h"
#endif

namespace node {

using v8::ArrayBuffer;
Expand Down Expand Up @@ -197,7 +201,16 @@ void SetFipsCrypto(const FunctionCallbackInfo<Value>& args) {

void TestFipsCrypto(const v8::FunctionCallbackInfo<v8::Value>& args) {
#ifdef OPENSSL_FIPS
#if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER* fips_provider = nullptr;
if (OSSL_PROVIDER_available(nullptr, "fips")) {
fips_provider = OSSL_PROVIDER_load(nullptr, "fips");
}
const auto enabled = fips_provider == nullptr ? 0 :
OSSL_PROVIDER_self_test(fips_provider) ? 1 : 0;
#else
const auto enabled = FIPS_selftest() ? 1 : 0;
#endif
#else // OPENSSL_FIPS
const auto enabled = 0;
#endif // OPENSSL_FIPS
Expand Down
6 changes: 5 additions & 1 deletion test/parallel/test-crypto-fips.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ testHelper(
'require("crypto").getFips()',
{ ...process.env, 'OPENSSL_CONF': '' });

// This should succeed for both FIPS and non-FIPS builds in combination with
// OpenSSL 1.1.1 or OpenSSL 3.0
const test_result = testFipsCrypto();
assert.ok(test_result === 1 || test_result === 0);

// If Node was configured using --shared-openssl fips support might be
// available depending on how OpenSSL was built. If fips support is
Expand All @@ -79,7 +83,7 @@ testHelper(
// ("Error: Cannot set FIPS mode in a non-FIPS build.").
// Due to this uncertainty the following tests are skipped when configured
// with --shared-openssl.
if (!sharedOpenSSL()) {
if (!sharedOpenSSL() && !common.hasOpenSSL3) {
// OpenSSL config file should be able to turn on FIPS mode
testHelper(
'stdout',
Expand Down