- provision-agent-host.yml: multica_pat now comes from vault_multica_pats[inventory_hostname_short], one daemon-<host> token per host. A pre_task asserts the key exists (never prints the value) so a missing entry fails by host name instead of as a no_log "censored". - provision-wong-host.yml: spaceship-earth only. Grows the root partition, PV and LV/filesystem with growpart + community.general.lvg/lvol; idempotent (second run changed=0). Then applies obsidian_headless. - roles/obsidian_headless: Obsidian's official headless Sync client (obsidian-headless 0.0.14) as system user obsync under a sandboxed systemd unit with UMask=0077. Vault at /srv/obsidian/vault: everpure/ and wip/ obsync-only, inbox/ granted to multica via access + default ACLs, vault root traverse-only. Verifies the isolation as multica. Sign-in to Obsidian Sync stays manual (see the role README). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.9 KiB
YAML
76 lines
2.9 KiB
YAML
---
|
|
# Permission model
|
|
#
|
|
# <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
|
|
#
|
|
# 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,
|
|
# which is why the modes here are 0710/0770 rather than 0700: a 0700 chmod
|
|
# would zero the mask and silently void every named entry.
|
|
#
|
|
# Anything else in the vault (folders Sync creates later, .obsidian/) is born
|
|
# private: the service runs with UMask=0077, and the umask is ignored only
|
|
# under a directory that has a default ACL — i.e. only in the shared folders.
|
|
|
|
- name: Layout | Collect the users granted shared folders
|
|
ansible.builtin.set_fact:
|
|
_obh_shared_users: "{{ obsidian_headless_shared_folders | map(attribute='user') | unique | list }}"
|
|
|
|
- name: Layout | Create the vault and its parent
|
|
ansible.builtin.file:
|
|
path: "{{ item }}"
|
|
state: directory
|
|
owner: "{{ obsidian_headless_user }}"
|
|
group: "{{ obsidian_headless_group }}"
|
|
mode: "{{ '0710' if _obh_shared_users | length > 0 else '0700' }}"
|
|
loop:
|
|
- "{{ obsidian_headless_vault_path | dirname }}"
|
|
- "{{ obsidian_headless_vault_path }}"
|
|
|
|
- name: Layout | Let shared users traverse (not list) the vault and its parent
|
|
ansible.posix.acl:
|
|
path: "{{ item.0 }}"
|
|
entity: "{{ item.1 }}"
|
|
etype: user
|
|
permissions: x
|
|
state: present
|
|
loop: >-
|
|
{{ [obsidian_headless_vault_path | dirname, obsidian_headless_vault_path]
|
|
| product(_obh_shared_users) | list }}
|
|
|
|
- name: Layout | Create the private folders
|
|
ansible.builtin.file:
|
|
path: "{{ obsidian_headless_vault_path }}/{{ item }}"
|
|
state: directory
|
|
owner: "{{ obsidian_headless_user }}"
|
|
group: "{{ obsidian_headless_group }}"
|
|
mode: "0700"
|
|
loop: "{{ obsidian_headless_private_folders }}"
|
|
|
|
- name: Layout | Create the shared folders
|
|
ansible.builtin.file:
|
|
path: "{{ obsidian_headless_vault_path }}/{{ item.path }}"
|
|
state: directory
|
|
owner: "{{ obsidian_headless_user }}"
|
|
group: "{{ obsidian_headless_group }}"
|
|
mode: "0770"
|
|
loop: "{{ obsidian_headless_shared_folders }}"
|
|
loop_control:
|
|
label: "{{ item.path }}"
|
|
|
|
- name: Layout | Grant shared folders (access and default entries)
|
|
ansible.posix.acl:
|
|
path: "{{ obsidian_headless_vault_path }}/{{ item.0.path }}"
|
|
entity: "{{ item.0.user }}"
|
|
etype: user
|
|
permissions: rwx
|
|
default: "{{ item.1 }}"
|
|
state: present
|
|
loop: "{{ obsidian_headless_shared_folders | product([false, true]) | list }}"
|
|
loop_control:
|
|
label: "{{ item.0.path }} -> {{ item.0.user }}{{ ' (default)' if item.1 else '' }}"
|