obsidian_headless: grant the service user on shared folders too (WDWL-22)
Content multica creates in inbox/ is owned multica:multica, so obsync fell into "other" (---) and Sync could neither read nor upload Wong's submissions. Found after the real Sync sign-in on spaceship-earth; the earlier verify only tested the obsync -> multica direction. Shared folders now carry user:obsync:rwx as an access and a default entry alongside the shared user's, and verify tests the multica -> obsync direction (read + rewrite, as merge needs). Second run changed=0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2d039ae32f
commit
fc2ad85e19
3 files changed
+79
-4
No files matched your search
@@ -25,7 +25,7 @@ only. Entry point: `playbooks/provision-wong-host.yml`.
|
||||
| `/srv/obsidian/` | `0710 obsync` | `user:multica:--x` |
|
||||
| `/srv/obsidian/vault/` | `0710 obsync` | `user:multica:--x`, so `multica` can traverse but not list |
|
||||
| `vault/everpure/`, `vault/wip/` | `0700 obsync` | none |
|
||||
| `vault/inbox/` | `0770 obsync` | `user:multica:rwx`, plus the same as a **default** entry |
|
||||
| `vault/inbox/` | `0770 obsync` | `user:multica:rwx` and `user:obsync:rwx`, each also as a **default** entry |
|
||||
|
||||
On a directory with an ACL, the mode's group bits are the ACL mask. A
|
||||
`chmod 0700` would therefore void every named entry, which is why the
|
||||
@@ -38,6 +38,9 @@ only. Entry point: `playbooks/provision-wong-host.yml`.
|
||||
- `multica` writes, reads and deletes in `inbox/`.
|
||||
- A folder and file created by `obsync` under umask `0077` (the way Sync
|
||||
creates them) stay readable and writable by `multica`.
|
||||
- What `multica` creates stays readable and writable by `obsync`. Those
|
||||
files are owned `multica:multica`, so without the named `user:obsync`
|
||||
default entry Sync could not read them and would never upload them.
|
||||
- `multica` is refused both listing the vault root and listing or writing
|
||||
`everpure/` and `wip/`.
|
||||
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
# <parent>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
||||
# <vault>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
||||
# <vault>/<private>/ obsync 0700 nobody else
|
||||
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx, and the same as a
|
||||
# DEFAULT entry, so new children inherit
|
||||
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx and user:obsync:rwx,
|
||||
# each also as a DEFAULT entry, so new
|
||||
# children inherit both
|
||||
#
|
||||
# The user:obsync entry matters for content the SHARED user creates: that is
|
||||
# owned <u>:<u>, so without a named entry obsync falls into "other" (---)
|
||||
# and Sync can neither read nor upload it.
|
||||
#
|
||||
# The owning group is obsync's private group, so its bits grant nobody. On a
|
||||
# directory that carries an ACL, the group bits of the mode ARE the ACL mask,
|
||||
@@ -62,6 +67,18 @@
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
|
||||
# Each shared folder gets its user AND the service user, both as access and
|
||||
# default entries.
|
||||
- name: Layout | Build the shared-folder grants
|
||||
ansible.builtin.set_fact:
|
||||
_obh_grants: >-
|
||||
{{ (obsidian_headless_shared_folders
|
||||
+ (obsidian_headless_shared_folders
|
||||
| map(attribute='path')
|
||||
| map('community.general.dict_kv', 'path') | list
|
||||
| map('combine', {'user': obsidian_headless_user}) | list))
|
||||
| unique | list }}
|
||||
|
||||
- name: Layout | Grant shared folders (access and default entries)
|
||||
ansible.posix.acl:
|
||||
path: "{{ obsidian_headless_vault_path }}/{{ item.0.path }}"
|
||||
@@ -70,6 +87,6 @@
|
||||
permissions: rwx
|
||||
default: "{{ item.1 }}"
|
||||
state: present
|
||||
loop: "{{ obsidian_headless_shared_folders | product([false, true]) | list }}"
|
||||
loop: "{{ _obh_grants | product([false, true]) | list }}"
|
||||
loop_control:
|
||||
label: "{{ item.0.path }} -> {{ item.0.user }}{{ ' (default)' if item.1 else '' }}"
|
||||
@@ -18,6 +18,61 @@
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
|
||||
# The reverse direction: what the shared user writes, Sync must be able to
|
||||
# read (to upload it) and rewrite (to merge remote edits into it).
|
||||
- name: Verify | Content a shared user creates stays usable by the service (create)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'd="$1/.obh-verify-rev"; mkdir -p "$d" && echo ok > "$d/note.md"'
|
||||
- sh
|
||||
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
|
||||
become: true
|
||||
become_user: "{{ item.user }}"
|
||||
loop: "{{ obsidian_headless_shared_folders }}"
|
||||
loop_control:
|
||||
label: "{{ item.user }} -> {{ item.path }}"
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: Verify | Content a shared user creates stays usable by the service (use)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'd="$1/.obh-verify-rev"; grep -q ok "$d/note.md" && echo more >> "$d/note.md" && echo ok > "$d/reply.md"'
|
||||
- sh
|
||||
- "{{ obsidian_headless_vault_path }}/{{ item.path }}"
|
||||
become: true
|
||||
become_user: "{{ obsidian_headless_user }}"
|
||||
loop: "{{ obsidian_headless_shared_folders }}"
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
register: _obh_reverse
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: Verify | Remove the reverse probes
|
||||
ansible.builtin.file:
|
||||
path: "{{ obsidian_headless_vault_path }}/{{ item.path }}/.obh-verify-rev"
|
||||
state: absent
|
||||
loop: "{{ obsidian_headless_shared_folders }}"
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Verify | The service can read and rewrite shared users' content
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- _obh_reverse.results | rejectattr('rc', 'equalto', 0) | list | length == 0
|
||||
fail_msg: >-
|
||||
{{ obsidian_headless_user }} cannot read or write content a shared user
|
||||
created; Sync would never upload it. Check the user:{{ obsidian_headless_user }}
|
||||
default ACL entry on the shared folder.
|
||||
quiet: true
|
||||
|
||||
# Simulates the service: obsync, umask 0077, creating a folder and a file the
|
||||
# way Sync does. The shared user must still reach both, via the default ACL.
|
||||
- name: Verify | Content the service creates in a shared folder inherits the grant (create)
|
||||
|
||||
Reference in new issue
Block a user