From 99ef10ee2d5b829f6059854a309a0a0dddda6484 Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Mon, 13 Jan 2025 11:38:28 +0100 Subject: [PATCH 1/5] MINIFICPP-2524 Add FIPS compliancy support --- CMakeLists.txt | 5 + CONFIGURE.md | 17 +++ cmake/BundledOpenSSL.cmake | 75 ++++++++++++- cmake/Fetchlibrdkafka.cmake | 4 +- cmake/PahoMqttC.cmake | 4 +- cmake/ssl/FindOpenSSL.cmake | 2 +- conf/minifi.properties | 3 + .../integration/cluster/ContainerStore.py | 3 + .../integration/cluster/DockerTestCluster.py | 3 + .../cluster/containers/MinifiContainer.py | 11 ++ .../MiNiFi_integration_test_driver.py | 3 + .../test/integration/features/https.feature | 3 +- .../test/integration/features/kafka.feature | 18 ++-- .../features/minifi_c2_server.feature | 6 +- .../integration/features/opensearch.feature | 9 +- .../integration/features/prometheus.feature | 3 +- docker/test/integration/features/s2s.feature | 12 ++- .../test/integration/features/splunk.feature | 3 +- .../test/integration/features/steps/steps.py | 5 + .../tests/ConfigFileEncryptorTests.cpp | 2 +- encrypt-config/tests/ConfigFileTests.cpp | 8 +- .../tests/resources/minifi.properties | 3 + ...ditional-sensitive-props.minifi.properties | 3 + .../mqtt/processors/AbstractMQTTProcessor.cpp | 2 +- fips/openssl.cnf | 9 ++ libminifi/src/Configuration.cpp | 3 +- .../resources/encrypted.minifi.properties | 3 + .../minifi-cpp/properties/Configuration.h | 2 + minifi_main/MiNiFiMain.cpp | 100 ++++++++++++++++++ msi/WixWin.wsi.in | 7 ++ thirdparty/paho-mqtt/cmake-openssl.patch | 90 ++++++++-------- 31 files changed, 346 insertions(+), 75 deletions(-) create mode 100644 fips/openssl.cnf diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a01eccc34..bed297c48b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,7 @@ if(WIN32) set(CPACK_WIX_UI_DIALOG "${CMAKE_CURRENT_SOURCE_DIR}/msi/bgr.png") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf/" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf/") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/fips/" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/fips/") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/NOTICE" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") file(GLOB markdown_docs "${CMAKE_CURRENT_SOURCE_DIR}/*.md") @@ -552,6 +553,10 @@ if (NOT WIN32) DESTINATION conf COMPONENT bin) + install(FILES fips/openssl.cnf + DESTINATION fips + COMPONENT bin) + install(DIRECTORY extensions/python/pythonprocessors/ DESTINATION minifi-python COMPONENT bin) diff --git a/CONFIGURE.md b/CONFIGURE.md index 1464320c13..0449690624 100644 --- a/CONFIGURE.md +++ b/CONFIGURE.md @@ -805,6 +805,23 @@ To notify the agent which extensions it should load see [Loading extensions](Ext ### Python processors Please see the [Python Processors Readme](extensions/python/PYTHON.md). +### Enabling FIPS support + +To enable FIPS support, and use MiNiFi C++ in FIPS compliant mode, there are a few steps that need to be taken before the application startup. First the following property needs to be set in the minifi.properties file: + + # in minifi.properties + nifi.openssl.fips.support.enable=true + +Before first starting the application, the fipsmodule.cnf needs to be generated. To do this run the following command with the openssl binary (openssl on Unix and openssl.exe on windows) with the following parameters provided in the $MINIFI_HOME/fips directory: + + # on Unix platform + ./openssl fipsinstall -out fipsmodule.cnf -module $MINIFI_HOME/fips/fips.so + + # on Windows platform + openssl.exe fipsinstall -out fipsmodule.cnf -module $MINIFI_HOME\fips\fips.dll + +If the command finishes successfully, the fipsmodule.cnf file will be generated in the $MINIFI_HOME/fips directory. After this the application can be started and it will configure OpenSSL to start in FIPS mode. + ## Log configuration By default the application logs for Apache MiNiFi C++ can be found in the ${MINIFI_HOME}/logs/minifi-app.log file with default INFO level logging. The logger can be reconfigured in the ${MINIFI_HOME}/conf/minifi-log.properties file to use different output streams, log level, and output format. diff --git a/cmake/BundledOpenSSL.cmake b/cmake/BundledOpenSSL.cmake index 0b9ed6f2e7..9d36c2f763 100644 --- a/cmake/BundledOpenSSL.cmake +++ b/cmake/BundledOpenSSL.cmake @@ -38,6 +38,12 @@ function(use_openssl SOURCE_DIR BINARY_DIR) set(BYPRODUCT_SUFFIX ".a" CACHE STRING "" FORCE) endif() + if (WIN32) + set(EXECUTABLE_SUFFIX ".exe" CACHE STRING "" FORCE) + else() + set(EXECUTABLE_SUFFIX "" CACHE STRING "" FORCE) + endif() + set(BYPRODUCTS "${LIBDIR}/${BYPRODUCT_PREFIX}ssl${BYPRODUCT_SUFFIX}" "${LIBDIR}/${BYPRODUCT_PREFIX}crypto${BYPRODUCT_SUFFIX}" @@ -59,7 +65,8 @@ function(use_openssl SOURCE_DIR BINARY_DIR) no-module # disable dynamically loadable engines no-pinshared # don't pin shared libraries in the process memory enable-tfo # Enable TCP Fast Open - no-ssl) # disable SSLv3 + no-ssl # disable SSLv3 + no-engine) set(OPENSSL_BIN_DIR "${BINARY_DIR}/thirdparty/openssl-install" CACHE STRING "" FORCE) @@ -132,6 +139,7 @@ function(use_openssl SOURCE_DIR BINARY_DIR) set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES_LIST};${CMAKE_DL_LIBS}" CACHE STRING "" FORCE) set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_BIN_DIR}/${LIBDIR}/${BYPRODUCT_PREFIX}crypto${BYPRODUCT_SUFFIX}" CACHE STRING "" FORCE) set(OPENSSL_SSL_LIBRARY "${OPENSSL_BIN_DIR}/${LIBDIR}/${BYPRODUCT_PREFIX}ssl${BYPRODUCT_SUFFIX}" CACHE STRING "" FORCE) + set(OPENSSL_VERSION "3.3.2" CACHE STRING "" FORCE) # Set exported variables for FindPackage.cmake set(PASSTHROUGH_VARIABLES ${PASSTHROUGH_VARIABLES} "-DEXPORTED_OPENSSL_INCLUDE_DIR=${OPENSSL_INCLUDE_DIR}" CACHE STRING "" FORCE) @@ -165,4 +173,69 @@ function(use_openssl SOURCE_DIR BINARY_DIR) set_property(TARGET OpenSSL::SSL APPEND PROPERTY INTERFACE_LINK_LIBRARIES crypt32.lib) endif() + if (WIN32) + set(BYPRODUCT_DYN_SUFFIX ".dll" CACHE STRING "" FORCE) + elseif(APPLE) + set(BYPRODUCT_DYN_SUFFIX ".dylib" CACHE STRING "" FORCE) + else() + set(BYPRODUCT_DYN_SUFFIX ".so" CACHE STRING "" FORCE) + endif() + + set(FIPS_BYPRODUCTS + "${LIBDIR}/ossl-modules/fips${BYPRODUCT_DYN_SUFFIX}" + ) + + set(OPENSSL_FIPS_BIN_DIR "${BINARY_DIR}/thirdparty/openssl-fips-install" CACHE STRING "" FORCE) + + FOREACH(BYPRODUCT ${FIPS_BYPRODUCTS}) + LIST(APPEND OPENSSL_FIPS_FILE_LIST "${OPENSSL_FIPS_BIN_DIR}/${BYPRODUCT}") + ENDFOREACH(BYPRODUCT) + + install(FILES ${OPENSSL_FIPS_FILE_LIST} DESTINATION fips COMPONENT bin) + install(FILES "${OPENSSL_BIN_DIR}/bin/openssl${EXECUTABLE_SUFFIX}" DESTINATION fips COMPONENT bin + PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) + + if (WIN32) + find_program(JOM_EXECUTABLE_PATH + NAMES jom.exe + PATHS ENV PATH + NO_DEFAULT_PATH) + if(JOM_EXECUTABLE_PATH) + include(ProcessorCount) + processorcount(jobs) + set(OPENSSL_BUILD_COMMAND ${JOM_EXECUTABLE_PATH} -j${jobs}) + set(OPENSSL_WINDOWS_COMPILE_FLAGS /FS) + else() + message("Using nmake for OpenSSL build") + set(OPENSSL_BUILD_COMMAND nmake) + set(OPENSSL_WINDOWS_COMPILE_FLAGS "") + endif() + ExternalProject_Add( + openssl-fips-external + URL https://github.com/openssl/openssl/releases/download/openssl-3.0.9/openssl-3.0.9.tar.gz + URL_HASH "SHA256=eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90" + SOURCE_DIR "${BINARY_DIR}/thirdparty/openssl-fips-src" + BUILD_IN_SOURCE true + CONFIGURE_COMMAND perl Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" ${OPENSSL_SHARED_FLAG} no-engine enable-fips no-tests "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" + BUILD_BYPRODUCTS ${OPENSSL_FIPS_FILE_LIST} + EXCLUDE_FROM_ALL TRUE + BUILD_COMMAND ${OPENSSL_BUILD_COMMAND} + INSTALL_COMMAND nmake install_fips + ) + else() + ExternalProject_Add( + openssl-fips-external + URL https://github.com/openssl/openssl/releases/download/openssl-3.0.9/openssl-3.0.9.tar.gz + URL_HASH "SHA256=eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90" + SOURCE_DIR "${BINARY_DIR}/thirdparty/openssl-fips-src" + BUILD_IN_SOURCE true + CONFIGURE_COMMAND ./Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} -fPIC" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} -fPIC" ${OPENSSL_SHARED_FLAG} no-engine enable-fips no-tests "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" + BUILD_BYPRODUCTS ${OPENSSL_FIPS_FILE_LIST} + EXCLUDE_FROM_ALL TRUE + INSTALL_COMMAND make install_fips + ) + endif() + + add_dependencies(OpenSSL::Crypto openssl-fips-external) + endfunction(use_openssl) diff --git a/cmake/Fetchlibrdkafka.cmake b/cmake/Fetchlibrdkafka.cmake index 49b600b599..021334c1da 100644 --- a/cmake/Fetchlibrdkafka.cmake +++ b/cmake/Fetchlibrdkafka.cmake @@ -34,8 +34,8 @@ set(PATCH_FILE "${CMAKE_SOURCE_DIR}/thirdparty/librdkafka/0001-remove-findLZ4-an set(PC "${Patch_EXECUTABLE}" -p1 -i "${PATCH_FILE}") FetchContent_Declare(libkafka - URL https://github.com/confluentinc/librdkafka/archive/refs/tags/v2.6.0.tar.gz - URL_HASH SHA256=abe0212ecd3e7ed3c4818a4f2baf7bf916e845e902bb15ae48834ca2d36ac745 + URL https://github.com/confluentinc/librdkafka/archive/refs/tags/v2.8.0.tar.gz + URL_HASH SHA256=5bd1c46f63265f31c6bfcedcde78703f77d28238eadf23821c2b43fc30be3e25 PATCH_COMMAND "${PC}" ) diff --git a/cmake/PahoMqttC.cmake b/cmake/PahoMqttC.cmake index 5ae355fa8a..0209edf94f 100644 --- a/cmake/PahoMqttC.cmake +++ b/cmake/PahoMqttC.cmake @@ -29,8 +29,8 @@ set(PC ${Bash_EXECUTABLE} -c "set -x &&\ FetchContent_Declare( paho.mqtt.c-external - URL "https://github.com/eclipse/paho.mqtt.c/archive/refs/tags/v1.3.9.tar.gz" - URL_HASH "SHA256=386c9b5fa1cf6d0d516db12d57fd8f6a410dd0fdc5e9a2da870aae437a2535ed" + URL "https://github.com/eclipse/paho.mqtt.c/archive/refs/tags/v1.3.14.tar.gz" + URL_HASH "SHA256=7af7d906e60a696a80f1b7c2bd7d6eb164aaad908ff4c40c3332ac2006d07346" PATCH_COMMAND "${PC}" ) diff --git a/cmake/ssl/FindOpenSSL.cmake b/cmake/ssl/FindOpenSSL.cmake index 3b6adc46fa..0111b855f4 100644 --- a/cmake/ssl/FindOpenSSL.cmake +++ b/cmake/ssl/FindOpenSSL.cmake @@ -23,7 +23,7 @@ if(NOT OPENSSL_FOUND) set(OPENSSL_CRYPTO_LIBRARY "${EXPORTED_OPENSSL_CRYPTO_LIBRARY}" CACHE STRING "" FORCE) set(OPENSSL_SSL_LIBRARY "${EXPORTED_OPENSSL_SSL_LIBRARY}" CACHE STRING "" FORCE) set(OPENSSL_LIBRARIES "${EXPORTED_OPENSSL_LIBRARIES}" CACHE STRING "" FORCE) - set(OPENSSL_VERSION "3.1.0" CACHE STRING "" FORCE) + set(OPENSSL_VERSION "3.3.2" CACHE STRING "" FORCE) endif() if(NOT TARGET OpenSSL::Crypto) diff --git a/conf/minifi.properties b/conf/minifi.properties index 1a18fb076d..f681e9a282 100644 --- a/conf/minifi.properties +++ b/conf/minifi.properties @@ -142,3 +142,6 @@ nifi.python.processor.dir=${MINIFI_HOME}/minifi-python/ nifi.python.virtualenv.directory=${MINIFI_HOME}/minifi-python-env nifi.python.install.packages.automatically=true # nifi.python.env.setup.binary=python3 + +# FIPS +# nifi.openssl.fips.support.enable=false diff --git a/docker/test/integration/cluster/ContainerStore.py b/docker/test/integration/cluster/ContainerStore.py index 9c8527458a..baa5e99073 100644 --- a/docker/test/integration/cluster/ContainerStore.py +++ b/docker/test/integration/cluster/ContainerStore.py @@ -397,6 +397,9 @@ def enable_log_metrics_publisher_in_minifi(self): def enable_example_minifi_python_processors(self): self.minifi_options.enable_example_minifi_python_processors = True + def enable_openssl_fips_mode_in_minifi(self): + self.minifi_options.enable_openssl_fips_mode = True + def get_startup_finished_log_entry(self, container_name): container_name = self.get_container_name_with_postfix(container_name) return self.containers[container_name].get_startup_finished_log_entry() diff --git a/docker/test/integration/cluster/DockerTestCluster.py b/docker/test/integration/cluster/DockerTestCluster.py index e161d5da47..a758b873f9 100644 --- a/docker/test/integration/cluster/DockerTestCluster.py +++ b/docker/test/integration/cluster/DockerTestCluster.py @@ -138,6 +138,9 @@ def enable_log_metrics_publisher_in_minifi(self): def enable_example_minifi_python_processors(self): self.container_store.enable_example_minifi_python_processors() + def enable_openssl_fips_mode_in_minifi(self): + self.container_store.enable_openssl_fips_mode_in_minifi() + def get_app_log(self, container_name): container_name = self.container_store.get_container_name_with_postfix(container_name) log_source = self.container_store.log_source(container_name) diff --git a/docker/test/integration/cluster/containers/MinifiContainer.py b/docker/test/integration/cluster/containers/MinifiContainer.py index 8d0894446d..75f698faf9 100644 --- a/docker/test/integration/cluster/containers/MinifiContainer.py +++ b/docker/test/integration/cluster/containers/MinifiContainer.py @@ -43,6 +43,7 @@ def __init__(self): self.enable_controller_socket = False self.enable_log_metrics_publisher = False self.enable_example_minifi_python_processors = False + self.enable_openssl_fips_mode = False class MinifiContainer(FlowContainer): @@ -52,6 +53,13 @@ class MinifiContainer(FlowContainer): def __init__(self, feature_context, config_dir, options, name, vols, network, image_store, command=None): self.options = options + if options.enable_openssl_fips_mode: + if command is not None: + command = ["/bin/sh", "-c", MinifiContainer.MINIFI_ROOT + "/fips/openssl fipsinstall -out " + MinifiContainer.MINIFI_ROOT + "/fips/fipsmodule.cnf -module " + + MinifiContainer.MINIFI_ROOT + "/fips/fips.so && " + command] + else: + command = ["/bin/sh", "-c", MinifiContainer.MINIFI_ROOT + "/fips/openssl fipsinstall -out " + MinifiContainer.MINIFI_ROOT + "/fips/fipsmodule.cnf -module " + + MinifiContainer.MINIFI_ROOT + "/fips/fips.so && " + MinifiContainer.MINIFI_ROOT + "/bin/minifi.sh run"] super().__init__(feature_context=feature_context, config_dir=config_dir, @@ -159,6 +167,9 @@ def _create_properties(self): if self.options.use_nifi_python_processors_with_virtualenv or self.options.remove_python_requirements_txt: f.write("nifi.python.install.packages.automatically=true\n") + if self.options.enable_openssl_fips_mode: + f.write("nifi.openssl.fips.support.enable=true\n") + def _setup_config(self): self._create_properties() if not self.options.use_flow_config_from_url: diff --git a/docker/test/integration/features/MiNiFi_integration_test_driver.py b/docker/test/integration/features/MiNiFi_integration_test_driver.py index 57526342db..e7171865e8 100644 --- a/docker/test/integration/features/MiNiFi_integration_test_driver.py +++ b/docker/test/integration/features/MiNiFi_integration_test_driver.py @@ -437,6 +437,9 @@ def enable_log_metrics_publisher_in_minifi(self): def enable_example_minifi_python_processors(self): self.cluster.enable_example_minifi_python_processors() + def enable_openssl_fips_mode_in_minifi(self): + self.cluster.enable_openssl_fips_mode_in_minifi() + def debug_bundle_can_be_retrieved_through_minifi_controller(self, container_name: str): assert self.cluster.debug_bundle_can_be_retrieved_through_minifi_controller(container_name) or self.cluster.log_app_output() diff --git a/docker/test/integration/features/https.feature b/docker/test/integration/features/https.feature index f4ff9d246c..6c6f459dbf 100644 --- a/docker/test/integration/features/https.feature +++ b/docker/test/integration/features/https.feature @@ -131,7 +131,8 @@ Feature: Transfer data from and to MiNiFi using HTTPS Scenario: InvokeHTTP to ListenHTTP with mutual TLS, using the system certificate store, requires a server cert signed by a CA - Given a GenerateFlowFile processor with the "Data Format" property set to "Text" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GenerateFlowFile processor with the "Data Format" property set to "Text" And the "Unique FlowFiles" property of the GenerateFlowFile processor is set to "false" And the "Custom Text" property of the GenerateFlowFile processor is set to "Duis aute irure dolor in reprehenderit in voluptate" And a InvokeHTTP processor with the "Remote URL" property set to "https://server-${feature_id}:4430/contentListener" diff --git a/docker/test/integration/features/kafka.feature b/docker/test/integration/features/kafka.feature index e7dedc2b22..84353f712b 100644 --- a/docker/test/integration/features/kafka.feature +++ b/docker/test/integration/features/kafka.feature @@ -83,7 +83,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka Then a flowfile with the content "no broker" is placed in the monitored directory in less than 60 seconds Scenario: PublishKafka sends can use SSL connect with security properties - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "test" is present in "/tmp/input" And a PublishKafka processor set up to communicate with a kafka broker instance And these processor properties are set: @@ -139,7 +140,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka Then a flowfile with the content "test" is placed in the monitored directory in less than 60 seconds Scenario: PublishKafka sends can use SASL SSL connect with security properties - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "test" is present in "/tmp/input" And a PublishKafka processor set up to communicate with a kafka broker instance And these processor properties are set: @@ -170,7 +172,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka Then a flowfile with the content "test" is placed in the monitored directory in less than 60 seconds Scenario: PublishKafka sends can use SASL SSL connect with SSL Context - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "test" is present in "/tmp/input" And a PublishKafka processor set up to communicate with a kafka broker instance And these processor properties are set: @@ -199,7 +202,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka Then a flowfile with the content "test" is placed in the monitored directory in less than 60 seconds Scenario: PublishKafka sends can use SSL connect with SSL Context Service - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "test" is present in "/tmp/input" And a PublishKafka processor set up to communicate with a kafka broker instance And these processor properties are set: @@ -423,7 +427,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka | Shogun | James Clavell | Message Header Encoding | UTF-32 | Scenario: ConsumeKafka receives data via SSL - Given a ConsumeKafka processor set up in a "kafka-consumer-flow" flow + Given OpenSSL FIPS mode is enabled in MiNiFi + And a ConsumeKafka processor set up in a "kafka-consumer-flow" flow And these processor properties are set: | processor name | property name | property value | | ConsumeKafka | Kafka Brokers | kafka-broker-${feature_id}:9093 | @@ -441,7 +446,8 @@ Feature: Sending data to using Kafka streaming platform using PublishKafka Then two flowfiles with the contents "Alice's Adventures in Wonderland" and "Lewis Carroll" are placed in the monitored directory in less than 60 seconds Scenario: ConsumeKafka receives data via SASL SSL - Given a ConsumeKafka processor set up in a "kafka-consumer-flow" flow + Given OpenSSL FIPS mode is enabled in MiNiFi + And a ConsumeKafka processor set up in a "kafka-consumer-flow" flow And these processor properties are set: | processor name | property name | property value | | ConsumeKafka | Kafka Brokers | kafka-broker-${feature_id}:9095 | diff --git a/docker/test/integration/features/minifi_c2_server.feature b/docker/test/integration/features/minifi_c2_server.feature index ff6640deba..b11e63d664 100644 --- a/docker/test/integration/features/minifi_c2_server.feature +++ b/docker/test/integration/features/minifi_c2_server.feature @@ -30,7 +30,8 @@ Feature: MiNiFi can communicate with Apache NiFi MiNiFi C2 server And the Minifi logs do not contain the following message: "Failed to parse json response: The document is empty. at 0" after 0 seconds Scenario: MiNiFi flow config is updated from MiNiFi C2 server through SSL with SSL controller service - Given a file with the content "test" is present in "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a file with the content "test" is present in "/tmp/input" And a ssl context service is set up for MiNiFi C2 server And a MiNiFi C2 server is set up with SSL When all instances start up @@ -47,7 +48,8 @@ Feature: MiNiFi can communicate with Apache NiFi MiNiFi C2 server And a flowfile with the content "test" is placed in the monitored directory in less than 10 seconds Scenario: MiNiFi flow config is updated from MiNiFi C2 server through SSL with SSL properties - Given a file with the content "test" is present in "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a file with the content "test" is present in "/tmp/input" And a GenerateFlowFile processor And ssl properties are set up for MiNiFi C2 server And a MiNiFi C2 server is set up with SSL diff --git a/docker/test/integration/features/opensearch.feature b/docker/test/integration/features/opensearch.feature index f4e2fabb86..5a6877f499 100644 --- a/docker/test/integration/features/opensearch.feature +++ b/docker/test/integration/features/opensearch.feature @@ -20,7 +20,8 @@ Feature: PostElasticsearch works on Opensearch (Opensearch doesnt support API Ke Given the content of "/tmp/output" is monitored Scenario Outline: MiNiFi instance creates a document on Opensearch using Basic Authentication - Given an Opensearch server is set up and running + Given OpenSSL FIPS mode is enabled in MiNiFi + And an Opensearch server is set up and running And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "{ "field1" : "value1" }" is present in "/tmp/input" And a PostElasticsearch processor @@ -44,7 +45,8 @@ Feature: PostElasticsearch works on Opensearch (Opensearch doesnt support API Ke | "create" | Scenario: MiNiFi instance deletes a document from Opensearch using Basic Authentication - Given an Opensearch server is set up and a single document is present with "preloaded_id" in "my_index" + Given OpenSSL FIPS mode is enabled in MiNiFi + And an Opensearch server is set up and a single document is present with "preloaded_id" in "my_index" And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "hello world" is present in "/tmp/input" And a PostElasticsearch processor @@ -63,7 +65,8 @@ Feature: PostElasticsearch works on Opensearch (Opensearch doesnt support API Ke And Opensearch is empty Scenario: MiNiFi instance partially updates a document in Opensearch using Basic Authentication - Given an Opensearch server is set up and a single document is present with "preloaded_id" in "my_index" with "value1" in "field1" + Given OpenSSL FIPS mode is enabled in MiNiFi + And an Opensearch server is set up and a single document is present with "preloaded_id" in "my_index" with "value1" in "field1" And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "{ "field2" : "value2" }" is present in "/tmp/input" And a PostElasticsearch processor diff --git a/docker/test/integration/features/prometheus.feature b/docker/test/integration/features/prometheus.feature index f336990b58..33a3ea2e79 100644 --- a/docker/test/integration/features/prometheus.feature +++ b/docker/test/integration/features/prometheus.feature @@ -37,7 +37,8 @@ Feature: MiNiFi can publish metrics to Prometheus server And all Prometheus metric types are only defined once Scenario: Published metrics are scraped by Prometheus server through SSL connection - Given a GetFile processor with the name "GetFile1" and the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the name "GetFile1" and the "Input Directory" property set to "/tmp/input" And a file with the content "test" is present in "/tmp/input" And a PutFile processor with the "Directory" property set to "/tmp/output" And the "success" relationship of the GetFile1 processor is connected to the PutFile diff --git a/docker/test/integration/features/s2s.feature b/docker/test/integration/features/s2s.feature index 80a8eb648c..f279a40587 100644 --- a/docker/test/integration/features/s2s.feature +++ b/docker/test/integration/features/s2s.feature @@ -79,7 +79,8 @@ Feature: Sending data from MiNiFi-C++ to NiFi using S2S protocol Then no files are placed in the monitored directory in 50 seconds of running time Scenario: A MiNiFi instance produces and transfers data to a NiFi instance via s2s using SSL - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And the "Keep Source File" property of the GetFile processor is set to "true" And a file with the content "test" is present in "/tmp/input" And a RemoteProcessGroup node opened on "https://nifi-${feature_id}:8443/nifi" @@ -96,7 +97,8 @@ Feature: Sending data from MiNiFi-C++ to NiFi using S2S protocol And the Minifi logs do not contain the following message: "ProcessSession rollback" after 1 seconds Scenario: A MiNiFi instance produces and transfers data to a NiFi instance via s2s using SSL with YAML config - Given a MiNiFi CPP server with yaml config + Given OpenSSL FIPS mode is enabled in MiNiFi + And a MiNiFi CPP server with yaml config And a GetFile processor with the "Input Directory" property set to "/tmp/input" And the "Keep Source File" property of the GetFile processor is set to "true" And a file with the content "test" is present in "/tmp/input" @@ -114,7 +116,8 @@ Feature: Sending data from MiNiFi-C++ to NiFi using S2S protocol And the Minifi logs do not contain the following message: "ProcessSession rollback" after 1 seconds Scenario: A MiNiFi instance produces and transfers data to a NiFi instance via s2s using SSL config defined in minifi.properties - Given a GetFile processor with the "Input Directory" property set to "/tmp/input" + Given OpenSSL FIPS mode is enabled in MiNiFi + And a GetFile processor with the "Input Directory" property set to "/tmp/input" And the "Keep Source File" property of the GetFile processor is set to "true" And a file with the content "test" is present in "/tmp/input" And a RemoteProcessGroup node opened on "https://nifi-${feature_id}:8443/nifi" @@ -131,7 +134,8 @@ Feature: Sending data from MiNiFi-C++ to NiFi using S2S protocol And the Minifi logs do not contain the following message: "ProcessSession rollback" after 1 seconds Scenario: A MiNiFi instance produces and transfers data to a NiFi instance via s2s using YAML config and SSL config defined in minifi.properties - Given a MiNiFi CPP server with yaml config + Given OpenSSL FIPS mode is enabled in MiNiFi + And a MiNiFi CPP server with yaml config And a GetFile processor with the "Input Directory" property set to "/tmp/input" And the "Keep Source File" property of the GetFile processor is set to "true" And a file with the content "test" is present in "/tmp/input" diff --git a/docker/test/integration/features/splunk.feature b/docker/test/integration/features/splunk.feature index a6f99285e6..3c18654d6b 100644 --- a/docker/test/integration/features/splunk.feature +++ b/docker/test/integration/features/splunk.feature @@ -44,7 +44,8 @@ Feature: Sending data to Splunk HEC using PutSplunkHTTP Scenario: A MiNiFi instance transfers data to a Splunk HEC with SSL enabled - Given a Splunk HEC is set up and running + Given OpenSSL FIPS mode is enabled in MiNiFi + And a Splunk HEC is set up and running And a GetFile processor with the "Input Directory" property set to "/tmp/input" And a file with the content "foobar" is present in "/tmp/input" And a PutSplunkHTTP processor set up to communicate with the Splunk HEC instance diff --git a/docker/test/integration/features/steps/steps.py b/docker/test/integration/features/steps/steps.py index e65ca2f29f..5d8a7c5565 100644 --- a/docker/test/integration/features/steps/steps.py +++ b/docker/test/integration/features/steps/steps.py @@ -372,6 +372,11 @@ def step_impl(context): context.test.enable_prometheus_with_ssl_in_minifi() +@given("OpenSSL FIPS mode is enabled in MiNiFi") +def step_impl(context): + context.test.enable_openssl_fips_mode_in_minifi() + + # HTTP proxy setup @given("the http proxy server is set up") @given("a http proxy server is set up accordingly") diff --git a/encrypt-config/tests/ConfigFileEncryptorTests.cpp b/encrypt-config/tests/ConfigFileEncryptorTests.cpp index ab7a9d2f88..dd326c5b97 100644 --- a/encrypt-config/tests/ConfigFileEncryptorTests.cpp +++ b/encrypt-config/tests/ConfigFileEncryptorTests.cpp @@ -77,7 +77,7 @@ TEST_CASE("ConfigFileEncryptor can encrypt the sensitive properties", "[encrypt- uint32_t num_properties_encrypted = encryptSensitivePropertiesInFile(test_file, KEY); REQUIRE(num_properties_encrypted == 1); - REQUIRE(test_file.size() == 101); + REQUIRE(test_file.size() == 104); REQUIRE(check_encryption(test_file, Configuration::nifi_rest_api_password, original_password.length())); SECTION("calling encryptSensitiveValuesInMinifiProperties a second time does nothing") { diff --git a/encrypt-config/tests/ConfigFileTests.cpp b/encrypt-config/tests/ConfigFileTests.cpp index cd45ac3cb1..cd998ff726 100644 --- a/encrypt-config/tests/ConfigFileTests.cpp +++ b/encrypt-config/tests/ConfigFileTests.cpp @@ -90,7 +90,7 @@ TEST_CASE("ConfigFile creates an empty object from a nonexistent file", "[encryp TEST_CASE("ConfigFile can parse a simple config file", "[encrypt-config][constructor]") { ConfigFile test_file{std::ifstream{"resources/minifi.properties"}}; - REQUIRE(test_file.size() == 100); + REQUIRE(test_file.size() == 103); } TEST_CASE("ConfigFile can test whether a key is present", "[encrypt-config][hasValue]") { @@ -102,7 +102,7 @@ TEST_CASE("ConfigFile can test whether a key is present", "[encrypt-config][hasV TEST_CASE("ConfigFile can read empty properties correctly", "[encrypt-config][constructor]") { ConfigFile test_file{std::ifstream{"resources/with-additional-sensitive-props.minifi.properties"}}; - REQUIRE(test_file.size() == 102); + REQUIRE(test_file.size() == 105); auto empty_property = test_file.getValue(Configuration::nifi_security_need_ClientAuth); REQUIRE(empty_property); @@ -143,7 +143,7 @@ TEST_CASE("ConfigFile can add a new setting after an existing setting", "[encryp SECTION("valid key") { test_file.insertAfter(Configuration::nifi_rest_api_password, "nifi.rest.api.password.protected", "my-cipher-name"); - REQUIRE(test_file.size() == 101); + REQUIRE(test_file.size() == 104); REQUIRE(test_file.getValue("nifi.rest.api.password.protected") == "my-cipher-name"); } @@ -158,7 +158,7 @@ TEST_CASE("ConfigFile can add a new setting at the end", "[encrypt-config][appen const std::string KEY = "nifi.bootstrap.sensitive.key"; const std::string VALUE = "aa411f289c91685ef9d5a9e5a4fad9393ff4c7a78ab978484323488caed7a9ab"; test_file.append(KEY, VALUE); - REQUIRE(test_file.size() == 101); + REQUIRE(test_file.size() == 104); REQUIRE(test_file.getValue(KEY) == std::make_optional(VALUE)); } diff --git a/encrypt-config/tests/resources/minifi.properties b/encrypt-config/tests/resources/minifi.properties index 2f2db68bb7..6bb6566df5 100644 --- a/encrypt-config/tests/resources/minifi.properties +++ b/encrypt-config/tests/resources/minifi.properties @@ -98,3 +98,6 @@ nifi.python.processor.dir=${MINIFI_HOME}/minifi-python/ nifi.python.virtualenv.directory=${MINIFI_HOME}/minifi-python-env nifi.python.install.packages.automatically=true # nifi.python.env.setup.binary=python3 + +# FIPS +# nifi.openssl.fips.support.enable=false diff --git a/encrypt-config/tests/resources/with-additional-sensitive-props.minifi.properties b/encrypt-config/tests/resources/with-additional-sensitive-props.minifi.properties index d2702c34df..8f468b2c83 100644 --- a/encrypt-config/tests/resources/with-additional-sensitive-props.minifi.properties +++ b/encrypt-config/tests/resources/with-additional-sensitive-props.minifi.properties @@ -100,3 +100,6 @@ nifi.python.processor.dir=${MINIFI_HOME}/minifi-python/ nifi.python.virtualenv.directory=${MINIFI_HOME}/minifi-python-env nifi.python.install.packages.automatically=true # nifi.python.env.setup.binary=python3 + +# FIPS +# nifi.openssl.fips.support.enable=false diff --git a/extensions/mqtt/processors/AbstractMQTTProcessor.cpp b/extensions/mqtt/processors/AbstractMQTTProcessor.cpp index 3c73ae586b..c7dc5dd823 100644 --- a/extensions/mqtt/processors/AbstractMQTTProcessor.cpp +++ b/extensions/mqtt/processors/AbstractMQTTProcessor.cpp @@ -318,7 +318,7 @@ void AbstractMQTTProcessor::disconnect() { void AbstractMQTTProcessor::setBrokerLimits(MQTTAsync_successData5* response) { auto readProperty = [response] (MQTTPropertyCodes property_code, auto& out_var) { - const int value = MQTTProperties_getNumericValue(&response->properties, property_code); + const auto value = MQTTProperties_getNumericValue(&response->properties, property_code); if (value != PAHO_MQTT_C_FAILURE_CODE) { if constexpr (std::is_same_v&>) { out_var = std::chrono::seconds(value); diff --git a/fips/openssl.cnf b/fips/openssl.cnf new file mode 100644 index 0000000000..bbedfdbfb1 --- /dev/null +++ b/fips/openssl.cnf @@ -0,0 +1,9 @@ +openssl_conf = openssl_init + +.include ${MINIFI_HOME}/fips/fipsmodule.cnf + +[openssl_init] +providers = prov + +[prov] +fips = fips_sect diff --git a/libminifi/src/Configuration.cpp b/libminifi/src/Configuration.cpp index df50a7d728..8acabcdb2f 100644 --- a/libminifi/src/Configuration.cpp +++ b/libminifi/src/Configuration.cpp @@ -154,7 +154,8 @@ const std::unordered_map Configuration::DEFAULT_SENSITIVE_PROPERTIES = {Configuration::nifi_security_client_pass_phrase, diff --git a/libminifi/test/resources/encrypted.minifi.properties b/libminifi/test/resources/encrypted.minifi.properties index f19422e43f..dc1bf52728 100644 --- a/libminifi/test/resources/encrypted.minifi.properties +++ b/libminifi/test/resources/encrypted.minifi.properties @@ -104,3 +104,6 @@ nifi.python.processor.dir=${MINIFI_HOME}/minifi-python/ nifi.python.virtualenv.directory=${MINIFI_HOME}/minifi-python-env nifi.python.install.packages.automatically=true # nifi.python.env.setup.binary=python3 + +# FIPS +# nifi.openssl.fips.support.enable=false diff --git a/minifi-api/include/minifi-cpp/properties/Configuration.h b/minifi-api/include/minifi-cpp/properties/Configuration.h index 280771ca54..2009e746f1 100644 --- a/minifi-api/include/minifi-cpp/properties/Configuration.h +++ b/minifi-api/include/minifi-cpp/properties/Configuration.h @@ -200,6 +200,8 @@ class Configuration : public virtual Properties { static constexpr const char *nifi_python_env_setup_binary = "nifi.python.env.setup.binary"; static constexpr const char *nifi_python_install_packages_automatically = "nifi.python.install.packages.automatically"; + static constexpr const char *nifi_openssl_fips_support_enable = "nifi.openssl.fips.support.enable"; + MINIFIAPI static const std::unordered_map> CONFIGURATION_PROPERTIES; MINIFIAPI static const std::array DEFAULT_SENSITIVE_PROPERTIES; diff --git a/minifi_main/MiNiFiMain.cpp b/minifi_main/MiNiFiMain.cpp index 2297048f8f..c2727c7f00 100644 --- a/minifi_main/MiNiFiMain.cpp +++ b/minifi_main/MiNiFiMain.cpp @@ -44,6 +44,10 @@ #include #include #include +#include +#include +#include +#include #include "ResourceClaim.h" #include "core/Core.h" @@ -183,6 +187,100 @@ void writeSchemaIfRequested(const argparse::ArgumentParser& parser, const std::s std::exit(0); } +bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, std::string minifi_home_path, const std::shared_ptr& logger) { + std::ifstream input_file(file_path); + if (!input_file) { + logger->log_error("Failed to open file: {}", file_path.string()); + return false; + } + + std::ostringstream buffer; + buffer << input_file.rdbuf(); + std::string content = buffer.str(); + input_file.close(); + + const std::string placeholder = "${MINIFI_HOME}"; + size_t pos = content.find(placeholder, 0); + if (pos == std::string::npos) { + return true; + } + + std::replace(minifi_home_path.begin(), minifi_home_path.end(), '\\', '/'); + do { + content.replace(pos, placeholder.length(), minifi_home_path); + pos += minifi_home_path.length(); + } while((pos = content.find(placeholder, pos)) != std::string::npos); + + std::ofstream output_file(file_path); + if (!output_file) { + logger->log_error("Failed to open file for writing: {}", file_path.string()); + return false; + } + + output_file << content; + output_file.close(); + return true; +} + +void initializeFipsMode(const std::shared_ptr& configure, const std::filesystem::path& minifi_home, const std::shared_ptr& logger) { + if (!(configure->get(minifi::Configure::nifi_openssl_fips_support_enable) | utils::andThen(utils::string::toBool)).value_or(false)) { + return; + } + +#ifdef WIN32 + static constexpr std::string_view FIPS_LIB = "fips.dll"; +#elif defined(__APPLE__) + static constexpr std::string_view FIPS_LIB = "fips.dylib"; +#else + static constexpr std::string_view FIPS_LIB = "fips.so"; +#endif + + if (!std::filesystem::exists(minifi_home / "fips" / FIPS_LIB)) { + logger->log_error("FIPS mode is enabled, but {} is not available in MINIFI_HOME/fips directory", FIPS_LIB); + std::exit(1); + } + + if (!std::filesystem::exists(minifi_home / "fips" / "fipsmodule.cnf")) { + logger->log_error("FIPS mode is enabled, but fipsmodule.cnf is not available in MINIFI_HOME/fips directory. " + "Run MINIFI_HOME/fips/openssl fipsinstall -out fipsmodule.cnf -module MINIFI_HOME/fips/{} command to generate the configuration file", FIPS_LIB); + std::exit(1); + } + + if (!std::filesystem::exists(minifi_home / "fips" / "openssl.cnf")) { + logger->log_error("FIPS mode is enabled, but openssl.cnf is not available in MINIFI_HOME/fips directory"); + std::exit(1); + } + + if (!replaceMinifiHomeVariable(minifi_home / "fips" / "openssl.cnf", minifi_home.string(), logger)) { + logger->log_error("Failed to replace MINIFI_HOME variable in openssl.cnf"); + std::exit(1); + } + + utils::Environment::setEnvironmentVariable("OPENSSL_CONF", (minifi_home / "fips" / "openssl.cnf").string().c_str(), true); + + if (!OSSL_PROVIDER_set_default_search_path(nullptr, (minifi_home / "fips").string().c_str())) { + logger->log_error("Failed to set FIPS module path: {}", (minifi_home / "fips").string()); + std::exit(1); + } + + if (OSSL_PROVIDER_available(nullptr, "fips") != 1) { + logger->log_error("FIPS provider not available in default search path"); + std::exit(1); + } + + if (!EVP_default_properties_enable_fips(nullptr, 1)) { + logger->log_error("Failed to enable FIPS mode"); + std::exit(1); + } + + if (!EVP_default_properties_is_fips_enabled(nullptr)) { + logger->log_error("FIPS mode is not enabled"); + std::exit(1); + } + + logger->log_info("FIPS mode enabled in MiNiFi C++"); +} + int main(int argc, char **argv) { argparse::ArgumentParser argument_parser("Apache MiNiFi C++", minifi::AgentBuild::VERSION); argument_parser.add_argument("-p", "--property") @@ -333,6 +431,8 @@ int main(int argc, char **argv) { configure->loadConfigureFile(DEFAULT_NIFI_PROPERTIES_FILE); overridePropertiesFromCommandLine(argument_parser, configure); + initializeFipsMode(configure, minifiHome, logger); + minifi::core::extension::ExtensionManagerImpl::get().initialize(configure); dumpDocsIfRequested(argument_parser, configure); diff --git a/msi/WixWin.wsi.in b/msi/WixWin.wsi.in index d39c5daedd..3e4ccb5b37 100644 --- a/msi/WixWin.wsi.in +++ b/msi/WixWin.wsi.in @@ -59,6 +59,7 @@ ${WIX_EXTRA_FEATURES} + @@ -326,6 +327,12 @@ ${WIX_EXTRA_FEATURES} + + + + + + diff --git a/thirdparty/paho-mqtt/cmake-openssl.patch b/thirdparty/paho-mqtt/cmake-openssl.patch index 4f94b89e0c..b62f72fda6 100644 --- a/thirdparty/paho-mqtt/cmake-openssl.patch +++ b/thirdparty/paho-mqtt/cmake-openssl.patch @@ -1,56 +1,58 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 7bfee10..5debc1f 100644 +index ef145cd..a069f83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -23,7 +23,7 @@ MESSAGE(STATUS "CMake version: " ${CMAKE_VERSION}) - MESSAGE(STATUS "CMake system name: " ${CMAKE_SYSTEM_NAME}) +@@ -26,7 +26,7 @@ message(STATUS "CMake version: " ${CMAKE_VERSION}) + message(STATUS "CMake system name: " ${CMAKE_SYSTEM_NAME}) - SET(CMAKE_SCRIPTS "${CMAKE_SOURCE_DIR}/cmake") --SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") -+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") + set(CMAKE_SCRIPTS "${PROJECT_SOURCE_DIR}/cmake") +-set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") ++list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") - ## build settings - file(READ version.major PAHO_VERSION_MAJOR) + ## Project Version + ## Previously we read in the version from these files, but now we use the diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 0bc7194..d2f8e6d 100644 +index 16382c1..c56d703 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt -@@ -316,20 +316,22 @@ INSTALL(FILES - "${CMAKE_CURRENT_BINARY_DIR}/eclipse-paho-mqtt-cConfigVersion.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c) +@@ -412,22 +412,24 @@ install( + ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c + ) -# Base64 test --ADD_EXECUTABLE( Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h ) --TARGET_COMPILE_DEFINITIONS( Base64Test PUBLIC "-DBASE64_TEST" ) --IF (PAHO_WITH_SSL) -- ADD_EXECUTABLE( Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h ) -- TARGET_LINK_LIBRARIES( Base64TestOpenSSL OpenSSL::SSL OpenSSL::Crypto) -- TARGET_COMPILE_DEFINITIONS( Base64TestOpenSSL PUBLIC "-DBASE64_TEST -DOPENSSL=1" ) --ENDIF (PAHO_WITH_SSL) -+IF(PAHO_ENABLE_TESTING) -+ # Base64 test -+ ADD_EXECUTABLE( Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h ) -+ TARGET_COMPILE_DEFINITIONS( Base64Test PUBLIC "-DBASE64_TEST" ) -+ IF (PAHO_WITH_SSL) -+ ADD_EXECUTABLE( Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h ) -+ TARGET_LINK_LIBRARIES( Base64TestOpenSSL OpenSSL::SSL OpenSSL::Crypto) -+ TARGET_COMPILE_DEFINITIONS( Base64TestOpenSSL PUBLIC "-DBASE64_TEST -DOPENSSL=1" ) -+ ENDIF (PAHO_WITH_SSL) +-add_executable(Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h) +-target_compile_definitions(Base64Test PUBLIC BASE64_TEST) +- +-if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL) +- add_executable(Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h ) +- target_link_libraries(Base64TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto) +- target_compile_definitions(Base64TestOpenSSL PUBLIC BASE64_TEST OPENSSL=1) +-endif() ++if(PAHO_ENABLE_TESTING) ++ # Base64 test ++ add_executable(Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h) ++ target_compile_definitions(Base64Test PUBLIC BASE64_TEST) ++ ++ if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL) ++ add_executable(Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h ) ++ target_link_libraries(Base64TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto) ++ target_compile_definitions(Base64TestOpenSSL PUBLIC BASE64_TEST OPENSSL=1) ++ endif() -# SHA1 test --ADD_EXECUTABLE( Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h ) --TARGET_COMPILE_DEFINITIONS( Sha1Test PUBLIC "-DSHA1_TEST" ) --IF (PAHO_WITH_SSL) -- ADD_EXECUTABLE( Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h ) -- TARGET_LINK_LIBRARIES( Sha1TestOpenSSL OpenSSL::SSL OpenSSL::Crypto) -- TARGET_COMPILE_DEFINITIONS( Sha1TestOpenSSL PUBLIC "-DSHA1_TEST -DOPENSSL=1" ) --ENDIF (PAHO_WITH_SSL) -+ # SHA1 test -+ ADD_EXECUTABLE( Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h ) -+ TARGET_COMPILE_DEFINITIONS( Sha1Test PUBLIC "-DSHA1_TEST" ) -+ IF (PAHO_WITH_SSL) -+ ADD_EXECUTABLE( Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h ) -+ TARGET_LINK_LIBRARIES( Sha1TestOpenSSL OpenSSL::SSL OpenSSL::Crypto) -+ TARGET_COMPILE_DEFINITIONS( Sha1TestOpenSSL PUBLIC "-DSHA1_TEST -DOPENSSL=1" ) -+ ENDIF (PAHO_WITH_SSL) -+ENDIF() +-add_executable(Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h) +-target_compile_definitions(Sha1Test PUBLIC SHA1_TEST) ++ # SHA1 test ++ add_executable(Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h) ++ target_compile_definitions(Sha1Test PUBLIC SHA1_TEST) + +-if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL) +- add_executable(Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h) +- target_link_libraries(Sha1TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto) +- target_compile_definitions(Sha1TestOpenSSL PUBLIC SHA1_TEST OPENSSL=1) ++ if(PAHO_WITH_SSL OR PAHO_WITH_LIBRESSL) ++ add_executable(Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h) ++ target_link_libraries(Sha1TestOpenSSL ${SSL_LIBRARY_NAME}::SSL ${SSL_LIBRARY_NAME}::Crypto) ++ target_compile_definitions(Sha1TestOpenSSL PUBLIC SHA1_TEST OPENSSL=1) ++ endif() + endif() From a893f09f0598bcc9d38bcf2a23bee3f06868c985 Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Wed, 5 Mar 2025 16:26:50 +0100 Subject: [PATCH 2/5] Review update --- minifi_main/MiNiFiMain.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/minifi_main/MiNiFiMain.cpp b/minifi_main/MiNiFiMain.cpp index c2727c7f00..d537a026f7 100644 --- a/minifi_main/MiNiFiMain.cpp +++ b/minifi_main/MiNiFiMain.cpp @@ -187,7 +187,7 @@ void writeSchemaIfRequested(const argparse::ArgumentParser& parser, const std::s std::exit(0); } -bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, std::string minifi_home_path, const std::shared_ptr& logger) { +bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, const std::filesystem::path& minifi_home_path, const std::shared_ptr& logger) { std::ifstream input_file(file_path); if (!input_file) { logger->log_error("Failed to open file: {}", file_path.string()); @@ -205,10 +205,10 @@ bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, std::stri return true; } - std::replace(minifi_home_path.begin(), minifi_home_path.end(), '\\', '/'); + auto minifi_home_path_str = minifi_home_path.generic_string(); do { - content.replace(pos, placeholder.length(), minifi_home_path); - pos += minifi_home_path.length(); + content.replace(pos, placeholder.length(), minifi_home_path_str); + pos += minifi_home_path_str.length(); } while((pos = content.find(placeholder, pos)) != std::string::npos); std::ofstream output_file(file_path); @@ -251,7 +251,7 @@ void initializeFipsMode(const std::shared_ptr& configure, con std::exit(1); } - if (!replaceMinifiHomeVariable(minifi_home / "fips" / "openssl.cnf", minifi_home.string(), logger)) { + if (!replaceMinifiHomeVariable(minifi_home / "fips" / "openssl.cnf", minifi_home, logger)) { logger->log_error("Failed to replace MINIFI_HOME variable in openssl.cnf"); std::exit(1); } @@ -260,21 +260,25 @@ void initializeFipsMode(const std::shared_ptr& configure, con if (!OSSL_PROVIDER_set_default_search_path(nullptr, (minifi_home / "fips").string().c_str())) { logger->log_error("Failed to set FIPS module path: {}", (minifi_home / "fips").string()); + ERR_print_errors_fp(stderr); std::exit(1); } if (OSSL_PROVIDER_available(nullptr, "fips") != 1) { logger->log_error("FIPS provider not available in default search path"); + ERR_print_errors_fp(stderr); std::exit(1); } if (!EVP_default_properties_enable_fips(nullptr, 1)) { logger->log_error("Failed to enable FIPS mode"); + ERR_print_errors_fp(stderr); std::exit(1); } if (!EVP_default_properties_is_fips_enabled(nullptr)) { logger->log_error("FIPS mode is not enabled"); + ERR_print_errors_fp(stderr); std::exit(1); } @@ -431,13 +435,13 @@ int main(int argc, char **argv) { configure->loadConfigureFile(DEFAULT_NIFI_PROPERTIES_FILE); overridePropertiesFromCommandLine(argument_parser, configure); - initializeFipsMode(configure, minifiHome, logger); - minifi::core::extension::ExtensionManagerImpl::get().initialize(configure); dumpDocsIfRequested(argument_parser, configure); writeSchemaIfRequested(argument_parser, configure); + initializeFipsMode(configure, minifiHome, logger); + std::chrono::milliseconds stop_wait_time = configure->get(minifi::Configure::nifi_graceful_shutdown_seconds) | utils::andThen(utils::timeutils::StringToDuration) | utils::valueOrElse([] { return std::chrono::milliseconds(STOP_WAIT_TIME_MS);}); From 6633f77520728571c814955ea23443d17f861141 Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Thu, 6 Mar 2025 10:27:45 +0100 Subject: [PATCH 3/5] Fix after rebase --- cmake/BundledOpenSSL.cmake | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cmake/BundledOpenSSL.cmake b/cmake/BundledOpenSSL.cmake index 9d36c2f763..f69ff30ec8 100644 --- a/cmake/BundledOpenSSL.cmake +++ b/cmake/BundledOpenSSL.cmake @@ -57,16 +57,12 @@ function(use_openssl SOURCE_DIR BINARY_DIR) set(OPENSSL_EXTRA_FLAGS no-tests # Disable tests - no-apps # disable executables no-capieng # disable CAPI engine (legacy) - no-dso # disable dynamic libraries no-docs # disable docs and manpages no-legacy # disable legacy modules - no-module # disable dynamically loadable engines - no-pinshared # don't pin shared libraries in the process memory enable-tfo # Enable TCP Fast Open no-ssl # disable SSLv3 - no-engine) + no-engine) # disable Engine API as it is deprecated since OpenSSL 3.0 and not FIPS compatible set(OPENSSL_BIN_DIR "${BINARY_DIR}/thirdparty/openssl-install" CACHE STRING "" FORCE) @@ -195,6 +191,14 @@ function(use_openssl SOURCE_DIR BINARY_DIR) install(FILES "${OPENSSL_BIN_DIR}/bin/openssl${EXECUTABLE_SUFFIX}" DESTINATION fips COMPONENT bin PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) + set(OPENSSL_FIPS_EXTRA_FLAGS + no-tests # Disable tests + no-capieng # disable CAPI engine (legacy) + no-legacy # disable legacy modules + no-ssl # disable SSLv3 + no-engine # disable Engine API as it is deprecated since OpenSSL 3.0 and not FIPS compatible + enable-fips) # enable FIPS module + if (WIN32) find_program(JOM_EXECUTABLE_PATH NAMES jom.exe @@ -216,7 +220,7 @@ function(use_openssl SOURCE_DIR BINARY_DIR) URL_HASH "SHA256=eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90" SOURCE_DIR "${BINARY_DIR}/thirdparty/openssl-fips-src" BUILD_IN_SOURCE true - CONFIGURE_COMMAND perl Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" ${OPENSSL_SHARED_FLAG} no-engine enable-fips no-tests "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" + CONFIGURE_COMMAND perl Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} ${OPENSSL_WINDOWS_COMPILE_FLAGS}" ${OPENSSL_SHARED_FLAG} ${OPENSSL_FIPS_EXTRA_FLAGS} enable-fips "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" BUILD_BYPRODUCTS ${OPENSSL_FIPS_FILE_LIST} EXCLUDE_FROM_ALL TRUE BUILD_COMMAND ${OPENSSL_BUILD_COMMAND} @@ -229,7 +233,7 @@ function(use_openssl SOURCE_DIR BINARY_DIR) URL_HASH "SHA256=eb1ab04781474360f77c318ab89d8c5a03abc38e63d65a603cabbf1b00a1dc90" SOURCE_DIR "${BINARY_DIR}/thirdparty/openssl-fips-src" BUILD_IN_SOURCE true - CONFIGURE_COMMAND ./Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} -fPIC" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} -fPIC" ${OPENSSL_SHARED_FLAG} no-engine enable-fips no-tests "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" + CONFIGURE_COMMAND ./Configure "CFLAGS=${PASSTHROUGH_CMAKE_C_FLAGS} -fPIC" "CXXFLAGS=${PASSTHROUGH_CMAKE_CXX_FLAGS} -fPIC" ${OPENSSL_SHARED_FLAG} ${OPENSSL_FIPS_EXTRA_FLAGS} "--prefix=${OPENSSL_FIPS_BIN_DIR}" "--openssldir=${OPENSSL_FIPS_BIN_DIR}" BUILD_BYPRODUCTS ${OPENSSL_FIPS_FILE_LIST} EXCLUDE_FROM_ALL TRUE INSTALL_COMMAND make install_fips From 0418b1866dcc20de4494130deacae025d61879e5 Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Thu, 6 Mar 2025 11:11:42 +0100 Subject: [PATCH 4/5] Review update --- minifi_main/MiNiFiMain.cpp | 105 +------------------------ utils/include/utils/FipsUtils.h | 28 +++++++ utils/src/utils/FipsUtils.cpp | 131 ++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 103 deletions(-) create mode 100644 utils/include/utils/FipsUtils.h create mode 100644 utils/src/utils/FipsUtils.cpp diff --git a/minifi_main/MiNiFiMain.cpp b/minifi_main/MiNiFiMain.cpp index d537a026f7..340b294883 100644 --- a/minifi_main/MiNiFiMain.cpp +++ b/minifi_main/MiNiFiMain.cpp @@ -44,10 +44,6 @@ #include #include #include -#include -#include -#include -#include #include "ResourceClaim.h" #include "core/Core.h" @@ -73,6 +69,7 @@ #include "core/state/MetricsPublisherStore.h" #include "argparse/argparse.hpp" #include "agent/agent_version.h" +#include "utils/FipsUtils.h" namespace minifi = org::apache::nifi::minifi; namespace core = minifi::core; @@ -187,104 +184,6 @@ void writeSchemaIfRequested(const argparse::ArgumentParser& parser, const std::s std::exit(0); } -bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, const std::filesystem::path& minifi_home_path, const std::shared_ptr& logger) { - std::ifstream input_file(file_path); - if (!input_file) { - logger->log_error("Failed to open file: {}", file_path.string()); - return false; - } - - std::ostringstream buffer; - buffer << input_file.rdbuf(); - std::string content = buffer.str(); - input_file.close(); - - const std::string placeholder = "${MINIFI_HOME}"; - size_t pos = content.find(placeholder, 0); - if (pos == std::string::npos) { - return true; - } - - auto minifi_home_path_str = minifi_home_path.generic_string(); - do { - content.replace(pos, placeholder.length(), minifi_home_path_str); - pos += minifi_home_path_str.length(); - } while((pos = content.find(placeholder, pos)) != std::string::npos); - - std::ofstream output_file(file_path); - if (!output_file) { - logger->log_error("Failed to open file for writing: {}", file_path.string()); - return false; - } - - output_file << content; - output_file.close(); - return true; -} - -void initializeFipsMode(const std::shared_ptr& configure, const std::filesystem::path& minifi_home, const std::shared_ptr& logger) { - if (!(configure->get(minifi::Configure::nifi_openssl_fips_support_enable) | utils::andThen(utils::string::toBool)).value_or(false)) { - return; - } - -#ifdef WIN32 - static constexpr std::string_view FIPS_LIB = "fips.dll"; -#elif defined(__APPLE__) - static constexpr std::string_view FIPS_LIB = "fips.dylib"; -#else - static constexpr std::string_view FIPS_LIB = "fips.so"; -#endif - - if (!std::filesystem::exists(minifi_home / "fips" / FIPS_LIB)) { - logger->log_error("FIPS mode is enabled, but {} is not available in MINIFI_HOME/fips directory", FIPS_LIB); - std::exit(1); - } - - if (!std::filesystem::exists(minifi_home / "fips" / "fipsmodule.cnf")) { - logger->log_error("FIPS mode is enabled, but fipsmodule.cnf is not available in MINIFI_HOME/fips directory. " - "Run MINIFI_HOME/fips/openssl fipsinstall -out fipsmodule.cnf -module MINIFI_HOME/fips/{} command to generate the configuration file", FIPS_LIB); - std::exit(1); - } - - if (!std::filesystem::exists(minifi_home / "fips" / "openssl.cnf")) { - logger->log_error("FIPS mode is enabled, but openssl.cnf is not available in MINIFI_HOME/fips directory"); - std::exit(1); - } - - if (!replaceMinifiHomeVariable(minifi_home / "fips" / "openssl.cnf", minifi_home, logger)) { - logger->log_error("Failed to replace MINIFI_HOME variable in openssl.cnf"); - std::exit(1); - } - - utils::Environment::setEnvironmentVariable("OPENSSL_CONF", (minifi_home / "fips" / "openssl.cnf").string().c_str(), true); - - if (!OSSL_PROVIDER_set_default_search_path(nullptr, (minifi_home / "fips").string().c_str())) { - logger->log_error("Failed to set FIPS module path: {}", (minifi_home / "fips").string()); - ERR_print_errors_fp(stderr); - std::exit(1); - } - - if (OSSL_PROVIDER_available(nullptr, "fips") != 1) { - logger->log_error("FIPS provider not available in default search path"); - ERR_print_errors_fp(stderr); - std::exit(1); - } - - if (!EVP_default_properties_enable_fips(nullptr, 1)) { - logger->log_error("Failed to enable FIPS mode"); - ERR_print_errors_fp(stderr); - std::exit(1); - } - - if (!EVP_default_properties_is_fips_enabled(nullptr)) { - logger->log_error("FIPS mode is not enabled"); - ERR_print_errors_fp(stderr); - std::exit(1); - } - - logger->log_info("FIPS mode enabled in MiNiFi C++"); -} - int main(int argc, char **argv) { argparse::ArgumentParser argument_parser("Apache MiNiFi C++", minifi::AgentBuild::VERSION); argument_parser.add_argument("-p", "--property") @@ -440,7 +339,7 @@ int main(int argc, char **argv) { dumpDocsIfRequested(argument_parser, configure); writeSchemaIfRequested(argument_parser, configure); - initializeFipsMode(configure, minifiHome, logger); + utils::fips::initializeFipsMode(configure, minifiHome, logger); std::chrono::milliseconds stop_wait_time = configure->get(minifi::Configure::nifi_graceful_shutdown_seconds) | utils::andThen(utils::timeutils::StringToDuration) diff --git a/utils/include/utils/FipsUtils.h b/utils/include/utils/FipsUtils.h new file mode 100644 index 0000000000..e8d461f3f9 --- /dev/null +++ b/utils/include/utils/FipsUtils.h @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include "core/logging/Logger.h" +#include "minifi-cpp/properties/Configure.h" + +namespace org::apache::nifi::minifi::utils::fips { + +void initializeFipsMode(const std::shared_ptr& configure, const std::filesystem::path& minifi_home, const std::shared_ptr& logger); + +} // namespace org::apache::nifi::minifi::utils::fips diff --git a/utils/src/utils/FipsUtils.cpp b/utils/src/utils/FipsUtils.cpp new file mode 100644 index 0000000000..2943392a35 --- /dev/null +++ b/utils/src/utils/FipsUtils.cpp @@ -0,0 +1,131 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "utils/FipsUtils.h" + +#include +#include +#include +#include +#include +#include +#include "utils/Environment.h" +#include "utils/StringUtils.h" +#include "utils/OptionalUtils.h" + +namespace org::apache::nifi::minifi::utils::fips { + +namespace { +bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, const std::filesystem::path& minifi_home_path, const std::shared_ptr& logger) { + std::ifstream input_file(file_path); + if (!input_file) { + logger->log_error("Failed to open file: {}", file_path.string()); + return false; + } + + std::ostringstream buffer; + buffer << input_file.rdbuf(); + std::string content = buffer.str(); + input_file.close(); + + const std::string placeholder = "${MINIFI_HOME}"; + size_t pos = content.find(placeholder, 0); + if (pos == std::string::npos) { + return true; + } + + auto minifi_home_path_str = minifi_home_path.generic_string(); + do { + content.replace(pos, placeholder.length(), minifi_home_path_str); + pos += minifi_home_path_str.length(); + } while((pos = content.find(placeholder, pos)) != std::string::npos); + + std::ofstream output_file(file_path); + if (!output_file) { + logger->log_error("Failed to open file for writing: {}", file_path.string()); + return false; + } + + output_file << content; + output_file.close(); + return true; +} +} // namespace + +void initializeFipsMode(const std::shared_ptr& configure, const std::filesystem::path& minifi_home, const std::shared_ptr& logger) { + if (!(configure->get(minifi::Configure::nifi_openssl_fips_support_enable) | utils::andThen(utils::string::toBool)).value_or(false)) { + return; + } + +#ifdef WIN32 + static constexpr std::string_view FIPS_LIB = "fips.dll"; +#elif defined(__APPLE__) + static constexpr std::string_view FIPS_LIB = "fips.dylib"; +#else + static constexpr std::string_view FIPS_LIB = "fips.so"; +#endif + + if (!std::filesystem::exists(minifi_home / "fips" / FIPS_LIB)) { + logger->log_error("FIPS mode is enabled, but {} is not available in MINIFI_HOME/fips directory", FIPS_LIB); + std::exit(1); + } + + if (!std::filesystem::exists(minifi_home / "fips" / "fipsmodule.cnf")) { + logger->log_error("FIPS mode is enabled, but fipsmodule.cnf is not available in MINIFI_HOME/fips directory. " + "Run MINIFI_HOME/fips/openssl fipsinstall -out fipsmodule.cnf -module MINIFI_HOME/fips/{} command to generate the configuration file", FIPS_LIB); + std::exit(1); + } + + if (!std::filesystem::exists(minifi_home / "fips" / "openssl.cnf")) { + logger->log_error("FIPS mode is enabled, but openssl.cnf is not available in MINIFI_HOME/fips directory"); + std::exit(1); + } + + if (!replaceMinifiHomeVariable(minifi_home / "fips" / "openssl.cnf", minifi_home, logger)) { + logger->log_error("Failed to replace MINIFI_HOME variable in openssl.cnf"); + std::exit(1); + } + + utils::Environment::setEnvironmentVariable("OPENSSL_CONF", (minifi_home / "fips" / "openssl.cnf").string().c_str(), true); + + if (!OSSL_PROVIDER_set_default_search_path(nullptr, (minifi_home / "fips").string().c_str())) { + logger->log_error("Failed to set FIPS module path: {}", (minifi_home / "fips").string()); + ERR_print_errors_fp(stderr); + std::exit(1); + } + + if (OSSL_PROVIDER_available(nullptr, "fips") != 1) { + logger->log_error("FIPS provider not available in default search path"); + ERR_print_errors_fp(stderr); + std::exit(1); + } + + if (!EVP_default_properties_enable_fips(nullptr, 1)) { + logger->log_error("Failed to enable FIPS mode"); + ERR_print_errors_fp(stderr); + std::exit(1); + } + + if (!EVP_default_properties_is_fips_enabled(nullptr)) { + logger->log_error("FIPS mode is not enabled"); + ERR_print_errors_fp(stderr); + std::exit(1); + } + + logger->log_info("FIPS mode enabled in MiNiFi C++"); +} + +} // namespace org::apache::nifi::minifi::utils::fips From 616769474c215ddfd4ac310f005a2d41cd70dbec Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Thu, 6 Mar 2025 13:41:00 +0100 Subject: [PATCH 5/5] Review update --- minifi_main/CMakeLists.txt | 2 +- utils/src/utils/FipsUtils.cpp => minifi_main/Fips.cpp | 6 +++--- utils/include/utils/FipsUtils.h => minifi_main/Fips.h | 2 +- minifi_main/MiNiFiMain.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename utils/src/utils/FipsUtils.cpp => minifi_main/Fips.cpp (97%) rename utils/include/utils/FipsUtils.h => minifi_main/Fips.h (95%) diff --git a/minifi_main/CMakeLists.txt b/minifi_main/CMakeLists.txt index 47a4ab6699..cddaf88736 100644 --- a/minifi_main/CMakeLists.txt +++ b/minifi_main/CMakeLists.txt @@ -33,7 +33,7 @@ endif() include(CppVersion) set_cpp_version() -set(MINIFIEXE_SOURCES MiNiFiMain.cpp MainHelper.cpp MiNiFiWindowsService.cpp AgentDocs.cpp TableFormatter.cpp) +set(MINIFIEXE_SOURCES MiNiFiMain.cpp MainHelper.cpp MiNiFiWindowsService.cpp AgentDocs.cpp TableFormatter.cpp Fips.cpp) if(WIN32) set_source_files_properties("${CMAKE_BINARY_DIR}/libminifi/src/core/logging/WindowsMessageTextFile.rc" PROPERTIES GENERATED TRUE) list(APPEND MINIFIEXE_SOURCES "${CMAKE_BINARY_DIR}/libminifi/src/core/logging/WindowsMessageTextFile.rc") diff --git a/utils/src/utils/FipsUtils.cpp b/minifi_main/Fips.cpp similarity index 97% rename from utils/src/utils/FipsUtils.cpp rename to minifi_main/Fips.cpp index 2943392a35..4a305a104d 100644 --- a/utils/src/utils/FipsUtils.cpp +++ b/minifi_main/Fips.cpp @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "utils/FipsUtils.h" +#include "Fips.h" #include #include @@ -26,7 +26,7 @@ #include "utils/StringUtils.h" #include "utils/OptionalUtils.h" -namespace org::apache::nifi::minifi::utils::fips { +namespace org::apache::nifi::minifi::fips { namespace { bool replaceMinifiHomeVariable(const std::filesystem::path& file_path, const std::filesystem::path& minifi_home_path, const std::shared_ptr& logger) { @@ -128,4 +128,4 @@ void initializeFipsMode(const std::shared_ptr& configure, con logger->log_info("FIPS mode enabled in MiNiFi C++"); } -} // namespace org::apache::nifi::minifi::utils::fips +} // namespace org::apache::nifi::minifi::fips diff --git a/utils/include/utils/FipsUtils.h b/minifi_main/Fips.h similarity index 95% rename from utils/include/utils/FipsUtils.h rename to minifi_main/Fips.h index e8d461f3f9..ab3261b0e1 100644 --- a/utils/include/utils/FipsUtils.h +++ b/minifi_main/Fips.h @@ -21,7 +21,7 @@ #include "core/logging/Logger.h" #include "minifi-cpp/properties/Configure.h" -namespace org::apache::nifi::minifi::utils::fips { +namespace org::apache::nifi::minifi::fips { void initializeFipsMode(const std::shared_ptr& configure, const std::filesystem::path& minifi_home, const std::shared_ptr& logger); diff --git a/minifi_main/MiNiFiMain.cpp b/minifi_main/MiNiFiMain.cpp index 340b294883..f1c506c431 100644 --- a/minifi_main/MiNiFiMain.cpp +++ b/minifi_main/MiNiFiMain.cpp @@ -69,7 +69,7 @@ #include "core/state/MetricsPublisherStore.h" #include "argparse/argparse.hpp" #include "agent/agent_version.h" -#include "utils/FipsUtils.h" +#include "Fips.h" namespace minifi = org::apache::nifi::minifi; namespace core = minifi::core; @@ -339,7 +339,7 @@ int main(int argc, char **argv) { dumpDocsIfRequested(argument_parser, configure); writeSchemaIfRequested(argument_parser, configure); - utils::fips::initializeFipsMode(configure, minifiHome, logger); + minifi::fips::initializeFipsMode(configure, minifiHome, logger); std::chrono::milliseconds stop_wait_time = configure->get(minifi::Configure::nifi_graceful_shutdown_seconds) | utils::andThen(utils::timeutils::StringToDuration)