Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e33e3c7
fix(playbooks/setup_librenms): apply the SELinux file contexts
NavidSassan Sep 2, 2026
e658f85
fix(playbooks/setup_librenms): create the librenms user before PHP-FP…
NavidSassan Sep 2, 2026
7f3d5d9
fix(roles/librenms): make a second run of the role work
NavidSassan Sep 2, 2026
ddca84b
refactor(roles/librenms): deploy http_fping through the selinux role
NavidSassan Sep 2, 2026
9b31ea7
feat(roles/librenms): store the RRD files through RRDCached
NavidSassan Sep 2, 2026
5a30b83
docs(CONTRIBUTING): name drop-ins after the role for foreign units
NavidSassan Sep 2, 2026
70001d3
test(extensions/molecule): add the setup_librenms scenario
NavidSassan Sep 2, 2026
d0ca8bf
fix(roles/librenms): install the Python modules the poller imports
NavidSassan Sep 2, 2026
be8ccc7
fix(roles/librenms): install php-gmp for exact 64-bit counter rates
NavidSassan Sep 2, 2026
8b72ffd
fix(roles/librenms): widen http_fping to the current upstream policy
NavidSassan Sep 2, 2026
7a7b4e3
fix(roles/librenms): stop and disable RRDCached when it is switched off
NavidSassan Sep 2, 2026
d742662
chore(roles/librenms): align packages and SELinux settings with upstream
NavidSassan Sep 2, 2026
abe90d9
feat(roles/librenms): add meta/argument_specs.yml
NavidSassan Sep 2, 2026
10f2468
feat(roles/librenms)!: stop trusting a reverse proxy by default
NavidSassan Sep 2, 2026
104f3eb
feat(roles/librenms): mark the session cookie as secure on HTTPS hosts
NavidSassan Sep 2, 2026
2fee534
feat(roles/librenms): manage the scheduler timer and follow the tag v…
NavidSassan Sep 3, 2026
2f03c89
fix(roles/librenms): keep the Ansible tmp dir out of /opt/librenms
NavidSassan Sep 3, 2026
96860da
docs(CHANGELOG): drop the superseded group-writable entry
NavidSassan Sep 3, 2026
33bae7e
docs(roles/librenms): document how to verify the trusted proxies
NavidSassan Sep 3, 2026
a3270b1
Merge branch 'main' into feat/librenms-rrdcached
NavidSassan Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

**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. Apache serves HTTP/2 to every client that offers it over TLS, which in a typical setup is the reverse proxy in front of an application; the hop from that proxy to the backend is unchanged.
**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. Apache serves HTTP/2 to every client that offers it over TLS, which in a typical setup is the reverse proxy in front of an application; the hop from that proxy to the backend is unchanged. LibreNMS keeps its RRD updates in RRDCached and writes them out every 30 minutes, taking a large share of the poller's disk I/O off the host.

### Breaking Changes

