From 5a3137ca8da66983600a017742e02d7aa6734e58 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 30 Oct 2026 08:52:30 +0200 Subject: [PATCH] Make the base directory owned by root Whoever owns it could rename any service's directory (even a root-owned one) and put another in its place. On Synology, the recursive chown now compares only the base path's gid (its uid is root's now) and runs before the base paths are ensured, so it doesn't undo their ownership. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 10 ++++++ roles/custom/matrix-base/defaults/main.yml | 5 +++ .../matrix-base/tasks/setup_matrix_base.yml | 35 +++++++++---------- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dad27a52..18abe2872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 2026-10-30 + +## The base directory (`/matrix`) is now owned by root + +The playbook now makes the base directory (`matrix_base_data_path`, `/matrix` by default) and its `bin` directory owned by `root`, with the `matrix` group and the same mode (`0750`). Previously they were owned by the `matrix` user. + +Whoever owns this directory can rename any service's directory in it, even one owned by root, and put another in its place. A service that runs something as root from its directory relies on this not being possible. + +Nothing changes for you: the services' own directories keep their owners, and the `matrix` user can still reach them through the group. Re-running the playbook (`just install-all`) changes the owner. To keep the old owner, set `matrix_base_data_path_owner: "{{ matrix_user_name }}"` in your `vars.yml` file (not recommended). + # 2026-10-19 ## `just start-all` and `just start-group` restart all targeted services again diff --git a/roles/custom/matrix-base/defaults/main.yml b/roles/custom/matrix-base/defaults/main.yml index 1a3baf51f..6e64d920e 100644 --- a/roles/custom/matrix-base/defaults/main.yml +++ b/roles/custom/matrix-base/defaults/main.yml @@ -227,6 +227,11 @@ matrix_base_synology_volume_path: "/volume1" matrix_base_data_path: "/matrix" matrix_base_data_path_mode: "750" +# Owned by root: whoever owns it could rename any service's directory (even a root-owned one) and put another in its place. +# The group lets the matrix user reach the services' directories. +matrix_base_data_path_owner: root +matrix_base_data_path_group: "{{ matrix_group_name }}" + matrix_bin_path: "{{ matrix_base_data_path }}/bin" matrix_host_command_sleep: "/usr/bin/env sleep" diff --git a/roles/custom/matrix-base/tasks/setup_matrix_base.yml b/roles/custom/matrix-base/tasks/setup_matrix_base.yml index 7fc7148e7..b7c23cb26 100644 --- a/roles/custom/matrix-base/tasks/setup_matrix_base.yml +++ b/roles/custom/matrix-base/tasks/setup_matrix_base.yml @@ -13,21 +13,33 @@ --- -# Snapshot ownership before any changes so we can decide whether a recursive -# chown is needed (only when uid/gid actually differs from expected). +# On Synology, name-based chown works for directly-touched paths but leaves +# existing sub-paths with stale numeric ownership when uid/gid changes between +# runs. We recurse only when the base path's gid doesn't match, so normal runs +# skip the expensive tree walk entirely. Its uid is not compared, as the base path is owned by root, not the matrix user. +# The recursive chown runs before the base paths are ensured below, so they get their own ownership back. +# chown -R is used instead of the file module's recurse option to avoid Ansible iterating every entry in Python. - name: Check current ownership of Matrix base path (Synology) ansible.builtin.stat: path: "{{ matrix_base_data_path }}" register: matrix_base_data_path_stat when: matrix_base_host_is_synology +- name: Ensure Matrix base path ownership is correct using numeric UID/GID (Synology) + ansible.builtin.command: chown -R {{ matrix_user_uid }}:{{ matrix_user_gid }} {{ matrix_base_data_path }} + changed_when: true + when: >- + matrix_base_host_is_synology and + matrix_base_data_path_stat.stat.exists and + matrix_base_data_path_stat.stat.gid | int != matrix_user_gid | int + - name: Ensure Matrix base paths exists ansible.builtin.file: path: "{{ item }}" state: directory mode: "{{ matrix_base_data_path_mode }}" - owner: "{{ matrix_user_name }}" - group: "{{ matrix_group_name }}" + owner: "{{ matrix_base_data_path_owner }}" + group: "{{ matrix_base_data_path_group }}" with_items: - "{{ matrix_base_data_path }}" - "{{ matrix_bin_path }}" @@ -37,18 +49,3 @@ src: "{{ role_path }}/templates/bin/remove-all.j2" dest: "{{ matrix_bin_path }}/remove-all" mode: '0750' - -# On Synology, name-based chown works for directly-touched paths but leaves -# existing sub-paths with stale numeric ownership when uid/gid changes between -# runs. We recurse only when the pre-task uid/gid didn't match, so normal runs -# skip the expensive tree walk entirely. chown -R is used instead of the file -# module's recurse option to avoid Ansible iterating every entry in Python. -- name: Ensure Matrix base path ownership is correct using numeric UID/GID (Synology) - ansible.builtin.command: chown -R {{ matrix_user_uid }}:{{ matrix_user_gid }} {{ matrix_base_data_path }} - changed_when: true - when: >- - matrix_base_host_is_synology and ( - not matrix_base_data_path_stat.stat.exists or - matrix_base_data_path_stat.stat.uid | int != matrix_user_uid | int or - matrix_base_data_path_stat.stat.gid | int != matrix_user_gid | int - )