Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tls: cli option to enable TLS key logging to file #30055

Closed
wants to merge 1 commit into from
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();
}));
}