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: add --trace-tls command-line flag #27497

Merged
merged 3 commits into from
May 2, 2019
Merged
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
9 changes: 9 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,14 @@ added: v2.1.0
Prints a stack trace whenever synchronous I/O is detected after the first turn
of the event loop.

### `--trace-tls`
<!-- YAML
added: REPLACEME
-->

Prints TLS packet trace information to `stderr`. This can be used to debug TLS
connection problems.

### `--trace-warnings`
<!-- YAML
added: v6.0.0
Expand Down Expand Up @@ -889,6 +897,7 @@ Node.js options that are allowed are:
- `--trace-event-file-pattern`
- `--trace-events-enabled`
- `--trace-sync-io`
- `--trace-tls`
- `--trace-warnings`
- `--track-heap-objects`
- `--unhandled-rejections`
Expand Down
9 changes: 9 additions & 0 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,9 @@ connection is open.
<!-- YAML
added: v0.11.4
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/27497
description: The `enableTrace` option is now supported.
- version: v5.0.0
pr-url: https://github.com/nodejs/node/pull/2564
description: ALPN options are supported now.
Expand All @@ -596,6 +599,7 @@ changes:
instance of [`net.Socket`][] (for generic `Duplex` stream support
on the client side, [`tls.connect()`][] must be used).
* `options` {Object}
* `enableTrace`: See [`tls.createServer()`][]
* `isServer`: The SSL/TLS protocol is asymmetrical, TLSSockets must know if
they are to behave as a server or a client. If `true` the TLS socket will be
instantiated as a server. **Default:** `false`.
Expand Down Expand Up @@ -1125,6 +1129,9 @@ being issued by trusted CA (`options.ca`).
<!-- YAML
added: v0.11.3
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/27497
description: The `enableTrace` option is now supported.
- version: v11.8.0
pr-url: https://github.com/nodejs/node/pull/25517
description: The `timeout` option is supported now.
Expand All @@ -1144,6 +1151,7 @@ changes:
-->

* `options` {Object}
* `enableTrace`: See [`tls.createServer()`][]
* `host` {string} Host the client should connect to. **Default:**
`'localhost'`.
* `port` {number} Port the client should connect to.
Expand Down Expand Up @@ -1647,6 +1655,7 @@ changes:
* `rejectUnauthorized` {boolean} If not `false` a server automatically reject
clients with invalid certificates. Only applies when `isServer` is `true`.
* `options`
* `enableTrace`: See [`tls.createServer()`][]
* `secureContext`: A TLS context object from [`tls.createSecureContext()`][]
* `isServer`: If `true` the TLS socket will be instantiated in server-mode.
**Default:** `false`.
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ Enable the collection of trace event tracing information.
.It Fl -trace-sync-io
Print a stack trace whenever synchronous I/O is detected after the first turn of the event loop.
.
.It Fl -trace-tls
Prints TLS packet trace information to stderr.
.
.It Fl -trace-warnings
Print stack traces for process warnings (including deprecations).
.
Expand Down
37 changes: 25 additions & 12 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ const {
ERR_TLS_SESSION_ATTACK,
ERR_TLS_SNI_FROM_SERVER
} = require('internal/errors').codes;
const { getOptionValue } = require('internal/options');
const { validateString } = require('internal/validators');
const traceTls = getOptionValue('--trace-tls');
const kConnectOptions = Symbol('connect-options');
const kDisableRenegotiation = Symbol('disable-renegotiation');
const kErrorEmitted = Symbol('error-emitted');
Expand All @@ -68,6 +70,7 @@ const kEnableTrace = Symbol('enableTrace');
const noop = () => {};

let ipServernameWarned = false;
let tlsTracingWarned = false;

// Server side times how long a handshake is taking to protect against slow
// handshakes being used for DoS.
Expand Down Expand Up @@ -343,6 +346,20 @@ function initRead(tlsSocket, socket) {

function TLSSocket(socket, opts) {
const tlsOptions = { ...opts };
let enableTrace = tlsOptions.enableTrace;

if (enableTrace == null) {
enableTrace = traceTls;

if (enableTrace && !tlsTracingWarned) {
tlsTracingWarned = true;
process.emitWarning('Enabling --trace-tls can expose sensitive data in ' +
'the resulting log.');
}
} else if (typeof enableTrace !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE(
'options.enableTrace', 'boolean', enableTrace);
}

if (tlsOptions.ALPNProtocols)
tls.convertALPNProtocols(tlsOptions.ALPNProtocols, tlsOptions);
Expand Down Expand Up @@ -397,6 +414,9 @@ function TLSSocket(socket, opts) {
this.readable = true;
this.writable = true;

if (enableTrace && this._handle)
this._handle.enableTrace();

// Read on next tick so the caller has a chance to setup listeners
process.nextTick(initRead, this, socket);
}
Expand Down Expand Up @@ -872,10 +892,9 @@ function tlsConnectionListener(rawSocket) {
rejectUnauthorized: this.rejectUnauthorized,
handshakeTimeout: this[kHandshakeTimeout],
ALPNProtocols: this.ALPNProtocols,
SNICallback: this[kSNICallback] || SNICallback
SNICallback: this[kSNICallback] || SNICallback,
enableTrace: this[kEnableTrace]
});
if (this[kEnableTrace] && socket._handle)
socket._handle.enableTrace();

socket.on('secure', onServerSocketSecure);

Expand Down Expand Up @@ -997,14 +1016,7 @@ function Server(options, listener) {
this.on('secureConnection', listener);
}

const enableTrace = options.enableTrace;
if (enableTrace === true)
this[kEnableTrace] = true;
else if (enableTrace === false || enableTrace == null)
; // Tracing explicitly disabled, or defaulting to disabled.
else
throw new ERR_INVALID_ARG_TYPE(
'options.enableTrace', 'boolean', enableTrace);
this[kEnableTrace] = options.enableTrace;
}

Object.setPrototypeOf(Server.prototype, net.Server.prototype);
Expand Down Expand Up @@ -1365,7 +1377,8 @@ exports.connect = function connect(...args) {
rejectUnauthorized: options.rejectUnauthorized !== false,
session: options.session,
ALPNProtocols: options.ALPNProtocols,
requestOCSP: options.requestOCSP
requestOCSP: options.requestOCSP,
enableTrace: options.enableTrace
});

tlssock[kConnectOptions] = options;
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"first tick",
&EnvironmentOptions::trace_sync_io,
kAllowedInEnvironment);
AddOption("--trace-tls",
"prints TLS packet trace information to stderr",
&EnvironmentOptions::trace_tls,
kAllowedInEnvironment);
AddOption("--trace-warnings",
"show stack traces on process warnings",
&EnvironmentOptions::trace_warnings,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class EnvironmentOptions : public Options {
bool throw_deprecation = false;
bool trace_deprecation = false;
bool trace_sync_io = false;
bool trace_tls = false;
bool trace_warnings = false;
std::string unhandled_rejections;
std::string userland_loader;
Expand Down
59 changes: 59 additions & 0 deletions test/parallel/test-tls-enable-trace-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
const fixtures = require('../common/fixtures');

// Test --trace-tls CLI flag.

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

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

const binding = require('internal/test/binding').internalBinding;

if (!binding('tls_wrap').HAVE_SSL_TRACE)
return common.skip('no SSL_trace() compiled into openssl');

const child = fork(__filename, ['test'], {
silent: true,
execArgv: ['--trace-tls']
});

let stderr = '';
child.stderr.setEncoding('utf8');
child.stderr.on('data', (data) => stderr += data);
child.on('close', common.mustCall(() => {
assert(/Warning: Enabling --trace-tls can expose sensitive/.test(stderr));
assert(/Received Record/.test(stderr));
assert(/ClientHello/.test(stderr));
}));

// For debugging and observation of actual trace output.
child.stderr.pipe(process.stderr);
child.stdout.pipe(process.stdout);

child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));

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) => {
return cleanup();
}));
}