* **role:librenms**: LibreNMS trusts no reverse proxy any more, where it previously trusted one running on the LibreNMS host itself. It therefore ignores the `X-Forwarded-*` headers of such a proxy, and logs, access control and the detected protocol use the proxy's address instead of the client's. Hosts with a proxy in front of LibreNMS list it in `librenms__config_app_trusted_proxies`; `['127.0.0.1']` restores the previous behaviour.
* **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.
Expand All @@ -28,6 +29,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* **role:librenms**: `librenms__scheduler_service_enabled` and `librenms__scheduler_service_state` start, stop or disable the timer of the LibreNMS scheduler on its own.
* **role:librenms**: The `librenms:cron`, `librenms:logrotate` and `librenms:state` tags deploy the scheduled jobs, deploy the logrotate configuration and manage the service state without touching the rest of the installation.
* **role:librenms**: The session cookie is marked as secure on hosts whose `librenms__config_app_url` is an `https://` URL, so a browser only sends it over HTTPS. LibreNMS reports the missing flag as a failure in its own validation. Set `librenms__config_session_secure_cookie` to override.
* **role:librenms**: LibreNMS stores its RRD files through RRDCached, which cuts the disk I/O of the poller by roughly a third.
* **role:librenms**: The `librenms:rrdcached` tag deploys and configures RRDCached without touching the rest of the installation.
* **role:php**: PHP-FPM pools are now fully configurable, each with its own user and group, process-manager tuning, timeouts and `php_admin_value` overrides. Every pool gets an isolated session directory, its own error and slow logs, and its own socket, so several applications can share a host without sharing a PHP process, a session store or a memory limit. One pool template now serves both distribution families.
* **role:php**: Add `meta/argument_specs.yml` declaring the user-facing variables, so role-entry validation catches type mismatches and unknown variables, and an explicit `vars/Ubuntu.yml`.
* **role:apache_httpd**: `apache_httpd__mod_http2_protocols` sets the protocols offered server-wide, the `conf_protocols` vHost key overrides it for a single vHost, and the remaining `apache_httpd__mod_http2_*` variables size the HTTP/2 worker pool and its per-connection buffers.
Expand All @@ -46,6 +52,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

* **playbook:setup_librenms**: The skip variables of the playbook are all named after the playbook, `setup_librenms__skip_php` for example, instead of after the `librenms` role.
* **playbook:setup_librenms**: The playbook no longer runs the `apps` role, since the `librenms` role installs `git` itself.
* **role:librenms**: The `http_fping` SELinux policy module carries the rules the LibreNMS documentation now lists, so pinging a device from the web interface also works where fping uses an ICMP socket or binds a source address.
* **role:librenms**: PHP GMP is installed, so rates derived from 64-bit interface counters stay exact instead of silently losing precision in float arithmetic.
* **role:librenms**: The `httpd_can_sendmail` SELinux boolean is enabled, and `httpd_can_network_connect_db` in addition when the database is on another host.
* **role:librenms**: The SELinux file context of `/opt/librenms/cache` is set.
* **role:librenms**: ImageMagick is no longer installed, LibreNMS does not use it.
* **role:librenms**: The `http_fping` SELinux policy module is deployed by the `selinux` role, so a playbook run that skips that role no longer installs it.
* **role:librenms**: The role no longer makes the RRD, log, cache and storage directories group-writable, LibreNMS grants the access it needs itself while installing.
* **role:apache_httpd**: Restarting Apache no longer takes 90 seconds on a host that holds long-lived connections, such as a proxy for WebSockets. The server now waits 3 seconds for running requests to finish instead of waiting for the last connection to close by itself and being killed by systemd. Apache stops accepting new connections for the whole of that wait, so this shortens the outage rather than extending it. Hosts with legitimately long-running requests raise `apache_httpd__conf_graceful_shutdown_timeout`.
* **role:apache_httpd**: HTTP/2 is enabled and is the preferred protocol on every connection that terminates TLS, while a client that does not offer it is still served HTTP/1.1 and cleartext HTTP/2 (h2c) is not offered; on RedHat the `mod_http2` package is installed for this.
* **role:keycloak**: `keycloak__https_cipher_suites`, `keycloak__https_protocols`, `keycloak__log` and `keycloak__proxy_trusted_addresses` are YAML lists instead of comma-separated strings. A comma-separated value already in an inventory keeps working, Ansible splits it into the same list.
Expand All @@ -61,6 +76,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* **role:librenms**: An update of LibreNMS that changes the units of its scheduler takes effect, because systemd is told to read them again.
* **role:librenms**: The poller works on a host whose database is not local, because the Python modules it imports are installed by the role instead of arriving through the MariaDB role.
* **role:librenms**: A second run of the role updates an existing installation instead of aborting at the git checkout.
* **playbook:setup_librenms**: A fresh installation no longer aborts while starting PHP-FPM.
* **playbook:setup_librenms**: The SELinux file contexts for `/opt/librenms` are applied.
* **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.
Expand Down
2 changes: 1 addition & 1 deletion COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Which Ansible role is proven to run on which OS?
| kvm_host | (x) | (x) | x | (x) | (x) | (x) | x | (x) | |
| kvm_vm | (x) | (x) | x | (x) | (x) | (x) | x | (x) | |
| libmaxminddb | (x) | (x) | x | (x) | (x) | (x) | (x) | (x) | |
| librenms | | | x | (x) | (x) | | | | |
| librenms | | | x | x | x | | | | |
| libreoffice | | | x | (x) | (x) | | | | |
| login | (x) | (x) | x | x | x | (x) | (x) | (x) | Fedora 35+ |
| logrotate | (x) | (x) | x | x | x | (x) | (x) | (x) | Fedora |
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ Make sure to use the following format when passing multiple injections to avoid
#### systemd Drop-ins and Service Ordering

