From e6adb392902daf6a14d7f4f77d836cadd496f69d Mon Sep 17 00:00:00 2001 From: Sugat <233906380+SugatD@users.noreply.github.com> Date: Mon, 22 Jun 2026 20:00:23 +0530 Subject: [PATCH] (MODULES-11851) Restore ModSecurity engine on RHEL 10 via EPEL PR #2629 (MODULES-11739) marked apache::mod::security unsupported on RHEL 10 to stop acceptance tests failing on the missing mod_security_crs package. That was broader than necessary: the ModSecurity engine itself is available on EL10 via EPEL (2.9.9); only the OWASP CRS rules are the genuine gap. Re-scope EL10 support to engine-only: - params.pp: on EL10 set $modsec_crs_package to undef and $modsec_default_rules to []. The engine package (mod_security) still installs; CRS is simply not managed. $modsec_crs_path keeps the EL8+ default since it is only referenced when rules are activated (none on EL10). - security.pp: replace the "Unsupported platforms: RedHat: 10" @note with an explanatory engine-only note. This re-enables both the unit specs and the existing acceptance test (guarded by mod_supported_on_platform?) on RHEL 10. With crs_package undef and activated_rules empty, the engine-only apply is clean (no CRS package, no rule_link symlinks; IncludeOptional globs tolerate the empty dir). - security_spec.rb: make the mod_security_crs package and CRS rule_link expectations conditional on major < 10, and add an explicit RedHat 10 context with hardcoded facts (FacterDB ships no RedHat-named EL10 factset yet, so on_supported_os cannot generate one). Co-Authored-By: Claude Opus 4.8 --- manifests/mod/security.pp | 5 ++- manifests/params.pp | 30 +++++++++++++--- spec/classes/mod/security_spec.rb | 58 +++++++++++++++++++++++++++++-- 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/manifests/mod/security.pp b/manifests/mod/security.pp index 3d00e61b27..bbcf497f11 100644 --- a/manifests/mod/security.pp +++ b/manifests/mod/security.pp @@ -137,7 +137,10 @@ # @see https://github.com/SpiderLabs/ModSecurity/wiki for additional documentation. # @see https://coreruleset.org/docs/ for addional documentation # -# @note Unsupported platforms: RedHat: 10 +# @note On RHEL/EL 10 the ModSecurity engine is provided by EPEL (enable EPEL +# yourself; this module does not manage it). The OWASP CRS package +# (`mod_security_crs`) is not available on EL10, so the class manages the +# engine only there and does not install or activate CRS rules. class apache::mod::security ( Stdlib::Absolutepath $logroot = $apache::params::logroot, Integer $version = $apache::params::modsec_version, diff --git a/manifests/params.pp b/manifests/params.pp index 6ca2e15703..48763db608 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -115,12 +115,23 @@ $mellon_cache_size = 100 $mellon_post_directory = undef $modsec_version = 1 - $modsec_crs_package = 'mod_security_crs' + # EL10 base/EPEL ships the ModSecurity engine but not the mod_security_crs + # package, so install the engine only and leave CRS unmanaged there. + $modsec_crs_package = $facts['os']['release']['major'] ? { + '10' => undef, # RedHat 10 doesn't ship mod_security_crs (CRS tracked separately) + default => 'mod_security_crs', + } $modsec_dir = '/etc/httpd/modsecurity.d' $secpcrematchlimit = 1500 $secpcrematchlimitrecursion = 1500 $modsec_secruleengine = 'On' - if $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '7') <= 0 { + if $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '10') >= 0 { + # Engine-only on EL10: no CRS package, so no rules are activated. The path + # is kept at the EL8+ default since it is only referenced when rules are + # activated (none here). + $modsec_crs_path = '/usr/share/mod_modsecurity_crs' + $modsec_default_rules = [] + } elsif $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '7') <= 0 { $modsec_crs_path = '/usr/lib/modsecurity.d' $modsec_default_rules = [ 'base_rules/modsecurity_35_bad_robots.data', @@ -260,12 +271,23 @@ $mellon_cache_size = 100 $mellon_post_directory = undef $modsec_version = 1 - $modsec_crs_package = 'mod_security_crs' + # EL10 base/EPEL ships the ModSecurity engine but not the mod_security_crs + # package, so install the engine only and leave CRS unmanaged there. + $modsec_crs_package = $facts['os']['release']['major'] ? { + '10' => undef, # RedHat 10 doesn't ship mod_security_crs (CRS tracked separately) + default => 'mod_security_crs', + } $modsec_dir = '/etc/httpd/modsecurity.d' $secpcrematchlimit = 1500 $secpcrematchlimitrecursion = 1500 $modsec_secruleengine = 'On' - if $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '7') <= 0 { + if $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '10') >= 0 { + # Engine-only on EL10: no CRS package, so no rules are activated. The path + # is kept at the EL8+ default since it is only referenced when rules are + # activated (none here). + $modsec_crs_path = '/usr/share/mod_modsecurity_crs' + $modsec_default_rules = [] + } elsif $facts['os']['family'] == 'RedHat' and versioncmp($facts['os']['release']['major'], '7') <= 0 { $modsec_crs_path = '/usr/lib/modsecurity.d' $modsec_default_rules = [ 'base_rules/modsecurity_35_bad_robots.data', diff --git a/spec/classes/mod/security_spec.rb b/spec/classes/mod/security_spec.rb index 1263777670..b9ff1a8de9 100644 --- a/spec/classes/mod/security_spec.rb +++ b/spec/classes/mod/security_spec.rb @@ -33,7 +33,12 @@ ) } - it { is_expected.to contain_package('mod_security_crs') } + if facts[:os]['release']['major'].to_i >= 10 + # EL10 ships the engine via EPEL but not the mod_security_crs package. + it { is_expected.not_to contain_package('mod_security_crs') } + else + it { is_expected.to contain_package('mod_security_crs') } + end if (facts[:os]['release']['major'].to_i > 6 && facts[:os]['release']['major'].to_i <= 7) || (facts[:os]['release']['major'].to_i >= 8) it { @@ -73,7 +78,10 @@ ) } - if facts[:os]['release']['major'].to_i <= 7 + if facts[:os]['release']['major'].to_i >= 10 + # EL10 is engine-only (no CRS package), so no rules are activated. + it { is_expected.not_to contain_apache__security__rule_link('rules/crawlers-user-agents.data') } + elsif facts[:os]['release']['major'].to_i <= 7 it { is_expected.to contain_apache__security__rule_link('base_rules/modsecurity_35_bad_robots.data') } it { @@ -384,4 +392,50 @@ end end end + + # FacterDB does not yet ship a RedHat-named EL10 factset, so on_supported_os + # cannot generate a redhat-10 context. Exercise the engine-only EL10 path + # explicitly with hardcoded facts. + context 'on RedHat 10 (engine-only via EPEL)' do + let :facts do + { + os: { + 'architecture' => 'x86_64', + 'family' => 'RedHat', + 'hardware' => 'x86_64', + 'name' => 'RedHat', + 'release' => { 'full' => '10.0', 'major' => '10' }, + 'selinux' => { 'enabled' => false }, + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it { + expect(subject).to contain_apache__mod('security').with( + id: 'security2_module', + lib: 'mod_security2.so', + ) + } + + it { + expect(subject).to contain_apache__mod('unique_id').with( + id: 'unique_id_module', + lib: 'mod_unique_id.so', + ) + } + + # EL10 ships the engine via EPEL but not the mod_security_crs package, so + # the CRS package must not be managed and no rules are activated. + it { is_expected.not_to contain_package('mod_security_crs') } + it { is_expected.not_to contain_apache__security__rule_link('rules/crawlers-user-agents.data') } + + # The engine config is still written. + it { + expect(subject).to contain_file('security.conf').with( + path: '/etc/httpd/conf.modules.d/security.conf', + ) + } + end end