initial commit
This commit is contained in:
45
roles/container_isolation/files/group.containers
Normal file
45
roles/container_isolation/files/group.containers
Normal file
@@ -0,0 +1,45 @@
|
||||
# /etc/group.containers
|
||||
# Source of truth for container service groups. Sync to all Docker VMs
|
||||
# and the NFS server using sync-container-users.sh. Append-only — add new
|
||||
# lines here when adding containers, never renumber existing ones.
|
||||
#
|
||||
# Primary per-app groups mirror passwd.containers UIDs (2000-2099).
|
||||
# Shared groups start at 3000 for cross-container access.
|
||||
# format: name:x:GID:secondary,members
|
||||
2fauth:x:2000:
|
||||
audiobookshelf:x:2001:
|
||||
authelia:x:2002:
|
||||
bazarr:x:2003:
|
||||
cronjobs:x:2004:
|
||||
dashy:x:2005:
|
||||
dingus-archiver:x:2006:
|
||||
finance-app:x:2007:
|
||||
firefly3:x:2008:
|
||||
gitea:x:2009:
|
||||
gotify:x:2010:
|
||||
immich:x:2011:
|
||||
it-tools:x:2012:
|
||||
jellyfin:x:2013:
|
||||
joplin:x:2014:
|
||||
kiwix:x:2015:
|
||||
music-orchestrator:x:2016:
|
||||
nginxproxy:x:2017:
|
||||
ombi:x:2018:
|
||||
picoshare:x:2019:
|
||||
prowlarr:x:2020:
|
||||
qbittorrent:x:2021:
|
||||
radarr:x:2022:
|
||||
romm:x:2023:
|
||||
sonarr:x:2024:
|
||||
tandoor_recipes:x:2025:
|
||||
vault:x:2026:
|
||||
#
|
||||
# Shared groups (cross-container access, GID range 3000+)
|
||||
# mediapipeline: *arr apps + qbittorrent + jellyfin share the media/download
|
||||
# folders (e.g. /var/NFSFolder/AppData/media). Ombi is request-only (talks
|
||||
# to APIs, doesn't touch the filesystem) so it's excluded.
|
||||
mediapipeline:x:3000:bazarr,prowlarr,qbittorrent,radarr,sonarr,jellyfin
|
||||
#
|
||||
# finance-app and dingus-archiver are isolated custom apps with no shared
|
||||
# access — their primary groups above (2007, 2006) already cover them.
|
||||
# No secondary group needed unless that changes.
|
||||
34
roles/container_isolation/files/passwd.containers
Normal file
34
roles/container_isolation/files/passwd.containers
Normal file
@@ -0,0 +1,34 @@
|
||||
# /etc/passwd.containers
|
||||
# Source of truth for container service accounts. Sync to all Docker VMs
|
||||
# and the NFS server using sync-container-users.sh. Append-only — add new
|
||||
# lines here when adding containers, never renumber existing ones.
|
||||
#
|
||||
# UID range reserved: 2000-2099 (container service accounts)
|
||||
# format: name:x:UID:GID::/nonexistent:/usr/sbin/nologin
|
||||
2fauth:x:2000:2000::/nonexistent:/usr/sbin/nologin
|
||||
audiobookshelf:x:2001:2001::/nonexistent:/usr/sbin/nologin
|
||||
authelia:x:2002:2002::/nonexistent:/usr/sbin/nologin
|
||||
bazarr:x:2003:2003::/nonexistent:/usr/sbin/nologin
|
||||
cronjobs:x:2004:2004::/nonexistent:/usr/sbin/nologin
|
||||
dashy:x:2005:2005::/nonexistent:/usr/sbin/nologin
|
||||
dingus-archiver:x:2006:2006::/nonexistent:/usr/sbin/nologin
|
||||
finance-app:x:2007:2007::/nonexistent:/usr/sbin/nologin
|
||||
firefly3:x:2008:2008::/nonexistent:/usr/sbin/nologin
|
||||
gitea:x:2009:2009::/nonexistent:/usr/sbin/nologin
|
||||
gotify:x:2010:2010::/nonexistent:/usr/sbin/nologin
|
||||
immich:x:2011:2011::/nonexistent:/usr/sbin/nologin
|
||||
it-tools:x:2012:2012::/nonexistent:/usr/sbin/nologin
|
||||
jellyfin:x:2013:2013::/nonexistent:/usr/sbin/nologin
|
||||
joplin:x:2014:2014::/nonexistent:/usr/sbin/nologin
|
||||
kiwix:x:2015:2015::/nonexistent:/usr/sbin/nologin
|
||||
music-orchestrator:x:2016:2016::/nonexistent:/usr/sbin/nologin
|
||||
nginxproxy:x:2017:2017::/nonexistent:/usr/sbin/nologin
|
||||
ombi:x:2018:2018::/nonexistent:/usr/sbin/nologin
|
||||
picoshare:x:2019:2019::/nonexistent:/usr/sbin/nologin
|
||||
prowlarr:x:2020:2020::/nonexistent:/usr/sbin/nologin
|
||||
qbittorrent:x:2021:2021::/nonexistent:/usr/sbin/nologin
|
||||
radarr:x:2022:2022::/nonexistent:/usr/sbin/nologin
|
||||
romm:x:2023:2023::/nonexistent:/usr/sbin/nologin
|
||||
sonarr:x:2024:2024::/nonexistent:/usr/sbin/nologin
|
||||
tandoor_recipes:x:2025:2025::/nonexistent:/usr/sbin/nologin
|
||||
vault:x:2026:2026::/nonexistent:/usr/sbin/nologin
|
||||
45
roles/container_isolation/tasks/jerick_access.yml
Normal file
45
roles/container_isolation/tasks/jerick_access.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
# roles/container_isolation/tasks/jerick_access.yml
|
||||
#
|
||||
# jerick needs read-write access across every app folder for admin/
|
||||
# maintenance purposes, without weakening isolation between apps from
|
||||
# each other. Achieved by making jerick a secondary member of every
|
||||
# app's primary group, combined with 770 permissions on app folders
|
||||
# (see nfs_folders.yml) — owner: full access, group (incl. jerick):
|
||||
# full access, others: none.
|
||||
|
||||
- name: Check whether jerick exists on this host (read-only, does not create the account)
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ jerick_user }}"
|
||||
register: jerick_lookup
|
||||
failed_when: false
|
||||
|
||||
- name: Warn if jerick user does not exist on this host
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
WARNING: user '{{ jerick_user }}' was not found on this host.
|
||||
This role does not create jerick's account (assumed to be a
|
||||
pre-existing human user, not a service account). Create it first,
|
||||
then re-run this role so jerick gets added to the app groups below.
|
||||
when: jerick_lookup.failed | default(false)
|
||||
|
||||
- name: Add jerick to every app's primary group
|
||||
ansible.builtin.user:
|
||||
name: "{{ jerick_user }}"
|
||||
groups: "{{ item.name }}"
|
||||
append: true
|
||||
loop: "{{ container_apps }}"
|
||||
loop_control:
|
||||
label: "{{ jerick_user }} -> {{ item.name }}"
|
||||
when: not (jerick_lookup.failed | default(false))
|
||||
|
||||
- name: Add jerick to every shared group
|
||||
ansible.builtin.user:
|
||||
name: "{{ jerick_user }}"
|
||||
groups: "{{ item.name }}"
|
||||
append: true
|
||||
loop: "{{ shared_groups }}"
|
||||
loop_control:
|
||||
label: "{{ jerick_user }} -> {{ item.name }}"
|
||||
when: not (jerick_lookup.failed | default(false))
|
||||
9
roles/container_isolation/tasks/main.yml
Normal file
9
roles/container_isolation/tasks/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# roles/container_isolation/tasks/main.yml
|
||||
|
||||
- name: Sync container service groups and users (all hosts)
|
||||
import_tasks: sync_users.yml
|
||||
|
||||
- name: Add jerick to every app and shared group (all hosts)
|
||||
import_tasks: jerick_access.yml
|
||||
|
||||
49
roles/container_isolation/tasks/sync_users.yml
Normal file
49
roles/container_isolation/tasks/sync_users.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
# roles/container_isolation/tasks/sync_users.yml
|
||||
#
|
||||
# Creates each app's primary group + user, and each shared group, using
|
||||
# Ansible's built-in group/user modules instead of hand-rolled file
|
||||
# appends — this is idempotent and safe to re-run, and avoids the
|
||||
# malformed-/etc/passwd risk of manual editing.
|
||||
|
||||
- name: Create primary group for each container app
|
||||
ansible.builtin.group:
|
||||
name: "{{ item.name }}"
|
||||
gid: "{{ item.uid }}"
|
||||
state: present
|
||||
loop: "{{ container_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }} (gid {{ item.uid }})"
|
||||
|
||||
- name: Create shared (secondary) groups
|
||||
ansible.builtin.group:
|
||||
name: "{{ item.name }}"
|
||||
gid: "{{ item.gid }}"
|
||||
state: present
|
||||
loop: "{{ shared_groups }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }} (gid {{ item.gid }})"
|
||||
|
||||
- name: Create service user for each container app
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.name }}"
|
||||
uid: "{{ item.uid }}"
|
||||
group: "{{ item.name }}"
|
||||
shell: /usr/sbin/nologin
|
||||
home: /nonexistent
|
||||
create_home: false
|
||||
system: true
|
||||
state: present
|
||||
loop: "{{ container_apps }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }} (uid {{ item.uid }})"
|
||||
|
||||
- name: Add app users to their shared groups
|
||||
ansible.builtin.user:
|
||||
name: "{{ item.0.name }}"
|
||||
groups: "{{ item.1.name }}"
|
||||
append: true
|
||||
loop: "{{ container_apps | product(shared_groups) | list }}"
|
||||
loop_control:
|
||||
label: "{{ item.0.name }} -> {{ item.1.name }}"
|
||||
when: item.0.name in item.1.members
|
||||
58
roles/container_isolation/vars/main.yml
Normal file
58
roles/container_isolation/vars/main.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
# roles/container_isolation/vars/main.yml
|
||||
#
|
||||
# Single source of truth for container service accounts. Mirrors
|
||||
# files/passwd.containers and files/group.containers — keep in sync if
|
||||
# you edit one, edit the other. Append-only: never renumber existing
|
||||
# UIDs/GIDs once folders have been chowned on the NFS server.
|
||||
|
||||
jerick_user: jerick
|
||||
|
||||
# Every container service account. uid == primary gid for each.
|
||||
# jerick is added as a secondary member of every app's primary group
|
||||
# in the group sync task, giving him rw access (770) without weakening
|
||||
# isolation between apps.
|
||||
container_apps:
|
||||
- { name: "2fauth", uid: 2000 }
|
||||
- { name: "audiobookshelf", uid: 2001 }
|
||||
- { name: "authelia", uid: 2002 }
|
||||
- { name: "bazarr", uid: 2003 }
|
||||
- { name: "cronjobs", uid: 2004 }
|
||||
- { name: "dashy", uid: 2005 }
|
||||
- { name: "dingus-archiver", uid: 2006 }
|
||||
- { name: "finance-app", uid: 2007 }
|
||||
- { name: "firefly3", uid: 2008 }
|
||||
- { name: "gitea", uid: 2009 }
|
||||
- { name: "gotify", uid: 2010 }
|
||||
- { name: "immich", uid: 2011 }
|
||||
- { name: "it-tools", uid: 2012 }
|
||||
- { name: "jellyfin", uid: 2013 }
|
||||
- { name: "joplin", uid: 2014 }
|
||||
- { name: "kiwix", uid: 2015 }
|
||||
- { name: "music-orchestrator", uid: 2016 }
|
||||
- { name: "nginxproxy", uid: 2017 }
|
||||
- { name: "ombi", uid: 2018 }
|
||||
- { name: "picoshare", uid: 2019 }
|
||||
- { name: "prowlarr", uid: 2020 }
|
||||
- { name: "qbittorrent", uid: 2021 }
|
||||
- { name: "radarr", uid: 2022 }
|
||||
- { name: "romm", uid: 2023 }
|
||||
- { name: "sonarr", uid: 2024 }
|
||||
- { name: "tandoor_recipes", uid: 2025 }
|
||||
- { name: "vault", uid: 2026 }
|
||||
|
||||
# Shared secondary groups for cross-container access. "members" lists
|
||||
# app names from container_apps that need to be added to this group
|
||||
# IN ADDITION to their own primary group. jerick is added automatically
|
||||
# to every shared group as well as every per-app primary group — see
|
||||
# tasks/sync_users.yml.
|
||||
shared_groups:
|
||||
- name: mediapipeline
|
||||
gid: 3000
|
||||
members:
|
||||
- bazarr
|
||||
- prowlarr
|
||||
- qbittorrent
|
||||
- radarr
|
||||
- sonarr
|
||||
- jellyfin
|
||||
Reference in New Issue
Block a user