Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Roberts <vieuxtech@gmail.com>2019-10-22 06:44:20 +0300
committerMyles Borins <mylesborins@google.com>2019-11-21 08:29:29 +0300
commit01fa18c99cd076bb29b6f3bf3085319dcb4e4d95 (patch)
treea5bbe78ab480ca4afc6fdad3c8450289fc29072f
parentf15a3b02816513b7a0b32a39b99cd0138b699f99 (diff)
tls: cli option to enable TLS key logging to file
Debugging HTTPS or TLS connections from a Node.js app with (for example) Wireshark is unreasonably difficult without the ability to get the TLS key log. In theory, the application can be modified to use the `'keylog'` event directly, but for complex apps, or apps that define there own HTTPS Agent (like npm), this is unreasonably difficult. Use of the option triggers a warning to be emitted so the user is clearly notified of what is happening and its effect. PR-URL: https://github.com/nodejs/node/pull/30055 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
-rw-r--r--doc/api/cli.md10
-rw-r--r--doc/node.15
-rw-r--r--lib/_tls_wrap.js22
-rw-r--r--src/node_options.cc4
-rw-r--r--src/node_options.h1
-rw-r--r--test/parallel/test-tls-enable-keylog-cli.js57
6 files changed, 99 insertions, 0 deletions
diff --git a/doc/api/cli.md b/doc/api/cli.md
index e88b16b1ead..26513946536 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -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
@@ -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`
diff --git a/doc/node.1 b/doc/node.1
index 4b653e702d6..e3628034e83 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -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.
.
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index 778afa73286..69fc0547589 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -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');
@@ -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;
@@ -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
diff --git a/src/node_options.cc b/src/node_options.cc
index 85256a7e0a8..0bc6730156c 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -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,
diff --git a/src/node_options.h b/src/node_options.h
index 30a976f48d5..ce0cee5fe56 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -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;
diff --git a/test/parallel/test-tls-enable-keylog-cli.js b/test/parallel/test-tls-enable-keylog-cli.js
new file mode 100644
index 00000000000..5d05069b15f
--- /dev/null
+++ b/test/parallel/test-tls-enable-keylog-cli.js
@@ -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();
+ }));
+}