Logical bug fixes and add Jellyfin
This commit is contained in:
parent
bd16ea01bc
commit
6363aa713e
74
playbook.yml
74
playbook.yml
|
|
@ -8,6 +8,9 @@
|
||||||
tags: firewall_base
|
tags: firewall_base
|
||||||
- role: cli_productivity
|
- role: cli_productivity
|
||||||
tags: cli_productivity
|
tags: cli_productivity
|
||||||
|
- role: tailscale
|
||||||
|
tags: tailscale
|
||||||
|
when: tailscale_enabled | bool
|
||||||
|
|
||||||
- name: Shared storage clients
|
- name: Shared storage clients
|
||||||
hosts: nfs_clients
|
hosts: nfs_clients
|
||||||
|
|
@ -26,7 +29,7 @@
|
||||||
tags: nfs_server
|
tags: nfs_server
|
||||||
|
|
||||||
- name: Containers stack
|
- name: Containers stack
|
||||||
hosts: services
|
hosts: nas:services
|
||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
- role: container_runtime
|
- role: container_runtime
|
||||||
|
|
@ -34,13 +37,55 @@
|
||||||
- role: selinux_containers
|
- role: selinux_containers
|
||||||
tags: selinux_containers
|
tags: selinux_containers
|
||||||
|
|
||||||
|
- name: VPN
|
||||||
|
hosts: vpn_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: vpn
|
||||||
|
tags: vpn
|
||||||
|
|
||||||
|
- name: Postgres stack
|
||||||
|
hosts: postgres_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: postgres
|
||||||
|
tags: postgres
|
||||||
|
|
||||||
|
- name: Matrix stack
|
||||||
|
hosts: matrix_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: matrix_synapse
|
||||||
|
tags: matrix
|
||||||
|
when: matrix_enabled | bool
|
||||||
|
|
||||||
|
- name: Coturn stack
|
||||||
|
hosts: coturn_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: coturn
|
||||||
|
tags: coturn
|
||||||
|
|
||||||
- name: Media services
|
- name: Media services
|
||||||
hosts: media
|
hosts: media
|
||||||
become: true
|
become: true
|
||||||
roles:
|
roles:
|
||||||
# Jellyfin role will go here later.
|
- role: jellyfin
|
||||||
# - role: jellyfin
|
tags: jellyfin
|
||||||
# tags: jellyfin
|
|
||||||
|
- name: Notes stack
|
||||||
|
hosts: notes_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: trilium
|
||||||
|
tags: trilium
|
||||||
|
|
||||||
|
- name: Servarr stack
|
||||||
|
hosts: servarr_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: servarr
|
||||||
|
tags: servarr
|
||||||
|
|
||||||
- name: DNS and reverse proxy
|
- name: DNS and reverse proxy
|
||||||
hosts: services
|
hosts: services
|
||||||
|
|
@ -51,27 +96,6 @@
|
||||||
- role: caddy
|
- role: caddy
|
||||||
tags: caddy
|
tags: caddy
|
||||||
|
|
||||||
- name: Servarr stack
|
|
||||||
hosts: servarr_hosts
|
|
||||||
become: true
|
|
||||||
roles:
|
|
||||||
- role: servarr
|
|
||||||
tags: servarr
|
|
||||||
|
|
||||||
- name: Matrix stack
|
|
||||||
hosts: matrix_hosts
|
|
||||||
become: true
|
|
||||||
roles:
|
|
||||||
- role: matrix_synapse
|
|
||||||
tags: matrix
|
|
||||||
|
|
||||||
- name: Notes stack
|
|
||||||
hosts: notes_hosts
|
|
||||||
become: true
|
|
||||||
roles:
|
|
||||||
- role: trilium
|
|
||||||
tags: trilium
|
|
||||||
|
|
||||||
- name: ML workloads
|
- name: ML workloads
|
||||||
hosts: ml_hosts
|
hosts: ml_hosts
|
||||||
become: true
|
become: true
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
# adguard/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: selinux_containers
|
|
||||||
|
|
@ -2,58 +2,25 @@
|
||||||
#adguard/tasks/main.yml
|
#adguard/tasks/main.yml
|
||||||
- import_tasks: firewall.yml
|
- import_tasks: firewall.yml
|
||||||
|
|
||||||
- name: Create stack and config directories
|
- name: Create adguard container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
loop: "{{ adguard_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ adguard_base_directories }}"
|
dir_list: "{{ adguard_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for adguard config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
- name: Ensure container app config directories are owned by container UID
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ adguard_dir }}/conf"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
recurse: true
|
|
||||||
|
|
||||||
- name: Deploy AdGuard configuration template
|
|
||||||
template:
|
|
||||||
src: AdGuardHome.yaml.j2
|
|
||||||
dest: "{{ stack_root }}/adguard/conf/AdGuardHome.yaml"
|
|
||||||
mode: '0600'
|
|
||||||
force: "{{ adguard_overwrite_config | default(false) | bool }}"
|
|
||||||
|
|
||||||
- name: Deploy AdGuard Quadlet
|
- name: Deploy AdGuard Quadlet
|
||||||
template:
|
template:
|
||||||
src: adguard.container.j2
|
src: adguard.container.j2
|
||||||
dest: "{{ container_config_dir }}/adguard.container"
|
dest: "{{ container_config_dir }}/adguard.container"
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "adguard"
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start and enable AdGuard service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
systemd:
|
|
||||||
name: adguard.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
http:
|
|
||||||
pprof:
|
|
||||||
port: 6060
|
|
||||||
enabled: false
|
|
||||||
doh:
|
|
||||||
routes:
|
|
||||||
- GET /dns-query
|
|
||||||
- POST /dns-query
|
|
||||||
- GET /dns-query/{ClientID}
|
|
||||||
- POST /dns-query/{ClientID}
|
|
||||||
insecure_enabled: false
|
|
||||||
address: 0.0.0.0:80
|
|
||||||
session_ttl: 720h
|
|
||||||
users:
|
|
||||||
- name: "{{ adguard_admin_user }}"
|
|
||||||
password: "{{ adguard_admin_password_hash }}"
|
|
||||||
auth_attempts: 5
|
|
||||||
block_auth_min: 15
|
|
||||||
http_proxy: ""
|
|
||||||
language: ""
|
|
||||||
theme: auto
|
|
||||||
dns:
|
|
||||||
bind_hosts:
|
|
||||||
- 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
anonymize_client_ip: false
|
|
||||||
ratelimit: 20
|
|
||||||
ratelimit_subnet_len_ipv4: 24
|
|
||||||
ratelimit_subnet_len_ipv6: 56
|
|
||||||
ratelimit_whitelist: []
|
|
||||||
refuse_any: true
|
|
||||||
upstream_dns:
|
|
||||||
- https://dns10.quad9.net/dns-query
|
|
||||||
upstream_dns_file: ""
|
|
||||||
bootstrap_dns:
|
|
||||||
- 9.9.9.10
|
|
||||||
- 149.112.112.10
|
|
||||||
- 2620:fe::10
|
|
||||||
- 2620:fe::fe:10
|
|
||||||
fallback_dns: []
|
|
||||||
upstream_mode: load_balance
|
|
||||||
fastest_timeout: 1s
|
|
||||||
allowed_clients: []
|
|
||||||
disallowed_clients: []
|
|
||||||
blocked_hosts:
|
|
||||||
- version.bind
|
|
||||||
- id.server
|
|
||||||
- hostname.bind
|
|
||||||
trusted_proxies:
|
|
||||||
- 127.0.0.0/8
|
|
||||||
- ::1/128
|
|
||||||
cache_enabled: true
|
|
||||||
cache_size: 4194304
|
|
||||||
cache_ttl_min: 0
|
|
||||||
cache_ttl_max: 0
|
|
||||||
cache_optimistic: false
|
|
||||||
cache_optimistic_answer_ttl: 30s
|
|
||||||
cache_optimistic_max_age: 12h
|
|
||||||
bogus_nxdomain: []
|
|
||||||
aaaa_disabled: false
|
|
||||||
enable_dnssec: false
|
|
||||||
edns_client_subnet:
|
|
||||||
custom_ip: ""
|
|
||||||
enabled: false
|
|
||||||
use_custom: false
|
|
||||||
max_goroutines: 300
|
|
||||||
handle_ddr: true
|
|
||||||
ipset: []
|
|
||||||
ipset_file: ""
|
|
||||||
bootstrap_prefer_ipv6: false
|
|
||||||
upstream_timeout: 10s
|
|
||||||
private_networks: []
|
|
||||||
use_private_ptr_resolvers: true
|
|
||||||
local_ptr_upstreams: []
|
|
||||||
use_dns64: false
|
|
||||||
dns64_prefixes: []
|
|
||||||
serve_http3: false
|
|
||||||
use_http3_upstreams: false
|
|
||||||
serve_plain_dns: true
|
|
||||||
hostsfile_enabled: true
|
|
||||||
pending_requests:
|
|
||||||
enabled: true
|
|
||||||
tls:
|
|
||||||
enabled: false
|
|
||||||
server_name: ""
|
|
||||||
force_https: false
|
|
||||||
port_https: 443
|
|
||||||
port_dns_over_tls: 853
|
|
||||||
port_dns_over_quic: 853
|
|
||||||
port_dnscrypt: 0
|
|
||||||
dnscrypt_config_file: ""
|
|
||||||
certificate_chain: ""
|
|
||||||
private_key: ""
|
|
||||||
certificate_path: ""
|
|
||||||
private_key_path: ""
|
|
||||||
strict_sni_check: false
|
|
||||||
querylog:
|
|
||||||
dir_path: ""
|
|
||||||
ignored: []
|
|
||||||
interval: 2160h
|
|
||||||
size_memory: 1000
|
|
||||||
enabled: true
|
|
||||||
ignored_enabled: false
|
|
||||||
file_enabled: true
|
|
||||||
statistics:
|
|
||||||
dir_path: ""
|
|
||||||
ignored: []
|
|
||||||
interval: 24h
|
|
||||||
enabled: true
|
|
||||||
ignored_enabled: false
|
|
||||||
filters:
|
|
||||||
- enabled: true
|
|
||||||
url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
|
|
||||||
name: AdGuard DNS filter
|
|
||||||
id: 1
|
|
||||||
- enabled: false
|
|
||||||
url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt
|
|
||||||
name: AdAway Default Blocklist
|
|
||||||
id: 2
|
|
||||||
whitelist_filters: []
|
|
||||||
user_rules: []
|
|
||||||
dhcp:
|
|
||||||
enabled: false
|
|
||||||
interface_name: ""
|
|
||||||
local_domain_name: lan
|
|
||||||
dhcpv4:
|
|
||||||
gateway_ip: ""
|
|
||||||
subnet_mask: ""
|
|
||||||
range_start: ""
|
|
||||||
range_end: ""
|
|
||||||
lease_duration: 86400
|
|
||||||
icmp_timeout_msec: 1000
|
|
||||||
options: []
|
|
||||||
dhcpv6:
|
|
||||||
range_start: ""
|
|
||||||
lease_duration: 86400
|
|
||||||
ra_slaac_only: false
|
|
||||||
ra_allow_slaac: false
|
|
||||||
filtering:
|
|
||||||
blocking_ipv4: ""
|
|
||||||
blocking_ipv6: ""
|
|
||||||
blocked_services:
|
|
||||||
schedule:
|
|
||||||
time_zone: UTC
|
|
||||||
ids: []
|
|
||||||
protection_disabled_until: null
|
|
||||||
safe_search:
|
|
||||||
enabled: false
|
|
||||||
bing: true
|
|
||||||
duckduckgo: true
|
|
||||||
ecosia: true
|
|
||||||
google: true
|
|
||||||
pixabay: true
|
|
||||||
yandex: true
|
|
||||||
youtube: true
|
|
||||||
blocking_mode: default
|
|
||||||
parental_block_host: family-block.dns.adguard.com
|
|
||||||
safebrowsing_block_host: standard-block.dns.adguard.com
|
|
||||||
rewrites: []
|
|
||||||
safe_fs_patterns:
|
|
||||||
- /opt/adguardhome/work/userfilters/*
|
|
||||||
safebrowsing_cache_size: 1048576
|
|
||||||
safesearch_cache_size: 1048576
|
|
||||||
parental_cache_size: 1048576
|
|
||||||
cache_time: 30
|
|
||||||
filters_update_interval: 24
|
|
||||||
blocked_response_ttl: 10
|
|
||||||
filtering_enabled: true
|
|
||||||
rewrites_enabled: true
|
|
||||||
parental_enabled: false
|
|
||||||
safebrowsing_enabled: false
|
|
||||||
protection_enabled: true
|
|
||||||
clients:
|
|
||||||
runtime_sources:
|
|
||||||
whois: true
|
|
||||||
arp: true
|
|
||||||
rdns: true
|
|
||||||
dhcp: true
|
|
||||||
hosts: true
|
|
||||||
persistent: []
|
|
||||||
log:
|
|
||||||
enabled: true
|
|
||||||
file: ""
|
|
||||||
max_backups: 0
|
|
||||||
max_size: 100
|
|
||||||
max_age: 3
|
|
||||||
compress: false
|
|
||||||
local_time: false
|
|
||||||
verbose: false
|
|
||||||
os:
|
|
||||||
group: ""
|
|
||||||
user: ""
|
|
||||||
rlimit_nofile: 0
|
|
||||||
schema_version: 34
|
|
||||||
|
|
@ -7,15 +7,15 @@ Requires=homelab-network.service
|
||||||
[Container]
|
[Container]
|
||||||
Image=docker.io/adguard/adguardhome:latest
|
Image=docker.io/adguard/adguardhome:latest
|
||||||
ContainerName=adguard
|
ContainerName=adguard
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
NetworkAlias=adguard
|
NetworkAlias=adguard
|
||||||
|
|
||||||
Volume={{ adguard_dir }}/work:/opt/adguardhome/work
|
Volume={{ adguard_dir }}/work:/opt/adguardhome/work
|
||||||
Volume={{ adguard_dir }}/conf:/opt/adguardhome/conf
|
Volume={{ adguard_dir }}/conf:/opt/adguardhome/conf
|
||||||
|
|
||||||
PublishPort=53:53/tcp
|
PublishPort={{ adguard_bind_ip }}:{{ adguard_dns_port | default(53) }}:53/tcp
|
||||||
PublishPort=53:53/udp
|
PublishPort={{ adguard_bind_ip }}:{{ adguard_dns_port | default(53) }}:53/udp
|
||||||
PublishPort=3000:3000/tcp
|
PublishPort={{ adguard_bind_ip }}:{{ adguard_web_port | default(3000) }}:3000/tcp
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
# caddy/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: selinux_containers
|
|
||||||
|
|
@ -1,22 +1,13 @@
|
||||||
---
|
---
|
||||||
#caddy/tasks/main.yml
|
#caddy/tasks/main.yml
|
||||||
- name: Create stack and config directories
|
- name: Create caddy container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
loop: "{{ caddy_base_directories }}"
|
|
||||||
|
|
||||||
- name: Base SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ caddy_base_directories }}"
|
dir_list: "{{ caddy_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for caddy config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
|
|
||||||
- name: Ensure Caddyfile is deployed
|
- name: Ensure Caddyfile is deployed
|
||||||
template:
|
template:
|
||||||
|
|
@ -31,19 +22,11 @@
|
||||||
src: caddy.container.j2
|
src: caddy.container.j2
|
||||||
dest: "{{ container_config_dir }}/caddy.container"
|
dest: "{{ container_config_dir }}/caddy.container"
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "caddy"
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
systemd:
|
|
||||||
name: caddy.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
|
|
@ -4,6 +4,12 @@
|
||||||
reverse_proxy {{ adguard_upstream }}
|
reverse_proxy {{ adguard_upstream }}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Jellyfin
|
||||||
|
{{ jellyfin_domain }} {
|
||||||
|
tls internal
|
||||||
|
reverse_proxy {{ jellyfin_upstream }}
|
||||||
|
}
|
||||||
|
|
||||||
# QBittorrent
|
# QBittorrent
|
||||||
{{ qbittorrent_domain }} {
|
{{ qbittorrent_domain }} {
|
||||||
tls internal
|
tls internal
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ After=homelab-network.service
|
||||||
[Container]
|
[Container]
|
||||||
Image=docker.io/caddy:latest
|
Image=docker.io/caddy:latest
|
||||||
ContainerName=caddy
|
ContainerName=caddy
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
|
|
||||||
Volume={{ caddy_dir }}/Caddyfile:/etc/caddy/Caddyfile
|
Volume={{ caddy_dir }}/Caddyfile:/etc/caddy/Caddyfile
|
||||||
Volume={{ caddy_dir }}/data:/data
|
Volume={{ caddy_dir }}/data:/data
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ cli_user: "{{ container_user }}"
|
||||||
cli_group: "{{ cli_user }}"
|
cli_group: "{{ cli_user }}"
|
||||||
|
|
||||||
cli_install_epel: true
|
cli_install_epel: true
|
||||||
cli_use_epel: "{{ cli_install_epel | bool and ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora' }}"
|
cli_use_epel: "{{ cli_install_epel | bool and ansible_facts['os_family'] == 'RedHat' and ansible_facts['distribution'] != 'Fedora' }}"
|
||||||
|
|
||||||
cli_set_fish_as_default_shell: true
|
cli_set_fish_as_default_shell: true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
# container_runtime/tasks/config-setup.yml
|
||||||
|
- import_tasks: directory.yml
|
||||||
|
|
||||||
|
- name: Directory SELinux requirement
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
selinux_container_paths: "{{ missing_dirs }}"
|
||||||
|
when: missing_dirs | length > 0
|
||||||
|
|
||||||
|
- name: Apply SELinux container labels
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: selinux_containers
|
||||||
|
tasks_from: labels
|
||||||
|
when: missing_dirs | length > 0
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
- name: Check directories exist
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item.path if item is mapping else item }}"
|
||||||
|
loop: "{{ dir_list }}"
|
||||||
|
register: dir_stats
|
||||||
|
|
||||||
|
- name: Build list of missing directories
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
missing_dirs: >-
|
||||||
|
{{
|
||||||
|
dir_stats.results
|
||||||
|
| rejectattr('stat.exists', 'equalto', true)
|
||||||
|
| map(attribute='item')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
|
- name: Ensure missing directories exist
|
||||||
|
become: true
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.path if item is mapping else item }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ container_user }}"
|
||||||
|
group: "{{ container_group }}"
|
||||||
|
mode: "{{ item.mode | default('0755') if item is mapping else '0755' }}"
|
||||||
|
loop: "{{ missing_dirs }}"
|
||||||
|
when: missing_dirs | length > 0
|
||||||
|
|
@ -20,6 +20,24 @@
|
||||||
value: '53'
|
value: '53'
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Check configuration directories exist
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "{{ stack_root }}"
|
||||||
|
- "{{ container_config_dir }}"
|
||||||
|
register: config_dirs_stats
|
||||||
|
|
||||||
|
- name: Build list of missing configuration directories
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
missing_config_dirs: >-
|
||||||
|
{{
|
||||||
|
config_dirs_stats.results
|
||||||
|
| rejectattr('stat.exists')
|
||||||
|
| map(attribute='item')
|
||||||
|
| list
|
||||||
|
}}
|
||||||
|
|
||||||
- name: Create stack directories
|
- name: Create stack directories
|
||||||
file:
|
file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
|
|
@ -28,9 +46,9 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
recurse: yes
|
recurse: yes
|
||||||
loop:
|
loop: "{{ missing_config_dirs }}"
|
||||||
- "{{ stack_root }}"
|
when: missing_config_dirs | length > 0
|
||||||
- "{{ container_config_dir }}"
|
|
||||||
|
|
||||||
- name: Configure SELinux container policies
|
- name: Configure SELinux container policies
|
||||||
ansible.builtin.import_tasks: ../selinux_containers/tasks/main.yml
|
ansible.builtin.import_tasks: ../selinux_containers/tasks/main.yml
|
||||||
|
|
@ -44,17 +62,11 @@
|
||||||
owner: "{{ container_user }}"
|
owner: "{{ container_user }}"
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "homelab-network"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Start homelab network
|
- name: Execute systemd tasks
|
||||||
become: true
|
ansible.builtin.import_role:
|
||||||
become_user: "{{ container_user }}"
|
name: container_runtime
|
||||||
systemd:
|
tasks_from: systemd
|
||||||
name: homelab-network.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
# container_runtime/tasks/systemd.yml
|
||||||
|
- name: Build container service list
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
container_service_names: "{{ service_names | default([service_name]) }}"
|
||||||
|
|
||||||
|
- name: Force systemd reload (blocking)
|
||||||
|
become: true
|
||||||
|
become_user: "{{ container_user }}"
|
||||||
|
environment:
|
||||||
|
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
||||||
|
command: systemctl --user daemon-reload
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Wait for quadlet generation
|
||||||
|
pause:
|
||||||
|
seconds: 1
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Start and enable container services
|
||||||
|
become: true
|
||||||
|
become_user: "{{ container_user }}"
|
||||||
|
environment:
|
||||||
|
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
||||||
|
systemd:
|
||||||
|
name: "{{ item }}.service"
|
||||||
|
scope: user
|
||||||
|
state: started
|
||||||
|
enabled: true
|
||||||
|
loop: "{{ container_service_names }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
# coturn/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: selinux_containers
|
|
||||||
|
|
@ -2,32 +2,14 @@
|
||||||
# coturn/tasks/main.yml
|
# coturn/tasks/main.yml
|
||||||
- import_tasks: firewall.yml
|
- import_tasks: firewall.yml
|
||||||
|
|
||||||
- name: Create stack and config directories
|
- name: Create coturn container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
loop: "{{ coturn_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ coturn_base_directories }}"
|
dir_list: "{{ coturn_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for coturn config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
- name: Ensure container app config directories are owned by container UID
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ coturn_dir }}/conf"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
recurse: true
|
|
||||||
|
|
||||||
- name: Deploy Turnserver configuration template
|
- name: Deploy Turnserver configuration template
|
||||||
template:
|
template:
|
||||||
|
|
@ -36,6 +18,8 @@
|
||||||
owner: "{{ container_user }}"
|
owner: "{{ container_user }}"
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
|
force: "{{ coturn_overwrite_config | default(false) | bool }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Deploy Coturn Quadlet
|
- name: Deploy Coturn Quadlet
|
||||||
template:
|
template:
|
||||||
|
|
@ -45,24 +29,11 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "coturn"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start and enable Coturn service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: coturn.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
||||||
|
|
@ -7,7 +7,7 @@ Requires=homelab-network.service
|
||||||
ContainerName=coturn
|
ContainerName=coturn
|
||||||
Image=docker.io/coturn/coturn:latest
|
Image=docker.io/coturn/coturn:latest
|
||||||
Volume={{ coturn_config_dir }}/turnserver.conf:/etc/coturn/turnserver.conf:Z
|
Volume={{ coturn_config_dir }}/turnserver.conf:/etc/coturn/turnserver.conf:Z
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
PublishPort=3478:3478/tcp
|
PublishPort=3478:3478/tcp
|
||||||
PublishPort=3478:3478/udp
|
PublishPort=3478:3478/udp
|
||||||
PublishPort=49152-49172:49152-49172/udp
|
PublishPort=49152-49172:49152-49172/udp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
# jellyfin/tasks/firewall.yml
|
||||||
|
|
||||||
|
- name: Open required Jellyfin firewall rules
|
||||||
|
become: true
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
port: "{{ item.port | default(omit) }}"
|
||||||
|
service: "{{ item.service | default(omit) }}"
|
||||||
|
permanent: true
|
||||||
|
state: enabled
|
||||||
|
immediate: true
|
||||||
|
loop: "{{ jellyfin_firewall_rules }}"
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
# jellyfin/tasks/main.yml
|
||||||
|
|
||||||
|
- import_tasks: firewall.yml
|
||||||
|
|
||||||
|
- name: Create jellyfin container directory
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
dir_list: "{{ jellyfin_base_directories }}"
|
||||||
|
|
||||||
|
- name: Setup directory for jellyfin config
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
|
|
||||||
|
- name: Deploy Jellyfin Quadlet
|
||||||
|
template:
|
||||||
|
src: jellyfin.container.j2
|
||||||
|
dest: "{{ container_config_dir }}/jellyfin.container"
|
||||||
|
owner: "{{ container_user }}"
|
||||||
|
group: "{{ container_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Set fact for systemd
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
service_name: "jellyfin"
|
||||||
|
|
||||||
|
- name: Execute systemd tasks
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Jellyfin Media Server
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=jellyfin
|
||||||
|
Image=docker.io/jellyfin/jellyfin:10.11.7
|
||||||
|
Network=host
|
||||||
|
|
||||||
|
Volume={{ jellyfin_config_dir }}:/config:Z
|
||||||
|
Volume={{ jellyfin_cache_dir }}:/cache:Z
|
||||||
|
Volume={{ jellyfin_media_dir }}:/data:ro
|
||||||
|
|
||||||
|
Environment=TZ={{ timezone }}
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
# matrix_synapse/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: tailscale
|
|
||||||
- role: postgres
|
|
||||||
- role: coturn
|
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
---
|
---
|
||||||
# matrix_synapse/tasks/main.yml
|
# matrix_synapse/tasks/main.yml
|
||||||
- name: Create stack and config directories
|
- name: Create synapse container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
loop: "{{ synapse_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ synapse_base_directories }}"
|
dir_list: "{{ synapse_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Create directory for synapse config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
|
|
||||||
- name: Deploy homesever configuration template
|
- name: Deploy homesever configuration template
|
||||||
template:
|
template:
|
||||||
|
|
@ -25,6 +17,7 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
force: "{{ matrix_overwrite_config | default(false) | bool }}"
|
force: "{{ matrix_overwrite_config | default(false) | bool }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Ensure Synapse signing key is deployed
|
- name: Ensure Synapse signing key is deployed
|
||||||
copy:
|
copy:
|
||||||
|
|
@ -34,6 +27,7 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: '0600'
|
mode: '0600'
|
||||||
force: "{{ matrix_overwrite_signing_key | default(false) | bool }}"
|
force: "{{ matrix_overwrite_signing_key | default(false) | bool }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Deploy Synapse Quadlet
|
- name: Deploy Synapse Quadlet
|
||||||
template:
|
template:
|
||||||
|
|
@ -43,24 +37,11 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "synapse"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start and enable Synapse service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: synapse.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
||||||
|
|
@ -7,7 +7,7 @@ Requires=postgres.service
|
||||||
ContainerName=synapse
|
ContainerName=synapse
|
||||||
Image=docker.io/matrixdotorg/synapse:latest
|
Image=docker.io/matrixdotorg/synapse:latest
|
||||||
Volume={{ synapse_config_dir }}:/data:Z
|
Volume={{ synapse_config_dir }}:/data:Z
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
# Expose the client-server API port
|
# Expose the client-server API port
|
||||||
PublishPort={{ tailscale_ip }}:8008:8008
|
PublishPort={{ tailscale_ip }}:8008:8008
|
||||||
UserNS=keep-id
|
UserNS=keep-id
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,5 @@
|
||||||
---
|
---
|
||||||
# nfs_client/tasks/main.yml
|
# nfs_client/tasks/main.yml
|
||||||
- name: Configure dummy NAS storage for test environment
|
|
||||||
when: env == "test"
|
|
||||||
block:
|
|
||||||
- name: Create dummy NAS root for test environment
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ nfs_mount_point }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
|
|
||||||
- name: Create dummy NAS storage tree for test environment
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ nfs_mount_point }}/{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0775"
|
|
||||||
loop: "{{ storage_tree }}"
|
|
||||||
|
|
||||||
- name: Set SELinux context for dummy NAS storage in test environment
|
|
||||||
become: true
|
|
||||||
community.general.sefcontext:
|
|
||||||
target: "{{ nfs_mount_point }}(/.*)?"
|
|
||||||
setype: container_file_t
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Apply SELinux context for dummy NAS storage in test environment
|
|
||||||
become: true
|
|
||||||
command: restorecon -Rv "{{ nfs_mount_point }}"
|
|
||||||
changed_when: false
|
|
||||||
|
|
||||||
- name: Configure NFS client for non-test environments
|
|
||||||
when: env != "test"
|
|
||||||
block:
|
|
||||||
- name: Install required NFS client packages
|
- name: Install required NFS client packages
|
||||||
become: true
|
become: true
|
||||||
dnf:
|
dnf:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
# postgres/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: selinux_containers
|
|
||||||
|
|
@ -1,30 +1,13 @@
|
||||||
---
|
---
|
||||||
# postgres/tasks/main.yml
|
# postgres/tasks/main.yml
|
||||||
- name: Create stack and config directories
|
- name: Create postgres container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
loop: "{{ postgres_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ postgres_base_directories }}"
|
dir_list: "{{ postgres_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for postgres config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
- name: Ensure container app config directories are owned by container UID
|
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ postgres_config_dir }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
recurse: true
|
|
||||||
|
|
||||||
- name: Deploy Postgres Quadlet
|
- name: Deploy Postgres Quadlet
|
||||||
template:
|
template:
|
||||||
|
|
@ -34,27 +17,14 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: "0600"
|
mode: "0600"
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "postgres"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start and enable Postgres service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: postgres.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
- name: Wait for Postgres to be ready (Handling the double-start)
|
- name: Wait for Postgres to be ready (Handling the double-start)
|
||||||
become: true
|
become: true
|
||||||
|
|
@ -68,3 +38,4 @@
|
||||||
retries: 20
|
retries: 20
|
||||||
delay: 5
|
delay: 5
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
@ -11,7 +11,7 @@ Environment=POSTGRES_PASSWORD={{ vault_matrix_postgres_password }}
|
||||||
Environment=POSTGRES_DB=synapse
|
Environment=POSTGRES_DB=synapse
|
||||||
Environment=POSTGRES_INITDB_ARGS="--lc-collate=C --lc-ctype=C --encoding=UTF8"
|
Environment=POSTGRES_INITDB_ARGS="--lc-collate=C --lc-ctype=C --encoding=UTF8"
|
||||||
Volume={{ postgres_data_dir }}:/var/lib/postgresql/data
|
Volume={{ postgres_data_dir }}:/var/lib/postgresql/data
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
IP=10.89.0.54
|
IP=10.89.0.54
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
- name: Ensure SELinux context for container config paths
|
- name: Ensure SELinux context for container config paths
|
||||||
become: true
|
become: true
|
||||||
community.general.sefcontext:
|
community.general.sefcontext:
|
||||||
target: "{{ item }}(/.*)?"
|
target: "{{ (item.path if item is mapping else item) }}(/.*)?"
|
||||||
setype: container_file_t
|
setype: container_file_t
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ selinux_container_paths | default([]) }}"
|
loop: "{{ selinux_container_paths | default([]) }}"
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
# selinux_containers/tasks/storage.yml
|
|
||||||
- name: Set SELinux context for storage mounts
|
|
||||||
become: true
|
|
||||||
community.general.sefcontext:
|
|
||||||
target: "{{ item }}(/.*)?"
|
|
||||||
setype: container_file_t
|
|
||||||
state: present
|
|
||||||
loop: "{{ storage_backends }}"
|
|
||||||
when: "'nas' in group_names"
|
|
||||||
|
|
||||||
- name: Apply SELinux context (storage)
|
|
||||||
become: true
|
|
||||||
command: restorecon -R -F -v "{{ item }}"
|
|
||||||
loop: "{{ storage_backends }}"
|
|
||||||
changed_when: false
|
|
||||||
when: "'nas' in group_names"
|
|
||||||
|
|
@ -7,3 +7,5 @@ servarr_firewall_rules:
|
||||||
- port: 8191/tcp
|
- port: 8191/tcp
|
||||||
- port: 8989/tcp
|
- port: 8989/tcp
|
||||||
- port: 9696/tcp
|
- port: 9696/tcp
|
||||||
|
|
||||||
|
vpn_guard_enabled: false
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
# servarr/meta/main.yml
|
|
||||||
dependencies:
|
|
||||||
- role: selinux_containers
|
|
||||||
- role: vpn
|
|
||||||
|
|
@ -1,31 +1,14 @@
|
||||||
#servarr/tasks/main.yml
|
#servarr/tasks/main.yml
|
||||||
- import_tasks: firewall.yml
|
- import_tasks: firewall.yml
|
||||||
|
|
||||||
- name: Create stack and config directories
|
- name: Create servarr container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
loop: "{{ servarr_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ servarr_base_directories }}"
|
dir_list: "{{ servarr_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for servarr config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
- name: Ensure systemd directory exists for rootless user
|
|
||||||
file:
|
|
||||||
path: "{{ container_config_dir }}"
|
|
||||||
state: directory
|
|
||||||
mode: '0755'
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
|
|
||||||
- name: Deploy Quadlet files
|
- name: Deploy Quadlet files
|
||||||
template:
|
template:
|
||||||
|
|
@ -34,33 +17,25 @@
|
||||||
loop: "{{ servarr_stack }}"
|
loop: "{{ servarr_stack }}"
|
||||||
register: quadlets_deployed
|
register: quadlets_deployed
|
||||||
|
|
||||||
- name: Ensure app config directories are writable
|
- name: Validate VPN
|
||||||
become: true
|
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
recurse: true
|
|
||||||
loop:
|
|
||||||
- "{{ radarr_dir }}/config"
|
|
||||||
- "{{ sonarr_dir }}/config"
|
|
||||||
- "{{ qbittorrent_dir }}/config"
|
|
||||||
- "{{ prowlarr_dir }}/config"
|
|
||||||
- "{{ bazarr_dir }}/config"
|
|
||||||
|
|
||||||
#- name: Force systemd reload (blocking)
|
|
||||||
# become: true
|
|
||||||
# become_user: "{{ container_user }}"
|
|
||||||
# command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Validate VPN and start arr stack
|
|
||||||
ansible.builtin.import_role:
|
ansible.builtin.import_role:
|
||||||
name: vpn_guard
|
name: vpn_guard
|
||||||
|
when:
|
||||||
|
- vpn_guard_enabled | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Skip VPN guard validation in check mode
|
||||||
|
debug:
|
||||||
|
msg: "Skipping VPN guard validation during Ansible check mode."
|
||||||
|
when:
|
||||||
|
- vpn_guard_enabled | bool
|
||||||
|
- ansible_check_mode
|
||||||
|
|
||||||
|
- name: Set fact for systemd
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
service_names: "{{ arr_suite }}"
|
||||||
|
|
||||||
|
- name: Execute systemd tasks
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
|
|
@ -1,43 +1,53 @@
|
||||||
---
|
|
||||||
#storage_client/tasks/main.yml
|
#storage_client/tasks/main.yml
|
||||||
- name: Install required packages
|
- name: Install required packages
|
||||||
become: true
|
become: true
|
||||||
dnf:
|
ansible.builtin.dnf:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
state: present
|
state: present
|
||||||
loop: "{{ base_storage_install_packages }}"
|
loop: "{{ base_storage_install_packages }}"
|
||||||
|
when: env == "prod"
|
||||||
|
|
||||||
- name: Install mergerfs repo package
|
- name: Install mergerfs repo package
|
||||||
become: true
|
become: true
|
||||||
dnf:
|
ansible.builtin.dnf:
|
||||||
name: https://github.com/trapexit/mergerfs/releases/download/2.41.1/mergerfs-2.41.1-1.el10.x86_64.rpm
|
name: https://github.com/trapexit/mergerfs/releases/download/2.41.1/mergerfs-2.41.1-1.el10.x86_64.rpm
|
||||||
disable_gpg_check: true
|
disable_gpg_check: true
|
||||||
state: present
|
state: present
|
||||||
|
when: env == "prod"
|
||||||
|
|
||||||
- name: Ensure source data directories exist
|
- name: Set storage mount directory list
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
file:
|
dir_list: "{{ storage_drives + mergerfs }}"
|
||||||
path: "{{ item }}"
|
when: env == "prod"
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
loop: "{{ storage_backends }}"
|
|
||||||
|
|
||||||
- name: Create mergerfs mountpoint
|
- name: Ensure storage mount directories exist
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: container_runtime
|
||||||
|
tasks_from: directory
|
||||||
|
when: env == "prod"
|
||||||
|
|
||||||
|
- name: Mount filesystem by UUID
|
||||||
become: true
|
become: true
|
||||||
file:
|
ansible.posix.mount:
|
||||||
path: /mnt/nas01
|
path: "{{ item.path }}"
|
||||||
state: directory
|
src: "UUID={{ item.uuid }}"
|
||||||
owner: "{{ container_user }}"
|
fstype: "{{ item.fstype }}"
|
||||||
group: "{{ container_group }}"
|
opts: "{{ item.opts }}"
|
||||||
mode: "0755"
|
dump: "{{ item.dump }}"
|
||||||
|
passno: "{{ item.passno }}"
|
||||||
|
state: mounted
|
||||||
|
loop: "{{ storage_drives }}"
|
||||||
|
when: env == "prod"
|
||||||
|
|
||||||
- name: Mount mergerfs pool
|
- name: Mount mergerfs pool
|
||||||
become: true
|
become: true
|
||||||
ansible.posix.mount:
|
ansible.posix.mount:
|
||||||
path: "{{ mergerfs_path }}"
|
path: "{{ item.path }}"
|
||||||
src: "{{ drive_a_path }}/data:{{ drive_b_path }}/data"
|
src: "{{ item.src_path }}"
|
||||||
fstype: fuse.mergerfs
|
fstype: fuse.mergerfs
|
||||||
opts: defaults,allow_other,use_ino,category.create=mfs
|
opts: "{{ item.opts }}"
|
||||||
|
dump: "{{ item.dump | default('0') }}"
|
||||||
|
passno: "{{ item.passno | default('0') }}"
|
||||||
state: mounted
|
state: mounted
|
||||||
|
loop: "{{ mergerfs }}"
|
||||||
|
when: env == "prod"
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
name: tailscaled
|
name: tailscaled
|
||||||
state: started
|
state: started
|
||||||
enabled: true
|
enabled: true
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Bring Tailscale up (without hijacking DNS)
|
- name: Bring Tailscale up (without hijacking DNS)
|
||||||
become: true
|
become: true
|
||||||
|
|
@ -34,3 +35,5 @@
|
||||||
failed_when:
|
failed_when:
|
||||||
- ts_up.rc != 0
|
- ts_up.rc != 0
|
||||||
- "'already authenticated' not in ts_up.stderr"
|
- "'already authenticated' not in ts_up.stderr"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
no_log: true
|
||||||
|
|
@ -2,23 +2,14 @@
|
||||||
# trilium/tasks/main.yml
|
# trilium/tasks/main.yml
|
||||||
- import_tasks: firewall.yml
|
- import_tasks: firewall.yml
|
||||||
|
|
||||||
- name: Create stack and config directories
|
- name: Create trilium container directory
|
||||||
file:
|
|
||||||
path: "{{ item }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ container_user }}"
|
|
||||||
group: "{{ container_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
recurse: yes
|
|
||||||
loop: "{{ trilium_base_directories }}"
|
|
||||||
|
|
||||||
- name: Directory SELinux requirement
|
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
selinux_container_paths: "{{ trilium_base_directories }}"
|
dir_list: "{{ trilium_base_directories }}"
|
||||||
|
|
||||||
- import_role:
|
- name: Setup directory for trilium config
|
||||||
name: selinux_containers
|
ansible.builtin.import_role:
|
||||||
tasks_from: labels
|
name: container_runtime
|
||||||
|
tasks_from: config-setup
|
||||||
|
|
||||||
- name: Deploy Trilium Quadlet
|
- name: Deploy Trilium Quadlet
|
||||||
template:
|
template:
|
||||||
|
|
@ -28,24 +19,11 @@
|
||||||
group: "{{ container_group }}"
|
group: "{{ container_group }}"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Force systemd reload
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "trilium"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start and enable Trilium service
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: trilium.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
enabled: true
|
|
||||||
|
|
@ -7,9 +7,8 @@ Requires=homelab-network.service
|
||||||
ContainerName=trilium
|
ContainerName=trilium
|
||||||
Image=docker.io/triliumnext/notes:latest
|
Image=docker.io/triliumnext/notes:latest
|
||||||
Volume={{ trilium_data_dir }}:/home/node/trilium-data:Z
|
Volume={{ trilium_data_dir }}:/home/node/trilium-data:Z
|
||||||
Network=homelab.network
|
Network=homelab
|
||||||
NetworkAlias=trilium
|
NetworkAlias=trilium
|
||||||
IP={{ trilium_ip }}
|
|
||||||
Environment=TZ={{ timezone }}
|
Environment=TZ={{ timezone }}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@
|
||||||
|
|
||||||
- name: Check whether xt_conntrack is available for running kernel
|
- name: Check whether xt_conntrack is available for running kernel
|
||||||
become: true
|
become: true
|
||||||
command: modinfo xt_conntrack
|
ansible.builtin.command: modinfo xt_conntrack
|
||||||
register: xt_conntrack_modinfo
|
register: xt_conntrack_modinfo
|
||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
|
check_mode: false
|
||||||
|
|
||||||
- name: Reboot if kernel modules were installed but running kernel cannot find xt_conntrack
|
- name: Reboot if kernel modules were installed but running kernel cannot find xt_conntrack
|
||||||
become: true
|
become: true
|
||||||
|
|
@ -20,6 +21,8 @@
|
||||||
msg: "Rebooting to load kernel matching installed kernel-modules-extra for Gluetun firewall"
|
msg: "Rebooting to load kernel matching installed kernel-modules-extra for Gluetun firewall"
|
||||||
reboot_timeout: 600
|
reboot_timeout: 600
|
||||||
when:
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
- kernel_modules_extra_install.changed
|
||||||
- xt_conntrack_modinfo.rc != 0
|
- xt_conntrack_modinfo.rc != 0
|
||||||
|
|
||||||
- name: Load xt_conntrack for Gluetun firewall
|
- name: Load xt_conntrack for Gluetun firewall
|
||||||
|
|
@ -42,30 +45,21 @@
|
||||||
tasks_from: vpn
|
tasks_from: vpn
|
||||||
|
|
||||||
- name: Deploy Quadlet files
|
- name: Deploy Quadlet files
|
||||||
template:
|
ansible.builtin.template:
|
||||||
src: "gluetun.container.j2"
|
src: "gluetun.container.j2"
|
||||||
dest: "{{ container_config_dir }}/gluetun.container"
|
dest: "{{ container_config_dir }}/gluetun.container"
|
||||||
|
owner: "{{ container_user }}"
|
||||||
|
group: "{{ container_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
- name: Force systemd reload (blocking)
|
- name: Set fact for systemd
|
||||||
become: true
|
ansible.builtin.set_fact:
|
||||||
become_user: "{{ container_user }}"
|
service_name: "gluetun"
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
command: systemctl --user daemon-reload
|
|
||||||
|
|
||||||
- name: Wait for quadlet generation
|
- name: Execute systemd tasks
|
||||||
pause:
|
ansible.builtin.import_role:
|
||||||
seconds: 1
|
name: container_runtime
|
||||||
|
tasks_from: systemd
|
||||||
- name: Start vpn
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: gluetun.service
|
|
||||||
scope: user
|
|
||||||
state: started
|
|
||||||
|
|
||||||
- name: Wait for Gluetun container to exist
|
- name: Wait for Gluetun container to exist
|
||||||
become: true
|
become: true
|
||||||
|
|
@ -76,7 +70,9 @@
|
||||||
delay: 2
|
delay: 2
|
||||||
until: gluetun_exists.rc == 0
|
until: gluetun_exists.rc == 0
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Wait for Gluetun to stabilize
|
- name: Skip Gluetun runtime validation in check mode
|
||||||
pause:
|
debug:
|
||||||
seconds: 5
|
msg: "Skipping Gluetun container existence check during Ansible check mode."
|
||||||
|
when: ansible_check_mode
|
||||||
|
|
@ -7,7 +7,6 @@ Wants=network-online.target
|
||||||
[Container]
|
[Container]
|
||||||
Image=docker.io/qmcgaw/gluetun:latest
|
Image=docker.io/qmcgaw/gluetun:latest
|
||||||
ContainerName=gluetun
|
ContainerName=gluetun
|
||||||
UserNS=keep-id
|
|
||||||
|
|
||||||
AddCapability=NET_ADMIN
|
AddCapability=NET_ADMIN
|
||||||
AddCapability=NET_RAW
|
AddCapability=NET_RAW
|
||||||
|
|
@ -38,7 +37,8 @@ Environment=FIREWALL_ENABLED=on
|
||||||
Environment=DNS_ENABLED=false
|
Environment=DNS_ENABLED=false
|
||||||
Environment=FIREWALL_INPUT_PORTS=6767,7878,8080,8191,8989,9696
|
Environment=FIREWALL_INPUT_PORTS=6767,7878,8080,8191,8989,9696
|
||||||
|
|
||||||
Network=homelab:alias=gluetun
|
Network=homelab
|
||||||
|
NetworkAlias=gluetun
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
command: curl -s https://ipinfo.io/ip
|
command: curl -s https://ipinfo.io/ip
|
||||||
register: host_ip
|
register: host_ip
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Get VPN public IP (via Gluetun)
|
- name: Get VPN public IP (via Gluetun)
|
||||||
become: true
|
become: true
|
||||||
|
|
@ -11,30 +12,11 @@
|
||||||
command: podman exec gluetun wget -qO- https://ipinfo.io/ip
|
command: podman exec gluetun wget -qO- https://ipinfo.io/ip
|
||||||
register: vpn_ip
|
register: vpn_ip
|
||||||
changed_when: false
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Fail if VPN is not active (kill switch check)
|
- name: Fail if VPN is not active (kill switch check)
|
||||||
fail:
|
fail:
|
||||||
msg: "VPN is NOT active (host={{ host_ip.stdout }} vpn={{ vpn_ip.stdout }}). Aborting arr stack start."
|
msg: "VPN is NOT active (host={{ host_ip.stdout }} vpn={{ vpn_ip.stdout }}). Aborting arr stack start."
|
||||||
when: host_ip.stdout == vpn_ip.stdout
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
#- name: Start arr stack only after VPN validation
|
- host_ip.stdout == vpn_ip.stdout
|
||||||
# become: true
|
|
||||||
# become_user: "{{ container_user }}"
|
|
||||||
# systemd:
|
|
||||||
# name: "{{ item }}.service"
|
|
||||||
# enabled: yes
|
|
||||||
# state: started
|
|
||||||
# scope: user
|
|
||||||
# loop: "{{ arr_suite }}"
|
|
||||||
|
|
||||||
- name: Start arr stack only after VPN validation
|
|
||||||
become: true
|
|
||||||
become_user: "{{ container_user }}"
|
|
||||||
environment:
|
|
||||||
XDG_RUNTIME_DIR: "{{ container_runtime_dir }}"
|
|
||||||
systemd:
|
|
||||||
name: "{{ item }}.service"
|
|
||||||
enabled: yes
|
|
||||||
state: started
|
|
||||||
scope: user
|
|
||||||
loop: "{{ arr_suite }}"
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue