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/` | `0710 obsync` | `user:multica:--x` |
|
||||||
| `/srv/obsidian/vault/` | `0710 obsync` | `user:multica:--x`, so `multica` can traverse but not list |
|
| `/srv/obsidian/vault/` | `0710 obsync` | `user:multica:--x`, so `multica` can traverse but not list |
|
||||||
| `vault/everpure/`, `vault/wip/` | `0700 obsync` | none |
|
| `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
|
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
|
`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/`.
|
- `multica` writes, reads and deletes in `inbox/`.
|
||||||
- A folder and file created by `obsync` under umask `0077` (the way Sync
|
- A folder and file created by `obsync` under umask `0077` (the way Sync
|
||||||
creates them) stay readable and writable by `multica`.
|
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
|
- `multica` is refused both listing the vault root and listing or writing
|
||||||
`everpure/` and `wip/`.
|
`everpure/` and `wip/`.
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,13 @@
|
|||||||
# <parent>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
# <parent>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
||||||
# <vault>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
# <vault>/ obsync 0710 + user:<shared>:--x traverse, no listing
|
||||||
# <vault>/<private>/ obsync 0700 nobody else
|
# <vault>/<private>/ obsync 0700 nobody else
|
||||||
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx, and the same as a
|
# <vault>/<shared>/ obsync 0770 + user:<u>:rwx and user:obsync:rwx,
|
||||||
# DEFAULT entry, so new children inherit
|
# 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
|
# 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,
|
# directory that carries an ACL, the group bits of the mode ARE the ACL mask,
|
||||||
@@ -62,6 +67,18 @@
|
|||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.path }}"
|
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)
|
- name: Layout | Grant shared folders (access and default entries)
|
||||||
ansible.posix.acl:
|
ansible.posix.acl:
|
||||||
path: "{{ obsidian_headless_vault_path }}/{{ item.0.path }}"
|
path: "{{ obsidian_headless_vault_path }}/{{ item.0.path }}"
|
||||||
@@ -70,6 +87,6 @@
|
|||||||
permissions: rwx
|
permissions: rwx
|
||||||
default: "{{ item.1 }}"
|
default: "{{ item.1 }}"
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ obsidian_headless_shared_folders | product([false, true]) | list }}"
|
loop: "{{ _obh_grants | product([false, true]) | list }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
label: "{{ item.0.path }} -> {{ item.0.user }}{{ ' (default)' if item.1 else '' }}"
|
label: "{{ item.0.path }} -> {{ item.0.user }}{{ ' (default)' if item.1 else '' }}"
|
||||||
@@ -18,6 +18,61 @@
|
|||||||
changed_when: false
|
changed_when: false
|
||||||
check_mode: 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
|
# 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.
|
# 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)
|
- name: Verify | Content the service creates in a shared folder inherits the grant (create)
|
||||||
|
|||||||
Reference in new issue
Block a user