Docker Compose media stack (gluetun, *arr, Jellyfin), Ternary-Bonsai AI (Open WebUI, Forgejo, optional OpenHands), and Ansible bootstrap for the SER5 Ubuntu host.
171 lines
4.9 KiB
YAML
171 lines
4.9 KiB
YAML
---
|
|
# 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]
|