From 488fbea4cde144bece9e853fab025810ec24f1cf Mon Sep 17 00:00:00 2001 From: Leonid Fedorov Date: Wed, 27 Aug 2025 22:48:06 +0000 Subject: [PATCH] feat(SELinux): policy builded and packaged for RHEL-like >= 10 --- CMakeLists.txt | 1 + build/bootstrap_mcs.sh | 4 ++ build/selinux_policy_rpm_post.sh | 28 ++++++++ build/selinux_policy_rpm_postun.sh | 15 +++++ cmake/ColumnstoreLibrary.cmake | 43 +++++++++++++ cmake/selinux_policy.cmake | 100 +++++++++++++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 build/selinux_policy_rpm_post.sh create mode 100644 build/selinux_policy_rpm_postun.sh create mode 100644 cmake/selinux_policy.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b4fb6e1e2..47de6382c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ include(configureEngine) include(compiler_flags) include(misc) include(cpack_manage) +include(selinux_policy) if(NOT __msg1_CS_NO_CXX20) add_subdirectory(dbcon/mysql) diff --git a/build/bootstrap_mcs.sh b/build/bootstrap_mcs.sh index ce40b8d4c..65a0d98b5 100755 --- a/build/bootstrap_mcs.sh +++ b/build/bootstrap_mcs.sh @@ -143,6 +143,10 @@ install_deps() { exit 17 fi + if is_rocky_version_ge $OS 10; then + command="${command} && dnf install -y selinux-policy-devel" + fi + if [[ $OS == 'ubuntu:22.04' || $OS == 'ubuntu:24.04' ]]; then if [ -f /.dockerenv ]; then change_ubuntu_mirror us diff --git a/build/selinux_policy_rpm_post.sh b/build/selinux_policy_rpm_post.sh new file mode 100644 index 000000000..0e77e2465 --- /dev/null +++ b/build/selinux_policy_rpm_post.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# Post-install script to load ColumnStore SELinux policy if SELinux is enabled +# This script must not introduce new runtime dependencies; it only uses coreutils and typical SELinux tools if present. + +set -e + +POLICY_PATH="/usr/share/columnstore/policy/selinux/columnstore.pp" + +# If SELinux tooling is not present, or policy file missing, silently exit +command -v getenforce >/dev/null 2>&1 || exit 0 +command -v semodule >/dev/null 2>&1 || exit 0 + +# Only attempt to install when SELinux is enforcing or permissive +MODE=$(getenforce 2>/dev/null || echo Disabled) +case "$MODE" in + Enforcing|Permissive) + if [ -r "$POLICY_PATH" ]; then + # Install or upgrade the module; do not fail the entire package if this fails + semodule -i "$POLICY_PATH" || true + fi + ;; + *) + # Disabled or unknown, do nothing + : + ;; +esac + +exit 0 diff --git a/build/selinux_policy_rpm_postun.sh b/build/selinux_policy_rpm_postun.sh new file mode 100644 index 000000000..10b8df5a0 --- /dev/null +++ b/build/selinux_policy_rpm_postun.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Post-uninstall script to remove ColumnStore SELinux policy module if present +# No new runtime dependencies; use SELinux tools only if available. + +set -e + +# If SELinux tooling is not present, silently exit +command -v semodule >/dev/null 2>&1 || exit 0 + +# Remove the module if it is installed; do not fail package removal if this fails +if semodule -l 2>/dev/null | grep -q '^columnstore\b'; then + semodule -r columnstore || true +fi + +exit 0 diff --git a/cmake/ColumnstoreLibrary.cmake b/cmake/ColumnstoreLibrary.cmake index e9f35bbf7..0f247524d 100644 --- a/cmake/ColumnstoreLibrary.cmake +++ b/cmake/ColumnstoreLibrary.cmake @@ -107,3 +107,46 @@ macro(columnstore_executable executable_name) endif() columnstore_install_target(${executable_name} ${ENGINE_BINDIR}) endmacro() + +# Read /etc/os-release and output: ID (lowercase) and VERSION_ID major number +function(columnstore_detect_os OUT_ID OUT_VER_MAJOR) + set(_os_id "") + set(_os_version_major "") + + set(_os_release "/etc/os-release") + if(EXISTS "${_os_release}") + file(READ "${_os_release}" _osr) + # Extract ID + string(REGEX MATCH "\nID=([^\n]+)" _id_match "\nID=([^\n]+)" ${_osr}) + if(_id_match) + string(REGEX REPLACE ".*\nID=\"?([^\"\n]+)\"?.*" "\\1" _os_id "${_osr}") + string(TOLOWER "${_os_id}" _os_id) + endif() + # Extract VERSION_ID major digits + string(REGEX MATCH "\nVERSION_ID=([^\n]+)" _vid_match "\nVERSION_ID=([^\n]+)" ${_osr}) + if(_vid_match) + string(REGEX REPLACE ".*\nVERSION_ID=\"?([0-9]+).*" "\\1" _os_version_major "${_osr}") + endif() + endif() + + set(${OUT_ID} + "${_os_id}" + PARENT_SCOPE + ) + set(${OUT_VER_MAJOR} + "${_os_version_major}" + PARENT_SCOPE + ) +endfunction() + +# Check whether a given lowercase OS ID is RHEL-like (RHEL/Rocky/Alma/CentOS/RedHat) +function(columnstore_is_rhel_like OS_ID OUT_BOOL) + set(_is_rhel_like FALSE) + if(${OS_ID} MATCHES "^(rhel|rocky|almalinux|centos|redhatenterpriseserver|redhatenterprise|redhat)$") + set(_is_rhel_like TRUE) + endif() + set(${OUT_BOOL} + "${_is_rhel_like}" + PARENT_SCOPE + ) +endfunction() diff --git a/cmake/selinux_policy.cmake b/cmake/selinux_policy.cmake new file mode 100644 index 000000000..7382660da --- /dev/null +++ b/cmake/selinux_policy.cmake @@ -0,0 +1,100 @@ +# Build SELinux policy and package it for RPM on RHEL-like systems >= 10 only +# Builds from: storage/columnstore/columnstore/build/security/columnstore.te +# Produces: columnstore.pp packaged under ${ENGINE_SUPPORTDIR}/policy/selinux +# Adds BuildRequires: selinux-policy-devel (RPM, RHEL-like >= 10) + +# Detect if we are building an RPM package +if(NOT RPM) + return() +endif() + +columnstore_detect_os(_os_id _os_version_major) +columnstore_is_rhel_like("${_os_id}" _is_rhel_like) + +# We only build on RHEL-like >= 10 +if(NOT _is_rhel_like + OR (NOT _os_version_major) + OR (_os_version_major LESS 10) +) + message( + STATUS + "SELinux policy build skipped: OS '${_os_id}' version '${_os_version_major}' not matching RHEL-like >= 10 or undetected." + ) + return() +endif() + +# Add RPM BuildRequires for the engine component only on matching systems Use the common appender macro to handle comma +# separation +columnstore_append_for_cpack(CPACK_RPM_columnstore-engine_PACKAGE_BUILDREQUIRES "selinux-policy-devel") + +# Paths +set(SELINUX_SRC_DIR "${CMAKE_CURRENT_LIST_DIR}/../build/security") +set(SELINUX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/selinux") +set(SELINUX_TE "${SELINUX_SRC_DIR}/columnstore.te") +set(SELINUX_PP "${SELINUX_BUILD_DIR}/columnstore.pp") + +file(MAKE_DIRECTORY "${SELINUX_BUILD_DIR}") + +# Ensure selinux-policy-devel is available +if(NOT EXISTS "/usr/share/selinux/devel/Makefile") + message( + FATAL_ERROR + "SELinux policy build requires '/usr/share/selinux/devel/Makefile'. Please install 'selinux-policy-devel' (RHEL/Rocky >= 10) and re-run CMake." + ) +endif() + +# Custom command to build the .pp from .te using the upstream devel Makefile +add_custom_command( + OUTPUT "${SELINUX_PP}" + COMMAND ${CMAKE_COMMAND} -E copy "${SELINUX_TE}" "${SELINUX_BUILD_DIR}/columnstore.te" + COMMAND make -f /usr/share/selinux/devel/Makefile columnstore.pp + WORKING_DIRECTORY "${SELINUX_BUILD_DIR}" + DEPENDS "${SELINUX_TE}" + COMMENT "Building SELinux policy columnstore.pp from columnstore.te" + VERBATIM +) + +add_custom_target(selinux_policy ALL DEPENDS "${SELINUX_PP}") + +# Install the compiled policy into the package (no runtime dep). Post-install will load it conditionally. +install( + FILES "${SELINUX_PP}" + DESTINATION "${ENGINE_SUPPORTDIR}/policy/selinux" + COMPONENT columnstore-engine +) + +# Register RPM post-install and post-uninstall scripts for the component +set(_selinux_post "${CMAKE_CURRENT_LIST_DIR}/../build/selinux_policy_rpm_post.sh") +set(_selinux_postun "${CMAKE_CURRENT_LIST_DIR}/../build/selinux_policy_rpm_postun.sh") + +# POST_INSTALL: preserve existing script if set by wrapping it +if(EXISTS "${_selinux_post}") + if(DEFINED CPACK_RPM_columnstore-engine_POST_INSTALL_SCRIPT_FILE + AND CPACK_RPM_columnstore-engine_POST_INSTALL_SCRIPT_FILE + ) + set(_orig_post "${CPACK_RPM_columnstore-engine_POST_INSTALL_SCRIPT_FILE}") + set(_wrap_post "${SELINUX_BUILD_DIR}/post_install_wrapper.sh") + file(WRITE "${_wrap_post}" "#!/bin/sh\n\n'${_orig_post}' \"$@\" || true\n'${_selinux_post}' \"$@\" || true\n") + execute_process(COMMAND ${CMAKE_COMMAND} -E chmod +x "${_wrap_post}") + set(CPACK_RPM_columnstore-engine_POST_INSTALL_SCRIPT_FILE "${_wrap_post}") + else() + set(CPACK_RPM_columnstore-engine_POST_INSTALL_SCRIPT_FILE "${_selinux_post}") + endif() +endif() + +# POST_UNINSTALL: preserve existing script if set by wrapping it +if(EXISTS "${_selinux_postun}") + if(DEFINED CPACK_RPM_columnstore-engine_POST_UNINSTALL_SCRIPT_FILE + AND CPACK_RPM_columnstore-engine_POST_UNINSTALL_SCRIPT_FILE + ) + set(_orig_postun "${CPACK_RPM_columnstore-engine_POST_UNINSTALL_SCRIPT_FILE}") + set(_wrap_postun "${SELINUX_BUILD_DIR}/post_uninstall_wrapper.sh") + file(WRITE "${_wrap_postun}" + "#!/bin/sh\n\n'${_orig_postun}' \"$@\" || true\n'${_selinux_postun}' \"$@\" || true\n" + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E chmod +x "${_wrap_postun}") + set(CPACK_RPM_columnstore-engine_POST_UNINSTALL_SCRIPT_FILE "${_wrap_postun}") + else() + set(CPACK_RPM_columnstore-engine_POST_UNINSTALL_SCRIPT_FILE "${_selinux_postun}") + endif() +endif()