Skip to content

Commit

Permalink
src: suggest --use-system-ca when a certificate error occurs
Browse files Browse the repository at this point in the history
  • Loading branch information
Aditi-1400 committed Mar 7, 2025
1 parent a790901 commit e9b2b8f
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 17 deletions.
6 changes: 6 additions & 0 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ description are taken from deps/openssl/openssl/crypto/x509/x509_txt.c
* `'CERT_REJECTED'`: Certificate rejected.
* `'HOSTNAME_MISMATCH'`: Hostname mismatch.

Note: 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
Expand Down
14 changes: 13 additions & 1 deletion src/crypto/crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions test/parallel/test-https-agent-create-connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand Down Expand Up @@ -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();
}));
}));
Expand All @@ -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();
}));
}));
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-tls-addca.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion test/parallel/test-tls-friendly-error-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}));
}));
14 changes: 8 additions & 6 deletions test/parallel/test-tls-set-secure-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -57,17 +56,20 @@ 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), (err) => {
assert.strictEqual(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT');
return true;
});

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, 3), (err) => {
assert.strictEqual(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT');
return true;
});

assert.strictEqual(await firstRequest, 'multi-request-success-fun!');
server.close();
Expand Down
10 changes: 5 additions & 5 deletions test/parallel/test-tls-socket-default-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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) {
Expand All @@ -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);
Expand Down

0 comments on commit e9b2b8f

Please sign in to comment.