diff --git a/doc/api/cli.md b/doc/api/cli.md index 2052f010f4b..32f197ee5b1 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -4262,8 +4262,15 @@ added: v6.11.0 Load an OpenSSL configuration file on startup. The file can be used as part of a [FIPS mode][] configuration. +If the variable is set to an empty value, Node.js starts without loading any +OpenSSL configuration file. This is a way past a default configuration file +that exists but cannot be read, for example when `/etc/ssl` is not accessible +to the user Node.js runs as, which is otherwise fatal at startup. No +configuration is applied in that case, including any [FIPS mode][] setup the +file would have performed. + If the [`--openssl-config`][] command-line option is used, the environment -variable is ignored. +variable is ignored, and an empty value has no effect. ### `SSL_CERT_DIR=dir` diff --git a/doc/node.1 b/doc/node.1 index 1098bcff047..e4932cf76de 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -2375,8 +2375,14 @@ environment variable is arbitrary. .It Ev OPENSSL_CONF Ar file Load an OpenSSL configuration file on startup. The file can be used as part of a FIPS mode configuration. +If the variable is set to an empty value, Node.js starts without loading any +OpenSSL configuration file. This is a way past a default configuration file +that exists but cannot be read, for example when \fB/etc/ssl\fR is not accessible +to the user Node.js runs as, which is otherwise fatal at startup. No +configuration is applied in that case, including any FIPS mode setup the +file would have performed. If the \fB--openssl-config\fR command-line option is used, the environment -variable is ignored. +variable is ignored, and an empty value has no effect. . .It Ev SSL_CERT_DIR Ar dir If \fB--use-openssl-ca\fR is enabled, or if \fB--use-system-ca\fR is enabled on diff --git a/test/parallel/test-openssl-unreadable-config.js b/test/parallel/test-openssl-unreadable-config.js new file mode 100644 index 00000000000..99681e0ecba --- /dev/null +++ b/test/parallel/test-openssl-unreadable-config.js @@ -0,0 +1,40 @@ +'use strict'; + +// A default OpenSSL configuration file that cannot be read is fatal, and an +// empty OPENSSL_CONF is the documented way past it. +// Refs: https://github.com/nodejs/node/issues/62230 + +const common = require('../common'); +const assert = require('node:assert'); +const { spawnSync } = require('node:child_process'); + +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.isLinux) + common.skip('linux only'); +if (process.config.variables.node_shared_openssl) + common.skip('shared openssl may read a different configuration file'); + +// Replace /etc/ssl with an empty tmpfs in a private mount namespace, where +// openssl.cnf is a symlink loop: opening it then fails with ELOOP instead of +// ENOENT, which OpenSSL ignores on its own. The namespace goes away with the +// process, so the host /etc/ssl is left alone. +const setup = 'mount -t tmpfs tmpfs /etc/ssl && ln -s openssl.cnf /etc/ssl/openssl.cnf'; + +if (spawnSync('unshare', ['-Urm', 'sh', '-c', setup]).status !== 0) + common.skip('cannot set up an unprivileged user and mount namespace'); + +function run(env) { + return spawnSync( + 'unshare', + ['-Urm', 'sh', '-c', `${setup} && exec "$0" -p 42`, process.execPath], + { encoding: 'utf8', env: { ...process.env, ...env } }); +} + +const failed = run({}); +assert.notStrictEqual(failed.status, 0); +assert.match(failed.stderr, /OpenSSL configuration error/); + +const skipped = run({ OPENSSL_CONF: '' }); +assert.strictEqual(skipped.status, 0); +assert.strictEqual(skipped.stdout.trim(), '42');