- New jellyfin Ansible role: Docker stack (Jellyfin, qBittorrent, Radarr, Sonarr, Prowlarr, FlareSolverr, Jellyseerr) with NVIDIA GPU passthrough - configure_services.yml automates download clients, root folders, Prowlarr indexers/apps, qBittorrent credential pre-seeding, and Jellyseerr setup - Fix TPB Cardigann season search: apibay.org returns 0 for season-level queries (e.g. the.boys.s05); patch strips season/ep from tv-search params and locks the definition file read-only to survive Prowlarr refreshes - Fix Pi-hole update_hosts.py regex: use re.DOTALL + count=1 to handle multi-line arrays without overwriting the DHCP section - Add phone WireGuard peer, devbox Traefik/DNS entries, TLS cert plumbing, firewall rule for Traefik IP to Proxmox web UI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,8 @@ jellyfin_pgid: "1000"
|
||||
|
||||
nvidia_driver_version: "580.159.03"
|
||||
|
||||
qbittorrent_webui_password: "{{ vault_qbittorrent_password }}"
|
||||
|
||||
jellyfin_config_dirs:
|
||||
- /containers/jellyfin/config
|
||||
- /containers/qbittorrent/config
|
||||
|
||||
@@ -0,0 +1,566 @@
|
||||
---
|
||||
# --- Media library directories ---
|
||||
|
||||
- name: Create media library directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ jellyfin_puid }}"
|
||||
group: "{{ jellyfin_pgid }}"
|
||||
mode: "0755"
|
||||
loop:
|
||||
- /media/movies
|
||||
- /media/tv
|
||||
|
||||
# --- qBittorrent: pre-seed credentials on fresh install ---
|
||||
|
||||
- name: Check if qBittorrent config exists
|
||||
ansible.builtin.stat:
|
||||
path: /containers/qbittorrent/config/qBittorrent/qBittorrent.conf
|
||||
register: _qbt_conf
|
||||
|
||||
- name: Deploy qBittorrent credential seed script
|
||||
ansible.builtin.template:
|
||||
src: qbittorrent_seed.py.j2
|
||||
dest: /tmp/qbt_seed.py
|
||||
mode: "0700"
|
||||
when: not _qbt_conf.stat.exists
|
||||
|
||||
- name: Pre-seed qBittorrent credentials
|
||||
ansible.builtin.command:
|
||||
cmd: python3 /tmp/qbt_seed.py
|
||||
when: not _qbt_conf.stat.exists
|
||||
|
||||
# --- Wait for services to be ready ---
|
||||
|
||||
- name: Read Radarr config
|
||||
ansible.builtin.slurp:
|
||||
src: /containers/radarr/config/config.xml
|
||||
register: _radarr_config
|
||||
|
||||
- name: Read Sonarr config
|
||||
ansible.builtin.slurp:
|
||||
src: /containers/sonarr/config/config.xml
|
||||
register: _sonarr_config
|
||||
|
||||
- name: Read Prowlarr config
|
||||
ansible.builtin.slurp:
|
||||
src: /containers/prowlarr/config/config.xml
|
||||
register: _prowlarr_config
|
||||
|
||||
- name: Set API key facts
|
||||
ansible.builtin.set_fact:
|
||||
_radarr_key: "{{ (_radarr_config.content | b64decode | regex_search('<ApiKey>(.*?)</ApiKey>', '\\1'))[0] }}"
|
||||
_sonarr_key: "{{ (_sonarr_config.content | b64decode | regex_search('<ApiKey>(.*?)</ApiKey>', '\\1'))[0] }}"
|
||||
_prowlarr_key: "{{ (_prowlarr_config.content | b64decode | regex_search('<ApiKey>(.*?)</ApiKey>', '\\1'))[0] }}"
|
||||
|
||||
- name: Wait for Radarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:7878/api/v3/system/status"
|
||||
headers:
|
||||
X-Api-Key: "{{ _radarr_key }}"
|
||||
register: _r
|
||||
until: _r.status == 200
|
||||
retries: 12
|
||||
delay: 5
|
||||
|
||||
- name: Wait for Sonarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8989/api/v3/system/status"
|
||||
headers:
|
||||
X-Api-Key: "{{ _sonarr_key }}"
|
||||
register: _r
|
||||
until: _r.status == 200
|
||||
retries: 12
|
||||
delay: 5
|
||||
|
||||
- name: Wait for Prowlarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/system/status"
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
register: _r
|
||||
until: _r.status == 200
|
||||
retries: 12
|
||||
delay: 5
|
||||
|
||||
# --- Prowlarr: fix TPB season search ---
|
||||
# apibay.org fulltext indexes "s05e01" as a single token, not "s05". When Prowlarr
|
||||
# appends the season number to the query (the.boys.s05), apibay returns 0 results.
|
||||
# Removing season/ep from the tv-search capability makes Prowlarr search with just
|
||||
# the show title; Sonarr's parser then filters the results by season/episode.
|
||||
# The file is locked read-only so Prowlarr cannot revert it on definition refresh.
|
||||
|
||||
- name: Wait for Prowlarr to write TPB definition file
|
||||
ansible.builtin.stat:
|
||||
path: /containers/prowlarr/config/Definitions/thepiratebay.yml
|
||||
register: _tpb_def
|
||||
until: _tpb_def.stat.exists
|
||||
retries: 12
|
||||
delay: 5
|
||||
|
||||
- name: Fix TPB tv-search to not append season to query
|
||||
ansible.builtin.lineinfile:
|
||||
path: /containers/prowlarr/config/Definitions/thepiratebay.yml
|
||||
regexp: '^\s+tv-search: \[q, season, ep\]'
|
||||
line: ' tv-search: [q]'
|
||||
register: _tpb_fix
|
||||
|
||||
- name: Lock TPB definition read-only to prevent Prowlarr reverting the fix
|
||||
ansible.builtin.file:
|
||||
path: /containers/prowlarr/config/Definitions/thepiratebay.yml
|
||||
mode: "0444"
|
||||
|
||||
- name: Restart Prowlarr to apply TPB definition fix
|
||||
ansible.builtin.command:
|
||||
cmd: docker restart prowlarr
|
||||
changed_when: true
|
||||
when: _tpb_fix.changed
|
||||
|
||||
- name: Wait for Prowlarr after restart
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/system/status"
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
register: _r
|
||||
until: _r.status == 200
|
||||
retries: 12
|
||||
delay: 5
|
||||
when: _tpb_fix.changed
|
||||
|
||||
# --- Radarr: download client ---
|
||||
|
||||
- name: Get Radarr download clients
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:7878/api/v3/downloadclient"
|
||||
headers:
|
||||
X-Api-Key: "{{ _radarr_key }}"
|
||||
register: _radarr_dc
|
||||
|
||||
- name: Add qBittorrent to Radarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:7878/api/v3/downloadclient"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _radarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: qBittorrent
|
||||
enable: true
|
||||
protocol: torrent
|
||||
priority: 1
|
||||
implementation: QBittorrent
|
||||
configContract: QBittorrentSettings
|
||||
fields:
|
||||
- name: host
|
||||
value: qbittorrent
|
||||
- name: port
|
||||
value: 5080
|
||||
- name: useSsl
|
||||
value: false
|
||||
- name: username
|
||||
value: admin
|
||||
- name: password
|
||||
value: "{{ qbittorrent_webui_password }}"
|
||||
- name: movieCategory
|
||||
value: radarr
|
||||
tags: []
|
||||
status_code: 201
|
||||
when: _radarr_dc.json | selectattr('name', 'equalto', 'qBittorrent') | list | length == 0
|
||||
|
||||
# --- Radarr: root folder ---
|
||||
|
||||
- name: Get Radarr root folders
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:7878/api/v3/rootfolder"
|
||||
headers:
|
||||
X-Api-Key: "{{ _radarr_key }}"
|
||||
register: _radarr_rf
|
||||
|
||||
- name: Add movies root folder to Radarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:7878/api/v3/rootfolder"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _radarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
path: /downloads/movies
|
||||
status_code: 201
|
||||
when: _radarr_rf.json | selectattr('path', 'equalto', '/downloads/movies') | list | length == 0
|
||||
|
||||
# --- Sonarr: download client ---
|
||||
|
||||
- name: Get Sonarr download clients
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8989/api/v3/downloadclient"
|
||||
headers:
|
||||
X-Api-Key: "{{ _sonarr_key }}"
|
||||
register: _sonarr_dc
|
||||
|
||||
- name: Add qBittorrent to Sonarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8989/api/v3/downloadclient"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _sonarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: qBittorrent
|
||||
enable: true
|
||||
protocol: torrent
|
||||
priority: 1
|
||||
implementation: QBittorrent
|
||||
configContract: QBittorrentSettings
|
||||
fields:
|
||||
- name: host
|
||||
value: qbittorrent
|
||||
- name: port
|
||||
value: 5080
|
||||
- name: useSsl
|
||||
value: false
|
||||
- name: username
|
||||
value: admin
|
||||
- name: password
|
||||
value: "{{ qbittorrent_webui_password }}"
|
||||
- name: tvCategory
|
||||
value: sonarr
|
||||
tags: []
|
||||
status_code: 201
|
||||
when: _sonarr_dc.json | selectattr('name', 'equalto', 'qBittorrent') | list | length == 0
|
||||
|
||||
# --- Sonarr: root folder ---
|
||||
|
||||
- name: Get Sonarr root folders
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8989/api/v3/rootfolder"
|
||||
headers:
|
||||
X-Api-Key: "{{ _sonarr_key }}"
|
||||
register: _sonarr_rf
|
||||
|
||||
- name: Add TV root folder to Sonarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8989/api/v3/rootfolder"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _sonarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
path: /downloads/tv
|
||||
status_code: 201
|
||||
when: _sonarr_rf.json | selectattr('path', 'equalto', '/downloads/tv') | list | length == 0
|
||||
|
||||
# --- Prowlarr: apps ---
|
||||
|
||||
- name: Get Prowlarr applications
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/applications"
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
register: _prowlarr_apps
|
||||
|
||||
- name: Add Radarr to Prowlarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/applications"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: Radarr
|
||||
syncLevel: fullSync
|
||||
implementation: Radarr
|
||||
configContract: RadarrSettings
|
||||
fields:
|
||||
- name: prowlarrUrl
|
||||
value: "http://prowlarr:9696"
|
||||
- name: baseUrl
|
||||
value: "http://radarr:7878"
|
||||
- name: apiKey
|
||||
value: "{{ _radarr_key }}"
|
||||
- name: syncCategories
|
||||
value: [2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060]
|
||||
tags: []
|
||||
status_code: 201
|
||||
when: _prowlarr_apps.json | selectattr('name', 'equalto', 'Radarr') | list | length == 0
|
||||
|
||||
- name: Add Sonarr to Prowlarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/applications"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: Sonarr
|
||||
syncLevel: fullSync
|
||||
implementation: Sonarr
|
||||
configContract: SonarrSettings
|
||||
fields:
|
||||
- name: prowlarrUrl
|
||||
value: "http://prowlarr:9696"
|
||||
- name: baseUrl
|
||||
value: "http://sonarr:8989"
|
||||
- name: apiKey
|
||||
value: "{{ _sonarr_key }}"
|
||||
- name: syncCategories
|
||||
value: [5000, 5010, 5020, 5030, 5040, 5045, 5050, 5060, 5070, 5080]
|
||||
tags: []
|
||||
status_code: 201
|
||||
when: _prowlarr_apps.json | selectattr('name', 'equalto', 'Sonarr') | list | length == 0
|
||||
|
||||
# --- Prowlarr: FlareSolverr proxy ---
|
||||
|
||||
- name: Get Prowlarr indexer proxies
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexerproxy"
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
register: _prowlarr_proxies
|
||||
|
||||
- name: Add FlareSolverr proxy to Prowlarr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexerproxy"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: FlareSolverr
|
||||
implementation: FlareSolverr
|
||||
configContract: FlareSolverrSettings
|
||||
tags: [1]
|
||||
fields:
|
||||
- name: host
|
||||
value: "http://flaresolverr:8191"
|
||||
- name: requestTimeout
|
||||
value: 60
|
||||
status_code: 201
|
||||
when: _prowlarr_proxies.json | selectattr('name', 'equalto', 'FlareSolverr') | list | length == 0
|
||||
|
||||
# --- Prowlarr: indexers ---
|
||||
|
||||
- name: Get Prowlarr indexers
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexer"
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
register: _prowlarr_indexers
|
||||
|
||||
- name: Add YTS indexer
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexer"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: YTS
|
||||
enable: true
|
||||
appProfileId: 1
|
||||
priority: 25
|
||||
implementation: Cardigann
|
||||
configContract: CardigannSettings
|
||||
tags: []
|
||||
fields:
|
||||
- name: definitionFile
|
||||
value: yts
|
||||
- name: baseSettings.limitsUnit
|
||||
value: 0
|
||||
- name: torrentBaseSettings.preferMagnetUrl
|
||||
value: false
|
||||
- name: apiurl
|
||||
value: movies-api.accel.li
|
||||
status_code: 201
|
||||
when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'YTS') | list | length == 0
|
||||
|
||||
- name: Add The Pirate Bay indexer
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexer"
|
||||
method: POST
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: The Pirate Bay
|
||||
enable: true
|
||||
appProfileId: 1
|
||||
priority: 25
|
||||
implementation: Cardigann
|
||||
configContract: CardigannSettings
|
||||
tags: []
|
||||
fields:
|
||||
- name: definitionFile
|
||||
value: thepiratebay
|
||||
- name: baseSettings.limitsUnit
|
||||
value: 0
|
||||
- name: torrentBaseSettings.preferMagnetUrl
|
||||
value: false
|
||||
- name: apiurl
|
||||
value: apibay.org
|
||||
status_code: 201
|
||||
when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'The Pirate Bay') | list | length == 0
|
||||
|
||||
- name: Add EZTV indexer
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexer"
|
||||
method: POST
|
||||
timeout: 90
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: EZTV
|
||||
enable: true
|
||||
appProfileId: 1
|
||||
priority: 25
|
||||
implementation: Cardigann
|
||||
configContract: CardigannSettings
|
||||
tags: [1]
|
||||
fields:
|
||||
- name: definitionFile
|
||||
value: eztv
|
||||
- name: baseSettings.limitsUnit
|
||||
value: 0
|
||||
- name: torrentBaseSettings.preferMagnetUrl
|
||||
value: false
|
||||
status_code: 201
|
||||
when: _prowlarr_indexers.json | selectattr('name', 'equalto', 'EZTV') | list | length == 0
|
||||
ignore_errors: true
|
||||
|
||||
- name: Add 1337x indexer
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/indexer"
|
||||
method: POST
|
||||
timeout: 90
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: 1337x
|
||||
enable: true
|
||||
appProfileId: 1
|
||||
priority: 25
|
||||
implementation: Cardigann
|
||||
configContract: CardigannSettings
|
||||
tags: [1]
|
||||
fields:
|
||||
- name: definitionFile
|
||||
value: 1337x
|
||||
- name: baseSettings.limitsUnit
|
||||
value: 0
|
||||
- name: torrentBaseSettings.preferMagnetUrl
|
||||
value: false
|
||||
- name: downloadlink
|
||||
value: 0
|
||||
- name: downloadlink2
|
||||
value: 1
|
||||
- name: sort
|
||||
value: 2
|
||||
- name: type
|
||||
value: 1
|
||||
status_code: 201
|
||||
when: _prowlarr_indexers.json | selectattr('name', 'equalto', '1337x') | list | length == 0
|
||||
ignore_errors: true
|
||||
|
||||
- name: Sync Prowlarr indexers to apps
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9696/api/v1/applications/syncindexers"
|
||||
method: GET
|
||||
headers:
|
||||
X-Api-Key: "{{ _prowlarr_key }}"
|
||||
status_code: [200, 202]
|
||||
ignore_errors: true
|
||||
|
||||
# --- Jellyseerr: connect Sonarr and Radarr ---
|
||||
|
||||
- name: Check if Jellyseerr is initialized
|
||||
ansible.builtin.stat:
|
||||
path: /containers/jellyseerr/config/settings.json
|
||||
register: _js_settings
|
||||
|
||||
- name: Read Jellyseerr settings
|
||||
ansible.builtin.slurp:
|
||||
src: /containers/jellyseerr/config/settings.json
|
||||
register: _js_raw
|
||||
when: _js_settings.stat.exists
|
||||
|
||||
- name: Set Jellyseerr API key fact
|
||||
ansible.builtin.set_fact:
|
||||
_js_key: "{{ (_js_raw.content | b64decode | from_json).main.apiKey }}"
|
||||
when: _js_settings.stat.exists
|
||||
|
||||
- name: Get Jellyseerr Sonarr servers
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:5055/api/v1/settings/sonarr"
|
||||
headers:
|
||||
X-Api-Key: "{{ _js_key }}"
|
||||
register: _js_sonarr
|
||||
when: _js_settings.stat.exists
|
||||
|
||||
- name: Configure Sonarr in Jellyseerr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:5055/api/v1/settings/sonarr/{{ '0' if (_js_sonarr.json | length == 0) else (_js_sonarr.json[0].id | string) }}"
|
||||
method: "{{ 'POST' if (_js_sonarr.json | length == 0) else 'PUT' }}"
|
||||
headers:
|
||||
X-Api-Key: "{{ _js_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: sonarr
|
||||
hostname: sonarr
|
||||
port: 8989
|
||||
apiKey: "{{ _sonarr_key }}"
|
||||
useSsl: false
|
||||
baseUrl: ""
|
||||
activeProfileId: 4
|
||||
activeProfileName: HD-1080p
|
||||
activeDirectory: /downloads/tv
|
||||
tags: []
|
||||
animeTags: []
|
||||
is4k: false
|
||||
isDefault: true
|
||||
enableSeasonFolders: true
|
||||
syncEnabled: false
|
||||
preventSearch: false
|
||||
tagRequests: false
|
||||
status_code: [200, 201]
|
||||
when: >
|
||||
_js_settings.stat.exists and
|
||||
(_js_sonarr.json | length == 0 or not _js_sonarr.json[0].isDefault)
|
||||
|
||||
- name: Get Jellyseerr Radarr servers
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:5055/api/v1/settings/radarr"
|
||||
headers:
|
||||
X-Api-Key: "{{ _js_key }}"
|
||||
register: _js_radarr
|
||||
when: _js_settings.stat.exists
|
||||
|
||||
- name: Configure Radarr in Jellyseerr
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:5055/api/v1/settings/radarr/{{ '0' if (_js_radarr.json | length == 0) else (_js_radarr.json[0].id | string) }}"
|
||||
method: "{{ 'POST' if (_js_radarr.json | length == 0) else 'PUT' }}"
|
||||
headers:
|
||||
X-Api-Key: "{{ _js_key }}"
|
||||
body_format: json
|
||||
body:
|
||||
name: radarr
|
||||
hostname: radarr
|
||||
port: 7878
|
||||
apiKey: "{{ _radarr_key }}"
|
||||
useSsl: false
|
||||
baseUrl: ""
|
||||
activeProfileId: 4
|
||||
activeProfileName: HD-1080p
|
||||
activeDirectory: /downloads/movies
|
||||
is4k: false
|
||||
minimumAvailability: released
|
||||
tags: []
|
||||
isDefault: true
|
||||
syncEnabled: false
|
||||
preventSearch: false
|
||||
tagRequests: false
|
||||
status_code: [200, 201]
|
||||
when: >
|
||||
_js_settings.stat.exists and
|
||||
(_js_radarr.json | length == 0 or not _js_radarr.json[0].isDefault)
|
||||
@@ -152,3 +152,6 @@
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
|
||||
- name: Configure services
|
||||
ansible.builtin.include_tasks: configure_services.yml
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib, os, base64
|
||||
|
||||
password = "{{ qbittorrent_webui_password }}"
|
||||
salt = os.urandom(16)
|
||||
dk = hashlib.pbkdf2_hmac("sha512", password.encode(), salt, 100000)
|
||||
pw_hash = "@ByteArray(" + base64.b64encode(salt).decode() + ":" + base64.b64encode(dk).decode() + ")"
|
||||
|
||||
os.makedirs("/containers/qbittorrent/config/qBittorrent", exist_ok=True)
|
||||
with open("/containers/qbittorrent/config/qBittorrent/qBittorrent.conf", "w") as f:
|
||||
f.write("[Preferences]\n")
|
||||
f.write('WebUI\\Password_PBKDF2="{}"\n'.format(pw_hash))
|
||||
f.write("WebUI\\Port=5080\n")
|
||||
f.write("WebUI\\Username=admin\n")
|
||||
f.write("WebUI\\LocalHostAuth=false\n")
|
||||
f.write("WebUI\\ServerDomains=*\n")
|
||||
f.write("WebUI\\Address=*\n")
|
||||
@@ -11,7 +11,7 @@ pihole_dns_hosts:
|
||||
- { ip: "10.10.1.3", host: "grafana.homelab" }
|
||||
- { ip: "10.10.1.3", host: "proxmox.homelab" }
|
||||
- { ip: "10.10.1.3", host: "adyrem.duckdns.org" }
|
||||
- { ip: "10.10.1.3", host: "mtg.homelab" }
|
||||
- { ip: "10.10.1.3", host: "devbox.homelab" }
|
||||
- { ip: "10.10.1.3", host: "jellyfin.homelab" }
|
||||
- { ip: "10.10.1.3", host: "jellyseerr.homelab" }
|
||||
- { ip: "10.10.1.3", host: "radarr.homelab" }
|
||||
|
||||
@@ -7,15 +7,18 @@ hosts = [
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
hosts_str = "[" + ", ".join('"' + h + '"' for h in hosts) + "]"
|
||||
hosts_str = "[\n" + "".join(' "' + h + '",\n' for h in hosts) + " ]"
|
||||
|
||||
with open("/etc/pihole/pihole.toml", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Only replace the first hosts= (DNS custom records section, not DHCP leases)
|
||||
content = re.sub(
|
||||
r"(?m)^( *hosts\s*=\s*)\[.*\]",
|
||||
r"( *hosts\s*=\s*)\[.*?\]",
|
||||
r"\g<1>" + hosts_str,
|
||||
content,
|
||||
count=1,
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
|
||||
with open("/etc/pihole/pihole.toml", "w") as f:
|
||||
|
||||
@@ -10,6 +10,7 @@ DESIRED = [
|
||||
{'proto': 'tcp', 'source': '10.10.10.0/24', 'dport': '2222'},
|
||||
{'proto': 'tcp', 'source': '192.168.1.0/24', 'dport': '8006'},
|
||||
{'proto': 'tcp', 'source': '10.10.10.0/24', 'dport': '8006'},
|
||||
{'proto': 'tcp', 'source': '10.10.1.3', 'dport': '8006'},
|
||||
{'proto': 'tcp', 'source': '192.168.1.0/24', 'dport': '3128'},
|
||||
{'proto': 'udp', 'source': '10.10.0.0/16', 'dport': '53'},
|
||||
{'proto': 'tcp', 'source': '10.10.0.0/16', 'dport': '53'},
|
||||
|
||||
@@ -13,6 +13,10 @@ traefik_services:
|
||||
public_host: "adyrem.duckdns.org"
|
||||
public_path: "/git"
|
||||
strip_prefix: "/git"
|
||||
- name: devbox
|
||||
host: "devbox.homelab"
|
||||
backend: "http://10.10.2.10:4001"
|
||||
public_host: "adyrem.duckdns.org"
|
||||
- name: mtg
|
||||
host: "mtg.homelab"
|
||||
backend: "http://10.10.2.10:3000"
|
||||
|
||||
@@ -60,6 +60,41 @@
|
||||
when: traefik_version not in (traefik_installed_version.stdout | default(''))
|
||||
notify: Restart traefik
|
||||
|
||||
- name: Create traefik certs directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/traefik/certs
|
||||
state: directory
|
||||
owner: traefik
|
||||
group: traefik
|
||||
mode: "0750"
|
||||
|
||||
- name: Copy homelab TLS certificate
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../../certs/wildcard.homelab.pem"
|
||||
dest: /etc/traefik/certs/homelab.pem
|
||||
owner: traefik
|
||||
group: traefik
|
||||
mode: "0640"
|
||||
notify: Restart traefik
|
||||
|
||||
- name: Copy homelab TLS key
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../../certs/wildcard.homelab-key.pem"
|
||||
dest: /etc/traefik/certs/homelab-key.pem
|
||||
owner: traefik
|
||||
group: traefik
|
||||
mode: "0640"
|
||||
notify: Restart traefik
|
||||
|
||||
- name: Write Traefik TLS config
|
||||
ansible.builtin.template:
|
||||
src: tls.yml.j2
|
||||
dest: /etc/traefik/conf.d/tls.yml
|
||||
owner: traefik
|
||||
group: traefik
|
||||
mode: "0640"
|
||||
notify: Restart traefik
|
||||
|
||||
- name: Write Traefik static config
|
||||
ansible.builtin.template:
|
||||
src: traefik.yml.j2
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
tls:
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /etc/traefik/certs/homelab.pem
|
||||
keyFile: /etc/traefik/certs/homelab-key.pem
|
||||
Reference in New Issue
Block a user