src: suggest --use-system-ca when a certificate error occurs

PR-URL: https://github.com/nodejs/node/pull/57362
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
Aditi 2025-03-12 18:51:22 +05:30 committed by GitHub
parent 59f00d7132
commit d615a3cfcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 34 additions and 17 deletions

View File

@ -547,6 +547,12 @@ description are taken from deps/openssl/openssl/crypto/x509/x509_txt.c
* `'CERT_REJECTED'`: Certificate rejected.
* `'HOSTNAME_MISMATCH'`: Hostname mismatch.
When certificate errors like `UNABLE_TO_VERIFY_LEAF_SIGNATURE`,
`DEPTH_ZERO_SELF_SIGNED_CERT`, or `UNABLE_TO_GET_ISSUER_CERT` occur, Node.js
appends a hint suggesting that if the root CA is installed locally,
try running with the `--use-system-ca` flag to direct developers towards a
secure solution, to prevent unsafe workarounds.
## Class: `tls.CryptoStream`
<!-- YAML

View File

@ -53,8 +53,20 @@ SSLSessionPointer GetTLSSession(const unsigned char* buf, size_t length) {
}
MaybeLocal<Value> GetValidationErrorReason(Environment* env, int err) {
auto reason = X509Pointer::ErrorReason(err).value_or("");
auto reason = std::string(X509Pointer::ErrorReason(err).value_or(""));
if (reason == "") return Undefined(env->isolate());
// Suggest --use-system-ca if the error indicates a certificate issue
bool suggest_system_ca =
(err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) ||
(err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||
((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) &&
!per_process::cli_options->use_system_ca);
if (suggest_system_ca) {
reason.append("; if the root CA is installed locally, "
"try running Node.js with --use-system-ca");
}
return OneByteString(env->isolate(), reason);
}

View File

@ -17,7 +17,7 @@ const options = {
const expectedHeader = /^HTTP\/1\.1 200 OK/;
const expectedBody = /hello world\n/;
const expectCertError = /^Error: unable to verify the first certificate$/;
const expectCertError = /^UNABLE_TO_VERIFY_LEAF_SIGNATURE$/;
const checkRequest = (socket, server) => {
let result = '';
@ -112,7 +112,7 @@ function createServer() {
const options = null;
const socket = agent.createConnection(port, host, options);
socket.on('error', common.mustCall((e) => {
assert.match(e.toString(), expectCertError);
assert.match(e.code, expectCertError);
server.close();
}));
}));
@ -127,7 +127,7 @@ function createServer() {
const options = undefined;
const socket = agent.createConnection(port, host, options);
socket.on('error', common.mustCall((e) => {
assert.match(e.toString(), expectCertError);
assert.match(e.code, expectCertError);
server.close();
}));
}));

View File

@ -34,7 +34,7 @@ connect({
server: serverOptions,
}, common.mustCall((err, pair, cleanup) => {
assert(err);
assert.strictEqual(err.message, 'unable to verify the first certificate');
assert.strictEqual(err.code, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
cleanup();
// This time it should connect because contextWithCert includes the needed CA

View File

@ -40,6 +40,5 @@ tls.createServer({ key, cert }).on('connection', common.mustCall(function() {
const options = { port: this.address().port, rejectUnauthorized: true };
tls.connect(options).on('error', common.mustCall(function(err) {
assert.strictEqual(err.code, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
assert.strictEqual(err.message, 'unable to verify the first certificate');
}));
}));

View File

@ -13,7 +13,6 @@ const assert = require('assert');
const events = require('events');
const https = require('https');
const timers = require('timers/promises');
const { hasOpenSSL3 } = require('../common/crypto');
const fixtures = require('../common/fixtures');
const credentialOptions = [
{
@ -57,17 +56,18 @@ server.listen(0, common.mustCall(() => {
server.setSecureContext(credentialOptions[1]);
firstResponse.write('request-');
const errorMessageRegex = hasOpenSSL3 ?
/^Error: self-signed certificate$/ :
/^Error: self signed certificate$/;
await assert.rejects(makeRequest(port, 3), errorMessageRegex);
await assert.rejects(makeRequest(port, 3), {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
});
server.setSecureContext(credentialOptions[0]);
assert.strictEqual(await makeRequest(port, 4), 'success');
server.setSecureContext(credentialOptions[1]);
firstResponse.end('fun!');
await assert.rejects(makeRequest(port, 5), errorMessageRegex);
await assert.rejects(makeRequest(port, 5), {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT',
});
assert.strictEqual(await firstRequest, 'multi-request-success-fun!');
server.close();

View File

@ -10,11 +10,11 @@ const {
} = require(fixtures.path('tls-connect'));
test(undefined, (err) => {
assert.strictEqual(err.message, 'unable to verify the first certificate');
assert.strictEqual(err.code, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
});
test({}, (err) => {
assert.strictEqual(err.message, 'unable to verify the first certificate');
assert.strictEqual(err.code, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
});
test(
@ -30,8 +30,8 @@ test(
test(
{ secureContext: tls.createSecureContext(), ca: keys.agent1.ca },
(err) => {
assert.strictEqual(err.message,
'unable to verify the first certificate');
assert.strictEqual(err.code,
'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
});
function test(client, callback) {
@ -42,7 +42,7 @@ function test(client, callback) {
cert: keys.agent1.cert,
},
}, function(err, pair, cleanup) {
assert.strictEqual(err.message, 'unable to verify the first certificate');
assert.strictEqual(err.code, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
let recv = '';
pair.server.server.once('secureConnection', common.mustCall((conn) => {
conn.on('data', (data) => recv += data);