From 4bf057803d3d25769818a4e2ceda82a10a41826b Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 30 Aug 2023 13:51:10 -0500 Subject: [PATCH] [node] Enable openssl legacy provider (#163190) This is to prevent a breaking change in a minor release of Kibana due to an underlying upgrade of Node.js to v18. The legacy provider can be disabled by removing `--openssl-legacy-provider` in `config/node.options`. [Node.js documentation](https://nodejs.org/docs/latest-v18.x/api/cli.html#--openssl-legacy-provider) [OpenSSL documentation](https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html) --------- Co-authored-by: Thomas Watson --- config/node.options | 3 + .../production.asciidoc | 9 +++ .../src/integration_tests/build.test.ts | 6 +- .../templates/base/Dockerfile | 3 + .../templates/dockerfile.template.ts | 1 + src/setup_node_env/no_transpilation_dist.js | 1 + .../openssl_legacy_provider/index.js | 18 +++++ .../openssl_legacy_provider_enabled.js | 14 ++++ .../openssl_legacy_provider_enabled.test.js | 78 +++++++++++++++++++ 9 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/setup_node_env/openssl_legacy_provider/index.js create mode 100644 src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.js create mode 100644 src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.test.js diff --git a/config/node.options b/config/node.options index d5799f2c2068a..abcb40a5c19d4 100644 --- a/config/node.options +++ b/config/node.options @@ -10,3 +10,6 @@ ## restore < Node 16 default DNS lookup behavior --dns-result-order=ipv4first + +## enable OpenSSL 3 legacy provider +--openssl-legacy-provider diff --git a/docs/user/production-considerations/production.asciidoc b/docs/user/production-considerations/production.asciidoc index a7fb5f2135f66..a995c5ac0ae6f 100644 --- a/docs/user/production-considerations/production.asciidoc +++ b/docs/user/production-considerations/production.asciidoc @@ -109,3 +109,12 @@ The option accepts a limit in MB: -------- --max-old-space-size=2048 -------- + +[float] +[[openssl-legacy-provider]] +=== OpenSSL Legacy Provider + +Starting in 7.17.13, {kib} has upgraded its runtime environment, Node.js, from version 16 to version 18 and with it the underlying version of OpenSSL to version 3. +Algorithms deemed legacy by OpenSSL 3 have been re-enabled to avoid potential breaking changes in a minor version release of {kib}. +If SSL certificates configured for {kib} are not using any of the legacy algorithms mentioned in the https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html[OpenSSL legacy provider documentation], +we recommend disabling this setting by removing `--openssl-legacy-provider` in the `node.options` config file. diff --git a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts index 9e904839dfdf4..183d5ca3ce09c 100644 --- a/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts +++ b/packages/kbn-plugin-helpers/src/integration_tests/build.test.ts @@ -54,7 +54,8 @@ it('builds a generated plugin into a viable archive', async () => { }; expect(filterLogs(generateProc.all)).toMatchInlineSnapshot(` - " succ 🎉 + "Kibana is currently running with legacy OpenSSL providers enabled! For details and instructions on how to disable see https://www.elastic.co/guide/en/kibana/7.17/production.html#openssl-legacy-provider + succ 🎉 Your plugin has been created in plugins/foo_test_plugin " @@ -73,7 +74,8 @@ it('builds a generated plugin into a viable archive', async () => { ); expect(filterLogs(buildProc.all)).toMatchInlineSnapshot(` - " info deleting the build and target directories + "Kibana is currently running with legacy OpenSSL providers enabled! For details and instructions on how to disable see https://www.elastic.co/guide/en/kibana/7.17/production.html#openssl-legacy-provider + info deleting the build and target directories info running @kbn/optimizer │ info initialized, 0 bundles cached │ info starting worker [1 bundle] diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 93e3a64de661c..17102eb26438b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -119,6 +119,9 @@ ENV PATH=/usr/share/kibana/bin:$PATH # Set some Kibana configuration defaults. COPY --chown=1000:0 config/kibana.yml /usr/share/kibana/config/kibana.yml +{{^opensslLegacyProvider}} +RUN sed 's/\(--openssl-legacy-provider\)/#\1/' -i config/node.options +{{/opensslLegacyProvider}} # Add the launcher/wrapper script. It knows how to interpret environment # variables and translate them to Kibana CLI options. diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts index 94068f2b64b12..d98cd654662e0 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/dockerfile.template.ts @@ -17,6 +17,7 @@ function generator(options: TemplateContext) { const template = readFileSync(resolve(__dirname, dir, './Dockerfile')); return Mustache.render(template.toString(), { packageManager: options.ubi ? 'microdnf' : 'apt-get', + opensslLegacyProvider: !options.cloud, ...options, }); } diff --git a/src/setup_node_env/no_transpilation_dist.js b/src/setup_node_env/no_transpilation_dist.js index c52eba70f4ad3..a62b0efb7e431 100644 --- a/src/setup_node_env/no_transpilation_dist.js +++ b/src/setup_node_env/no_transpilation_dist.js @@ -14,3 +14,4 @@ require('./harden'); require('symbol-observable'); require('source-map-support/register'); require('./node_version_validator'); +require('./openssl_legacy_provider'); diff --git a/src/setup_node_env/openssl_legacy_provider/index.js b/src/setup_node_env/openssl_legacy_provider/index.js new file mode 100644 index 0000000000000..159c2a5e62e9a --- /dev/null +++ b/src/setup_node_env/openssl_legacy_provider/index.js @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +var branch = require('../../../package.json').branch; +var docsBranch = branch.match(/^\d\.\d\d?$/) || 'current'; +var openSSLLegacyProviderEnabled = require('./openssl_legacy_provider_enabled')(); + +if (openSSLLegacyProviderEnabled) { + console.log( + 'Kibana is currently running with legacy OpenSSL providers enabled! For details and instructions on how to disable see https://www.elastic.co/guide/en/kibana/' + + docsBranch + + '/production.html#openssl-legacy-provider' + ); +} diff --git a/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.js b/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.js new file mode 100644 index 0000000000000..8c9771b1bf521 --- /dev/null +++ b/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +var crypto = require('crypto'); + +// The blowfish cipher is only available when node is running with the --openssl-legacy-provider flag +module.exports = function () { + return crypto.getCiphers().includes('blowfish'); +}; diff --git a/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.test.js b/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.test.js new file mode 100644 index 0000000000000..30772d00bb165 --- /dev/null +++ b/src/setup_node_env/openssl_legacy_provider/openssl_legacy_provider_enabled.test.js @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +var spawnSync = require('child_process').spawnSync; + +describe('openSSLLegacyProviderEnabled', function () { + function runLegacyProviderCheck(execOptions, nodeOptions) { + var result = spawnSync( + process.execPath, + (execOptions ? execOptions.split(' ') : []).concat([ + '-p', + "require('./openssl_legacy_provider_enabled')()", + ]), + { + env: { + NODE_OPTIONS: nodeOptions || '', + }, + encoding: 'utf-8', + cwd: __dirname, + } + ); + var stdout = result.stdout.trim(); + return stdout === 'true'; + } + + it('should be disabled by default', function () { + expect(runLegacyProviderCheck()).toBe(false); + }); + + describe('using NODE_OPTIONS', function () { + it('should be enabled when --openssl-legacy-provider is set', function () { + expect(runLegacyProviderCheck(null, '--openssl-legacy-provider')).toBe(true); + }); + + it('should be enabled when --openssl-legacy-provider is set after --no-openssl-legacy-provider', function () { + expect( + runLegacyProviderCheck(null, '--no-openssl-legacy-provider --openssl-legacy-provider') + ).toBe(true); + }); + + it('should be disabled when --no-openssl-legacy-provider is set', function () { + expect(runLegacyProviderCheck(null, '--no-openssl-legacy-provider')).toBe(false); + }); + + it('should be disabled when --no-openssl-legacy-provider is set after --openssl-legacy-provider', function () { + expect( + runLegacyProviderCheck(null, '--openssl-legacy-provider --no-openssl-legacy-provider') + ).toBe(false); + }); + }); + + describe('using exec arguments', function () { + it('should be enabled when --openssl-legacy-provider is set', function () { + expect(runLegacyProviderCheck('--openssl-legacy-provider')).toBe(true); + }); + + it('should be enabled when --openssl-legacy-provider is set after --no-openssl-legacy-provider', function () { + expect(runLegacyProviderCheck('--no-openssl-legacy-provider --openssl-legacy-provider')).toBe( + true + ); + }); + + it('should be disabled when --no-openssl-legacy-provider is set', function () { + expect(runLegacyProviderCheck('--no-openssl-legacy-provider')).toBe(false); + }); + + it('should be disabled when --no-openssl-legacy-provider is set after --openssl-legacy-provider', function () { + expect(runLegacyProviderCheck('--openssl-legacy-provider --no-openssl-legacy-provider')).toBe( + false + ); + }); + }); +});