From 53517b9a30865c9dd2560f7b231865a06bcf5b14 Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 16:47:55 +0200 Subject: [PATCH 1/6] feat(roles/valkey): listen on a Unix socket that the valkey group may use The RHEL package enables /run/valkey/valkey.sock, the templates commented it out. valkey__conf_unixsocket follows the package path on RHEL and uses /run/valkey/valkey-server.sock on Debian and Ubuntu, whose package ships the socket off. valkey__conf_unixsocketperm defaults to 770: without it the mode follows the umask of the service (0755), so only valkey itself could connect. valkey__conf_unixsocket stays out of argument_specs, since its default comes from vars/.yml, which is loaded after the role-entry validation. --- roles/valkey/README.md | 16 ++++++++++++++++ roles/valkey/defaults/main.yml | 2 ++ roles/valkey/meta/argument_specs.yml | 6 ++++++ .../templates/etc/valkey/7.2-valkey.conf.j2 | 7 +++++-- .../templates/etc/valkey/8.0-valkey.conf.j2 | 7 +++++-- .../templates/etc/valkey/8.1-valkey.conf.j2 | 7 +++++-- .../templates/etc/valkey/9.0-valkey.conf.j2 | 7 +++++-- roles/valkey/vars/Debian.yml | 1 + roles/valkey/vars/RedHat.yml | 1 + roles/valkey/vars/Ubuntu.yml | 1 + 10 files changed, 47 insertions(+), 8 deletions(-) diff --git a/roles/valkey/README.md b/roles/valkey/README.md index 3e1e6e728..871cccb95 100644 --- a/roles/valkey/README.md +++ b/roles/valkey/README.md @@ -193,6 +193,20 @@ Variables for `valkey.conf` directives and their default values, defined and sup * Type: Number. * Default: unset +`valkey__conf_unixsocket` + +* Path of the Unix socket Valkey listens on, in addition to the TCP port. Set it to `''` to listen on no Unix socket. [valkey.conf](https://github.com/valkey-io/valkey/blob/8.0/valkey.conf) +* Type: String. +* Default: `'/run/valkey/valkey.sock'` on RHEL, `'/run/valkey/valkey-server.sock'` on Debian and Ubuntu +* Deviates from the upstream default on Debian and Ubuntu, whose package listens on no Unix socket: local clients such as Nextcloud connect faster through the socket than through TCP, and `valkey__conf_unixsocketperm` keeps it closed to anyone outside the `valkey` group. + +`valkey__conf_unixsocketperm` + +* Permissions of the Unix socket. Only the `valkey` user and the members of the `valkey` group can connect with `770`; a role that needs access adds its service user to the group. [valkey.conf](https://github.com/valkey-io/valkey/blob/8.0/valkey.conf) +* Type: String. +* Default: `'770'` +* Deviates from the upstream default, which leaves the mode to the umask of the service, so that the socket is writable, and thus usable, by the `valkey` user only. + Example: ```yaml @@ -216,6 +230,8 @@ valkey__conf_tls_ca_cert_file: '/etc/valkey/ca.pem' valkey__conf_tls_cert_file: '/etc/valkey/valkey.pem' valkey__conf_tls_key_file: '/etc/valkey/valkey.key' valkey__conf_tls_port: 6379 +valkey__conf_unixsocket: '/run/valkey/valkey.sock' +valkey__conf_unixsocketperm: '770' ``` diff --git a/roles/valkey/defaults/main.yml b/roles/valkey/defaults/main.yml index 6cd3aaeb1..28aba90af 100644 --- a/roles/valkey/defaults/main.yml +++ b/roles/valkey/defaults/main.yml @@ -8,6 +8,8 @@ valkey__conf_maxmemory_policy: 'noeviction' valkey__conf_port: 6379 valkey__conf_protected_mode: 'yes' valkey__conf_replica_serve_stale_data: 'yes' +valkey__conf_unixsocket: '{{ __valkey__conf_unixsocket }}' # upstream default: /run/valkey/valkey.sock on RHEL, off on Debian / Ubuntu +valkey__conf_unixsocketperm: '770' # upstream default: unset, the mode then follows the umask valkey__service_enabled: true valkey__service_limit_nofile: 10240 # upstream default: 10240 on RHEL, 65535 on Debian / Ubuntu diff --git a/roles/valkey/meta/argument_specs.yml b/roles/valkey/meta/argument_specs.yml index 3fcd38392..4b1dfd8cd 100644 --- a/roles/valkey/meta/argument_specs.yml +++ b/roles/valkey/meta/argument_specs.yml @@ -101,6 +101,12 @@ argument_specs: required: false description: 'TLS port to listen on. Set valkey__conf_port to 0 to only listen with TLS.' + valkey__conf_unixsocketperm: + type: 'str' + required: false + default: '770' + description: 'Permissions of the Unix socket.' + valkey__kernel_settings__sysctl__dependent_var: type: 'list' elements: 'dict' diff --git a/roles/valkey/templates/etc/valkey/7.2-valkey.conf.j2 b/roles/valkey/templates/etc/valkey/7.2-valkey.conf.j2 index f039e192c..b4bb4ac01 100644 --- a/roles/valkey/templates/etc/valkey/7.2-valkey.conf.j2 +++ b/roles/valkey/templates/etc/valkey/7.2-valkey.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026081401 +# 2026092201 # Valkey v7.2 configuration file. # @@ -158,7 +158,10 @@ tcp-backlog 511 # # unixsocket /run/valkey/valkey-server.sock # unixsocketperm 700 -{# RHEL package default: unixsocket /run/valkey/valkey.sock #} +{% if valkey__conf_unixsocket | length > 0 %} +unixsocket {{ valkey__conf_unixsocket }} +unixsocketperm {{ valkey__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/valkey/templates/etc/valkey/8.0-valkey.conf.j2 b/roles/valkey/templates/etc/valkey/8.0-valkey.conf.j2 index a90f57cb9..1ea3eadae 100644 --- a/roles/valkey/templates/etc/valkey/8.0-valkey.conf.j2 +++ b/roles/valkey/templates/etc/valkey/8.0-valkey.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026081401 +# 2026092201 # Valkey v8.0 configuration file. # @@ -160,7 +160,10 @@ tcp-backlog 511 # unixsocket /run/valkey/valkey.sock # unixsocketgroup wheel # unixsocketperm 700 -{# RHEL package default: unixsocket /run/valkey/valkey.sock #} +{% if valkey__conf_unixsocket | length > 0 %} +unixsocket {{ valkey__conf_unixsocket }} +unixsocketperm {{ valkey__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/valkey/templates/etc/valkey/8.1-valkey.conf.j2 b/roles/valkey/templates/etc/valkey/8.1-valkey.conf.j2 index 4b5221f28..c7df4d3a8 100644 --- a/roles/valkey/templates/etc/valkey/8.1-valkey.conf.j2 +++ b/roles/valkey/templates/etc/valkey/8.1-valkey.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026081401 +# 2026092201 # Valkey v8.1 configuration file. # @@ -160,7 +160,10 @@ tcp-backlog 511 # unixsocket /run/valkey/valkey-server.sock # unixsocketgroup wheel # unixsocketperm 700 -{# RHEL package default: unixsocket /run/valkey/valkey.sock #} +{% if valkey__conf_unixsocket | length > 0 %} +unixsocket {{ valkey__conf_unixsocket }} +unixsocketperm {{ valkey__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/valkey/templates/etc/valkey/9.0-valkey.conf.j2 b/roles/valkey/templates/etc/valkey/9.0-valkey.conf.j2 index 83f3963e5..248a5fe77 100644 --- a/roles/valkey/templates/etc/valkey/9.0-valkey.conf.j2 +++ b/roles/valkey/templates/etc/valkey/9.0-valkey.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026081401 +# 2026092201 # Valkey v9.0 configuration file. # @@ -170,7 +170,10 @@ tcp-backlog 511 # unixsocket /run/valkey/valkey-server.sock # unixsocketgroup wheel # unixsocketperm 700 -{# RHEL package default: unixsocket /run/valkey/valkey.sock #} +{% if valkey__conf_unixsocket | length > 0 %} +unixsocket {{ valkey__conf_unixsocket }} +unixsocketperm {{ valkey__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/valkey/vars/Debian.yml b/roles/valkey/vars/Debian.yml index ac0764469..621bac59d 100644 --- a/roles/valkey/vars/Debian.yml +++ b/roles/valkey/vars/Debian.yml @@ -1,4 +1,5 @@ __valkey__conf_logfile: '/var/log/valkey/valkey-server.log' +__valkey__conf_unixsocket: '/run/valkey/valkey-server.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' __valkey__data_dir: '/var/lib/valkey' diff --git a/roles/valkey/vars/RedHat.yml b/roles/valkey/vars/RedHat.yml index ce3d2317e..dc3db9721 100644 --- a/roles/valkey/vars/RedHat.yml +++ b/roles/valkey/vars/RedHat.yml @@ -1,4 +1,5 @@ __valkey__conf_logfile: '/var/log/valkey/valkey.log' +__valkey__conf_unixsocket: '/run/valkey/valkey.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' __valkey__data_dir: '/var/lib/valkey' diff --git a/roles/valkey/vars/Ubuntu.yml b/roles/valkey/vars/Ubuntu.yml index ac0764469..621bac59d 100644 --- a/roles/valkey/vars/Ubuntu.yml +++ b/roles/valkey/vars/Ubuntu.yml @@ -1,4 +1,5 @@ __valkey__conf_logfile: '/var/log/valkey/valkey-server.log' +__valkey__conf_unixsocket: '/run/valkey/valkey-server.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' __valkey__data_dir: '/var/lib/valkey' From b68b1f1cb7d7798d38acc384305e2cd5d3383337 Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 16:47:56 +0200 Subject: [PATCH 2/6] feat(roles/redis): listen on a Unix socket that the redis group may use Same as for Valkey. The Remi package for EL9 (redis 8.8) enables /run/redis/redis.sock with the mode left to the umask. Redis has no unixsocketgroup (Valkey added it in 8.0), so access goes through the redis group on both. --- CHANGELOG.md | 1 + roles/redis/README.md | 16 ++++++++++++++++ roles/redis/defaults/main.yml | 2 ++ .../redis/templates/etc/redis/7.0-redis.conf.j2 | 6 +++++- .../redis/templates/etc/redis/7.2-redis.conf.j2 | 6 +++++- .../redis/templates/etc/redis/7.4-redis.conf.j2 | 6 +++++- .../redis/templates/etc/redis/8.0-redis.conf.j2 | 6 +++++- .../redis/templates/etc/redis/8.2-redis.conf.j2 | 6 +++++- .../redis/templates/etc/redis/8.8-redis.conf.j2 | 6 +++++- roles/redis/vars/Debian.yml | 1 + roles/redis/vars/RedHat.yml | 1 + 11 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb90a98b8..6e0d86424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +* **role:redis, role:valkey**: Redis and Valkey also listen on a Unix socket that only the members of their group may use, on RHEL at the path the package ships (`/run/redis/redis.sock`, `/run/valkey/valkey.sock`). * **role:apache_httpd**: Apache answers requests for PHP files that do not exist with 404 itself instead of passing them to PHP-FPM, so scanners no longer tie up pool workers or fill the error log. * **playbook:setup_basic**: Checks the variables of all its roles, the compiled firewall in the Firewall Builder repository and the reachability of the Icinga2 master before it changes anything on the host, so a missing prerequisite no longer aborts the run halfway through, after SSH was already hardened. * Handlers that a role notified before a later task failed run anyway, so a changed configuration reaches the running service; until now it stayed inactive, also across later runs, which reported no change (all playbooks). diff --git a/roles/redis/README.md b/roles/redis/README.md index 2d8bb5002..b8c02947c 100644 --- a/roles/redis/README.md +++ b/roles/redis/README.md @@ -190,6 +190,20 @@ Variables for `redis.conf` directives and their default values, defined and supp * Type: Number. * Default: unset +`redis__conf_unixsocket` + +* Path of the Unix socket Redis listens on, in addition to the TCP port. Set it to `''` to listen on no Unix socket. [redis.conf](https://github.com/redis/redis/blob/8.8/redis.conf) +* Type: String. +* Default: `'/run/redis/redis.sock'` on RHEL, `'/run/redis/redis-server.sock'` on Debian and Ubuntu +* Deviates from the upstream default on Debian and Ubuntu, whose package listens on no Unix socket: local clients such as Nextcloud connect faster through the socket than through TCP, and `redis__conf_unixsocketperm` keeps it closed to anyone outside the `redis` group. + +`redis__conf_unixsocketperm` + +* Permissions of the Unix socket. Only the `redis` user and the members of the `redis` group can connect with `770`; a role that needs access adds its service user to the group. [redis.conf](https://github.com/redis/redis/blob/8.8/redis.conf) +* Type: String. +* Default: `'770'` +* Deviates from the upstream default, which leaves the mode to the umask of the service, so that the socket is writable, and thus usable, by the `redis` user only. + Example: ```yaml @@ -213,6 +227,8 @@ redis__conf_tls_ca_cert_file: '/etc/redis/ca.pem' redis__conf_tls_cert_file: '/etc/redis/redis.pem' redis__conf_tls_key_file: '/etc/redis/redis.key' redis__conf_tls_port: 6379 +redis__conf_unixsocket: '/run/redis/redis.sock' +redis__conf_unixsocketperm: '770' ``` diff --git a/roles/redis/defaults/main.yml b/roles/redis/defaults/main.yml index f6e17e8c2..7d056e181 100644 --- a/roles/redis/defaults/main.yml +++ b/roles/redis/defaults/main.yml @@ -8,6 +8,8 @@ redis__conf_maxmemory_policy: 'noeviction' redis__conf_port: 6379 redis__conf_protected_mode: 'yes' redis__conf_replica_serve_stale_data: 'yes' +redis__conf_unixsocket: '{{ __redis__conf_unixsocket }}' # upstream default: /run/redis/redis.sock on RHEL, off on Debian / Ubuntu +redis__conf_unixsocketperm: '770' # upstream default: unset, the mode then follows the umask redis__service_enabled: true redis__service_limit_nofile: 10240 diff --git a/roles/redis/templates/etc/redis/7.0-redis.conf.j2 b/roles/redis/templates/etc/redis/7.0-redis.conf.j2 index f69d18e82..e46461a49 100644 --- a/roles/redis/templates/etc/redis/7.0-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/7.0-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2024061301 +# 2026092201 # Redis v7.0 configuration file. # @@ -157,6 +157,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/templates/etc/redis/7.2-redis.conf.j2 b/roles/redis/templates/etc/redis/7.2-redis.conf.j2 index 2598173cb..8438c95c9 100644 --- a/roles/redis/templates/etc/redis/7.2-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/7.2-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2024061301 +# 2026092201 # Redis v7.2 configuration file. # @@ -157,6 +157,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/templates/etc/redis/7.4-redis.conf.j2 b/roles/redis/templates/etc/redis/7.4-redis.conf.j2 index 2f95eab1f..1ef56e436 100644 --- a/roles/redis/templates/etc/redis/7.4-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/7.4-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2024080701 +# 2026092201 # Redis v7.4 configuration file. # @@ -158,6 +158,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/templates/etc/redis/8.0-redis.conf.j2 b/roles/redis/templates/etc/redis/8.0-redis.conf.j2 index 0e514563b..0b3a252cc 100644 --- a/roles/redis/templates/etc/redis/8.0-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/8.0-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2025051601 +# 2026092201 # Redis v8.0 configuration file. # @@ -165,6 +165,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/templates/etc/redis/8.2-redis.conf.j2 b/roles/redis/templates/etc/redis/8.2-redis.conf.j2 index f41d2047a..73ad46a36 100644 --- a/roles/redis/templates/etc/redis/8.2-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/8.2-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2025110701 +# 2026092201 # Redis v8.2 configuration file. # @@ -160,6 +160,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/templates/etc/redis/8.8-redis.conf.j2 b/roles/redis/templates/etc/redis/8.8-redis.conf.j2 index 8d2ace7e4..25e3e785e 100644 --- a/roles/redis/templates/etc/redis/8.8-redis.conf.j2 +++ b/roles/redis/templates/etc/redis/8.8-redis.conf.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026052801 +# 2026092201 # Redis v8.8 configuration file. # @@ -160,6 +160,10 @@ tcp-backlog 511 # # unixsocket /run/redis.sock # unixsocketperm 700 +{% if redis__conf_unixsocket | length > 0 %} +unixsocket {{ redis__conf_unixsocket }} +unixsocketperm {{ redis__conf_unixsocketperm }} +{% endif %} # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 diff --git a/roles/redis/vars/Debian.yml b/roles/redis/vars/Debian.yml index 82e581deb..a18c9f805 100644 --- a/roles/redis/vars/Debian.yml +++ b/roles/redis/vars/Debian.yml @@ -1,4 +1,5 @@ __redis__conf_logfile: '/var/log/redis/redis-server.log' +__redis__conf_unixsocket: '/run/redis/redis-server.sock' __redis__config_dir: '/etc/redis' __redis__config_file: 'redis.conf' __redis__data_dir: '/var/lib/redis' diff --git a/roles/redis/vars/RedHat.yml b/roles/redis/vars/RedHat.yml index 511c4ccd7..b237f9c7a 100644 --- a/roles/redis/vars/RedHat.yml +++ b/roles/redis/vars/RedHat.yml @@ -1,4 +1,5 @@ __redis__conf_logfile: '/var/log/redis/redis.log' +__redis__conf_unixsocket: '/run/redis/redis.sock' __redis__config_dir: '/etc/redis' __redis__config_file: 'redis.conf' __redis__data_dir: '/var/lib/redis' From 07fd7dccafc26d68278ded0f180c4f19c30a268c Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 16:47:57 +0200 Subject: [PATCH 3/6] feat(roles/redis): add meta/argument_specs.yml Derived from the valkey role, whose variables and static defaults are the same; checked with validate_argument_spec against every static default of the role. --- CHANGELOG.md | 1 + roles/redis/meta/argument_specs.yml | 154 ++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 roles/redis/meta/argument_specs.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e0d86424..03eec8279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* **role:redis**: The role's inventory variables are type-checked when it starts, so a mistyped value fails the run right away instead of surfacing further in as a confusing error. * **role:graylog_datanode, role:graylog_server**: Add template for Graylog 7.2. * **role:duplicity**: `duplicity__archive_dir` sets the directory of duplicity's local metadata cache, for example to keep the cache of a separate long-term backup apart from the daily one. Without it, the cache stays in `~/.cache/duplicity`. * **role:systemd_unit**: Manages systemd socket units with `systemd_unit__sockets__host_var` / `systemd_unit__sockets__group_var`, for example to forward a TCP port with `systemd-socket-proxyd` without installing a proxy server. diff --git a/roles/redis/meta/argument_specs.yml b/roles/redis/meta/argument_specs.yml new file mode 100644 index 000000000..1b87a4b88 --- /dev/null +++ b/roles/redis/meta/argument_specs.yml @@ -0,0 +1,154 @@ +# argument_specs validates required variables and types automatically at role entry. +# use this for simple "is defined" / type checks. for complex validations +# (value ranges, cross-variable logic), use ansible.builtin.assert in the tasks. +argument_specs: + main: + options: + + redis__conf_appendonly: + type: 'str' + required: false + default: 'no' + description: 'Enables the append only file (AOF) persistence mode.' + + redis__conf_auto_aof_rewrite_min_size: + type: 'str' + required: false + default: '64mb' + description: 'Minimum size of the append only file before a rewrite is triggered.' + + redis__conf_bind: + type: 'str' + required: false + default: '127.0.0.1 -::1' + description: 'Space-separated list of addresses Redis listens on.' + + redis__conf_databases: + type: 'int' + required: false + default: 16 + description: 'Number of databases.' + + redis__conf_loglevel: + type: 'str' + required: false + default: 'notice' + description: 'Server verbosity level.' + + redis__conf_maxmemory: + type: 'str' + required: false + default: '50M' + description: 'Memory limit after which the eviction policy takes effect.' + + redis__conf_maxmemory_policy: + type: 'str' + required: false + default: 'noeviction' + description: 'How Redis evicts keys once the memory limit is reached.' + + redis__conf_port: + type: 'int' + required: false + default: 6379 + description: 'TCP port to listen on. Set to 0 to disable the TCP socket.' + + redis__conf_protected_mode: + type: 'str' + required: false + default: 'yes' + description: 'Refuses queries from non-loopback addresses while no password is set.' + + redis__conf_replica_serve_stale_data: + type: 'str' + required: false + default: 'yes' + description: 'Lets a replica answer queries while the link to the primary is down.' + + redis__conf_requirepass: + type: 'str' + required: false + description: 'Password clients have to authenticate with.' + + redis__conf_save: + type: 'list' + elements: 'str' + required: false + description: 'Snapshotting rules, each entry being a " " pair.' + + redis__conf_tls_auth_clients: + type: 'str' + required: false + description: 'Whether clients have to authenticate with a certificate.' + + redis__conf_tls_ca_cert_file: + type: 'str' + required: false + description: 'Path to the CA certificate used to verify clients.' + + redis__conf_tls_cert_file: + type: 'str' + required: false + description: 'Path to the TLS certificate.' + + redis__conf_tls_key_file: + type: 'str' + required: false + description: 'Path to the TLS private key.' + + redis__conf_tls_port: + type: 'int' + required: false + description: 'TLS port to listen on. Set redis__conf_port to 0 to only listen with TLS.' + + redis__conf_unixsocketperm: + type: 'str' + required: false + default: '770' + description: 'Permissions of the Unix socket.' + + redis__kernel_settings__sysctl__dependent_var: + type: 'list' + elements: 'dict' + required: false + description: 'Kernel settings injected into the kernel_settings role.' + + redis__kernel_settings__transparent_hugepages__dependent_var: + type: 'str' + required: false + default: 'madvise' + description: 'Transparent hugepages setting injected into the kernel_settings role.' + + redis__service_enabled: + type: 'bool' + required: false + default: true + description: 'Enables or disables the Redis service.' + + redis__service_limit_nofile: + type: 'int' + required: false + default: 10240 + description: 'systemd: resource limit for the number of file descriptors.' + + redis__service_state: + type: 'str' + required: false + choices: + - 'reloaded' + - 'restarted' + - 'started' + - 'stopped' + description: 'Changes the state of the Redis service.' + + redis__service_timeout_start_sec: + type: 'str' + required: false + default: '90s' + description: 'systemd: time to wait for start-up.' + + redis__service_timeout_stop_sec: + type: 'str' + required: false + default: '90s' + description: 'systemd: time to wait for the service to stop.' From b1cb03dd5d3f21d0a6db511536a8a190ec00b831 Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 16:48:02 +0200 Subject: [PATCH 4/6] fix(roles/valkey): keep the ownership of valkey.conf that the package sets The RHEL package ships `Z /etc/valkey ~0750 valkey root` in tmpfiles.d, which systemd-tmpfiles applies at every boot and on every RPM tmpfiles trigger, so the root:valkey the role set flipped back to valkey:root and the next run reported a change (verified on Rocky 10). Debian and Ubuntu keep root:valkey. With this, a second run of setup_nextcloud on a fresh Rocky 10 reports changed=0. --- roles/valkey/tasks/main.yml | 4 ++-- roles/valkey/vars/Debian.yml | 2 ++ roles/valkey/vars/RedHat.yml | 4 ++++ roles/valkey/vars/Ubuntu.yml | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/roles/valkey/tasks/main.yml b/roles/valkey/tasks/main.yml index b5e957901..dc0b9abe6 100644 --- a/roles/valkey/tasks/main.yml +++ b/roles/valkey/tasks/main.yml @@ -34,8 +34,8 @@ backup: true src: 'etc/valkey/{{ valkey__installed_version }}-valkey.conf.j2' dest: '{{ __valkey__config_dir }}/{{ __valkey__config_file }}' - owner: 'root' - group: '{{ __valkey__group }}' + owner: '{{ __valkey__config_file_owner }}' + group: '{{ __valkey__config_file_group }}' mode: 0o640 notify: 'valkey: restart valkey' diff --git a/roles/valkey/vars/Debian.yml b/roles/valkey/vars/Debian.yml index 621bac59d..027384060 100644 --- a/roles/valkey/vars/Debian.yml +++ b/roles/valkey/vars/Debian.yml @@ -2,6 +2,8 @@ __valkey__conf_logfile: '/var/log/valkey/valkey-server.log' __valkey__conf_unixsocket: '/run/valkey/valkey-server.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' +__valkey__config_file_group: 'valkey' +__valkey__config_file_owner: 'root' __valkey__data_dir: '/var/lib/valkey' __valkey__group: 'valkey' __valkey__package: 'valkey-server' diff --git a/roles/valkey/vars/RedHat.yml b/roles/valkey/vars/RedHat.yml index dc3db9721..bdf6a0e68 100644 --- a/roles/valkey/vars/RedHat.yml +++ b/roles/valkey/vars/RedHat.yml @@ -2,6 +2,10 @@ __valkey__conf_logfile: '/var/log/valkey/valkey.log' __valkey__conf_unixsocket: '/run/valkey/valkey.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' +# The package's tmpfiles.d rule (Z /etc/valkey ~0750 valkey root) resets the ownership to this on +# every boot and every systemd-tmpfiles run, so the role keeps it rather than fighting it. +__valkey__config_file_group: 'root' +__valkey__config_file_owner: 'valkey' __valkey__data_dir: '/var/lib/valkey' __valkey__group: 'valkey' __valkey__package: 'valkey' diff --git a/roles/valkey/vars/Ubuntu.yml b/roles/valkey/vars/Ubuntu.yml index 621bac59d..027384060 100644 --- a/roles/valkey/vars/Ubuntu.yml +++ b/roles/valkey/vars/Ubuntu.yml @@ -2,6 +2,8 @@ __valkey__conf_logfile: '/var/log/valkey/valkey-server.log' __valkey__conf_unixsocket: '/run/valkey/valkey-server.sock' __valkey__config_dir: '/etc/valkey' __valkey__config_file: 'valkey.conf' +__valkey__config_file_group: 'valkey' +__valkey__config_file_owner: 'root' __valkey__data_dir: '/var/lib/valkey' __valkey__group: 'valkey' __valkey__package: 'valkey-server' From 97d875dd5095a515d85fbceae1db4273175dca5d Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 16:48:02 +0200 Subject: [PATCH 5/6] fix(roles/redis): keep the ownership of redis.conf that the package sets Same as for Valkey: the Remi package ships `Z /etc/redis ~0750 redis root` in tmpfiles.d (redis 8.8 for EL9). --- CHANGELOG.md | 1 + roles/redis/tasks/main.yml | 4 ++-- roles/redis/vars/Debian.yml | 2 ++ roles/redis/vars/RedHat.yml | 4 ++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03eec8279..31a102d84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* **role:redis, role:valkey**: On RHEL, a run after a reboot no longer reports the configuration file as changed, since the role keeps the ownership that the package's tmpfiles.d rule restores at every boot. * **role:borg_local**: The Icinga downtime for `clamd@scan` during a backup is set, where Icinga had rejected the request as invalid JSON. * **role:acme_sh**: A changed `acme-sh.service` or `acme-sh.timer`, for example a new schedule, takes effect in the same run. Until now systemd kept the old unit until the next reload. * **role:clamav**: A changed `clamav-clamdscan.service` takes effect in the same run. Until now only a changed timer reloaded systemd. diff --git a/roles/redis/tasks/main.yml b/roles/redis/tasks/main.yml index c7a808463..c5295b50c 100644 --- a/roles/redis/tasks/main.yml +++ b/roles/redis/tasks/main.yml @@ -35,8 +35,8 @@ backup: true src: 'etc/redis/{{ redis__installed_version }}-redis.conf.j2' dest: '{{ __redis__config_dir }}/{{ __redis__config_file }}' - owner: 'root' - group: 'redis' + owner: '{{ __redis__config_file_owner }}' + group: '{{ __redis__config_file_group }}' mode: 0o640 notify: 'redis: restart redis' diff --git a/roles/redis/vars/Debian.yml b/roles/redis/vars/Debian.yml index a18c9f805..9fb811b40 100644 --- a/roles/redis/vars/Debian.yml +++ b/roles/redis/vars/Debian.yml @@ -2,6 +2,8 @@ __redis__conf_logfile: '/var/log/redis/redis-server.log' __redis__conf_unixsocket: '/run/redis/redis-server.sock' __redis__config_dir: '/etc/redis' __redis__config_file: 'redis.conf' +__redis__config_file_group: 'redis' +__redis__config_file_owner: 'root' __redis__data_dir: '/var/lib/redis' __redis__module_dir: '/etc/redis/modules' __redis__package: 'redis-server' diff --git a/roles/redis/vars/RedHat.yml b/roles/redis/vars/RedHat.yml index b237f9c7a..92288fa38 100644 --- a/roles/redis/vars/RedHat.yml +++ b/roles/redis/vars/RedHat.yml @@ -2,6 +2,10 @@ __redis__conf_logfile: '/var/log/redis/redis.log' __redis__conf_unixsocket: '/run/redis/redis.sock' __redis__config_dir: '/etc/redis' __redis__config_file: 'redis.conf' +# The package's tmpfiles.d rule (Z /etc/redis ~0750 redis root) resets the ownership to this on +# every boot and every systemd-tmpfiles run, so the role keeps it rather than fighting it. +__redis__config_file_group: 'root' +__redis__config_file_owner: 'redis' __redis__data_dir: '/var/lib/redis' __redis__module_dir: '/etc/redis/modules' __redis__package: 'redis' From e75fafd8703cdf6b863429a342d35f87e466bd00 Mon Sep 17 00:00:00 2001 From: Markus Frei Date: Tue, 22 Sep 2026 20:05:36 +0200 Subject: [PATCH 6/6] test(molecule): assert the valkey Unix socket and the config ownership --- extensions/molecule/valkey/verify.yml | 44 ++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/extensions/molecule/valkey/verify.yml b/extensions/molecule/valkey/verify.yml index a8ad971c6..0639f89a8 100644 --- a/extensions/molecule/valkey/verify.yml +++ b/extensions/molecule/valkey/verify.yml @@ -38,6 +38,35 @@ port: '{{ valkey__conf_port | d(6379) }}' timeout: 30 + # The socket is what local clients such as Nextcloud connect through. Its path is read back + # from the running server, and the PING over it proves the server actually listens there. + - name: 'valkey-cli CONFIG GET unixsocket' + ansible.builtin.command: '{{ __molecule__valkey_cli }} CONFIG GET unixsocket' + register: '__molecule__valkey_unixsocket_result' + changed_when: false + + - name: 'valkey-cli -s PING' + ansible.builtin.command: '{{ __molecule__valkey_cli }} -s {{ __molecule__valkey_unixsocket }} PING' + register: '__molecule__valkey_socket_ping_result' + changed_when: false + vars: + __molecule__valkey_unixsocket: '{{ __molecule__valkey_unixsocket_result["stdout_lines"][1] }}' + + - name: 'stat the Unix socket' + ansible.builtin.stat: + path: '{{ __molecule__valkey_unixsocket_result["stdout_lines"][1] }}' + register: '__molecule__valkey_socket_stat_result' + + # 0770 and the valkey group: a role that needs access adds its service user to that group, + # and nobody else reaches the socket. + - name: 'Assert valkey answers on its Unix socket, which only its group may use' + ansible.builtin.assert: + that: + - '__molecule__valkey_socket_ping_result["stdout"] == "PONG"' + - '__molecule__valkey_socket_stat_result["stat"]["mode"] == "0770"' + - '__molecule__valkey_socket_stat_result["stat"]["gr_name"] == "valkey"' + fail_msg: 'CONFIG GET unixsocket: {{ __molecule__valkey_unixsocket_result["stdout_lines"] | to_json }} - stat: {{ __molecule__valkey_socket_stat_result["stat"] | d({}) | to_json }}' + # The bind default carries the upstream '-::1', where the '-' makes the address optional. Two # halves to that: it answers on the IPv6 loopback where the host has one (asserted here), and # it merely warns where the host has none, instead of aborting - which the service assertion @@ -60,20 +89,25 @@ # The regression this role was split out for: on RHEL 10 the config file was deployed with a # hardcoded `redis` group while the package creates a `valkey` one, so the run died with - # "chgrp failed: failed to look up group redis". Assert the ownership the package expects. + # "chgrp failed: failed to look up group redis". Assert the ownership the package expects, + # which on RHEL is the valkey:root that its tmpfiles.d rule restores at every boot, and on + # Debian and Ubuntu the root:valkey their package ships. - name: 'stat /etc/valkey/valkey.conf' ansible.builtin.stat: path: '/etc/valkey/valkey.conf' register: '__molecule__valkey_conf_result' - - name: 'Assert the config file is owned by root:valkey and not world-readable' + - name: 'Assert the config file has the ownership of the package and is not world-readable' ansible.builtin.assert: that: - '__molecule__valkey_conf_result["stat"]["exists"]' - - '__molecule__valkey_conf_result["stat"]["pw_name"] == "root"' - - '__molecule__valkey_conf_result["stat"]["gr_name"] == "valkey"' + - '__molecule__valkey_conf_result["stat"]["pw_name"] == __molecule__valkey_conf_owner' + - '__molecule__valkey_conf_result["stat"]["gr_name"] == __molecule__valkey_conf_group' - '__molecule__valkey_conf_result["stat"]["mode"] == "0640"' - fail_msg: '/etc/valkey/valkey.conf is {{ __molecule__valkey_conf_result["stat"] | d("missing") }}' + fail_msg: '/etc/valkey/valkey.conf is {{ __molecule__valkey_conf_result["stat"] | d("missing") | to_json }}' + vars: + __molecule__valkey_conf_group: '{{ (ansible_facts["os_family"] == "RedHat") | ternary("root", "valkey") }}' + __molecule__valkey_conf_owner: '{{ (ansible_facts["os_family"] == "RedHat") | ternary("valkey", "root") }}' # The systemd drop-in is only proven by what the unit ends up with, not by the file on disk. - name: 'systemctl show {{ __molecule__valkey_service_name }} --property=LimitNOFILE' # noqa command-instead-of-module (read-only state query)