--- # Local homelab operator account — broad permissions (LAN-trusted host) - name: Ensure stack user exists with bash shell ansible.builtin.user: name: "{{ stack_owner }}" state: present shell: /bin/bash create_home: true groups: "{{ extra_user_groups }}" append: true tags: [users, permissions] - name: Ensure useful system groups exist (create if missing) ansible.builtin.group: name: "{{ item }}" state: present loop: "{{ extra_user_groups }}" failed_when: false tags: [users, permissions] - name: Add stack user to all useful groups ansible.builtin.user: name: "{{ stack_owner }}" groups: "{{ extra_user_groups }}" append: true tags: [users, permissions] - name: Passwordless sudo for stack user (full admin) ansible.builtin.copy: dest: "/etc/sudoers.d/{{ stack_owner }}" mode: "0440" owner: root group: root validate: "visudo -cf %s" content: | # Homelab operator — passwordless sudo (local trusted host) {{ stack_owner }} ALL=(ALL) NOPASSWD:ALL Defaults:{{ stack_owner }} !env_reset Defaults:{{ stack_owner }} secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" tags: [users, permissions, sudo] - name: Allow stack user to manage Docker and arr-stack via systemctl without password ansible.builtin.copy: dest: "/etc/sudoers.d/{{ stack_owner }}-services" mode: "0440" owner: root group: root validate: "visudo -cf %s" content: | # Redundant with NOPASSWD:ALL; kept explicit for clarity if sudo is tightened later {{ stack_owner }} ALL=(root) NOPASSWD: /bin/systemctl * docker*, /bin/systemctl * containerd*, /bin/systemctl * arr-stack*, /usr/bin/systemctl * docker*, /usr/bin/systemctl * containerd*, /usr/bin/systemctl * arr-stack* tags: [users, permissions, sudo] - name: Install ACL tools ansible.builtin.apt: name: acl state: present tags: [users, permissions] - name: Own stack and storage roots ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ stack_owner }}" group: "{{ stack_group }}" mode: "0775" loop: - "{{ stack_root }}" - "{{ storage_root }}" tags: [users, permissions] - name: Recursive ownership of stack tree ansible.builtin.file: path: "{{ stack_root }}" state: directory owner: "{{ stack_owner }}" group: "{{ stack_group }}" recurse: true tags: [users, permissions] - name: Recursive ownership of storage tree ansible.builtin.file: path: "{{ storage_root }}" state: directory owner: "{{ stack_owner }}" group: "{{ stack_group }}" recurse: true # Large trees: may take a moment; safe to re-run tags: [users, permissions] - name: Default ACLs so new files under stack stay group-writable ansible.builtin.command: > setfacl -R -d -m u:{{ stack_owner }}:rwx,g:{{ stack_group }}:rwx,o::r-x {{ item }} loop: - "{{ stack_root }}" - "{{ storage_root }}" changed_when: true failed_when: false tags: [users, permissions] - name: Current ACLs for stack owner full access ansible.builtin.command: > setfacl -R -m u:{{ stack_owner }}:rwx,g:{{ stack_group }}:rwx {{ item }} loop: - "{{ stack_root }}" - "{{ storage_root }}" changed_when: true failed_when: false tags: [users, permissions] - name: Discover DRM nodes for permission tweak ansible.builtin.find: paths: /dev/dri patterns: "*" file_type: any register: dri_nodes failed_when: false tags: [users, permissions, gpu] - name: Make /dev/dri nodes world-accessible (trusted local host) ansible.builtin.shell: | set -e if [ -d /dev/dri ]; then chmod a+rw /dev/dri/* 2>/dev/null || true ls -la /dev/dri fi register: dri_chmod changed_when: "'rw' in dri_chmod.stdout" failed_when: false tags: [users, permissions, gpu] - name: Persist permissive DRM udev rule for trusted host ansible.builtin.copy: dest: /etc/udev/rules.d/99-homelab-dri-permissive.rules mode: "0644" content: | # Trusted LAN homelab — allow any user/container access to GPU nodes KERNEL=="renderD*", MODE="0666" KERNEL=="card*", MODE="0666" KERNEL=="controlD*", MODE="0666" notify: Reload udev tags: [users, permissions, gpu] - name: Ensure docker socket is group-writable for docker group ansible.builtin.file: path: /var/run/docker.sock group: docker mode: "0660" failed_when: false tags: [users, permissions, docker] - name: Verify stack user can sudo without password become: true become_user: "{{ stack_owner }}" ansible.builtin.command: sudo -n true changed_when: false tags: [users, permissions] - name: Show stack user identity and groups become: true become_user: "{{ stack_owner }}" ansible.builtin.command: id register: stack_user_id changed_when: false tags: [users, permissions] - name: Report stack user permissions ansible.builtin.debug: msg: "{{ stack_user_id.stdout }}" tags: [users, permissions]