diff --git a/CHANGELOG.md b/CHANGELOG.md index d7aa46240..f54aff3b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes +* **role:apache_httpd**: A `conf-available` snippet and the Apache module it configures have to be enabled together from now on. The snippets no longer carry an `` guard, so enabling one without its module aborts the run at the config test instead of being ignored in silence. Getting the role's own defaults consistent changed two of them: the `deflate` and `mod_security` snippets are no longer enabled, matching their modules, which never were. A host that switched the `deflate` module on in its inventory had compression and loses it, so enable the snippet alongside the module to keep it. * **role:php**: The PHP-FPM pool configuration changed for existing hosts. Sessions now live in a per-pool directory (the default `www` pool moves from `/var/lib/php/session` to `/var/lib/php/session/www`), so logged-in users are signed out once after the upgrade. `memory_limit`, `max_execution_time`, `max_input_vars`, `post_max_size`, `upload_max_filesize`, `session.save_handler` and `session.save_path` are now enforced as `php_admin_value`, so applications can no longer change them at runtime via `ini_set()`. A pool that stores its sessions in redis or memcached sets `php_admin_value_session_save_handler` accordingly. `soap.wsdl_cache_dir` moves from the shared `/var/lib/php/wsdlcache` to a per-pool directory below it. On Debian the pool socket now belongs to `root` and grants the web server access through a POSIX ACL entry, where it used to be owned by the web server user; anything of your own that checks the socket's ownership rather than connecting to it needs adjusting. Worker processes recycle after 500 requests (`pm.max_requests`) instead of running indefinitely, and a worker still serving a single request after 60 seconds is killed (`request_terminate_timeout`, previously off). Hosts with legitimately long-running web requests raise `php__fpm_pool_conf_request_terminate_timeout__group_var`. * **role:keycloak**: Rename `keycloak__state` to `keycloak__service_state`, the name every other LFOps role uses. The value `reloaded` is gone: Keycloak's systemd unit has no `ExecReload`, so a reload never worked; use `restarted` instead. * **role:keycloak**: The role installs the OpenJDK its Keycloak version needs (OpenJDK 17 for Keycloak 24, OpenJDK 21 for 25 and newer) instead of relying on the `apps` role, which `setup_keycloak` no longer runs. Hosts that used `apps__apps__*_var` through this playbook to install further packages have to run the `apps` playbook for them. @@ -60,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* **role:apache_httpd**: The `cgi` and `cgid` modules are loaded when the inventory enables them; until now the entry was accepted but the module never ended up in the running server. * **role:apache_httpd**: Enabling an Apache module restarts httpd instead of reloading it, so the module actually takes effect. A reload loads the module but skips its initialization, which left HTTP/2 configured but silently inactive on RHEL 8 ([#339](https://github.com/Linuxfabrik/lfops/issues/339)). * **role:kvm_vm**: Two VMs created from the same inventory host no longer get the same MAC address, which made the second one fail with `The MAC address ... is in use by another virtual machine`. The generated MAC follows the VM name now. Hosts that leave `kvm_vm__name` at its default keep the MAC they had; on a host that sets it, a VM created from now on gets a different MAC, so check DHCP reservations and firewall rules keyed on it before recreating such a VM. Running VMs are not touched. * **role:keycloak**: A run against an unchanged host reports no changes any more. The tarball is only downloaded and extracted when the installed version differs from `keycloak__version`, and `kc.sh build` only runs when the installation or `keycloak.conf` actually changed, which also takes minutes off an ordinary run. diff --git a/roles/apache_httpd/EXAMPLES.md b/roles/apache_httpd/EXAMPLES.md index b0216a912..2e9bb5872 100644 --- a/roles/apache_httpd/EXAMPLES.md +++ b/roles/apache_httpd/EXAMPLES.md @@ -32,9 +32,6 @@ A minimal working "app" vHost definition for a PHP-FPM application, located unde ```yaml apache_httpd__conf_server_admin: 'webmaster@example.com' apache_httpd__mods__host_var: - - filename: 'cgi' - enabled: true - state: 'present' - filename: 'proxy_fcgi' enabled: true state: 'present' @@ -47,7 +44,17 @@ apache_httpd__vhosts__host_var: ### Reverse Proxy +Compression is off by default, so this example turns it on: `mod_deflate` and `mod_filter` supply +the filter, and the `deflate` conf snippet is what actually applies it to the MIME types listed in +it. Both halves are needed; enabling only the modules compresses nothing, enabling only the conf +fails the config test. + ```yaml +apache_httpd__conf__host_var: + - filename: 'deflate' + enabled: true + state: 'present' + template: 'deflate' apache_httpd__mods__host_var: - filename: 'deflate' enabled: true @@ -131,8 +138,14 @@ apache_httpd__vhosts__host_var: This is an Apache configuration that is close to the RHEL default configuration, without any CIS remediations. +It enables `cgid` because the stock RHEL configuration loads it, not because the vHost below needs it: that vHost is a pure reverse proxy and never runs a CGI script. Loading the module starts the external CGI daemon and creates its `ScriptSock` at every start, so drop the entry unless you actually serve CGI. + ```yaml apache_httpd__conf__host_var: + - filename: 'deflate' + enabled: true + state: 'present' + template: 'deflate' - filename: 'expires' enabled: false state: 'present' diff --git a/roles/apache_httpd/README.md b/roles/apache_httpd/README.md index dfe03c5be..983560b39 100644 --- a/roles/apache_httpd/README.md +++ b/roles/apache_httpd/README.md @@ -23,7 +23,11 @@ The config is split into several files forming the configuration hierarchy outli `-- sites-enabled/ ``` -We avoid using `` in vHost definitions and in the global `httpd.conf` to facilitate debugging. Without ``, a missing module causes a clear startup error instead of silently dropping configuration. `` is only used in `mods-available/` and `conf-available/` where it is necessary to guard module-specific configuration. +We avoid `` throughout. Without it, a missing module causes a clear startup error instead of silently dropping configuration, which is otherwise impossible to spot: `httpd -t` reports `Syntax OK` and the setting simply never applies. + +The consequence is a pairing rule: **a `conf-available` snippet and the module it configures have to be enabled together.** Enabling the `deflate` snippet without `mod_deflate` and `mod_filter` aborts the run at the config test with `Invalid command 'AddOutputFilterByType'`, which is the intended behaviour. The role's own defaults are consistent, so this only concerns snippets and modules you enable yourself via `apache_httpd__conf__*_var` and `apache_httpd__mods__*_var`. + +Two exceptions remain, both guarding against something other than a mismatch: `mods-available/wsgi_python3.conf` uses `` so that two mod_wsgi builds never load into one process, and `conf-available/php.conf` guards its user-directory block with `` because the role does not manage mod_userdir at all. `mod_info` is not enabled. It serves the complete configuration on `/server-info`, including the credentials other modules carry in their directives. The endpoint stays configured in the localhost vHost and answers with an empty response until the `info` module is enabled via `apache_httpd__mods__group_var` / `apache_httpd__mods__host_var`. @@ -885,14 +889,14 @@ apache_httpd__skip_mod_security_coreruleset: true `apache_httpd__mod_ssl_ssl_use_stapling` -* See [SSLUseStapling](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslusestapling). +* Whether the server staples an OCSP response into the TLS handshake. Off, because a Let's Encrypt certificate carries no OCSP responder URL, so there is nothing to staple and Apache logs a warning per certificate on every start. Set it to `'on'` for a certificate from a CA that does publish an OCSP endpoint. See [SSLUseStapling](https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslusestapling). * Type: String. * Default: `'off'` Example: ```yaml # optional - mod_ssl -apache_httpd__mod_ssl_ssl_use_stapling: 'on' +apache_httpd__mod_ssl_ssl_use_stapling: 'off' ``` diff --git a/roles/apache_httpd/defaults/main.yml b/roles/apache_httpd/defaults/main.yml index fe27802ac..838df9a04 100644 --- a/roles/apache_httpd/defaults/main.yml +++ b/roles/apache_httpd/defaults/main.yml @@ -55,8 +55,13 @@ apache_httpd__conf__group_var: [] apache_httpd__conf__host_var: [] # Apache conf used internally by this role. apache_httpd__conf__role_var: + - filename: 'autoindex' + enabled: false + state: 'absent' + template: 'autoindex' + - filename: 'deflate' - enabled: true + enabled: false state: 'present' template: 'deflate' @@ -113,7 +118,7 @@ apache_httpd__conf__role_var: template: 'mime_magic' - filename: 'mod_security' - enabled: true + enabled: false state: 'present' template: 'mod_security' diff --git a/roles/apache_httpd/meta/argument_specs.yml b/roles/apache_httpd/meta/argument_specs.yml index 43de84ada..cec8cf649 100644 --- a/roles/apache_httpd/meta/argument_specs.yml +++ b/roles/apache_httpd/meta/argument_specs.yml @@ -277,7 +277,7 @@ argument_specs: apache_httpd__mod_ssl_ssl_use_stapling: type: 'str' required: false - default: 'on' + default: 'off' description: >- Whether OCSP stapling is used (`SSLUseStapling`). diff --git a/roles/apache_httpd/templates/etc/httpd/conf-available/autoindex.conf.j2 b/roles/apache_httpd/templates/etc/httpd/conf-available/autoindex.conf.j2 index 4e1f78414..11bf0c083 100644 --- a/roles/apache_httpd/templates/etc/httpd/conf-available/autoindex.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/conf-available/autoindex.conf.j2 @@ -1,47 +1,47 @@ # {{ ansible_managed }} -# 2022102501 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} -# mod_autoindex.c - - Alias /icons/ "/usr/share/httpd/icons/" - - Options Indexes MultiViews FollowSymlinks - AllowOverride None - Require all granted - - AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip - AddIconByType (IMG,/icons/image2.gif) image/* - AddIconByType (SND,/icons/sound2.gif) audio/* - AddIconByType (TXT,/icons/text.gif) text/* - AddIconByType (VID,/icons/movie.gif) video/* - AddIcon /icons/a.gif .ps .ai .eps - AddIcon /icons/back.gif .. - AddIcon /icons/binary.gif .bin .exe - AddIcon /icons/binhex.gif .hqx - AddIcon /icons/blank.gif ^^BLANKICON^^ - AddIcon /icons/bomb.gif */core.* - AddIcon /icons/bomb.gif /core - AddIcon /icons/c.gif .c - AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip - AddIcon /icons/dvi.gif .dvi - AddIcon /icons/f.gif .for - AddIcon /icons/folder.gif ^^DIRECTORY^^ - AddIcon /icons/hand.right.gif README - AddIcon /icons/layout.gif .html .shtml .htm .pdf - AddIcon /icons/p.gif .pl .py - AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl - AddIcon /icons/tar.gif .tar - AddIcon /icons/tex.gif .tex - AddIcon /icons/text.gif .txt - AddIcon /icons/uuencoded.gif .uu - AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv - DefaultIcon /icons/unknown.gif +# autoindex_module +# Requires the autoindex module. No guard: enabling this conf without the module has to +# fail the config test rather than do nothing in silence. +Alias /icons/ "/usr/share/httpd/icons/" + + Options Indexes MultiViews FollowSymlinks + AllowOverride None + Require all granted + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (VID,/icons/movie.gif) video/* +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/back.gif .. +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/blank.gif ^^BLANKICON^^ +AddIcon /icons/bomb.gif */core.* +AddIcon /icons/bomb.gif /core +AddIcon /icons/c.gif .c +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/f.gif .for +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/hand.right.gif README +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/p.gif .pl .py +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tar.gif .tar +AddIcon /icons/tex.gif .tex +AddIcon /icons/text.gif .txt +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +DefaultIcon /icons/unknown.gif - HeaderName /HEADER.html - ReadmeName README.html - IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t assets favicon* - IndexStyleSheet "/assets/style.css" - +HeaderName /HEADER.html +ReadmeName README.html +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t assets favicon* +IndexStyleSheet "/assets/style.css" diff --git a/roles/apache_httpd/templates/etc/httpd/conf-available/deflate.conf.j2 b/roles/apache_httpd/templates/etc/httpd/conf-available/deflate.conf.j2 index e449a33c4..de819252f 100644 --- a/roles/apache_httpd/templates/etc/httpd/conf-available/deflate.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/conf-available/deflate.conf.j2 @@ -1,17 +1,15 @@ # {{ ansible_managed }} -# 2022082301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} -# mod_deflate.c - - # mod_filter.c - - AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript - AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript - AddOutputFilterByType DEFLATE application/rss+xml - AddOutputFilterByType DEFLATE application/wasm - AddOutputFilterByType DEFLATE application/xml - - +# deflate_module, filter_module +# AddOutputFilterByType comes from mod_filter and the DEFLATE filter from mod_deflate, so this +# snippet needs both modules enabled. It carries no guard on purpose: enabling the conf +# without the modules has to fail the config test rather than compress nothing in silence. +AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript +AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript +AddOutputFilterByType DEFLATE application/rss+xml +AddOutputFilterByType DEFLATE application/wasm +AddOutputFilterByType DEFLATE application/xml diff --git a/roles/apache_httpd/templates/etc/httpd/conf-available/mod_security.conf.j2 b/roles/apache_httpd/templates/etc/httpd/conf-available/mod_security.conf.j2 index a2d27d2ff..6092038a2 100644 --- a/roles/apache_httpd/templates/etc/httpd/conf-available/mod_security.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/conf-available/mod_security.conf.j2 @@ -1,12 +1,12 @@ # {{ ansible_managed }} -# 2021120101 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} # security2_module - - SecDataDir /tmp/ - SecPcreMatchLimit 100000 - SecPcreMatchLimitRecursion 100000 - +# Requires the security2 module. No guard: enabling this conf without the module has to +# fail the config test rather than leave the tuning silently unapplied. +SecDataDir /tmp/ +SecPcreMatchLimit 100000 +SecPcreMatchLimitRecursion 100000 diff --git a/roles/apache_httpd/templates/etc/httpd/conf-available/php.conf.j2 b/roles/apache_httpd/templates/etc/httpd/conf-available/php.conf.j2 index 8bf6e5f91..0aef764a7 100644 --- a/roles/apache_httpd/templates/etc/httpd/conf-available/php.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/conf-available/php.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2021111201 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} @@ -33,7 +33,7 @@ AddType text/html .php # To re-enable PHP in user directories comment the following lines # (from to .) Do NOT set it to On as it # prevents .htaccess files from disabling it. - + php_admin_flag engine Off diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/authn_file.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/authn_file.conf.j2 index 59331546d..65405139f 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/authn_file.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/authn_file.conf.j2 @@ -1,9 +1,9 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - LoadModule authn_file_module {{ __apache_httpd__modules_path }}/mod_authn_file.so - +# The Require directives this module provides are consumed by mod_authn_core, which the role enables +# by default. Loading it without authn_core is not an error, so there is nothing to guard against. +LoadModule authn_file_module {{ __apache_httpd__modules_path }}/mod_authn_file.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/authz_host.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/authz_host.conf.j2 index fcda031d4..33f5579ec 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/authz_host.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/authz_host.conf.j2 @@ -1,9 +1,9 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - LoadModule authz_host_module {{ __apache_httpd__modules_path }}/mod_authz_host.so - +# The Require directives this module provides are consumed by mod_authz_core, which the role enables +# by default. Loading it without authz_core is not an error, so there is nothing to guard against. +LoadModule authz_host_module {{ __apache_httpd__modules_path }}/mod_authz_host.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/authz_user.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/authz_user.conf.j2 index e5cbbcf04..742cfd56d 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/authz_user.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/authz_user.conf.j2 @@ -1,9 +1,9 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - LoadModule authz_user_module {{ __apache_httpd__modules_path }}/mod_authz_user.so - +# The Require directives this module provides are consumed by mod_authz_core, which the role enables +# by default. Loading it without authz_core is not an error, so there is nothing to guard against. +LoadModule authz_user_module {{ __apache_httpd__modules_path }}/mod_authz_user.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/cgi.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/cgi.conf.j2 index eb53b891b..f912b5cc9 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/cgi.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/cgi.conf.j2 @@ -1,12 +1,12 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} -# This configuration file loads a CGI module appropriate to the MPM -# which has been activated. mod_cgid should be used -# with a threaded MPM; mod_cgi with the prefork MPM. - - LoadModule cgi_module {{ __apache_httpd__modules_path }}/mod_cgi.so - +# mod_cgi forks a CGI process out of the httpd worker. Pair it with mpm_prefork; on a threaded MPM +# the fork replicates every thread of the parent, which is what mod_cgid exists to avoid. It does +# work on mpm_event and mpm_worker, so this is a sizing decision, not a hard requirement. +# Enable either this module or cgid, never both: they both handle `cgi-script`, and the one loaded +# first wins while the other only costs resources. +LoadModule cgi_module {{ __apache_httpd__modules_path }}/mod_cgi.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/cgid.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/cgid.conf.j2 index becf4fd2c..22bc896cb 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/cgid.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/cgid.conf.j2 @@ -1,15 +1,12 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} -# This configuration file loads a CGI module appropriate to the MPM -# which has been activated. mod_cgid should be used -# with a threaded MPM; mod_cgi with the prefork MPM. - - LoadModule cgid_module {{ __apache_httpd__modules_path }}/mod_cgid.so - - - LoadModule cgid_module {{ __apache_httpd__modules_path }}/mod_cgid.so - +# mod_cgid runs CGI scripts through an external daemon instead of forking out of a worker, which is +# what a threaded MPM wants. Pair it with mpm_event (the role default) or mpm_worker. The daemon and +# its ScriptSock are created at startup whether or not a CGI is ever requested. +# Enable either this module or cgi, never both: they both handle `cgi-script`, and the one loaded +# first wins while the other only costs resources. +LoadModule cgid_module {{ __apache_httpd__modules_path }}/mod_cgid.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/php7.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/php7.conf.j2 index 238387c30..1725afbe8 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/php7.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/php7.conf.j2 @@ -1,11 +1,12 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} # PHP 7 - - LoadModule php7_module {{ __apache_httpd__modules_path }}/libphp7.so - -# otherwise use PHP-FPM +# mod_php needs a non-threaded MPM (mpm_prefork). No guard: on a threaded MPM PHP itself +# refuses with "Apache is running a threaded MPM, but your PHP Module is not compiled to be +# threadsafe" and Apache does not start, which is a better message than a skipped LoadModule. +# The role defaults to mpm_event, so use PHP-FPM there instead. +LoadModule php7_module {{ __apache_httpd__modules_path }}/libphp7.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/php8.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/php8.conf.j2 index cecc8a033..e74ae6842 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/php8.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/php8.conf.j2 @@ -1,11 +1,12 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} # PHP 8+ - - LoadModule php_module {{ __apache_httpd__modules_path }}/libphp.so - -# otherwise use PHP-FPM +# mod_php needs a non-threaded MPM (mpm_prefork). No guard: on a threaded MPM PHP itself +# refuses with "Apache is running a threaded MPM, but your PHP Module is not compiled to be +# threadsafe" and Apache does not start, which is a better message than a skipped LoadModule. +# The role defaults to mpm_event, so use PHP-FPM there instead. +LoadModule php_module {{ __apache_httpd__modules_path }}/libphp.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_fcgi.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_fcgi.conf.j2 index ca984375d..961b990c3 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_fcgi.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_fcgi.conf.j2 @@ -1,10 +1,10 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - # Needed for PHP-FPM, for example. - LoadModule proxy_fcgi_module {{ __apache_httpd__modules_path }}/mod_proxy_fcgi.so - +# Needed for PHP-FPM, for example. +# Requires mod_proxy, which the role enables by default. Without it the load fails with +# "Cannot load mod_proxy_fcgi.so", which says more than a silently skipped LoadModule. +LoadModule proxy_fcgi_module {{ __apache_httpd__modules_path }}/mod_proxy_fcgi.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_http.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_http.conf.j2 index 68a1695b4..3dec979a8 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_http.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_http.conf.j2 @@ -1,9 +1,9 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - LoadModule proxy_http_module {{ __apache_httpd__modules_path }}/mod_proxy_http.so - +# Requires mod_proxy, which the role enables by default. Without it the load fails with +# "Cannot load mod_proxy_http.so", which says more than a silently skipped LoadModule. +LoadModule proxy_http_module {{ __apache_httpd__modules_path }}/mod_proxy_http.so diff --git a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_wstunnel.conf.j2 b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_wstunnel.conf.j2 index f5172f515..e934665b3 100644 --- a/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_wstunnel.conf.j2 +++ b/roles/apache_httpd/templates/etc/httpd/mods-available/proxy_wstunnel.conf.j2 @@ -1,9 +1,9 @@ # {{ ansible_managed }} -# 2021110301 +# 2026090201 {% if item['by_role'] | d() %} # Generated by Ansible role: {{ item['by_role'] }} {% endif %} - - LoadModule proxy_wstunnel_module {{ __apache_httpd__modules_path }}/mod_proxy_wstunnel.so - +# Requires mod_proxy, which the role enables by default. Without it the load fails with +# "Cannot load mod_proxy_wstunnel.so", which says more than a silently skipped LoadModule. +LoadModule proxy_wstunnel_module {{ __apache_httpd__modules_path }}/mod_proxy_wstunnel.so diff --git a/roles/apache_httpd/vars/RedHat.yml b/roles/apache_httpd/vars/RedHat.yml index 989676292..cc6de5299 100644 --- a/roles/apache_httpd/vars/RedHat.yml +++ b/roles/apache_httpd/vars/RedHat.yml @@ -214,7 +214,7 @@ apache_httpd__mods__role_var: template: 'proxy_wstunnel' - filename: 'qos' - enabled: true + enabled: false state: 'absent' template: 'qos'