From 44aa16c426f5276e7a854a61d77a7ee8b514ca90 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 (cherry picked from commit aebd6f392384b4e36241f1a1ad5f3c615b42bcca) --- config/node.options | 3 + .../production.asciidoc | 9 +++ .../src/integration_tests/build.test.ts | 6 +- .../templates/base/Dockerfile | 3 + .../templates/dockerfile.template.ts | 1 + .../openssl_legacy_provider/index.js | 18 +++++ .../openssl_legacy_provider_enabled.js | 14 ++++ .../openssl_legacy_provider_enabled.test.js | 78 +++++++++++++++++++ src/setup_node_env/setup_env.js | 1 + src/setup_node_env/tsconfig.json | 1 + 10 files changed, 132 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 92cb77cc401f7..e0878b4dbf849 100644 --- a/docs/user/production-considerations/production.asciidoc +++ b/docs/user/production-considerations/production.asciidoc @@ -118,3 +118,12 @@ The option accepts a limit in MB: -------- --max-old-space-size=2048 -------- + +[float] +[[openssl-legacy-provider]] +=== OpenSSL Legacy Provider + +Starting in 8.10.0, {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 e3d1ca81e2c26..7a3cf7f85ddf2 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/current/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/current/production.html#openssl-legacy-provider + info deleting the build and target directories info run bazel and build required artifacts for the optimizer succ bazel run successfully and artifacts were created info running @kbn/optimizer 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 a44fae27c4265..8bf470f489cb7 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 @@ -127,6 +127,9 @@ COPY --chown=1000:0 config/serverless.es.yml /usr/share/kibana/config/serverless COPY --chown=1000:0 config/serverless.oblt.yml /usr/share/kibana/config/serverless.oblt.yml COPY --chown=1000:0 config/serverless.security.yml /usr/share/kibana/config/serverless.security.yml {{/serverless}} +{{^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 ca597e5c38941..456a09ccc3db3 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 @@ -19,6 +19,7 @@ function generator(options: TemplateContext) { packageManager: options.baseImage.includes('ubi') ? 'microdnf' : 'apt-get', ubi: options.baseImage.includes('ubi'), ubuntu: options.baseImage === 'ubuntu', + opensslLegacyProvider: !(options.cloud || options.serverless), ...options, }); } 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 + ); + }); + }); +}); diff --git a/src/setup_node_env/setup_env.js b/src/setup_node_env/setup_env.js index 08897eb5a78c5..7b37d98011cfb 100644 --- a/src/setup_node_env/setup_env.js +++ b/src/setup_node_env/setup_env.js @@ -14,3 +14,4 @@ require('./harden'); require('symbol-observable'); require('source-map-support').install(); require('./node_version_validator'); +require('./openssl_legacy_provider'); diff --git a/src/setup_node_env/tsconfig.json b/src/setup_node_env/tsconfig.json index ed753806b9f4f..931afbdfaf0a3 100644 --- a/src/setup_node_env/tsconfig.json +++ b/src/setup_node_env/tsconfig.json @@ -6,6 +6,7 @@ "include": [ "harden/**/*", "root/**/*", + "openssl_legacy_provider/**/*", "*.js", "*.ts", ],