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

[v10.x] tls: support TLS min/max protocol defaults in CLI #27946

Closed
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
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