* Deploy overrides as a drop-in under `/etc/systemd/system/<unit>.d/`, never by templating the unit file itself. Name the file after what it does (`z00-linuxfabrik.conf` for the role's own `[Service]` settings, `z00-after-<dependency>.conf` for an ordering dependency), and run `systemctl daemon-reload` when it changed.
* Where a role configures a unit that belongs to *another* piece of software, name the drop-in `z00-<role>.conf` instead. `roles/librenms` writes `rrdcached.service.d/z00-librenms.conf`: the unit ships with the `rrdtool` package, the settings in it exist only because LibreNMS is on the host, and a role managing RRDCached in its own right would otherwise overwrite them with its own `z00-linuxfabrik.conf`.
* If a role declares a `<role>__kernel_settings__*__dependent_var`, check whether its service reads that value once at startup. If it does, the role also has to order the service after TuneD. TuneD applies the profile when its daemon starts and systemd starts `tuned.service` in parallel with everything else, so without the ordering the service can come up first and keep the old value for its whole runtime, while `sysctl` and `tuned-adm verify` already report the new one. The classic case is `net.core.somaxconn`, which the kernel clamps the accept queue to inside `listen()`.

Not every kernel setting needs this. `vm.swappiness` or `net.bridge.bridge-nf-call-iptables` are honoured by the kernel continuously, so ordering buys nothing there. Decide per parameter, not per role.
Expand Down
2 changes: 2 additions & 0 deletions extensions/molecule/setup_librenms/converge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- name: 'Converge setup_librenms playbook'
ansible.builtin.import_playbook: 'linuxfabrik.lfops.setup_librenms'
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apache_httpd__conf_server_admin: 'root@localhost'

# an https base URL is what makes the role mark the session cookie as secure. The vHost itself
# serves plain HTTP, which the probes in verify.yml talk to directly, so this only exercises the
# derived default of librenms__config_session_secure_cookie.
librenms__config_app_url: 'https://librenms.example.com'
librenms__database_login:
username: 'librenms'
password: 'linuxfabrik'
librenms__fqdn: 'librenms.example.com'

mariadb_server__admin_user:
username: 'mariadb-admin'
password: 'linuxfabrik'

repo_mariadb__version: '11.4'

repo_remi__enabled_php_version: '8.4'
15 changes: 15 additions & 0 deletions extensions/molecule/setup_librenms/inventory/hosts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# yamllint disable rule:empty-values

# setup_librenms targets 'lfops_setup_librenms' (see playbooks/setup_librenms.yml: hosts).
# Only Red Hat-family hosts run: the playbook installs PHP from Remi (repo_remi, RedHat-gated)
# and pulls in repo_epel and repo_mydumper the same way, and the librenms role installs SELinux
# policy modules. COMPATIBILITY.md lists librenms on RHEL 8, 9 and 10 only.
lfops_setup_librenms:
children:
systems_under_test:

systems_under_test:
hosts:
rocky8-vm:
rocky9-vm:
rocky10-vm:
1 change: 1 addition & 0 deletions extensions/molecule/setup_librenms/molecule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Molecule scenario marker
Loading
Loading