From 6d5c030f0d9b4228fc5f1d68364fb322d7101d80 Mon Sep 17 00:00:00 2001 From: Ali Bhatti Date: Wed, 26 Aug 2026 12:10:07 +0200 Subject: [PATCH 1/3] fix(roles/mariadb_server): mariadbd drops out of mysqld_t on upgrade --- CHANGELOG.md | 3 ++- .../molecule/setup_nextcloud/verify.yml | 26 +++++++++++++++++++ roles/mariadb_server/tasks/main.yml | 18 ++++++++----- .../socket-selinux-workaround.conf.j2 | 19 +++----------- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e50d578f6..a8ed565b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -**Highlights:** Apache no longer loads `mod_info`, which served the complete configuration including other modules' credentials. A broken PHP-FPM configuration aborts the run instead of taking the service down on the restart. Sudo rules deployed by `freeipa_server` can carry their commands again. The Bitwarden lookup can be told to abort instead of silently generating a new password, for runs against hosts whose credentials must already exist. The Grafana graph configuration for the Monitoring Plugins is no longer deployed on every ordinary run and has to be requested explicitly by its tag. +**Highlights:** On RHEL 8, a MariaDB package upgrade no longer cuts applications on the same host off from their database. Apache no longer loads `mod_info`, which served the complete configuration including other modules' credentials. A broken PHP-FPM configuration aborts the run instead of taking the service down on the restart. Sudo rules deployed by `freeipa_server` can carry their commands again. The Bitwarden lookup can be told to abort instead of silently generating a new password, for runs against hosts whose credentials must already exist. The Grafana graph configuration for the Monitoring Plugins is no longer deployed on every ordinary run and has to be requested explicitly by its tag. ### Breaking Changes @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* **role:mariadb_server**: On RHEL 8, MariaDB keeps its own SELinux confinement after a package upgrade, by installing `mysql-selinux` the way the MariaDB packages already do on RHEL 9 and 10. Without it, applications on the same host lose their database connection with `Permission denied` as of MariaDB 11.4.13 and 11.8.9. * **playbook:icingaweb2, playbook:setup_icinga2_master, role:icingaweb2** update `icingaweb2` dependent vars to ensure php.ini value `post_max_size` > `upload_max_filesize` by default. * **role:monitoring_plugins**: A source install installs the dependencies of the Linuxfabrik library, so checks that speak HTTP, MySQL, SMB or WinRM no longer report `Python module "httpx" is not installed` and its equivalents. * **role:monitoring_plugins**: A source install deploys the event plugins, which only the rpm/deb package used to ship. diff --git a/extensions/molecule/setup_nextcloud/verify.yml b/extensions/molecule/setup_nextcloud/verify.yml index 0c1ebd7d3..4ab586dc1 100644 --- a/extensions/molecule/setup_nextcloud/verify.yml +++ b/extensions/molecule/setup_nextcloud/verify.yml @@ -36,3 +36,29 @@ that: - '(__molecule__nextcloud_statusphp_result["content"] | from_json)["installed"] | bool' fail_msg: 'status.php did not report installed. response: {{ __molecule__nextcloud_statusphp_result["content"] | d("") }}' + + +# The role installs mysql-selinux so mariadbd gets `mysqld_exec_t` (MDEV-30520). Assert the domain +# of the running process, not the label on the file: only that tells `mysqld_t` apart from +# `initrc_t` and the `unconfined_service_t` that used to mask a missing label. +- name: 'Verify mariadbd runs in the mysqld_t SELinux domain' + hosts: 'systems_under_test' + gather_facts: true + become: true + tasks: + + - name: 'ps -o label= -C mariadbd' + ansible.builtin.command: 'ps -o label= -C mariadbd' + register: '__molecule__mariadbd_label_result' + changed_when: false + when: + - 'ansible_facts["os_family"] == "RedHat"' + - 'ansible_facts["selinux"]["status"] != "disabled"' + + - name: 'Assert mariadbd transitioned into mysqld_t' + ansible.builtin.assert: + that: + - '"mysqld_t" in __molecule__mariadbd_label_result["stdout"]' + fail_msg: 'mariadbd runs as "{{ __molecule__mariadbd_label_result["stdout"] | trim }}" instead of mysqld_t, so the mysqld_exec_t label on the binary is missing.' + when: + - '__molecule__mariadbd_label_result is not skipped' diff --git a/roles/mariadb_server/tasks/main.yml b/roles/mariadb_server/tasks/main.yml index 3d8410935..011c5280f 100644 --- a/roles/mariadb_server/tasks/main.yml +++ b/roles/mariadb_server/tasks/main.yml @@ -220,19 +220,25 @@ - 'ansible_facts["os_family"] == "RedHat"' - 'ansible_facts["selinux"]["status"] != "disabled"' - # The unit drop-in's `ExecStartPre=-/bin/chcon -t mysqld_exec_t /usr/sbin/mariadbd` workaround for MDEV-30520 cannot relabel the binary on EL10+, where the packaged mariadb.service applies `ProtectSystem` that mounts /usr read-only inside the service sandbox. Set the label persistently here instead. Use `/usr/bin/mariadbd` because SELinux has an equivalency rule `/usr/sbin /usr/bin` (UsrMerge) and `semanage fcontext` rejects the equivalent path. - - name: 'semanage fcontext --add --type mysqld_exec_t /usr/bin/mariadbd' - community.general.sefcontext: - setype: 'mysqld_exec_t' - target: '/usr/bin/mariadbd' + # mysql-selinux ships the file contexts for the `mariadbd` binary names (MDEV-30520); without it + # the binary keeps `bin_t` and never reaches `mysqld_t`. Since 11.4.13 / 11.8.9 that is no longer + # cosmetic: systemd execs `/bin/sh` (MDEV-40629), so an unlabeled binary lands in `initrc_t` + # instead of `unconfined_service_t` and `httpd_t` clients lose the socket. The MariaDB packages + # pull it in on EL9 and EL10, on EL8 they do not. Installed everywhere anyway: it overrides the + # distro `mysql` module (priority 200 over 100) and tracks the upstream packages we deploy. + - name: 'Install mysql-selinux' + ansible.builtin.package: + name: + - 'mysql-selinux' state: 'present' - register: '__mariadb_server__mariadbd_fcontext_result' when: - 'ansible_facts["os_family"] == "RedHat"' - 'ansible_facts["selinux"]["status"] != "disabled"' + # excluded on purpose: in `mysqld_t`, wsrep recovery fails the start with `Errcode: 13` - 'mariadb_server__cnf_wsrep_on is false' notify: 'mariadb_server: systemctl restart mariadb' + # applies the context to the binary the running package already installed - name: 'restorecon -Fv /usr/sbin/mariadbd' ansible.builtin.command: 'restorecon -Fv /usr/sbin/mariadbd' register: '__mariadb_server__mariadbd_restorecon_result' diff --git a/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 b/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 index f32b7063b..bc5888ece 100644 --- a/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 +++ b/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 @@ -1,21 +1,10 @@ # {{ ansible_managed }} -# 2024103101 +# 20260826 -[Service] -{# changing the context of mariadbd does not seem to work with galera. -`WSREP: Failed to start mysqld for wsrep recovery: '... [Warning] Can't create test file '/var/lib/mysql/example.com.lower-test' (Errcode: 13 "Permission denied")` -and `/usr/sbin/mariadbd: Cannot change uid/gid (errno: 1)` -during systemctl start, causes the mariadb.service to fail #} -{% if mariadb_server__cnf_wsrep_on is false %} -ExecStartPre=-/bin/chcon -t mysqld_exec_t /usr/sbin/mariadbd -# SELinux incorrectly labeled mariadbd (see https://jira.mariadb.org/browse/MDEV-30520) -# The bug is present in the MariaDB Repo (here, SELinux rules are missing in the SELinux database), not at Red Hat. - -# ls -Z /usr/sbin/mariadbd -# Wrong: system_u:object_r:bin_t:s0 /usr/sbin/mariadbd -# Correct: system_u:object_r:mysqld_exec_t:s0 /usr/sbin/mariadbd -{% endif %} +# mariadbd itself is labeled mysqld_exec_t by the role (MDEV-30520). Not from here: ProtectSystem=full +# mounts /usr read-only inside the sandbox, so a chcon against the binary always fails. The socket does not. +[Service] ExecStartPost=-/bin/chcon -t mysqld_var_run_t {{ mariadb_server__cnf_socket__combined_var }} # This is a workaround for https://jira.mariadb.org/browse/MDEV-24941. This only occurs when using the official MariaDB Repository. # After restarting the mariadb service, the socket /var/lib/mysql/mysql.sock has the mysqld_db_t SELinux file type, instead of mysqld_var_run_t From 1945e2ff17bf21565c2d674f7e0972ad9d42a173 Mon Sep 17 00:00:00 2001 From: Ali Bhatti Date: Wed, 26 Aug 2026 15:08:34 +0200 Subject: [PATCH 2/3] style(roles/mariadb_server): clarify status as dependency and fix bad wording --- roles/mariadb_server/tasks/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/mariadb_server/tasks/main.yml b/roles/mariadb_server/tasks/main.yml index 011c5280f..f28a9531f 100644 --- a/roles/mariadb_server/tasks/main.yml +++ b/roles/mariadb_server/tasks/main.yml @@ -224,7 +224,7 @@ # the binary keeps `bin_t` and never reaches `mysqld_t`. Since 11.4.13 / 11.8.9 that is no longer # cosmetic: systemd execs `/bin/sh` (MDEV-40629), so an unlabeled binary lands in `initrc_t` # instead of `unconfined_service_t` and `httpd_t` clients lose the socket. The MariaDB packages - # pull it in on EL9 and EL10, on EL8 they do not. Installed everywhere anyway: it overrides the + # depend on it on EL9 and EL10, but not on EL8. Installed everywhere anyway: it overrides the # distro `mysql` module (priority 200 over 100) and tracks the upstream packages we deploy. - name: 'Install mysql-selinux' ansible.builtin.package: @@ -238,7 +238,7 @@ - 'mariadb_server__cnf_wsrep_on is false' notify: 'mariadb_server: systemctl restart mariadb' - # applies the context to the binary the running package already installed + # relabel the mariadbd binary that is already on disk - name: 'restorecon -Fv /usr/sbin/mariadbd' ansible.builtin.command: 'restorecon -Fv /usr/sbin/mariadbd' register: '__mariadb_server__mariadbd_restorecon_result' From 3b9cb0494a01ae37afda3f09c9ab33ac7fb05a78 Mon Sep 17 00:00:00 2001 From: Navid Sassan Date: Wed, 26 Aug 2026 18:24:19 +0200 Subject: [PATCH 3/3] style(roles/mariadb_server): be more precise about the Galera exclusion The comment claimed the exclusion keeps Galera nodes out of mysqld_t. It only does so on EL8. On EL9 and EL10 MariaDB-server declares (mysql-selinux >= 1.0.14 if selinux-policy-targeted), so the policy is installed and rpm labels the binary regardless of this role, and Galera nodes run confined either way. Measured on a Rocky 9 node where both SELinux tasks were skipped: mariadbd still ran in mysqld_t. Also name the actual failure behind Errcode: 13, which is the donor's mariabackup failing to reach the local socket during SST, and point at #328, where the Galera breakage is tracked. The socket drop-in gains the same treatment: once mariadbd runs in mysqld_t the base policy labels the socket through a type_transition, so the ExecStartPost only matters on hosts where it does not, which the comment now says instead of implying the chcon is always needed. verify.yml: ps exits 1 when no process matches, so the label check failed with a bare rc=1 instead of the fail_msg. Split into a "is it running" assert and the domain assert. --- extensions/molecule/setup_nextcloud/verify.yml | 11 +++++++++++ roles/mariadb_server/tasks/main.yml | 6 +++++- .../socket-selinux-workaround.conf.j2 | 12 +++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/extensions/molecule/setup_nextcloud/verify.yml b/extensions/molecule/setup_nextcloud/verify.yml index 4ab586dc1..7735e45cd 100644 --- a/extensions/molecule/setup_nextcloud/verify.yml +++ b/extensions/molecule/setup_nextcloud/verify.yml @@ -51,10 +51,21 @@ ansible.builtin.command: 'ps -o label= -C mariadbd' register: '__molecule__mariadbd_label_result' changed_when: false + # ps exits 1 when no process matches, which would otherwise fail the task with a bare rc=1 + # and no indication of what is wrong. The assert below turns it into a sentence. + failed_when: '__molecule__mariadbd_label_result["rc"] not in [0, 1]' when: - 'ansible_facts["os_family"] == "RedHat"' - 'ansible_facts["selinux"]["status"] != "disabled"' + - name: 'Assert that mariadbd is running' + ansible.builtin.assert: + that: + - '__molecule__mariadbd_label_result["rc"] == 0' + fail_msg: 'No mariadbd process on {{ inventory_hostname }}.' + when: + - '__molecule__mariadbd_label_result is not skipped' + - name: 'Assert mariadbd transitioned into mysqld_t' ansible.builtin.assert: that: diff --git a/roles/mariadb_server/tasks/main.yml b/roles/mariadb_server/tasks/main.yml index f28a9531f..2ff976870 100644 --- a/roles/mariadb_server/tasks/main.yml +++ b/roles/mariadb_server/tasks/main.yml @@ -234,7 +234,11 @@ when: - 'ansible_facts["os_family"] == "RedHat"' - 'ansible_facts["selinux"]["status"] != "disabled"' - # excluded on purpose: in `mysqld_t`, wsrep recovery fails the start with `Errcode: 13` + # Galera excluded: in `mysqld_t` the donor's mariabackup cannot reach the local socket and + # SST fails with `Errcode: 13`. Note this only takes effect on EL8. On EL9 and EL10 the + # MariaDB packages depend on mysql-selinux, so it is installed and the binary labeled no + # matter what this role does, and Galera nodes run confined there either way. Tracked in + # https://github.com/Linuxfabrik/lfops/issues/328. - 'mariadb_server__cnf_wsrep_on is false' notify: 'mariadb_server: systemctl restart mariadb' diff --git a/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 b/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 index bc5888ece..1dde8ee1b 100644 --- a/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 +++ b/roles/mariadb_server/templates/etc/systemd/system/mariadb.service.d/socket-selinux-workaround.conf.j2 @@ -1,8 +1,14 @@ # {{ ansible_managed }} -# 20260826 +# 2026082601 -# mariadbd itself is labeled mysqld_exec_t by the role (MDEV-30520). Not from here: ProtectSystem=full -# mounts /usr read-only inside the sandbox, so a chcon against the binary always fails. The socket does not. +# mariadbd is labeled mysqld_exec_t by the role (MDEV-30520), not from here: ProtectSystem mounts +# /usr read-only inside the service sandbox, so a chcon against the binary always fails. +# +# The socket below is a different matter. Once mariadbd actually runs in mysqld_t, the policy +# labels it without help, through `type_transition mysqld_t mysqld_db_t:sock_file +# mysqld_var_run_t` in the base policy of RHEL 8, 9 and 10. This line stays for the hosts where +# that does not apply: Galera nodes, which the role deliberately leaves unlabeled, and a socket +# configured outside both the datadir and /run. Elsewhere it relabels to what is already there. [Service] ExecStartPost=-/bin/chcon -t mysqld_var_run_t {{ mariadb_server__cnf_socket__combined_var }}