From 9cbd536c4208500e1b143bce521e4b0efc59bdc9 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Tue, 17 Dec 2024 14:53:43 +0100 Subject: [PATCH] Add a test for Ruby OpenSSL to respect crypto-policies TLS minimal version. Add a test to test Ruby OpenSSL to respect cypto-policies TLS minimal version, and test the issue identified at the ticket RHEL-21019. https://issues.redhat.com/browse/RHEL-21019 If Ruby OpenSSL doesn't include the following upstream patch, the test fails as follows. https://github.com/ruby/openssl/pull/710 ``` $ tmt -c distro=fedora-rawhide run --all provision -h virtual -i fedora-rawhide tests -n '^/tls-minimal-version$' /var/tmp/tmt/run-007 ... total: 1 test failed $ tmt run --id run-007 report -vvv ... :: [ 16:03:29 ] :: [ BEGIN ] :: Running Ruby OpenSSL client :: actually running 'ruby /var/tmp/tmt/run-007/plans/all/discover/default-0/tests/tls-minimal-version/test.rb -v' STDOUT: Loaded suite /var/tmp/tmt/run-007/plans/all/discover/default-0/tests/tls-minimal-version/test STDOUT: Started STDOUT: TestRubyOpenSSLTLSMin: STDOUT: test_connection_to_tls_1_2_server_to_fail: F STDOUT: =============================================================================== STDOUT: Failure: test_connection_to_tls_1_2_server_to_fail(TestRubyOpenSSLTLSMin): exception was expected but none was thrown. STDOUT: /var/tmp/tmt/run-007/plans/all/discover/default-0/tests/tls-minimal-version/test.rb:13:in `test_connection_to_tls_1_2_server_to_fail' STDOUT: 10: STDOUT: 11: class TestRubyOpenSSLTLSMin < Test::Unit::TestCase STDOUT: 12: def test_connection_to_tls_1_2_server_to_fail STDOUT: => 13: assert_raise_with_message(OpenSSL::SSL::SSLError, STDOUT: 14: /tlsv1 alert protocol version/) do STDOUT: 15: connect(CONFIG_ITEMS[:tls_1_2][:host], CONFIG_ITEMS[:tls_1_2][:port]) STDOUT: 16: end STDOUT: =============================================================================== STDOUT: : (0.075125) STDOUT: test_onnection_to_tls_1_3_server_to_pass: .: (0.013862) STDOUT: STDOUT: Finished in 0.089378126 seconds. STDOUT: ------------------------------------------------------------------------------- STDOUT: 2 tests, 2 assertions, 1 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications STDOUT: 50% passed STDOUT: ------------------------------------------------------------------------------- STDOUT: 22.38 tests/s, 22.38 assertions/s :: [ 16:03:30 ] :: [ FAIL ] :: Running Ruby OpenSSL client (Expected 0, got 1) ... total: 1 test failed ``` --- tls-minimal-version/cert.conf | 10 +++ tls-minimal-version/main.fmf | 25 +++++++ tls-minimal-version/runtest.sh | 118 +++++++++++++++++++++++++++++++++ tls-minimal-version/test.rb | 33 +++++++++ 4 files changed, 186 insertions(+) create mode 100644 tls-minimal-version/cert.conf create mode 100644 tls-minimal-version/main.fmf create mode 100755 tls-minimal-version/runtest.sh create mode 100644 tls-minimal-version/test.rb diff --git a/tls-minimal-version/cert.conf b/tls-minimal-version/cert.conf new file mode 100644 index 0000000..0dd65be --- /dev/null +++ b/tls-minimal-version/cert.conf @@ -0,0 +1,10 @@ +# See the manual openssl-req(1) for details. +[ req ] +distinguished_name = req_distinguished_name + +[ req_distinguished_name ] +# The commonName is required. The openssl command fails with "Error: No objects +# specified in config file" without this setting. +commonName = Common Name (hostname, IP, or your name) +# Enable all the sub-domains of the domain. +commonName_default = *.localhost diff --git a/tls-minimal-version/main.fmf b/tls-minimal-version/main.fmf new file mode 100644 index 0000000..507fd0f --- /dev/null +++ b/tls-minimal-version/main.fmf @@ -0,0 +1,25 @@ +adjust: +- when: distro != fedora + require-: + - rubypick + +summary: Ensure Ruby OpenSSL respects crypto-policies +description: | + This test ensures that Ruby OpenSSL doesn't override the value of the + TLS.MinProtocol in the crypto-policies opensslcnf.config file. + If Ruby OpenSSL not including the upstream patch[1] had overridden the + value of the TLS.MinProtocol as TLS 1.0 as a default. + The test exists in Fedora tests/ruby rather than the upstream tests because + the test is Fedora-specific depending on the crypto-policies. + [1] https://github.com/ruby/openssl/pull/710 +component: +- openssl +- ruby +- iproute +require: +- openssl +- ruby +- rubygem-test-unit +# For ss command. +- iproute +duration: 30m diff --git a/tls-minimal-version/runtest.sh b/tls-minimal-version/runtest.sh new file mode 100755 index 0000000..c1dfa8b --- /dev/null +++ b/tls-minimal-version/runtest.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k + +# Include Beaker environment +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +PACKAGES=${PACKAGES:-ruby rubypick} +RUBY=${RUBY:-ruby} + +_TEST_DIR="$(realpath "$(dirname "${0}")")" +_OPENSSL_CONF="/etc/pki/tls/openssl.cnf" +# Symbolic link +_CRYPTO_POLICIES_OPENSSLCNF="/etc/crypto-policies/back-ends/opensslcnf.config" +_SSL_DOMAIN="localhost" +_TLS_12_PORT="44312" +_TLS_13_PORT="44313" +_TLS_12_SERVER_NAME="tls-12.${_SSL_DOMAIN}" +_TLS_13_SERVER_NAME="tls-13.${_SSL_DOMAIN}" + +rlJournalStart + rlPhaseStartSetup + rlAssertBinaryOrigin "${RUBY}" + rlAssertExists "${_CRYPTO_POLICIES_OPENSSLCNF}" || rlDie + rlLog "Using crypto-policies opensslcnf.config from $(rpm -qf ${_CRYPTO_POLICIES_OPENSSLCNF})." + rlRun "TmpDir=\$(mktemp -d)" 0 "Creating tmp directory" + rlRun "pushd ${TmpDir}" + + # Prepare custom crypto-policies OpenSSL file to test with + # TLS.MinProtocol TLSv1.3 forcibly because we want to test a connection + # to TLS 1.2 server is rejected, and we have to change SECLEVEL in the + # file on TLS < 1.2, and OpenSSL doesn't support TLS < 1.2. + rlRun "cp -p ${_OPENSSL_CONF} openssl.cnf" 0 \ + "Copying system OpenSSL configuration file to create custom file" \ + || rlDie + # Copy the source file of the symbolic file + rlRun "cp -pH ${_CRYPTO_POLICIES_OPENSSLCNF} opensslcnf.config" 0 \ + "Copying crypto-policies opensslcnf.config file to create custom file" \ + || rlDie + rlRun "sed -i -E -e '/^\.include = .*opensslcnf\.config$/ s|([^ ]+)$|'${TmpDir}'/opensslcnf\.config|' openssl.cnf" \ + 0 \ + "Modifying crypto-policies opensslcnf.config file path in OpenSSL configuration file" \ + || rlDie + rlRun -t "diff ${_OPENSSL_CONF} openssl.cnf" 1 \ + "Printing the modified part in OpenSSL configuration file for debugging use" \ + && rlDie + rlRun "sed -i -E -e '/^TLS.MinProtocol/ s/[^ ]+$/TLSv1.3/' opensslcnf.config" \ + 0 \ + "Modifying TLS.MinProtocol in crypto-policies opensslcnf.config file" \ + || rlDie + # If the TLSv1.3 is already set, there can be no difference between the files. + rlRun -t "diff ${_CRYPTO_POLICIES_OPENSSLCNF} opensslcnf.config || :" 0 \ + "Printing the modified part in crypto-policies opensslcnf.config file for debugging use" + + rlRun "export OPENSSL_CONF=${TmpDir}/openssl.cnf" 0 \ + "Setting environment variable OPENSSL_CONF to load custom OpenSSL configuration files" \ + || rlDie + + # Prepare files to run SSL servers + rlRun "openssl genrsa -out test.key 4096" 0 \ + "Creating RSA private key test.key" || rlDie + rlRun "openssl req -new -key test.key -config ${_TEST_DIR}/cert.conf -out test.csr -sha512 -batch" \ + 0 "Creating certificate request (CSR) test.csr" || rlDie + rlRun "openssl x509 -req -in test.csr -signkey test.key -out test.crt -sha512" \ + 0 "Creating certificate test.crt" || rlDie + + # Start SSL servers + # Stop existing SSL servers + pkill -f 'openssl s_server' || : + # Start SSL servers + TLS_12_CMD=" + openssl s_server \ + -port ${_TLS_12_PORT} \ + -servername ${_TLS_12_SERVER_NAME} \ + -tls1_2 \ + -cert "${TmpDir}/test.crt" -key "${TmpDir}/test.key" \ + -cert2 "${TmpDir}/test.crt" -key2 "${TmpDir}/test.key" \ + -www \ + -debug \ + " + rlRun "${TLS_12_CMD} >& /dev/null &" 0 \ + "Running openssl s_server TLS 1.2 in background" || rlDie + TLS_13_CMD=" + openssl s_server \ + -port ${_TLS_13_PORT} \ + -servername ${_TLS_13_SERVER_NAME} \ + -tls1_3 \ + -cert "${TmpDir}/test.crt" -key "${TmpDir}/test.key" \ + -cert2 "${TmpDir}/test.crt" -key2 "${TmpDir}/test.key" \ + -www \ + -debug \ + " + rlRun "${TLS_13_CMD} >& /dev/null &" 0 \ + "Running openssl s_server TLS 1.3 in background" || rlDie + + # Verify running SSL servers + rlRun "sleep 1" 0 "Waiting for 1 second to check SSL servers listening" + rlRun "pgrep -f 'openssl s_server -port ${_TLS_12_PORT} '" 0 \ + "Checking TLS 1.2 server starting" || rlDie + rlRun "pgrep -f 'openssl s_server -port ${_TLS_13_PORT} '" 0 \ + "Checking TLS 1.3 server starting" || rlDie + rlRun "ss -tnl | grep ${_TLS_12_PORT} " 0 \ + "Checking TLS 1.2 server listening" || rlDie + rlRun "ss -tnl | grep ${_TLS_13_PORT} " 0 \ + "Checking TLS 1.3 server listening" || rlDie + rlPhaseEnd + + rlPhaseStartTest + rlRun -t "${RUBY} ${_TEST_DIR}/test.rb -v" 0 \ + "Running Ruby OpenSSL client" + rlPhaseEnd + + rlPhaseStartCleanup + rlRun "pkill -f 'openssl s_server'" 0 "Stopping SSL servers" + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd diff --git a/tls-minimal-version/test.rb b/tls-minimal-version/test.rb new file mode 100644 index 0000000..5196e26 --- /dev/null +++ b/tls-minimal-version/test.rb @@ -0,0 +1,33 @@ +require 'net/http' +require 'test/unit' + +DOMAIN = 'localhost' +CONFIG_ITEMS = { + tls_1_2: { host: "tls-12.#{DOMAIN}", port: 44_312 }, + tls_1_3: { host: "tls-13.#{DOMAIN}", port: 44_313 } +}.freeze +CA_FILE = 'test.crt' + +class TestRubyOpenSSLTLSMin < Test::Unit::TestCase + def test_connection_to_tls_1_2_server_to_fail + assert_raise_with_message(OpenSSL::SSL::SSLError, + /tlsv1 alert protocol version/) do + connect(CONFIG_ITEMS[:tls_1_2][:host], CONFIG_ITEMS[:tls_1_2][:port]) + end + end + + def test_onnection_to_tls_1_3_server_to_pass + assert(connect(CONFIG_ITEMS[:tls_1_3][:host], + CONFIG_ITEMS[:tls_1_3][:port])) + end + + def connect(host, port) + uri = URI("https://#{host}") + Net::HTTP.start(host, + port, + use_ssl: true, + ca_file: CA_FILE) do |http| + http.get(uri) + end + end +end