Skip to content

Commit

Permalink
tls: support TLS min/max protocol defaults in CLI
Browse files Browse the repository at this point in the history
Backport CLI switches for default TLS versions:
- `--tls-max-v1.2`
- `--tls-min-v1.0`
- `--tls-min-v1.1`
- `--tls-min-v1.2`

PR-URL: #27946
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Beth Griggs <[email protected]>
Reviewed-By: Shelley Vohr <[email protected]>
  • Loading branch information
sam-github authored and BethGriggs committed Feb 24, 2020
1 parent f1a8791 commit 1cfb457
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 10 deletions.
32 changes: 32 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,38 @@ added: v4.0.0
Specify an alternative default TLS cipher list. Requires Node.js to be built
with crypto support (default).

### `--tls-max-v1.2`
<!-- YAML
added: REPLACEME
-->

Does nothing, [`tls.DEFAULT_MAX_VERSION`][] is always 'TLSv1.2'. Exists for
compatibility with Node.js 11.x and higher.

### `--tls-min-v1.0`
<!-- YAML
added: REPLACEME
-->

Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1'. Use for compatibility with
old TLS clients or servers.

### `--tls-min-v1.1`
<!-- YAML
added: REPLACEME
-->

Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.1'. Use for compatibility
with old TLS clients or servers.

### `--tls-min-v1.2`
<!-- YAML
added: REPLACEME
-->

Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.2'. Use this to disable
support for earlier TLS versions, which are less secure.

### `--trace-deprecation`
<!-- YAML
added: v0.8.0
Expand Down
6 changes: 5 additions & 1 deletion doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,11 @@ added: v10.6.0
* {string} The default value of the `minVersion` option of
[`tls.createSecureContext()`][]. It can be assigned any of the supported TLS
protocol versions, `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`.
**Default:** `'TLSv1'`.
**Default:** `'TLSv1'`, unless changed using CLI options. Using
`--tls-min-v1.0` sets the default to `'TLSv1'`. Using `--tls-min-v1.1` sets
the default to `'TLSv1.1'`. Using `--tls-min-v1.2` sets the default to
`'TLSv1.2'`. If multiple of the options are provided, the lowest minimum is
used.

## Deprecated APIs

Expand Down
16 changes: 16 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@ 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-max-v1.2
Does nothing, the default maxVersion is always 'TLSv1.2'. Exists for
compatibility with Node.js 11.x and higher.
.
.It Fl -tls-min-v1.0
Set default minVersion to 'TLSv1'. Use for compatibility with old TLS clients
or servers.
.
.It Fl -tls-min-v1.1
Set default minVersion to 'TLSv1.1'. Use for compatibility with old TLS clients
or servers.
.
.It Fl -tls-min-v1.2
Set default minVersion to 'TLSv1.2'. Use to disable support for earlier TLS
versions, which are less secure.
.
.It Fl -trace-deprecation
Print stack traces for deprecations.
.
Expand Down
17 changes: 14 additions & 3 deletions lib/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ internalUtil.assertCrypto();
const { isUint8Array } = require('internal/util/types');

const net = require('net');
const { getOptionValue } = require('internal/options');
const url = require('url');
const binding = internalBinding('crypto');
const { Buffer } = require('buffer');
Expand All @@ -52,9 +53,19 @@ exports.DEFAULT_CIPHERS =

exports.DEFAULT_ECDH_CURVE = 'auto';

exports.DEFAULT_MAX_VERSION = 'TLSv1.2';

exports.DEFAULT_MIN_VERSION = 'TLSv1';
if (getOptionValue('--tls-min-v1.0'))
exports.DEFAULT_MIN_VERSION = 'TLSv1';
else if (getOptionValue('--tls-min-v1.1'))
exports.DEFAULT_MIN_VERSION = 'TLSv1.1';
else if (getOptionValue('--tls-min-v1.2'))
exports.DEFAULT_MIN_VERSION = 'TLSv1.2';
else
exports.DEFAULT_MIN_VERSION = 'TLSv1';

if (getOptionValue('--tls-max-v1.2'))
exports.DEFAULT_MAX_VERSION = 'TLSv1.2';
else
exports.DEFAULT_MAX_VERSION = 'TLSv1.2'; // Will depend on node version.

