Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,15 @@ added: v4.0.0
Specify an alternative default TLS cipher list. Requires Node.js to be built
with crypto support (default).

### `--tls-keylog=file`
<!-- YAML
added: REPLACEME
-->

Log TLS key material to a file. The key material is in NSS `SSLKEYLOGFILE`
format and can be used by software (such as Wireshark) to decrypt the TLS
traffic.

### `--tls-max-v1.2`
<!-- YAML
added: v12.0.0
Expand Down Expand Up @@ -1073,6 +1082,7 @@ Node.js options that are allowed are:
* `--throw-deprecation`
* `--title`
* `--tls-cipher-list`
* `--tls-keylog`
* `--tls-max-v1.2`
* `--tls-max-v1.3`
* `--tls-min-v1.0`
Expand Down
5 changes: 5 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ Specify process.title on startup.
Specify an alternative default TLS cipher list.
Requires Node.js to be built with crypto support. (Default)
.
.It Fl -tls-keylog Ns = Ns Ar file
Log TLS key material to a file. The key material is in NSS SSLKEYLOGFILE
format and can be used by software (such as Wireshark) to decrypt the TLS
traffic.
.
.It Fl -tls-max-v1.2
Set default maxVersion to 'TLSv1.2'. Use to disable support for TLSv1.3.
.
Expand Down
22 changes: 22 additions & 0 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const {
const { getOptionValue } = require('internal/options');
const { validateString } = require('internal/validators');
const traceTls = getOptionValue('--trace-tls');
const tlsKeylog = getOptionValue('--tls-keylog');
const { appendFile } = require('fs');
const kConnectOptions = Symbol('connect-options');
const kDisableRenegotiation = Symbol('disable-renegotiation');
const kErrorEmitted = Symbol('error-emitted');
Expand Down Expand Up @@ -560,6 +562,8 @@ TLSSocket.prototype._destroySSL = function _destroySSL() {
};

// Constructor guts, arbitrarily factored out.
let warnOnTlsKeylog = true;
let warnOnTlsKeylogError = true;
TLSSocket.prototype._init = function(socket, wrap) {
const options = this._tlsOptions;
const ssl = this._handle;
Expand Down Expand Up @@ -643,6 +647,24 @@ TLSSocket.prototype._init = function(socket, wrap) {
}
}

if (tlsKeylog) {
if (warnOnTlsKeylog) {
warnOnTlsKeylog = false;
process.emitWarning('Using --tls-keylog makes TLS connections insecure ' +
'by writing secret key material to file ' + tlsKeylog);
ssl.enableKeylogCallback();
this.on('keylog', (line) => {
appendFile(tlsKeylog, line, { mode: 0o600 }, (err) => {
if (err && warnOnTlsKeylogError) {
warnOnTlsKeylogError = false;
process.emitWarning('Failed to write TLS keylog (this warning ' +
'will not be repeated): ' + err);
}
});
});
}
}

ssl.onerror = onerror;

// If custom SNICallback was given, or if
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {

AddOption("--napi-modules", "", NoOp{}, kAllowedInEnvironment);

AddOption("--tls-keylog",
"log TLS decryption keys to named file for traffic analysis",
&EnvironmentOptions::tls_keylog, kAllowedInEnvironment);

AddOption("--tls-min-v1.0",
"set default TLS minimum to TLSv1.0 (default: TLSv1.2)",
&EnvironmentOptions::tls_min_v1_0,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class EnvironmentOptions : public Options {
bool tls_min_v1_3 = false;
bool tls_max_v1_2 = false;
bool tls_max_v1_3 = false;
std::string tls_keylog;

std::vector<std::string> preload_modules;

Expand Down
57 changes: 57 additions & 0 deletions test/parallel/test-tls-enable-keylog-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
const fixtures = require('../common/fixtures');

// Test --tls-keylog CLI flag.

const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { fork } = require('child_process');

if (process.argv[2] === 'test')
return test();

const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const file = path.resolve(tmpdir.path, 'keylog.log');

const child = fork(__filename, ['test'], {
execArgv: ['--tls-keylog=' + file]
});

child.on('close', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
const log = fs.readFileSync(file, 'utf8');
assert(/SECRET/.test(log));
}));

function test() {
const {
connect, keys
} = require(fixtures.path('tls-connect'));

connect({
client: {
checkServerIdentity: (servername, cert) => { },
ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
},
server: {
cert: keys.agent6.cert,
key: keys.agent6.key
},
}, common.mustCall((err, pair, cleanup) => {
if (pair.server.err) {
console.trace('server', pair.server.err);
}
if (pair.client.err) {
console.trace('client', pair.client.err);
}
assert.ifError(pair.server.err);
assert.ifError(pair.client.err);

return cleanup();
}));
}