fix(roles/apache_httpd): stop hiding module mismatches behind IfModule - #340
Draft
NavidSassan wants to merge 6 commits into
Draft
fix(roles/apache_httpd): stop hiding module mismatches behind IfModule#340NavidSassan wants to merge 6 commits into
NavidSassan wants to merge 6 commits into
Conversation
Both files wrapped their LoadModule in an MPM guard, so that the MPM in use picked the matching CGI module by itself. The guard could never match: the role's httpd.conf includes only mods-enabled/*.conf, httpd expands that glob alphabetically, and cgi.conf and cgid.conf both sort before mpm_event.conf, mpm_prefork.conf and mpm_worker.conf. At the point they were parsed no MPM was loaded yet, so the block was skipped and the module never made it into the running server, with httpd -t reporting Syntax OK throughout. Verified on Rocky 9 by reproducing the role's include layout: cgid_module is absent as shipped and present once the guard is gone, or once the MPM file is renamed to sort first. Drop the guards rather than renaming the MPM entries, because filename is part of the public interface that inventories override, and because a silently skipped module is exactly the failure mode the role avoids elsewhere. Which module to pair with which MPM is now stated in the template comments; measured on Rocky 9 and Debian 13, every pairing serves CGI, so it is a sizing decision rather than a hard requirement. The minimal PHP-FPM example enabled cgi although that vHost serves PHP through proxy_fcgi and never runs a CGI script, so following it produced a working site with the module silently absent - which is a large part of why this went unnoticed. Drop it. The non-hardened example keeps cgid, since it deliberately mirrors the stock RHEL configuration, but now says why and what it costs.
The entry read enabled: true, state: 'absent', which reads as a contradiction: state: 'absent' removes the mods-available file and the mods-enabled symlink on its own, so enabled was inert. Debian and Ubuntu already say false. No change on a managed host, hence no CHANGELOG entry.
<IfModule userdir> never matched: find_module() (server/core.c) resolves either the module's __FILE__ (mod_userdir.c) or its LoadModule symbol (userdir_module), and "userdir" is neither. Measured on Rocky 9 against a control token. The block was therefore never applied, so the php_admin_flag engine Off it wraps has never disabled PHP in /home/*/public_html. Latent rather than exploitable: the php conf is not enabled by default and the role does not manage mod_userdir, so no host had both pieces in place. Kept as a guard rather than removed, unlike the rest of this branch, precisely because mod_userdir is outside the role's control.
An <IfModule> around a module's own directives turns a configuration mistake into silence: httpd -t reports Syntax OK and the setting never applies, which is the failure mode this role avoids everywhere else. Drop the guards from the conf-available snippets and from the guarded LoadModule lines. Two of the defaults were inconsistent and only worked because of the guard: the deflate and mod_security snippets were enabled while their modules were not, so compression has silently been off and ModSecurity's PCRE tuning has never applied. Disable the snippets to match the modules, which keeps the effective behaviour of every host unchanged; whether compression should be on by default is a separate question. The two EXAMPLES that enabled the deflate module now enable the snippet alongside it, so those recipes still compress. Removing each guard was measured on Rocky 9 rather than assumed: - deflate, mod_security, autoindex: the directives now abort with AH00526 Syntax error when the module is absent, which is the point - proxy_fcgi/_http/_wstunnel: without mod_proxy the load fails with "Cannot load mod_proxy_fcgi.so", clearer than a skipped LoadModule - authn_file, authz_host, authz_user: mod_authz_host loads perfectly well without authz_core, so the guard protected nothing at all - php7, php8: on a threaded MPM PHP itself refuses with "your PHP Module is not compiled to be threadsafe" and Apache does not start, a better message than any guard produces autoindex has a template but was missing from apache_httpd__conf__role_var; add it as enabled: false, state: 'absent' so the role's coverage is visible. mods-available/wsgi_python3.conf keeps <IfModule !wsgi_module>: a negated guard around its own LoadModule is the correct idiom for not loading two mod_wsgi builds into one process. BREAKING CHANGE: a conf-available snippet and the module it configures have to be enabled together; the deflate and mod_security snippets are no longer enabled by default, so a host that enabled only the deflate module loses compression.
…le-guards # Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stops the
apache_httpdrole from hiding module/conf mismatches behind<IfModule>guards, and fixes the two mismatches that were being hidden.Why
An
<IfModule>guard makes a conf snippet a no-op when its module is missing, silently. Two of them were wrong and nobody noticed:cgi.confandcgid.confguarded on the MPM.IncludeOptional mods-enabled/*.confexpands alphabetically, socgi*.confis read beforempm_*.confand the guard could never match. A host that enabledcgiorcgidin its inventory got the entry accepted and the module never loaded, withhttpd -treportingSyntax OK.http2.confcarried<IfModule !mpm_prefork>, which is not a valid module token, so it never matched either.Removing the guards makes a mismatch fail loudly at the config test instead of being ignored.
Breaking change
A
conf-availablesnippet and the module it configures have to be enabled together now. Getting the role's own defaults consistent required disabling two snippets whose modules were never enabled:deflateandmod_security.Consequence worth reading: compression has been silently off on every host managed by this role, because the
deflatesnippet was enabled while its module was not. This PR does not turn it on, it only makes the two settings consistent. Enabling compression properly is a separate discussion (BREACH).Commits
af9af7bfload the cgi and cgid modules when enabled8b9e2e2dset the qos mod toenabled: false4cf056fduse a valid module name in the userdir guardd4b3398fstop hiding module mismatches behind IfModule (breaking)1eb085c6correct the documented SSLUseStapling defaultOpen before this is ready
1eb085c6states that Apache "logs a warning per certificate on every start" when stapling is on without an OCSP responder URL. That was written from memory and never verified. It should be confirmed or trimmed before merge.apache_httpdscenario asserts HTTP/2 behaviour, not the guard removal. The cgi/cgid finding was proven by hand in Rocky 8/9 and Debian 13 containers.