exports.getCiphers = internalUtil.cachedResult(
() => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true)
Expand Down
17 changes: 17 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,23 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {

Insert(&DebugOptionsParser::instance,
&EnvironmentOptions::get_debug_options);

AddOption("--tls-min-v1.0",
"set default TLS minimum to TLSv1.0 (default: TLSv1.0)",
&EnvironmentOptions::tls_min_v1_0,
kAllowedInEnvironment);
AddOption("--tls-min-v1.1",
"set default TLS minimum to TLSv1.1 (default: TLSv1.0)",
&EnvironmentOptions::tls_min_v1_1,
kAllowedInEnvironment);
AddOption("--tls-min-v1.2",
"set default TLS minimum to TLSv1.2 (default: TLSv1.0)",
&EnvironmentOptions::tls_min_v1_2,
kAllowedInEnvironment);
AddOption("--tls-max-v1.2",
"set default TLS maximum to TLSv1.2 (default: TLSv1.2)",
&EnvironmentOptions::tls_max_v1_2,
kAllowedInEnvironment);
}

EnvironmentOptionsParser EnvironmentOptionsParser::instance;
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ class EnvironmentOptions : public Options {
bool force_repl = false;

bool insecure_http_parser = false;
bool tls_min_v1_0 = false;
bool tls_min_v1_1 = false;
bool tls_min_v1_2 = false;
bool tls_max_v1_2 = false;

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

Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-process-env-allowed-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ require('../common');
// assert all "canonical" flags begin with dash(es)
{
process.allowedNodeEnvironmentFlags.forEach((flag) => {
assert(/^--?[a-z28_-]+$/.test(flag), `Unexpected format for flag ${flag}`);
assert(/^--?[a-z.0-9_-]+$/.test(flag), `Unexpected format for flag ${flag}`);
});
}

Expand Down
15 changes: 15 additions & 0 deletions test/parallel/test-tls-cli-max-version-1.2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Flags: --tls-max-v1.2
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');

// Check that node `--tls-max-v1.2` is supported.

const assert = require('assert');
const tls = require('tls');

assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');

// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');
15 changes: 15 additions & 0 deletions test/parallel/test-tls-cli-min-version-1.0.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.0 --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');

// Check that `node --tls-v1.0` is supported, and overrides --tls-v1.1.

const assert = require('assert');
const tls = require('tls');

assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');

// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');
15 changes: 15 additions & 0 deletions test/parallel/test-tls-cli-min-version-1.1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.1
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');

// Check that node `--tls-v1.1` is supported.

const assert = require('assert');
const tls = require('tls');

assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1');

// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');
15 changes: 15 additions & 0 deletions test/parallel/test-tls-cli-min-version-1.2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Flags: --tls-min-v1.2
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');

// Check that node `--tls-min-v1.2` is supported.

const assert = require('assert');
const tls = require('tls');

assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');

// Check the min-max version protocol versions against these CLI settings.
require('./test-tls-min-max-version.js');
30 changes: 25 additions & 5 deletions test/parallel/test-tls-min-max-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ const {
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
const DEFAULT_MAX_VERSION = tls.DEFAULT_MAX_VERSION;

// For v11.x, the default is fixed and cannot be changed via CLI.
assert.strictEqual(DEFAULT_MIN_VERSION, 'TLSv1');

function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
assert(proto || cerr || serr, 'test missing any expectations');
// Report where test was called from. Strip leading garbage from
// at Object.<anonymous> (file:line)
// from the stack location, we only want the file:line part.
const where = (new Error()).stack.split('\n')[2].replace(/[^(]*/, '');
connect({
client: {
checkServerIdentity: (servername, cert) => { },
Expand All @@ -34,9 +35,28 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
function u(_) { return _ === undefined ? 'U' : _; }
console.log('test:', u(cmin), u(cmax), u(cprot), u(smin), u(smax), u(sprot),
'expect', u(proto), u(cerr), u(serr));
console.log(' ', where);
if (!proto) {
console.log('client', pair.client.err ? pair.client.err.code : undefined);
console.log('server', pair.server.err ? pair.server.err.code : undefined);
function setCode(err) {
if (!err) return;
if (err.code) return;
// Convert error message to a .code, because .code wasn't always present
// in older versions.
if (/unsupported protocol/.test(err.message))
err.code = 'ERR_SSL_UNSUPPORTED_PROTOCOL';
else if (/wrong version number/.test(err.message))
err.code = 'ERR_SSL_WRONG_VERSION_NUMBER';
else if (/version too low/.test(err.message))
err.code = 'ERR_SSL_UNSUPPORTED_PROTOCOL';
else
err.code = err.message;
}
setCode(pair.server.err);
setCode(pair.client.err);
console.log('client', pair.client.err ? pair.client.err.code :
pair.client.err);
console.log('server', pair.server.err ? pair.server.err.code :
pair.server.err);
// 11.x doesn't have https://github.com/nodejs/node/pull/24729
if (cerr === 'ERR_TLS_INVALID_PROTOCOL_METHOD' &&
pair.client.err &&
Expand Down

0 comments on commit 1cfb457

Please sign in to comment.