Skip to content

Commit ca76b39

Browse files
authored
cli: add NODE_USE_SYSTEM_CA=1
Similar to how NODE_USE_ENV_PROXY complements --use-env-proxy, this complements --use-system-ca. This will allow the setting to be applied to workers individually in the future. PR-URL: #59276 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ethan Arrowood <[email protected]> Reviewed-By: Darshan Sen <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 013190d commit ca76b39

File tree

5 files changed

+113
-0
lines changed

5 files changed

+113
-0
lines changed

doc/api/cli.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3696,6 +3696,18 @@ specified proxy.
36963696
This can also be enabled using the [`--use-env-proxy`][] command-line flag.
36973697
When both are set, `--use-env-proxy` takes precedence.
36983698

3699+
### `NODE_USE_SYSTEM_CA=1`
3700+
3701+
<!-- YAML
3702+
added: REPLACEME
3703+
-->
3704+
3705+
Node.js uses the trusted CA certificates present in the system store along with
3706+
the `--use-bundled-ca` option and the `NODE_EXTRA_CA_CERTS` environment variable.
3707+
3708+
This can also be enabled using the [`--use-system-ca`][] command-line flag.
3709+
When both are set, `--use-system-ca` takes precedence.
3710+
36993711
### `NODE_V8_COVERAGE=dir`
37003712

37013713
When set, Node.js will begin outputting [V8 JavaScript code coverage][] and
@@ -4025,6 +4037,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
40254037
[`--redirect-warnings`]: #--redirect-warningsfile
40264038
[`--require`]: #-r---require-module
40274039
[`--use-env-proxy`]: #--use-env-proxy
4040+
[`--use-system-ca`]: #--use-system-ca
40284041
[`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage
40294042
[`Buffer`]: buffer.md#class-buffer
40304043
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html

doc/node.1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,12 @@ This currently only affects requests sent over
844844
.Ar fetch() .
845845
Support for other built-in http and https methods is under way.
846846
.
847+
.It Ev NODE_USE_SYSTEM_CA
848+
Similar to
849+
.Fl -use-system-ca .
850+
Use the trusted CA certificates present in the system store, in addition to the certificates in the
851+
bundled Mozilla CA store and certificates from `NODE_EXTRA_CA_CERTS`.
852+
.
847853
.It Ev NODE_V8_COVERAGE Ar dir
848854
When set, Node.js writes JavaScript code coverage information to
849855
.Ar dir .

src/node.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,15 @@ static ExitCode InitializeNodeWithArgsInternal(
868868
// default value.
869869
V8::SetFlagsFromString("--rehash-snapshot");
870870

871+
#if HAVE_OPENSSL
872+
// TODO(joyeecheung): make this a per-env option and move the normalization
873+
// into HandleEnvOptions.
874+
std::string use_system_ca;
875+
if (credentials::SafeGetenv("NODE_USE_SYSTEM_CA", &use_system_ca) &&
876+
use_system_ca == "1") {
877+
per_process::cli_options->use_system_ca = true;
878+
}
879+
#endif // HAVE_OPENSSL
871880
HandleEnvOptions(per_process::cli_options->per_isolate->per_env);
872881

873882
std::string node_options;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
// This tests that NODE_USE_SYSTEM_CA environment variable works the same
3+
// as --use-system-ca flag by comparing certificate counts.
4+
5+
const common = require('../common');
6+
if (!common.hasCrypto) common.skip('missing crypto');
7+
8+
const tls = require('tls');
9+
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
10+
11+
const systemCerts = tls.getCACertificates('system');
12+
if (systemCerts.length === 0) {
13+
common.skip('no system certificates available');
14+
}
15+
16+
const { child: { stdout: expectedLength } } = spawnSyncAndExitWithoutError(process.execPath, [
17+
'--use-system-ca',
18+
'-p',
19+
`tls.getCACertificates('default').length`,
20+
], {
21+
env: { ...process.env, NODE_USE_SYSTEM_CA: '0' },
22+
});
23+
24+
spawnSyncAndExitWithoutError(process.execPath, [
25+
'-p',
26+
`assert.strictEqual(tls.getCACertificates('default').length, ${expectedLength.toString()})`,
27+
], {
28+
env: { ...process.env, NODE_USE_SYSTEM_CA: '1' },
29+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Env: NODE_USE_SYSTEM_CA=1
2+
// Same as test-native-root-certs.mjs, just testing the environment variable instead of the flag.
3+
4+
import * as common from '../common/index.mjs';
5+
import assert from 'node:assert/strict';
6+
import https from 'node:https';
7+
import fixtures from '../common/fixtures.js';
8+
import { it, beforeEach, afterEach, describe } from 'node:test';
9+
import { once } from 'events';
10+
11+
if (!common.hasCrypto) {
12+
common.skip('requires crypto');
13+
}
14+
15+
// To run this test, the system needs to be configured to trust
16+
// the CA certificate first (which needs an interactive GUI approval, e.g. TouchID):
17+
// see the README.md in this folder for instructions on how to do this.
18+
const handleRequest = (req, res) => {
19+
const path = req.url;
20+
switch (path) {
21+
case '/hello-world':
22+
res.writeHead(200);
23+
res.end('hello world\n');
24+
break;
25+
default:
26+
assert(false, `Unexpected path: ${path}`);
27+
}
28+
};
29+
30+
describe('use-system-ca', function() {
31+
32+
async function setupServer(key, cert) {
33+
const theServer = https.createServer({
34+
key: fixtures.readKey(key),
35+
cert: fixtures.readKey(cert),
36+
}, handleRequest);
37+
theServer.listen(0);
38+
await once(theServer, 'listening');
39+
40+
return theServer;
41+
}
42+
43+
let server;
44+
45+
beforeEach(async function() {
46+
server = await setupServer('agent8-key.pem', 'agent8-cert.pem');
47+
});
48+
49+
it('trusts a valid root certificate', async function() {
50+
await fetch(`https://localhost:${server.address().port}/hello-world`);
51+
});
52+
53+
afterEach(async function() {
54+
server?.close();
55+
});
56+
});

0 commit comments

Comments
 (0)