bk node and refactor

This commit is contained in:
il
2026-06-23 10:57:10 +09:00
parent 927b5b4934
commit fe146b25c7
131 changed files with 2310 additions and 920 deletions
+96 -19
View File
@@ -21,31 +21,62 @@ domain:
services:
crowdsec:
domain: "crowdsec"
domain:
internal: "crowdsec"
ports:
https: "8080"
dependency:
cert: true
bind:
domain: "bind"
domain:
internal: "bind"
ports:
dns: "53"
blocky:
domain: "blocky"
domain:
internal: "blocky"
ports:
https: "443"
dns: "53"
dot: "853"
dependency:
cert: true
kopia:
domain:
internal: "kopia"
ports:
https: "51515"
dependency:
cert: true
postgresql:
domain: "postgresql"
domain:
internal: "postgresql"
ports:
tcp: "5432" # postgresql db connection port
subuid: "100998"
dependency:
cert: true
ldap:
domain: "ldap"
domain:
internal: "ldap"
ports:
http: "17170"
ldaps: "6360"
subuid: "100999"
dependency:
cert: true
web: "infra-internal"
db: "postgresql"
mail:
domain:
internal: "mail"
ports: ""
subuid: ""
dependency:
cert: true
ca:
domain: "ca"
domain:
internal: "ca"
ports:
https: "9000"
subuid: "100999"
@@ -54,36 +85,43 @@ services:
http: "9793"
subuid: "165533"
prometheus:
domain: "prometheus"
domain:
internal: "prometheus"
ports:
https: "9090"
subuid: "165533"
dependency:
cert: true
web: "infra-internal"
loki:
domain: "loki"
domain:
internal: "loki"
ports:
https: "3100"
subuid: "110000"
dependency:
cert: true
grafana:
domain: "grafana"
domain:
internal: "grafana"
ports:
http: "3000"
subuid: "100471"
dependency:
web: "infra-internal"
db: "postgresql"
caddy:
ports:
http: "2080"
https: "2443"
nas:
domain: "nas"
ports:
https: "5001"
kopia:
domain: "nas"
ports:
https: "51515"
authelia:
domain: "authelia"
domain:
public: "authelia"
ports:
http: "9091"
dependency:
web: "auth-public"
db: "postgresql"
redis:
subuid: "100998"
vaultwarden:
@@ -92,6 +130,9 @@ services:
internal: "vault.app"
ports:
http: "8000"
dependency:
web: "app-public"
db: "postgresql"
gitea:
domain:
public: "gitea"
@@ -99,6 +140,9 @@ services:
ports:
http: "3001"
subuid: "100999"
dependency:
web: "app-public"
db: "postgresql"
immich:
domain:
public: "immich"
@@ -106,6 +150,9 @@ services:
ports:
http: "2283"
redis: "6379"
dependency:
web: "app-public"
db: "postgresql"
immich-ml:
ports:
http: "3003"
@@ -117,6 +164,9 @@ services:
http: "8001"
redis: "6380"
subuid: "100999"
dependency:
web: "app-public"
db: "postgresql"
nextcloud:
domain:
public: "nextcloud"
@@ -125,6 +175,9 @@ services:
http: "8002"
redis: "6382"
subuid: "100032"
dependency:
web: "app-public"
db: "postgresql"
collabora:
domain:
public: "collabora"
@@ -132,6 +185,8 @@ services:
ports:
http: "9980"
subuid: "101000"
dependency:
web: "app-public"
sure:
domain:
public: "sure"
@@ -140,6 +195,9 @@ services:
http: "3002"
redis: "6383"
subuid: "100999"
dependency:
web: "app-public"
db: "postgresql"
outline:
domain:
public: "outline"
@@ -148,6 +206,9 @@ services:
http: "3004"
redis: "6384"
subuid: "101000"
dependency:
web: "app-public"
db: "postgresql"
memos:
domain:
public: "memos"
@@ -155,6 +216,9 @@ services:
ports:
http: "3005"
subuid: "110000"
dependency:
web: "app-public"
db: "postgresql"
mealie:
domain:
public: "mealie"
@@ -162,6 +226,18 @@ services:
ports:
http: "9001"
subuid: "100910"
dependency:
web: "app-public"
db: "postgresql"
homebox:
domain:
public: "homebox"
internal: "homebox.app"
ports:
http: "7745"
dependency:
web: "app-public"
db: "postgresql"
version:
packages:
@@ -194,8 +270,9 @@ version:
immich: "v2.7.5"
paperless: "2.20.15"
nextcloud: "33.0.5"
collabora: "25.04.10.3.1"
collabora: "26.04.1.4.1"
sure: "0.7.1"
outline: "1.8.1"
memos: "0.29.1"
mealie: "v3.19.2"
homebox: "0.26.2"
+35 -8
View File
@@ -21,13 +21,40 @@ hardware:
bus: "0x00"
slot: "0x02"
function: "0x0"
hdd:
disk:
data:
hdd0: "WMC6N0E617S8"
hdd1: "P6HVTL0W"
hdd2: "P2GH354H"
hdd3: "P6GJKSMP"
btrfs:
serials:
- "WMC6N0E617S8"
- "P6HVTL0W"
- "P2GH354H"
- "P6GJKSMP"
fs:
type: "btrfs"
label: "DATA"
level: "raid10"
mount_point: "{{ node['libvirt_path'] }}/data"
mount_point: "{{ node['libvirt_path'] }}/disk/data"
mount_opts:
- "defaults"
- "noatime"
- "nodatacow"
- "degraded"
- "nofail"
btrfs:
data_profile: "raid10"
metadata_profile: "raid10"
min_devices: 4
backup:
serials:
- "6261S5G4S75H"
fs:
type: "btrfs"
label: "BACKUP"
mount_point: "{{ node['libvirt_path'] }}/disk/backup"
mount_opts:
- "defaults"
- "noatime"
- "nodatacow"
- "nofail"
btrfs:
data_profile: "single"
metadata_profile: "dup"
min_devices: 1
+1 -10
View File
@@ -24,17 +24,8 @@ vm:
storage: 256
data_storage:
name: "vdb"
pool: "data"
size: 3.5
lan_mac: "0a:49:6e:4d:03:00"
lan_net: "lan-net"
lan_group: "vlan10-access"
# PCIe passthrough address
# result of `lspci | grep -i -e "sata controller" -e "vga"` and parse it.
# Ex) 04:00.0 > domain: "0x0000", bus: "0x04", slot: "0x00", function: "0x0"
pass_through:
igpu:
address: "0000:00:02.0"
domain: "0x0000"
bus: "0x00"
slot: "0x02"
function: "0x0"
+4 -4
View File
@@ -12,12 +12,12 @@ vm:
name: "auth"
cpu: 2
cpu_pin: |
<vcpupin vcpu='0' cpuset='8'/>
<vcpupin vcpu='1' cpuset='9'/>
<emulatorpin cpuset='8-9'/>
<vcpupin vcpu='0' cpuset='6'/>
<vcpupin vcpu='1' cpuset='7'/>
<emulatorpin cpuset='6-7'/>
shares: 1024
memory: 2
storage: 64
storage: 32
lan_mac: "0a:49:6e:4d:02:00"
lan_net: "lan-net"
lan_group: "vlan10-access"
+27
View File
@@ -0,0 +1,27 @@
---
# Node Factors
node:
name: "bk"
group: "svadmins"
uid: 2009
home_path: "/home/bk"
ssh_san: "{{ node['name'] }},{{ node['name'] }}.{{ domain['internal'] }}"
local_san: "localhost {{ node['name'] }}.{{ domain['internal'] }}"
# VM Factors
vm:
name: "bk"
cpu: 2
cpu_pin: |
<vcpupin vcpu='0' cpuset='6'/>
<vcpupin vcpu='1' cpuset='7'/>
<emulatorpin cpuset='6-7'/>
shares: 512
memory: 2
storage: 32
data_storage:
name: "vdb"
pool: "backup"
size: 3.5
lan_mac: "0a:49:6e:4d:09:00"
lan_net: "lan-net"
lan_group: "vlan10-access"
+1 -1
View File
@@ -21,6 +21,6 @@ node:
data_path: "{{ node['homelab_path'] }}/data"
config_path: "{{ node['homelab_path'] }}/config"
ssh_san: "{{ node['name'] }},{{ node['name'] }}.{{ domain['internal'] }}"
ssh_users: "vmm,fw,infra,auth,app"
ssh_users: "vmm,fw,bk,infra,auth,app"
# add the hostname of wsl, it is needed to improve the sudo problem
local_san: "localhost {{ node['name'] }} {{ node['name'] }}.{{ domain['internal'] }}"
+4 -8
View File
@@ -52,6 +52,8 @@ network4:
vmm:
client: "192.168.1.10"
server: "192.168.10.10"
bk:
server: "192.168.10.9"
infra:
server: "192.168.10.11"
auth:
@@ -60,8 +62,6 @@ network4:
server: "192.168.10.13"
switch:
client: "192.168.1.2"
nas:
client: "192.168.1.11"
printer:
client: "192.168.1.101"
@@ -85,18 +85,14 @@ network6:
vmm:
client: "fd00:1::10"
server: "fd00:10::10"
bk:
server: "fd00:10::9"
infra:
server: "fd00:10::11"
auth:
server: "fd00:10::12"
app:
server: "fd00:10::13"
switch:
client: "fd00:1::2"
nas:
client: "fd00:1::11"
printer:
client: "fd00:1::101"
# Suricata Factors
# suricata_home_net: '[10.0.0.0/8,172.16.0.0/12,192.168.0.0/16]'
+4 -4
View File
@@ -15,11 +15,11 @@ vm:
cpu_pin: |
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<vcpupin vcpu='2' cpuset='6'/>
<vcpupin vcpu='3' cpuset='7'/>
<emulatorpin cpuset='6-7'/>
<vcpupin vcpu='2' cpuset='8'/>
<vcpupin vcpu='3' cpuset='9'/>
<emulatorpin cpuset='8-9'/>
shares: 2048
memory: 6
memory: 4
storage: 256
lan_mac: "0a:49:6e:4d:01:00"
lan_net: "lan-net"
+1
View File
@@ -9,6 +9,7 @@ vmm ansible_host=vmm.ilnmors.internal ansible_user=vmm
# --- Virtual Machines ---
[vms]
fw ansible_host=fw.ilnmors.internal ansible_user=fw
bk ansible_host=bk.ilnmors.internal ansible_user=bk
infra ansible_host=infra.ilnmors.internal ansible_user=infra
auth ansible_host=auth.ilnmors.internal ansible_user=auth
app ansible_host=app.ilnmors.internal ansible_user=app
+17 -1
View File
@@ -78,6 +78,14 @@
tags: ["init", "resolved"]
tags: ["init", "resolved"]
- name: Set timesyncd
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_timesyncd"
apply:
tags: ["init", "timesyncd"]
tags: ["init", "timesyncd"]
- name: Update and upgrade apt
ansible.builtin.apt:
upgrade: "dist"
@@ -100,7 +108,7 @@
- name: Set data volume
ansible.builtin.include_role:
name: "app"
name: "common"
tasks_from: "node/set_data_volume"
apply:
tags: ["init", "raid"]
@@ -242,6 +250,14 @@
tags: ["site", "mealie"]
tags: ["site", "mealie"]
- name: Set homebox
ansible.builtin.include_role:
name: "app"
tasks_from: "services/set_homebox"
apply:
tags: ["site", "homebox"]
tags: ["site", "homebox"]
- name: Flush handlers right now
ansible.builtin.meta: "flush_handlers"
+8
View File
@@ -78,6 +78,14 @@
tags: ["init", "resolved"]
tags: ["init", "resolved"]
- name: Set timesyncd
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_timesyncd"
apply:
tags: ["init", "timesyncd"]
tags: ["init", "timesyncd"]
- name: Update and upgrade apt
ansible.builtin.apt:
upgrade: "dist"
+163
View File
@@ -0,0 +1,163 @@
---
- name: Load secret values
hosts: "console"
gather_facts: false
become: false
tasks:
- name: Load secret from secrets.yaml
ansible.builtin.include_role:
name: "console"
tasks_from: "node/load_secret_vars"
apply:
tags: ["always"]
tags: ["always"]
- name: Site bk
hosts: "bk"
gather_facts: false
become: false
pre_tasks:
- name: Set become password
ansible.builtin.set_fact:
ansible_become_pass: "{{ hostvars['console']['sudo']['password']['bk'] }}"
no_log: true
tags: ["always"]
tasks:
- name: Set timezone
community.general.timezone:
name: "{{ timezone }}"
become: true
tags: ["init", "timezone"]
- name: Deploy root_ca certificate
ansible.builtin.include_role:
name: "common"
tasks_from: "node/deploy_root_ca"
apply:
tags: ["init", "root_crt"]
tags: ["init", "root_crt"]
- name: Deploy hosts file
ansible.builtin.include_role:
name: "common"
tasks_from: "node/deploy_hosts"
apply:
tags: ["init", "hosts"]
tags: ["init", "hosts"]
- name: Create default directory
ansible.builtin.include_role:
name: "common"
tasks_from: "node/create_default_dir"
apply:
tags: ["init", "default_dir"]
tags: ["init", "default_dir"]
- name: Set ssh host
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_ssh_host"
apply:
tags: ["init", "ssh_host"]
tags: ["init", "ssh_host"]
- name: Set networkd
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_networkd"
apply:
tags: ["init", "networkd"]
tags: ["init", "networkd"]
- name: Set resolved
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_resolved"
apply:
tags: ["init", "resolved"]
tags: ["init", "resolved"]
- name: Set timesyncd
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_timesyncd"
apply:
tags: ["init", "timesyncd"]
tags: ["init", "timesyncd"]
- name: Update and upgrade apt
ansible.builtin.apt:
upgrade: "dist"
update_cache: true
cache_valid_time: 3600
become: true
tags: ["init", "site", "upgrade-packages"]
- name: Install common packages
ansible.builtin.apt:
name:
- "acl"
- "curl"
- "jq"
- "netcat-openbsd"
- "dbus-user-session"
state: "present"
become: true
tags: ["init", "site", "install-packages"]
- name: Set data volume
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_data_volume"
apply:
tags: ["init", "raid"]
tags: ["init", "raid"]
- name: Set linger
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_linger"
apply:
tags: ["init", "linger"]
tags: ["init", "linger"]
- name: Set nftables
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_nftables"
apply:
tags: ["init", "nftables"]
tags: ["init", "nftables"]
- name: Set crowdsec
ansible.builtin.include_role:
name: "common"
tasks_from: "services/set_crowdsec"
apply:
tags: ["site", "crowdsec"]
tags: ["site", "crowdsec"]
- name: Set kopia
ansible.builtin.include_role:
name: "common"
tasks_from: "services/set_kopia"
apply:
tags: ["site", "kopia"]
tags: ["site", "kopia"]
- name: Set rclone
ansible.builtin.include_role:
name: "bk"
tasks_from: "services/set_rclone"
apply:
tags: ["site", "rclone"]
tags: ["site", "rclone"]
- name: Set alloy
ansible.builtin.include_role:
name: "common"
tasks_from: "services/set_alloy"
apply:
tags: ["site", "alloy"]
tags: ["site", "alloy"]
+223
View File
@@ -0,0 +1,223 @@
---
- name: Load secret values
hosts: "console"
gather_facts: false
become: false
tasks:
- name: Load secret from secrets.yaml
ansible.builtin.include_role:
name: "console"
tasks_from: "node/load_secret_vars"
apply:
tags: ["always"]
tags: ["always"]
- name: Site console
hosts: "console"
gather_facts: false
become: false
tasks:
- name: Create root CA
tags: ["never", "root"]
block:
- name: Clean previous CA temporary directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
state: "absent"
no_log: true
- name: Create ca directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
state: "directory"
mode: "0700"
- name: Get root CA password
ansible.builtin.copy:
content: |
{{ hostvars['console']['ca']['root']['password'] }}
dest: "/run/user/{{ node['uid'] }}/ca/root_ca.password"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0400"
no_log: true
- name: Create root CA
ansible.builtin.command: |
step certificate create \
"{{ domain['internal'] }} Root CA" \
/run/user/{{ node['uid'] }}/ca/root_ca.crt \
/run/user/{{ node['uid'] }}/ca/root_ca.key \
--password-file /run/user/{{ node['uid'] }}/ca/root_ca.password \
--profile root-ca \
--not-after 87600h
changed_when: true
no_log: true
- name: Update root CA
environment:
SOPS_AGE_KEY: "{{ hostvars['console']['age_key'] }}"
ansible.builtin.shell: |
set -euo pipefail
jq -Rs . "/run/user/{{ node['uid'] }}/ca/root_ca.crt" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["ca"]["root"]["crt"]'
jq -Rs . "/run/user/{{ node['uid'] }}/ca/root_ca.key" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["ca"]["root"]["key"]'
changed_when: true
no_log: true
always:
- name: Clean temporary files
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
state: "absent"
no_log: true
- name: Get root CA and create intermediate CA
tags: ["never", "intermediate"]
block:
- name: Clean previous CA temporary directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
state: "absent"
no_log: true
- name: Create ca certificates directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
state: "directory"
mode: "0700"
- name: Get root key and cert and password
ansible.builtin.copy:
content: |
{{ hostvars['console']['ca']['root'][item] }}
dest: "/run/user/{{ node['uid'] }}/ca/root_ca.{{ item }}"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0400"
loop:
- "key"
- "crt"
- "password"
no_log: true
- name: Get intermediate CA password
ansible.builtin.copy:
content: |
{{ hostvars['console']['ca']['intermediate']['password'] }}
dest: "/run/user/{{ node['uid'] }}/ca/intermediate_ca.password"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0400"
no_log: true
- name: Create intermediate CA
ansible.builtin.command: |
step certificate create \
"{{ domain['internal'] }} intermediate CA" \
/run/user/{{ node['uid'] }}/ca/intermediate_ca.crt \
/run/user/{{ node['uid'] }}/ca/intermediate_ca.key \
--password-file /run/user/{{ node['uid'] }}/ca/intermediate_ca.password \
--profile intermediate-ca \
--ca /run/user/{{ node['uid'] }}/ca/root_ca.crt \
--ca-key /run/user/{{ node['uid'] }}/ca/root_ca.key \
--ca-password-file /run/user/{{ node['uid'] }}/ca/root_ca.password \
--not-after 43800h
changed_when: true
no_log: true
- name: Update intermediate CA
environment:
SOPS_AGE_KEY: "{{ hostvars['console']['age_key'] }}"
ansible.builtin.shell: |
set -euo pipefail
jq -Rs . "/run/user/{{ node['uid'] }}/ca/intermediate_ca.crt" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["ca"]["intermediate"]["crt"]'
jq -Rs . "/run/user/{{ node['uid'] }}/ca/intermediate_ca.key" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["ca"]["intermediate"]["key"]'
changed_when: true
no_log: true
always:
- name: Clean temporary files
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/ca"
state: "absent"
no_log: true
- name: Get intermediate CA and create leaf cert
tags: ["never", "leaf"]
block:
- name: Initialize self-cert services list
ansible.builtin.set_fact:
cert_services: []
- name: Set self-cert services list
ansible.builtin.set_fact:
cert_services: >-
{{
services
| dict2items
| selectattr('value.dependency.cert', 'defined')
| selectattr('value.dependency.cert', 'equalto', true)
| map(attribute='key')
| list
}}
- name: Clean previous leaf temporary directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/leaf"
state: "absent"
no_log: true
- name: Create leaf certificates directory
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/leaf"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
state: "directory"
mode: "0700"
- name: Get intermediate key and cert and password
ansible.builtin.copy:
content: |
{{ hostvars['console']['ca']['intermediate'][item] }}
dest: "/run/user/{{ node['uid'] }}/leaf/intermediate_ca.{{ item }}"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0400"
loop:
- "key"
- "crt"
- "password"
no_log: true
- name: Create leaf certificates
ansible.builtin.command: |
step certificate create \
"{{ services[item]['domain']['internal'] }}.{{ domain['internal'] }}" \
/run/user/{{ node['uid'] }}/leaf/{{ item }}.crt \
/run/user/{{ node['uid'] }}/leaf/{{ item }}.key \
--profile leaf \
--san {{ services[item]['domain']['internal'] }}.{{ domain['internal'] }} \
--ca /run/user/{{ node['uid'] }}/leaf/intermediate_ca.crt \
--ca-key /run/user/{{ node['uid'] }}/leaf/intermediate_ca.key \
--ca-password-file /run/user/{{ node['uid'] }}/leaf/intermediate_ca.password \
--not-after 21900h \
--insecure --no-password
loop: "{{ cert_services }}"
changed_when: true
no_log: true
- name: Update leaf keys and certificates
environment:
SOPS_AGE_KEY: "{{ hostvars['console']['age_key'] }}"
ansible.builtin.shell: |
set -euo pipefail
jq -Rs . "/run/user/{{ node['uid'] }}/leaf/{{ item }}.crt" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["{{ item }}"]["crt"]'
jq -Rs . "/run/user/{{ node['uid'] }}/leaf/{{ item }}.key" \
| sops set --value-stdin "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml" \
'["{{ item }}"]["key"]'
loop: "{{ cert_services }}"
changed_when: true
no_log: true
always:
- name: Clean temporary files
ansible.builtin.file:
path: "/run/user/{{ node['uid'] }}/leaf"
state: "absent"
no_log: true
+2 -2
View File
@@ -108,8 +108,8 @@
ansible.builtin.get_url:
url: "https://cdimage.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
dest: "{{ node['data_path'] }}/images/debian-13-generic-amd64.qcow2"
owner: "console"
group: "svadmins"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0600"
tags: ["init", "site", "cloud-init-image"]
+8
View File
@@ -78,6 +78,14 @@
tags: ["init", "resolved"]
tags: ["init", "resolved"]
- name: Set timesyncd
ansible.builtin.include_role:
name: "common"
tasks_from: "node/set_timesyncd"
apply:
tags: ["init", "timesyncd"]
tags: ["init", "timesyncd"]
- name: Update and upgrade apt
ansible.builtin.apt:
upgrade: "dist"
+1
View File
@@ -19,6 +19,7 @@
vars:
valid_vm_names:
- "fw"
- "bk"
- "infra"
- "auth"
- "app"
+1 -1
View File
@@ -104,7 +104,7 @@
- name: Set raid
ansible.builtin.include_role:
name: "vmm"
tasks_from: "node/set_raid"
tasks_from: "node/set_disk"
apply:
tags: ["init", "raid"]
tags: ["init", "raid"]
+11
View File
@@ -124,3 +124,14 @@
changed_when: false
listen: "notification_restart_mealie"
ignore_errors: true # noqa: ignore-errors
- name: Restart homebox
ansible.builtin.systemd:
name: "homebox.service"
state: "restarted"
enabled: true
scope: "user"
daemon_reload: true
changed_when: false
listen: "notification_restart_homebox"
ignore_errors: true # noqa: ignore-errors
@@ -1,4 +1,11 @@
---
- name: Pull image
containers.podman.podman_image:
name: "docker.io/collabora/code"
tag: "{{ version['containers']['collabora'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/collabora/collabora.container.j2"
@@ -8,6 +8,7 @@
mode: "0770"
loop:
- "data/containers/gitea"
- "data/containers/gitea/data"
- "containers/gitea"
- "containers/gitea/ssl"
become: true
@@ -33,6 +34,13 @@
notify: "notification_restart_gitea"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/gitea/gitea"
tag: "{{ version['containers']['gitea'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/gitea/gitea.container.j2"
@@ -0,0 +1,86 @@
---
- name: Create homebox directory
ansible.builtin.file:
path: "{{ node['home_path'] }}/{{ item }}"
state: "directory"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0770"
loop:
- "data/containers/homebox"
- "data/containers/homebox/data"
- "containers/homebox"
- "containers/homebox/ssl"
become: true
- name: Deploy root certificate
ansible.builtin.copy:
content: |
{{ hostvars['console']['ca']['root']['crt'] }}
dest: "{{ node['home_path'] }}/containers/homebox/ssl/{{ root_cert_filename }}"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0440"
become: true
notify: "notification_restart_homebox"
no_log: true
- name: Register secret value to podman secret
containers.podman.podman_secret:
name: "{{ item.name }}"
data: "{{ item.value }}"
state: "present"
force: true
loop:
- name: "HBOX_AUTH_API_KEY_PEPPER"
value: "{{ hostvars['console']['homebox']['api_secret'] }}"
- name: "HBOX_DATABASE_PASSWORD"
value: "{{ hostvars['console']['postgresql']['password']['homebox'] }}"
- name: "HBOX_OIDC_CLIENT_SECRET"
value: "{{ hostvars['console']['homebox']['oidc']['secret'] }}"
notify: "notification_restart_homebox"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "ghcr.io/sysadminsmedia/homebox"
tag: "{{ version['containers']['homebox'] }}"
state: "present"
pull: true
- name: Deploy homebox.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/homebox/homebox.container.j2"
dest: "{{ node['home_path'] }}/.config/containers/systemd/homebox.container"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0644"
notify: "notification_restart_homebox"
- name: Enable homebox.service
ansible.builtin.systemd:
name: "homebox.service"
state: "started"
enabled: true
daemon_reload: true
scope: "user"
- name: Check container archive images
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/ghcr.io_sysadminsmedia_homebox_{{ version['containers']['homebox'] }}.tar"
register: container_archive_images
- name: Save container archive images
containers.podman.podman_save:
image:
- "ghcr.io/sysadminsmedia/homebox:{{ version['containers']['homebox'] }}"
dest: "{{ node['home_path'] }}/archives/containers/ghcr.io_sysadminsmedia_homebox_{{ version['containers']['homebox'] }}.tar"
format: "oci-archive"
force: false
when: not container_archive_images.stat.exists
- name: Fetch container archive images
ansible.builtin.fetch:
src: "{{ node['home_path'] }}/archives/containers/ghcr.io_sysadminsmedia_homebox_{{ version['containers']['homebox'] }}.tar"
dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/"
flat: true
@@ -60,6 +60,7 @@
mode: "0770"
loop:
- "data/containers/immich"
- "data/containers/immich/data"
- "containers/immich"
- "containers/immich/ssl"
- "containers/immich/ml"
@@ -85,6 +86,22 @@
notify: "notification_restart_immich"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "{{ item.image }}"
tag: "{{ item.tag }}"
state: "present"
pull: true
loop:
- name: "immich_ml"
image: "ghcr.io/immich-app/immich-machine-learning"
tag: "{{ version['containers']['immich'] }}-openvino"
- name: "immich"
image: "ghcr.io/immich-app/immich-server"
tag: "{{ version['containers']['immich'] }}"
loop_control:
label: "{{ item.name }}"
- name: Deploy immich.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/immich/immich.container.j2"
@@ -123,14 +140,17 @@
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
loop:
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
- name: "redis"
image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
- image: "ghcr.io/immich-app/immich-machine-learning:{{ version['containers']['immich'] }}-openvino"
- name: "immich_ml"
image: "ghcr.io/immich-app/immich-machine-learning:{{ version['containers']['immich'] }}-openvino"
file: "ghcr.io_immich-app_immich-machine-learning_{{ version['containers']['immich'] }}-openvino"
- image: "ghcr.io/immich-app/immich-server:{{ version['containers']['immich'] }}"
- name: "immich"
image: "ghcr.io/immich-app/immich-server:{{ version['containers']['immich'] }}"
file: "ghcr.io_immich-app_immich-server_{{ version['containers']['immich'] }}"
loop_control:
label: "{{ item.file }}"
label: "{{ item.name }}"
register: container_archive_images
- name: Save container archive images
@@ -39,6 +39,13 @@
notify: "notification_restart_mealie"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "ghcr.io/mealie-recipes/mealie"
tag: "{{ version['containers']['mealie'] }}"
state: "present"
pull: true
- name: Deploy mealie.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/mealie/mealie.container.j2"
@@ -29,13 +29,20 @@
containers.podman.podman_secret:
name: "MEMOS_DSN"
data: "postgresql://memos:{{ hostvars['console']['postgresql']['password']['memos'] | urlencode | replace('/', '%2F') }}\
@{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}/memos_db?sslmode=verify-full&\
@{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}/memos_db?sslmode=verify-full&\
sslrootcert=/etc/ssl/memos/{{ root_cert_filename }}"
state: "present"
force: true
no_log: true
notify: "notification_restart_memos"
- name: Pull image
containers.podman.podman_image:
name: "docker.io/neosmemo/memos"
tag: "{{ version['containers']['memos'] }}"
state: "present"
pull: true
- name: Deploy memos.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/memos/memos.container.j2"
@@ -90,7 +90,7 @@
NEXTCLOUD_UPDATE: "1"
NEXTCLOUD_ADMIN_USER: "{{ hostvars['console']['nextcloud']['admin']['id'] }}"
NEXTCLOUD_ADMIN_PASSWORD: "{{ hostvars['console']['nextcloud']['admin']['password'] }}"
POSTGRES_HOST: "{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}"
POSTGRES_HOST: "{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}"
POSTGRES_DB: "nextcloud_db"
POSTGRES_USER: "nextcloud"
POSTGRES_PASSWORD: "{{ hostvars['console']['postgresql']['password']['nextcloud'] }}"
@@ -139,6 +139,13 @@
- "upload.ini"
notify: "notification_restart_nextcloud"
- name: Pull image
containers.podman.podman_image:
name: "docker.io/library/nextcloud"
tag: "{{ version['containers']['nextcloud'] }}"
state: "present"
pull: true
- name: Deploy nextcloud.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/nextcloud.container.j2"
@@ -179,12 +186,14 @@
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
loop:
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
- name: "redis"
image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
- image: "docker.io/library/nextcloud:{{ version['containers']['nextcloud'] }}"
- name: "nextcloud"
image: "docker.io/library/nextcloud:{{ version['containers']['nextcloud'] }}"
file: "docker.io_library_nextcloud_{{ version['containers']['nextcloud'] }}"
loop_control:
label: "{{ item.file }}"
label: "{{ item.name }}"
register: container_archive_images
- name: Save container archive images
@@ -101,6 +101,13 @@
notify: "notification_restart_outline"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/outlinewiki/outline"
tag: "{{ version['containers']['outline'] }}"
state: "present"
pull: true
- name: Deploy outline.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/outline/outline.container.j2"
@@ -122,12 +129,14 @@
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
loop:
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
- name: "redis"
image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
- image: "docker.io/outlinewiki/outline:{{ version['containers']['outline'] }}"
- name: "outline"
image: "docker.io/outlinewiki/outline:{{ version['containers']['outline'] }}"
file: "docker.io_outlinewiki_outline_{{ version['containers']['outline'] }}"
loop_control:
label: "{{ item.file }}"
label: "{{ item.name }}"
register: container_archive_images
- name: Save container archive images
@@ -67,6 +67,8 @@
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0640"
register: "is_paperless_containerfile"
notify: "notification_restart_paperless"
- name: Deploy root certificate
ansible.builtin.copy:
@@ -87,6 +89,7 @@
tag: "{{ version['containers']['paperless'] }}"
state: "build"
path: "{{ node['home_path'] }}/containers/paperless/build"
force: "{{ is_paperless_containerfile.changed }}"
- name: Prune paperless dangling images
containers.podman.podman_prune:
@@ -116,7 +119,7 @@
"client_id": "paperless",
"secret": "{{ hostvars['console']['paperless']['oidc']['secret'] }}",
"settings": {
"server_url": "https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/.well-known/openid-configuration",
"server_url": "https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/.well-known/openid-configuration",
"token_auth_method": "client_secret_post"
}
}
@@ -147,12 +150,14 @@
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
loop:
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
- name: "redis"
image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
- image: "{{ domain['internal'] }}/{{ node['name'] }}/paperless-ngx:{{ version['containers']['paperless'] }}"
- name: "paperless"
image: "{{ domain['internal'] }}/{{ node['name'] }}/paperless-ngx:{{ version['containers']['paperless'] }}"
file: "{{ domain['internal'] }}_{{ node['name'] }}_paperless-ngx_{{ version['containers']['paperless'] }}"
loop_control:
label: "{{ item.file }}"
label: "{{ item.name }}"
register: container_archive_images
- name: Save container archive images
@@ -161,11 +166,11 @@
- "{{ item.item.image }}"
dest: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar"
format: "oci-archive"
force: false
force: "{{ is_paperless_containerfile.changed }}"
loop: "{{ container_archive_images.results }}"
loop_control:
label: "{{ item.item.file }}"
when: not item.stat.exists
when: not item.stat.exists or (item.item.name == 'paperless' and is_paperless_containerfile.changed)
- name: Fetch container archive images
ansible.builtin.fetch:
+12 -3
View File
@@ -86,6 +86,13 @@
notify: "notification_restart_sure"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "ghcr.io/we-promise/sure"
tag: "{{ version['containers']['sure'] }}"
state: "present"
pull: true
- name: Deploy sure.container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/sure/{{ item }}.j2"
@@ -113,12 +120,14 @@
ansible.builtin.stat:
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
loop:
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
- name: "redis"
image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
- image: "ghcr.io/we-promise/sure:{{ version['containers']['sure'] }}"
- name: "sure"
image: "ghcr.io/we-promise/sure:{{ version['containers']['sure'] }}"
file: "ghcr.io_we-promise_sure_{{ version['containers']['sure'] }}"
loop_control:
label: "{{ item.file }}"
label: "{{ item.name }}"
register: container_archive_images
- name: Save container archive images
@@ -8,6 +8,7 @@
mode: "0770"
loop:
- "data/containers/vaultwarden"
- "data/containers/vaultwarden/data"
- "containers/vaultwarden"
- "containers/vaultwarden/ssl"
@@ -34,11 +35,18 @@
value: "{{ hostvars['console']['vaultwarden']['admin']['hash'] }}"
- name: "VW_DATABASE_URL"
value: "postgresql://vaultwarden:{{ hostvars['console']['postgresql']['password']['vaultwarden'] | urlencode | replace('/', '%2F') }}\
@{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}/vaultwarden_db?sslmode=verify-full&\
@{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}/vaultwarden_db?sslmode=verify-full&\
sslrootcert=/etc/ssl/vaultwarden/{{ root_cert_filename }}"
notify: "notification_restart_vaultwarden"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/vaultwarden/server"
tag: "{{ version['containers']['vaultwarden'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/vaultwarden/vaultwarden.container.j2"
@@ -60,6 +60,13 @@
notify: "notification_restart_authelia"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/authelia/authelia"
tag: "{{ version['containers']['authelia'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/auth/authelia/authelia.container.j2"
@@ -0,0 +1,54 @@
---
- name: Check rclone installation
ansible.builtin.shell: |
command -v rclone
changed_when: false
failed_when: false
register: "is_rclone_installed"
ignore_errors: true
- name: Install rclone
ansible.builtin.apt:
name:
- "rclone"
state: "present"
become: true
when: is_rclone_installed.rc != 0
- name: Create rclone config directory
ansible.builtin.file:
path: "/etc/secrets/{{ node['uid'] }}/rclone"
state: "directory"
owner: "{{ ansible_user }}"
group: "root"
mode: "0700"
become: true
- name: Deploy rclone config
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/bk/rclone/config/rclone.config.j2"
dest: "/etc/secrets/{{ node['uid'] }}/rclone/rclone.config"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0600"
no_log: true
- name: Deploy rclone backup service files
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/bk/rclone/{{ item }}.j2"
dest: "{{ node['home_path'] }}/.config/systemd/user/{{ item }}"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0644"
validate: "/usr/bin/systemd-analyze verify %s"
loop:
- "rclone.service"
- "rclone.timer"
- name: Enable rclone timer
ansible.builtin.systemd:
name: "rclone.timer"
state: "started"
enabled: true
scope: "user"
daemon_reload: true
+11
View File
@@ -99,3 +99,14 @@
changed_when: false
listen: "notification_restart_alloy"
ignore_errors: true # noqa: ignore-errors
- name: Restart kopia server
ansible.builtin.systemd:
name: "kopia-server.service"
state: "restarted"
enabled: true
scope: "user"
daemon_reload: true
changed_when: false
listen: "notification_restart_kopia_server"
ignore_errors: true # noqa: ignore-errors
@@ -1,16 +1,16 @@
---
- name: Check app data volume filesystem already exists
ansible.builtin.command:
cmd: "blkid /dev/{{ vm['data_storage']['name'] }}"
register: is_app_data
- name: Check data volume filesystem already exists
ansible.builtin.command: |
blkid /dev/{{ vm['data_storage']['name'] }}
register: is_volume
changed_when: false
failed_when: false
become: true
- name: Create ext4 filesystem on app data raw
ansible.builtin.command:
cmd: "mkfs.ext4 -F /dev/{{ vm['data_storage']['name'] }}"
when: is_app_data.rc != 0
- name: Create ext4 filesystem on data raw
ansible.builtin.command: |
mkfs.ext4 -F /dev/{{ vm['data_storage']['name'] }}
when: is_volume.rc != 0
changed_when: true
become: true
@@ -22,7 +22,7 @@
group: "{{ node['group'] }}"
mode: "0770"
- name: Mount app data volume
- name: Mount data volume
ansible.posix.mount:
path: "{{ node['home_path'] }}/data"
src: "/dev/{{ vm['data_storage']['name'] }}"
@@ -31,7 +31,7 @@
fstype: "ext4"
become: true
- name: Set app data volume path permissions
- name: Set data volume path permissions
ansible.builtin.file:
path: "{{ node['home_path'] }}/data"
state: "directory"
@@ -9,8 +9,8 @@
become: true
- name: Deploy timesyncd.conf.d/local-ntp.conf
ansible.builtin.copy:
src: "{{ hostvars['console']['node']['config_path'] }}/node/common/timesyncd/local-ntp.conf"
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/node/common/timesyncd/local-ntp.conf.j2"
dest: "/etc/systemd/timesyncd.conf.d/local-ntp.conf"
owner: "root"
group: "systemd-timesync"
@@ -1,5 +1,5 @@
---
- name: Gather ystem facts (hardware)
- name: Gather system facts (hardware)
ansible.builtin.setup:
gather_subset:
- hardware
@@ -78,7 +78,7 @@
- "{{ node['home_path'] }}"
- "{{ node['home_path'] }}/data"
become: true
when: node['name'] == "app"
when: node['name'] in ["bk", "app"]
- name: Enable alloy service
ansible.builtin.systemd:
@@ -88,9 +88,16 @@
daemon_reload: true
become: true
- name: Check alloy.deb on console
delegate_to: "console"
ansible.builtin.stat:
path: "{{ hostvars['console']['node']['data_path'] }}/bin/alloy-{{ version['packages']['alloy'] }}.deb"
register: is_console_alloy_deb
- name: Fetch deb bin file
ansible.builtin.fetch:
src: "/var/cache/apt/archives/alloy-{{ version['packages']['alloy'] }}.deb"
dest: "{{ hostvars['console']['node']['data_path'] }}/bin/"
flat: true
when: not is_console_alloy_deb.stat.exists
become: true
@@ -1,5 +1,48 @@
---
- name: Initialize Caddy services list
ansible.builtin.set_fact:
caddy_services: []
# infra, auth, app (vmm, fw has no podman in it)
- name: Set Caddy infra services list
ansible.builtin.set_fact:
caddy_services: >-
{{
services
| dict2items
| selectattr('value.dependency.web', 'defined')
| selectattr('value.dependency.web', 'in', ['infra-internal', 'infra-public'])
| map(attribute='key')
| list
}}
when: node['name'] == 'infra'
- name: Set Caddy auth services list
ansible.builtin.set_fact:
caddy_services: >-
{{
services
| dict2items
| selectattr('value.dependency.web', 'defined')
| selectattr('value.dependency.web', 'in', ['auth-public', 'infra-public', 'app-public'])
| map(attribute='key')
| list
}}
when: node['name'] == 'auth'
- name: Set Caddy app services list
ansible.builtin.set_fact:
caddy_services: >-
{{
services
| dict2items
| selectattr('value.dependency.web', 'defined')
| selectattr('value.dependency.web', 'in', ['app-internal', 'app-public'])
| map(attribute='key')
| list
}}
when: node['name'] == 'app'
- name: Create caddy directory
ansible.builtin.file:
path: "{{ node['home_path'] }}/containers/{{ item }}"
@@ -50,6 +93,8 @@
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0640"
register: "is_caddy_containerfile"
notify: "notification_restart_caddy"
- name: Deploy root crt for build
ansible.builtin.copy:
@@ -58,6 +103,8 @@
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0640"
register: "is_caddy_root_crt"
notify: "notification_restart_caddy"
no_log: true
- name: Build caddy container image
@@ -67,6 +114,7 @@
tag: "{{ version['containers']['caddy'] }}"
state: "build"
path: "{{ node['home_path'] }}/containers/caddy/build"
force: "{{ is_caddy_root_crt.changed or is_caddy_containerfile.changed }}"
- name: Prune caddy dangling images
containers.podman.podman_prune:
@@ -74,7 +122,7 @@
- name: Deploy caddyfile
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/common/caddy/etc/{{ node['name'] }}/Caddyfile.j2"
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/common/caddy/etc/Caddyfile.j2"
dest: "{{ node['home_path'] }}/containers/caddy/etc/Caddyfile"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
@@ -109,8 +157,8 @@
- "{{ domain['internal'] }}/{{ node['name'] }}/caddy:{{ version['containers']['caddy'] }}"
dest: "{{ node['home_path'] }}/archives/containers/{{ domain['internal'] }}_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
format: "oci-archive"
force: false
when: not container_archive_images.stat.exists
force: "{{ is_caddy_root_crt.changed or is_caddy_containerfile.changed }}"
when: not container_archive_images.stat.exists or is_caddy_root_crt.changed or is_caddy_containerfile.changed
- name: Fetch container archive images
ansible.builtin.fetch:
@@ -120,8 +120,8 @@
no_log: true
- name: Get existing machines list
ansible.builtin.command:
cmd: "cscli machines list -o json"
ansible.builtin.command: |
cscli machines list -o json
become: true
changed_when: false
register: "existing_crowdsec_machines_list"
@@ -132,27 +132,27 @@
- name: Set goal machines' name
ansible.builtin.set_fact:
machines_name: ["fw", "vmm", "infra", "auth", "app"]
machines_name: ["fw", "vmm", "bk", "infra", "auth", "app"]
no_log: true
- name: Prune unknown (random) machines
ansible.builtin.command:
cmd: "cscli machines delete {{ item }}"
ansible.builtin.command: |
cscli machines delete {{ item }}
loop: "{{ existing_machines_name | difference(machines_name) }}"
become: true
changed_when: true
- name: Register crowdsec machines to LAPI server
ansible.builtin.command:
cmd: "cscli machines add {{ item }} --password {{ hostvars['console']['crowdsec']['machine'][item] }} --force -f /dev/null"
ansible.builtin.command: |
cscli machines add {{ item }} --password {{ hostvars['console']['crowdsec']['machine'][item] }} --force -f /dev/null
loop: "{{ machines_name }}"
become: true
changed_when: false
no_log: true
- name: Get existing bouncers list
ansible.builtin.command:
cmd: "cscli bouncers list -o json"
ansible.builtin.command: |
cscli bouncers list -o json
become: true
register: "existing_crowdsec_bouncers_list"
changed_when: false
@@ -162,8 +162,8 @@
existing_bouncers_name: "{{ existing_crowdsec_bouncers_list.stdout | from_json | map(attribute='name') | list }}"
- name: Flush bouncers
ansible.builtin.command:
cmd: "cscli bouncers delete {{ item }}"
ansible.builtin.command: |
cscli bouncers delete {{ item }}
loop: "{{ existing_bouncers_name }}"
become: true
changed_when: true
@@ -173,8 +173,8 @@
bouncers_name: ["fw", "caddy"]
- name: Register Firewall Bouncer to LAPI
ansible.builtin.command:
cmd: "cscli bouncers add {{ item }}-bouncer -k {{ hostvars['console']['crowdsec']['bouncer'][item] }}"
ansible.builtin.command: |
cscli bouncers add {{ item }}-bouncer -k {{ hostvars['console']['crowdsec']['bouncer'][item] }}
loop: "{{ bouncers_name }}"
become: true
changed_when: true
@@ -185,8 +185,8 @@
when: node['name'] in acquisd_list
block:
- name: Install crowdsec collection
ansible.builtin.command:
cmd: "cscli collections install {{ item }}"
ansible.builtin.command: |
cscli collections install {{ item }}
loop: "{{ acquisd_list[node['name']]['collection'] }}"
become: true
changed_when: "'overwrite' not in is_collection_installed.stderr"
@@ -196,8 +196,8 @@
register: "is_collection_installed"
- name: Install crowdsec parser
ansible.builtin.command:
cmd: "cscli parsers install {{ item }}"
ansible.builtin.command: |
cscli parsers install {{ item }}
loop: "{{ acquisd_list[node['name']]['parser'] }}"
become: true
changed_when: "'overwrite' not in is_parser_installed.stderr"
@@ -5,50 +5,288 @@
- hardware
become: true
- name: Set console kopia
when: node['name'] == 'console'
- name: Set backup kopia repository server
when: node['name'] == "bk"
block:
- name: Download kopia
- name: Download kopia deb file (x86_64)
ansible.builtin.get_url:
url: "https://github.com/kopia/kopia/releases/download/v{{ version['packages']['kopia'] }}/\
kopia_{{ version['packages']['kopia'] }}_linux_{{ item }}.deb"
dest: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-{{ item }}.deb"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0600"
loop:
- "amd64"
- "arm64"
- name: Install kopia (x86_64)
ansible.builtin.apt:
deb: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-amd64.deb"
state: "present"
kopia_{{ version['packages']['kopia'] }}_linux_amd64.deb"
dest: "/var/cache/apt/archives/kopia-{{ version['packages']['kopia'] }}.deb"
owner: "root"
group: "root"
mode: "0644"
become: true
when: ansible_facts['architecture'] == "x86_64"
- name: Install kopia (aarch64)
ansible.builtin.apt:
deb: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-arm64.deb"
state: "present"
- name: Download kopia deb file (aarch64)
ansible.builtin.get_url:
url: "https://github.com/kopia/kopia/releases/download/v{{ version['packages']['kopia'] }}/\
kopia_{{ version['packages']['kopia'] }}_linux_arm64.deb"
dest: "/var/cache/apt/archives/kopia-{{ version['packages']['kopia'] }}.deb"
owner: "root"
group: "root"
mode: "0644"
become: true
when: ansible_facts['architecture'] == "aarch64"
- name: Connect console kopia server
environment:
KOPIA_PASSWORD: "{{ hostvars['console']['kopia']['users']['console'] }}"
ansible.builtin.shell: |
/usr/bin/kopia repository connect server \
--url=https://{{ services['kopia']['domain'] }}.{{ domain['internal'] }}:{{ services['kopia']['ports']['https'] }} \
--override-username=console \
--override-hostname=console.{{ domain['internal'] }}
changed_when: false
failed_when: is_kopia_connected.rc != 0
register: "is_kopia_connected"
- name: Create kopia directory
ansible.builtin.file:
path: "{{ item.name }}"
state: "directory"
owner: "{{ ansible_user }}"
group: "root"
mode: "{{ item.mode }}"
loop:
- name: "/etc/kopia"
mode: "0700"
- name: "/etc/kopia/ssl"
mode: "0700"
- name: "/var/cache/kopia"
mode: "0700"
become: true
no_log: true
- name: Set infra/app kopia
when: node['name'] in ['infra', 'app']
- name: Install kopia
ansible.builtin.apt:
deb: "/var/cache/apt/archives/kopia-{{ version['packages']['kopia'] }}.deb"
state: "present"
become: true
- name: Deploy kopia env
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/common/kopia/kopia.env.j2"
dest: "/etc/secrets/{{ node['uid'] }}/kopia.env"
owner: "{{ ansible_user }}"
group: "root"
mode: "0400"
become: true
notify: "notification_restart_kopia_server"
no_log: true
- name: Deploy kopia certificate and key
ansible.builtin.copy:
content: |
{{ item.value }}
dest: "/etc/kopia/ssl/{{ item.name }}"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "{{ item.mode }}"
loop:
- name: "kopia.crt"
value: |
{{ hostvars['console']['kopia']['crt'] | trim }}
{{ hostvars['console']['ca']['intermediate']['crt'] }}
mode: "0440"
- name: "kopia.key"
value: "{{ hostvars['console']['kopia']['key'] }}"
mode: "0400"
notify: "notification_restart_kopia_server"
no_log: true
- name: Check repository existance
ansible.builtin.stat:
path: "{{ node['home_path'] }}/data/kopia/repository"
register: "is_kopia_repository"
- name: Create repository directory
ansible.builtin.file:
path: "{{ node['home_path'] }}/data/kopia/repository"
state: "directory"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0770"
when: not is_kopia_repository.stat.exists
- name: Create kopia repository
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia repository create filesystem \
--path="{{ node['home_path'] }}/data/kopia/repository" \
--description="ilnmors.internal kopia repository" \
--create-only
when: not is_kopia_repository.stat.exists
changed_when: true
register: "is_repository_created"
no_log: true
- name: Connect to kopia repository
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia repository connect filesystem \
--path="{{ node['home_path'] }}/data/kopia/repository" \
--override-username="il" \
--override-hostname="{{ node['name'] }}.{{ domain['internal'] }}" \
when:
- not is_kopia_repository.stat.exists
- is_repository_created.rc == 0
changed_when: true
register: "is_repository_connected"
no_log: true
- name: Create kopia user
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia server users add \
--user-password {{ item.password | quote }} \
{{ item.name }}
loop:
- name: "console@console.ilnmors.internal"
password: "{{ hostvars['console']['kopia']['users']['console'] }}"
- name: "infra@infra.ilnmors.internal"
password: "{{ hostvars['console']['kopia']['users']['infra'] }}"
- name: "app@app.ilnmors.internal"
password: "{{ hostvars['console']['kopia']['users']['app'] }}"
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
register: "is_user_created"
no_log: true
- name: Set kopia console ACL
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia server acl add \
--user console@console.ilnmors.internal \
--target {{ item }} \
--access FULL
loop:
- "type=snapshot"
- "type=policy"
- "type=user"
- "type=acl"
- "type=content"
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
no_log: true
- name: Set kopia infra ACL
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia server acl add \
--user infra@infra.ilnmors.internal \
--target {{ item.target }} \
--access {{ item.access }}
loop:
- target: "type=snapshot"
access: "FULL"
- target: "type=policy"
access: "READ"
- target: "type=policy,username=infra,hostname=infra.ilnmors.internal"
access: "FULL"
- target: "type=content"
access: "APPEND"
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
no_log: true
- name: Set kopia app ACL
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia server acl add \
--user app@app.ilnmors.internal \
--target {{ item.target }} \
--access {{ item.access }}
loop:
- target: "type=snapshot"
access: "FULL"
- target: "type=policy"
access: "READ"
- target: "type=policy,username=app,hostname=app.ilnmors.internal"
access: "FULL"
- target: "type=content"
access: "APPEND"
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
no_log: true
- name: Set kopia policy
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia policy set \
{{ item.content }}
loop:
- content: |
--global \
--compression=zstd
- content: |
infra@infra.ilnmors.internal \
--compression=zstd \
--keep-latest=10 \
--keep-hourly=0 \
--keep-daily=14 \
--keep-weekly=4 \
--keep-monthly=6 \
--keep-annual=1
- content: |
app@app.ilnmors.internal \
--compression=none \
--keep-latest=10 \
--keep-hourly=0 \
--keep-daily=14 \
--keep-weekly=4 \
--keep-monthly=6 \
--keep-annual=1
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
no_log: true
- name: Set kopia parameters
ansible.builtin.shell: |
set -a
. /etc/secrets/{{ node['uid'] }}/kopia.env
set +a
/usr/bin/kopia repository set-parameters \
--max-pack-size-mb=100
when:
- not is_kopia_repository.stat.exists
- is_repository_connected.rc == 0
changed_when: true
no_log: true
- name: Deploy kopia servcer service file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/common/kopia/kopia-server.service.j2"
dest: "{{ node['home_path'] }}/.config/systemd/user/kopia-server.service"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0644"
validate: "/usr/bin/systemd-analyze verify %s"
become: true
- name: Enable kopia server service
ansible.builtin.systemd:
name: "kopia-server.service"
state: "started"
enabled: true
scope: "user"
daemon_reload: true
- name: Set infra/app backup kopia
when: node['name'] in ["infra", "app"]
block:
- name: Set kopia uid
ansible.builtin.set_fact:
@@ -127,7 +365,7 @@
become: true
no_log: true
- name: Deploy kopia service files
- name: Deploy kopia backup service files
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/common/kopia/{{ item }}.j2"
dest: "/etc/systemd/system/{{ item }}"
@@ -140,10 +378,52 @@
- "kopia-backup.timer"
become: true
- name: Enable auto kopia rules update
- name: Enable Kopia backup timer
ansible.builtin.systemd:
name: "kopia-backup.timer"
state: "started"
enabled: true
daemon_reload: true
become: true
- name: Set console kopia
when: node['name'] == "console"
block:
- name: Download kopia
ansible.builtin.get_url:
url: "https://github.com/kopia/kopia/releases/download/v{{ version['packages']['kopia'] }}/\
kopia_{{ version['packages']['kopia'] }}_linux_{{ item }}.deb"
dest: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-{{ item }}.deb"
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0600"
loop:
- "amd64"
- "arm64"
- name: Install kopia (x86_64)
ansible.builtin.apt:
deb: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-amd64.deb"
state: "present"
become: true
when: ansible_facts['architecture'] == "x86_64"
- name: Install kopia (aarch64)
ansible.builtin.apt:
deb: "{{ node['data_path'] }}/bin/kopia-{{ version['packages']['kopia'] }}-arm64.deb"
state: "present"
become: true
when: ansible_facts['architecture'] == "aarch64"
- name: Connect console kopia server
environment:
KOPIA_PASSWORD: "{{ hostvars['console']['kopia']['users']['console'] }}"
ansible.builtin.shell: |
/usr/bin/kopia repository connect server \
--url=https://{{ services['kopia']['domain']['internal'] }}.{{ domain['internal'] }}:443 \
--override-username=console \
--override-hostname=console.{{ domain['internal'] }}
changed_when: false
failed_when: is_kopia_connected.rc != 0
register: "is_kopia_connected"
no_log: true
@@ -16,7 +16,7 @@
environment:
SOPS_AGE_KEY: "{{ hostvars['console']['age_key'] }}"
ansible.builtin.command: |
sops -d --output-type yaml {{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml
sops -d --output-type yaml "{{ hostvars['console']['node']['config_path'] }}/secrets/secrets.yaml"
changed_when: false
register: "decrypted_secrets"
run_once: true
@@ -24,16 +24,20 @@
notify: "notification_restart_ca"
no_log: true
- name: Deploy ca config files
- name: Pull image
containers.podman.podman_image:
name: "docker.io/smallstep/step-ca"
tag: "{{ version['containers']['step'] }}"
state: "present"
pull: true
- name: Deploy ca config file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/ca/config/{{ item }}.j2"
dest: "{{ node['home_path'] }}/containers/ca/config/{{ item }}"
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/ca/config/ca.json.j2"
dest: "{{ node['home_path'] }}/containers/ca/config/ca.json"
owner: "{{ services['ca']['subuid'] }}"
group: "{{ node['group'] }}"
mode: "0400"
loop:
- "ca.json"
- "defaults.json"
become: true
notify: "notification_restart_ca"
@@ -68,6 +68,13 @@
become: true
notify: "notification_restart_grafana"
- name: Pull image
containers.podman.podman_image:
name: "docker.io/grafana/grafana"
tag: "{{ version['containers']['grafana'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/grafana/grafana.container.j2"
@@ -46,7 +46,8 @@
# urlencode doesn't fix `/` as `%2F`. It needs replace
- name: "LLDAP_DATABASE_URL"
value: "postgres://ldap:{{ hostvars['console']['postgresql']['password']['ldap'] | urlencode | replace('/', '%2F') }}\
@{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}/ldap_db?sslmode=verify-full&sslrootcert=/etc/ssl/ldap/{{ root_cert_filename }}"
@{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}/\
ldap_db?sslmode=verify-full&sslrootcert=/etc/ssl/ldap/{{ root_cert_filename }}"
- name: "LLDAP_KEY_SEED"
value: "{{ hostvars['console']['ldap']['seed_key'] }}"
- name: "LLDAP_JWT_SECRET"
@@ -93,6 +94,13 @@
name: "LLDAP_LDAP_USER_PASSWORD"
state: "absent"
- name: Pull image
containers.podman.podman_image:
name: "docker.io/lldap/lldap"
tag: "{{ version['containers']['ldap'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/ldap/ldap.container.j2"
@@ -48,6 +48,13 @@
notify: "notification_restart_loki"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/grafana/loki"
tag: "{{ version['containers']['loki'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/loki/loki.container.j2"
@@ -1,19 +1,19 @@
---
- name: Initialize connected services list
ansible.builtin.set_fact:
connected_services: []
- name: Set connected services list
ansible.builtin.set_fact:
connected_services:
- "ldap"
- "grafana"
- "authelia"
- "vaultwarden"
- "gitea"
- "immich"
- "paperless"
- "nextcloud"
- "sure"
- "outline"
- "memos"
- "mealie"
connected_services: >-
{{
services
| dict2items
| selectattr('value.dependency.db', 'defined')
| selectattr('value.dependency.db', 'equalto', 'postgresql')
| map(attribute='key')
| list
}}
- name: Create postgresql directory
ansible.builtin.file:
@@ -39,6 +39,8 @@
owner: "{{ ansible_user }}"
group: "{{ node['group'] }}"
mode: "0640"
register: "is_postgresql_containerfile"
notify: "notification_restart_postgresql"
- name: Build postgresql container image
containers.podman.podman_image:
@@ -47,6 +49,7 @@
tag: "pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}"
state: "build"
path: "{{ node['home_path'] }}/containers/postgresql/build"
force: "{{ is_postgresql_containerfile.changed }}"
- name: Prune postgresql dangling images
containers.podman.podman_prune:
@@ -96,16 +99,33 @@
become: true
register: "is_postgresql_init"
- name: Check init directory empty
ansible.builtin.find:
path: "{{ node['home_path'] }}/containers/postgresql/init/"
file_type: "any"
patterns:
- "*.sql"
become: true
register: "is_init_dir_empty"
- name: Check pg_cluster.sql on console
delegate_to: "console"
ansible.builtin.stat:
path: "{{ hostvars['console']['node']['data_path'] }}/sql/postgresql/pg_cluster.sql"
register: is_console_pg_cluster_sql
- name: Prepare initiating DB
when:
- not is_postgresql_init.stat.exists
- is_init_dir_empty.matched == 0
- is_console_pg_cluster_sql.stat.exists
- (connected_services | default([]) | length) > 0
become: true
block:
# `init/pg_cluster.sql` should be fetched from postgresql's backup directory before running initiating
- name: Deploy init cluster sql file
ansible.builtin.copy:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/postgresql/init/pg_cluster.sql"
src: "{{ hostvars['console']['node']['data_path'] }}/sql/postgresql/pg_cluster.sql"
dest: "{{ node['home_path'] }}/containers/postgresql/init/00_pg_cluster.sql"
owner: "{{ services['postgresql']['subuid'] }}"
group: "{{ node['group'] }}"
@@ -113,7 +133,7 @@
- name: Deploy restoring data sql files
ansible.builtin.copy:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/postgresql/init/pg_{{ item }}.sql"
src: "{{ hostvars['console']['node']['data_path'] }}/sql/postgresql/pg_{{ item }}.sql"
dest: "{{ node['home_path'] }}/containers/postgresql/init/{{ '%02d' | format(index_num + 1) }}_pg_{{ item }}.sql"
owner: "{{ services['postgresql']['subuid'] }}"
group: "{{ node['group'] }}"
@@ -192,8 +212,8 @@
{{ domain['internal'] }}_{{ node['name'] }}_postgres_\
pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
format: "oci-archive"
force: false
when: not container_archive_images.stat.exists
force: "{{ is_postgresql_containerfile.changed }}"
when: not container_archive_images.stat.exists or is_postgresql_containerfile.changed
- name: Fetch container archive images
ansible.builtin.fetch:
@@ -52,6 +52,13 @@
notify: "notification_restart_prometheus"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/prom/prometheus"
tag: "{{ version['containers']['prometheus'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/prometheus/prometheus.container.j2"
@@ -1,4 +1,20 @@
---
- name: Initialize self-cert services list
ansible.builtin.set_fact:
cert_services: []
- name: Set self-cert services list
ansible.builtin.set_fact:
cert_services: >-
{{
services
| dict2items
| selectattr('value.dependency.cert', 'defined')
| selectattr('value.dependency.cert', 'equalto', true)
| map(attribute='key')
| list
}}
- name: Create x509-exporter directory
ansible.builtin.file:
path: "{{ node['home_path'] }}/containers/{{ item }}"
@@ -22,7 +38,7 @@
become: true
notify: "notification_restart_x509-exporter"
- name: Deploy certificates
- name: Deploy CA certificates
ansible.builtin.copy:
content: |
{{ item.value }}
@@ -35,23 +51,31 @@
value: "{{ hostvars['console']['ca']['root']['crt'] }}"
- name: "intermediate.crt"
value: "{{ hostvars['console']['ca']['intermediate']['crt'] }}"
- name: "crowdsec.crt"
value: "{{ hostvars['console']['crowdsec']['crt'] }}"
- name: "blocky.crt"
value: "{{ hostvars['console']['blocky']['crt'] }}"
- name: "postgresql.crt"
value: "{{ hostvars['console']['postgresql']['crt'] }}"
- name: "ldap.crt"
value: "{{ hostvars['console']['ldap']['crt'] }}"
- name: "prometheus.crt"
value: "{{ hostvars['console']['prometheus']['crt'] }}"
- name: "loki.crt"
value: "{{ hostvars['console']['loki']['crt'] }}"
- name: "dsm.crt"
value: "{{ hostvars['console']['dsm']['crt'] }}"
become: true
notify: "notification_restart_x509-exporter"
no_log: true
- name: Deploy leaf certificates
ansible.builtin.copy:
content: |
{{ hostvars['console'][item]['crt'] }}
dest: "{{ node['home_path'] }}/containers/x509-exporter/certs/{{ item }}.crt"
owner: "{{ services['x509-exporter']['subuid'] }}"
group: "{{ node['group'] }}"
mode: "0440"
loop: "{{ cert_services }}"
become: true
notify: "notification_restart_x509-exporter"
no_log: true
- name: Pull image
containers.podman.podman_image:
name: "docker.io/enix/x509-certificate-exporter"
tag: "{{ version['containers']['x509-exporter'] }}"
state: "present"
pull: true
- name: Deploy container file
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/x509-exporter/x509-exporter.container.j2"
+179
View File
@@ -0,0 +1,179 @@
---
- name: Check btrfs installation
ansible.builtin.shell: |
command -v btrfs
become: true # btrfs is located in /usr/sbin, which means root permission is needed.
changed_when: false
failed_when: false
register: "is_btrfs_installed"
- name: Install btrfs
ansible.builtin.apt:
name: "btrfs-progs"
state: "present"
become: true
when: is_btrfs_installed.rc != 0
- name: Mount data disk
block:
- name: Set hard data disk path
ansible.builtin.shell: |
set -o pipefail
ls -1 /dev/disk/by-id/ata-* | \
grep -F -e "{{ hardware['disk']['data']['serials'] | join('" -e "') }}" | \
grep -vE -- '-part[0-9]+$' | \
sort
changed_when: false
register: "data_disk_path"
- name: Check vmm_hdd filesystem already exists
ansible.builtin.command: |
blkid -L {{ hardware['disk']['data']['fs']['label'] }}
become: true
changed_when: false
failed_when: false
register: "is_data_disk_fs"
- name: Check disk number
ansible.builtin.fail:
msg: "Below 4 disks for RAID10, found {{ data_disk_path.stdout_lines | length }}"
when: (data_disk_path.stdout_lines | length) < hardware['disk']['data']['fs']['btrfs']['min_devices']
- name: Set btrfs raid10 volume
ansible.builtin.shell: |
mkfs.btrfs -f \
-L {{ hardware['disk']['data']['fs']['label'] }} \
-d {{ hardware['disk']['data']['fs']['btrfs']['data_profile'] }} \
-m {{ hardware['disk']['data']['fs']['btrfs']['metadata_profile'] }} \
{{ data_disk_path.stdout_lines | join(' ') }}
become: true
when:
- is_data_disk_fs.rc != 0
- (data_disk_path.stdout_lines | length) >= hardware['disk']['data']['fs']['btrfs']['min_devices']
changed_when: is_mkfs.rc == 0
register: "is_mkfs"
- name: Create mount volume
ansible.builtin.file:
path: "{{ hardware['disk']['data']['fs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Mount btrfs raid10 volume
ansible.posix.mount:
path: "{{ hardware['disk']['data']['fs']['mount_point'] }}"
src: "LABEL={{ hardware['disk']['data']['fs']['label'] }}"
state: "mounted"
fstype: "{{ hardware['disk']['data']['fs']['type'] }}"
opts: "{{ hardware['disk']['data']['fs']['mount_opts'] | join(',') }}"
become: true
- name: Set mounted btrfs volume permissions
ansible.builtin.file:
path: "{{ hardware['disk']['data']['fs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Mount backup disk
block:
- name: Set backup disk path
ansible.builtin.shell: |
set -o pipefail
ls -1 /dev/disk/by-id/ata-* | \
grep -F -e "{{ hardware['disk']['backup']['serials'] | join('" -e "') }}" | \
grep -vE -- '-part[0-9]+$' | \
sort
changed_when: false
register: "backup_disk_path"
- name: Check vmm_hdd filesystem already exists
ansible.builtin.command: |
blkid -L {{ hardware['disk']['backup']['fs']['label'] }}
become: true
changed_when: false
failed_when: false
register: "is_backup_disk_fs"
- name: Set btrfs volume
ansible.builtin.shell: |
mkfs.btrfs -f \
-L {{ hardware['disk']['backup']['fs']['label'] }} \
-d {{ hardware['disk']['backup']['fs']['btrfs']['data_profile'] }} \
-m {{ hardware['disk']['backup']['fs']['btrfs']['metadata_profile'] }} \
{{ backup_disk_path.stdout_lines | join(' ') }}
become: true
when:
- is_backup_disk_fs.rc != 0
- (backup_disk_path.stdout_lines | length) >= hardware['disk']['backup']['fs']['btrfs']['min_devices']
changed_when: is_mkfs.rc == 0
register: "is_mkfs"
- name: Create mount volume
ansible.builtin.file:
path: "{{ hardware['disk']['backup']['fs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Mount btrfs volume
ansible.posix.mount:
path: "{{ hardware['disk']['backup']['fs']['mount_point'] }}"
src: "LABEL={{ hardware['disk']['backup']['fs']['label'] }}"
state: "mounted"
fstype: "{{ hardware['disk']['backup']['fs']['type'] }}"
opts: "{{ hardware['disk']['backup']['fs']['mount_opts'] | join(',') }}"
become: true
- name: Set mounted btrfs volume permissions
ansible.builtin.file:
path: "{{ hardware['disk']['backup']['fs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Verify data disk is mounted
ansible.builtin.command: |
mountpoint -q {{ hardware['disk']['data']['fs']['mount_point'] }}
become: true
changed_when: false
- name: Verify backup disk is mounted
ansible.builtin.command: |
mountpoint -q {{ hardware['disk']['backup']['fs']['mount_point'] }}
become: true
changed_when: false
- name: Deploy btrfs scrub service and timer
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/vmm/btrfs/btrfs-scrub-{{ item }}.j2"
dest: "/etc/systemd/system/btrfs-scrub-{{ item }}"
owner: "root"
group: "root"
mode: "0644"
loop:
- "data.service"
- "data.timer"
- "backup.service"
- "backup.timer"
become: true
- name: Enable auto btrfs scrub
ansible.builtin.systemd:
name: "btrfs-scrub-{{ item }}.timer"
state: "started"
enabled: true
daemon_reload: true
loop:
- "data"
- "backup"
become: true
@@ -13,7 +13,9 @@
loop:
- "images"
- "seeds"
- "data"
- "disk"
- "disk/data"
- "disk/backup"
register: "is_libvirt_dir"
- name: Create libvirt directory
@@ -26,7 +28,6 @@
loop: "{{ is_libvirt_dir.results }}"
when: not item.stat.exists
become: true
no_log: true
- name: Set LIBVIRT_DEFAULT_URI
ansible.builtin.lineinfile:
@@ -74,6 +75,7 @@
- "images-pool"
- "seeds-pool"
- "data-pool"
- "backup-pool"
- name: Start virtual storage pool
community.libvirt.virt_pool:
@@ -84,6 +86,7 @@
- "images-pool"
- "seeds-pool"
- "data-pool"
- "backup-pool"
- name: Autostart virtual storage pool
community.libvirt.virt_pool:
@@ -94,3 +97,4 @@
- "images-pool"
- "seeds-pool"
- "data-pool"
- "backup-pool"
-100
View File
@@ -1,100 +0,0 @@
---
- name: Check btrfs installation
ansible.builtin.shell: |
command -v btrfs
become: true # btrfs is located in /usr/sbin, which means root permission is needed.
changed_when: false
failed_when: false
register: "is_btrfs_installed"
ignore_errors: true
- name: Install btrfs
ansible.builtin.apt:
name: "btrfs-progs"
state: "present"
become: true
when: is_btrfs_installed.rc != 0
- name: Set hard disk path
ansible.builtin.shell: |
set -o pipefail
ls -1 /dev/disk/by-id/ata-* | \
grep -F -e "{{ hardware['hdd']['data'].values() | join('" -e "') }}" | \
grep -vE -- '-part[0-9]+$' | \
sort
changed_when: false
register: "hdd_path_list"
- name: Check vmm_hdd filesystem already exists
ansible.builtin.command: |
blkid -L {{ hardware['btrfs']['label'] }}
register: is_vmm_data
changed_when: false
failed_when: false
become: true
- name: Check disk number
ansible.builtin.fail:
msg: "Below 4 disks for RAID10, found {{ hdd_path_list.stdout_lines | length }}"
when: (hdd_path_list.stdout_lines | length) < 4
- name: Set btrfs raid10 volume
ansible.builtin.shell: |
mkfs.btrfs -f \
-L {{ hardware['btrfs']['label'] }} \
-d {{ hardware['btrfs']['level'] }} \
-m {{ hardware['btrfs']['level'] }} \
{{ hdd_path_list.stdout_lines | join(' ') }}
become: true
when:
- is_vmm_data.rc != 0
- (hdd_path_list.stdout_lines | length) >= 4
changed_when: is_mkfs.rc == 0
register: "is_mkfs"
- name: Create mount volume
ansible.builtin.file:
path: "{{ hardware['btrfs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Mount btrfs raid10 volume
ansible.posix.mount:
path: "{{ hardware['btrfs']['mount_point'] }}"
src: "LABEL={{ hardware['btrfs']['label'] }}"
state: "mounted"
fstype: "btrfs"
opts: "defaults,noatime,nodatacow,degraded,nofail"
become: true
- name: Set mounted btrfs volume permissions
ansible.builtin.file:
path: "{{ hardware['btrfs']['mount_point'] }}"
state: "directory"
owner: "root"
group: "root"
mode: "0711"
become: true
- name: Deploy btrfs scrub service and timer
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/vmm/btrfs/{{ item }}.j2"
dest: "/etc/systemd/system/{{ item }}"
owner: "root"
group: "root"
mode: "0644"
loop:
- "btrfs-scrub.service"
- "btrfs-scrub.timer"
become: true
- name: Enable auto btrfs scrub
ansible.builtin.systemd:
name: "btrfs-scrub.timer"
state: "started"
enabled: true
daemon_reload: true
become: true
+1 -2
View File
@@ -49,8 +49,7 @@
register: "is_seediso"
- name: Create seed.iso
ansible.builtin.shell:
cmd: |
ansible.builtin.shell: |
cloud-localds -N {{ hostvars['console']['node']['data_path'] }}/images/seeds/{{ target_vm }}/network-config \
{{ hostvars['console']['node']['data_path'] }}/images/seeds/{{ target_vm }}/seed.iso \
{{ hostvars['console']['node']['data_path'] }}/images/seeds/{{ target_vm }}/user-data \
+12 -12
View File
@@ -28,8 +28,8 @@
become: true
- name: Check deployed cloud-init file info
ansible.builtin.command:
cmd: "qemu-img info {{ node['libvirt_path'] }}/images/{{ target_vm }}.qcow2 --output json"
ansible.builtin.command: |
qemu-img info {{ node['libvirt_path'] }}/images/{{ target_vm }}.qcow2 --output json
changed_when: false
failed_when:
- deployed_cloudfile_info.rc != 0
@@ -37,29 +37,29 @@
register: "deployed_cloudfile_info"
- name: Resize deployed cloud-init file
ansible.builtin.command:
cmd: "qemu-img resize {{ node['libvirt_path'] }}/images/{{ target_vm }}.qcow2 {{ hostvars[target_vm]['vm']['storage'] }}G"
ansible.builtin.command: |
qemu-img resize {{ node['libvirt_path'] }}/images/{{ target_vm }}.qcow2 {{ hostvars[target_vm]['vm']['storage'] }}G
when:
- deployed_cloudfile_info.rc == 0
- (deployed_cloudfile_info.stdout | from_json)['virtual-size'] < (hostvars[target_vm]['vm']['storage'] | int * 1024 * 1024 * 1024)
changed_when: true
- name: Check app data raw image
- name: Check bk/app raw image
ansible.builtin.stat:
path: "{{ hardware['btrfs']['mount_point'] }}/{{ target_vm }}.raw"
path: "{{ hardware['disk'][hostvars[target_vm]['vm']['data_storage']['pool']]['fs']['mount_point'] }}/{{ target_vm }}.raw"
get_checksum: false
get_mime: false
get_attributes: false
when: target_vm == "app"
when: target_vm in ["bk", "app"]
register: "is_app_data"
- name: Create app data raw image
ansible.builtin.command:
# 3.5T
cmd: "qemu-img create -f raw {{ hardware['btrfs']['mount_point'] }}/{{ target_vm }}.raw {{ hostvars[target_vm]['vm']['data_storage']['size'] }}T"
- name: Create bk/app raw image
ansible.builtin.command: |
qemu-img create -f raw {{ hardware['disk'][hostvars[target_vm]['vm']['data_storage']['pool']]['fs']['mount_point'] }}/{{ target_vm }}.raw \
{{ hostvars[target_vm]['vm']['data_storage']['size'] }}T
become: true
when:
- target_vm == "app"
- target_vm in ["bk", "app"]
- not is_app_data.stat.exists
changed_when: true
+3 -3
View File
@@ -8,11 +8,11 @@
command: define
- name: Deploy VM systemd file
ansible.builtin.copy:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/vmm/libvirt/services/{{ target_vm }}.service"
ansible.builtin.template:
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/vmm/libvirt/services/{{ target_vm }}.service.j2"
dest: "{{ node['home_path'] }}/.config/systemd/user/{{ target_vm }}.service"
owner: "{{ ansible_user }}"
group: "svadmins"
group: "{{ node['group'] }}"
mode: "0400"
- name: Register VM service
+48
View File
@@ -0,0 +1,48 @@
#!/usr/sbin/nft -f
flush ruleset
define NET4_SERVER = {{ hostvars['fw']['network4']['subnet']['server'] }}
define NET6_SERVER = {{ hostvars['fw']['network6']['subnet']['server'] }}
define HOSTS4_CONSOLE = { {{ hostvars['fw']['network4']['console'].values() | join(', ') }} }
define HOSTS6_CONSOLE = { {{ hostvars['fw']['network6']['console'].values() | join(', ') }} }
define HOSTS4_INFRA = {{ hostvars['fw']['network4']['infra']['server'] }}
define HOSTS6_INFRA = {{ hostvars['fw']['network6']['infra']['server'] }}
define HOSTS4_APP = {{ hostvars['fw']['network4']['app']['server'] }}
define HOSTS6_APP = {{ hostvars['fw']['network6']['app']['server'] }}
define PORTS_SSH = 22
define PORTS_HTTPS = 443
define PORTS_KOPIA = {{ services['kopia']['ports']['https'] }}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
tcp dport $PORTS_HTTPS dnat to :$PORTS_KOPIA comment "dnat https ports to $PORTS_KOPIA"
}
chain postrouting {
}
chain output {
type nat hook output priority dstnat; policy accept;
oifname "lo" tcp dport $PORTS_HTTPS dnat to :$PORTS_KOPIA comment "dnat https ports to $PORTS_KOPIA out of LOCALHOST"
}
}
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state invalid drop comment "deny invalid connection"
ct state established, related accept comment "allow all connection already existing"
iifname "lo" accept comment "allow local connection: BK > BK"
meta l4proto { icmp, icmpv6 } accept comment "allow icmp connection: BK"
ip saddr $HOSTS4_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv4 ssh connection: CONSOLE > BK"
ip6 saddr $HOSTS6_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv6 ssh connection: CONSOLE > BK"
ip saddr { $HOSTS4_CONSOLE, $HOSTS4_INFRA, $HOSTS4_APP } tcp dport $PORTS_KOPIA ct original proto-dst $PORTS_HTTPS accept comment "allow ipv4 https connection: > BK"
ip6 saddr { $HOSTS6_CONSOLE, $HOSTS6_INFRA, $HOSTS6_APP } tcp dport $PORTS_KOPIA ct original proto-dst $PORTS_HTTPS accept comment "allow ipv6 https connection: > BK"
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
+2 -3
View File
@@ -4,6 +4,7 @@
{% if node['name'] == 'console' %}
# Hosts IPv4
{{ hostvars['fw']['network4']['firewall']['server'] }} fw.{{ domain['internal'] }}
{{ hostvars['fw']['network4']['bk']['server'] }} bk.{{ domain['internal'] }} kopia.{{ domain['internal'] }}
{{ hostvars['fw']['network4']['vmm']['client'] }} init.vmm.{{ domain['internal'] }}
{{ hostvars['fw']['network4']['vmm']['server'] }} vmm.{{ domain['internal'] }}
{{ hostvars['fw']['network4']['infra']['server'] }} infra.{{ domain['internal'] }}
@@ -11,14 +12,12 @@
{{ hostvars['fw']['network4']['app']['server'] }} app.{{ domain['internal'] }}
# Hosts IPv6
{{ hostvars['fw']['network6']['firewall']['server'] }} fw.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['bk']['server'] }} bk.{{ domain['internal'] }} kopia.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['vmm']['client'] }} init.vmm.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['vmm']['server'] }} vmm.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['infra']['server'] }} infra.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['auth']['server'] }} auth.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['app']['server'] }} app.{{ domain['internal'] }}
# kopia repository
{{ hostvars['fw']['network4']['nas']['client'] }} nas.{{ domain['internal'] }}
{{ hostvars['fw']['network6']['nas']['client'] }} nas.{{ domain['internal'] }}
{% else %}
# IPv4
# Crowdsec, blocky, bind(fw)
+2 -2
View File
@@ -1,6 +1,6 @@
[Resolve]
{% if node['name'] in ['vmm', 'fw'] %}
{% if node['name'] in ["vmm", "fw"] %}
DNS=1.1.1.2 1.0.0.2
DNS=2606:4700:4700::1112 2606:4700:4700::1002
{% endif %}
cache=false
Cache=false
+14 -23
View File
@@ -26,10 +26,11 @@ define HOSTS4_BLOCKY = {{ hostvars['fw']['network4']['blocky']['server'] }}
define HOSTS4_BIND = {{ hostvars['fw']['network4']['bind']['server'] }}
define HOSTS4_CONSOLE = { {{ hostvars['fw']['network4']['console'].values() | join(', ') }} }
define HOSTS4_VMM = { {{ hostvars['fw']['network4']['vmm'].values() | join(', ') }} }
define HOSTS4_BK = {{ hostvars['fw']['network4']['bk']['server'] }}
define HOSTS4_INFRA = {{ hostvars['fw']['network4']['infra']['server'] }}
define HOSTS4_AUTH = {{ hostvars['fw']['network4']['auth']['server'] }}
define HOSTS4_APP = {{ hostvars['fw']['network4']['app']['server'] }}
define HOSTS4_NAS = {{ hostvars['fw']['network4']['nas']['client'] }}
define HOSTS4_SWITCH = {{ hostvars['fw']['network4']['switch']['client'] }}
define HOSTS4_PRINTER = {{ hostvars['fw']['network4']['printer']['client'] }}
define HOSTS6_FW = { {{ hostvars['fw']['network6']['firewall'].values() | join(', ') }} }
@@ -37,10 +38,10 @@ define HOSTS6_BLOCKY = {{ hostvars['fw']['network6']['blocky']['server'] }}
define HOSTS6_BIND = {{ hostvars['fw']['network6']['bind']['server'] }}
define HOSTS6_CONSOLE = { {{ hostvars['fw']['network6']['console'].values() | join(', ') }} }
define HOSTS6_VMM = { {{ hostvars['fw']['network6']['vmm'].values() | join(', ') }} }
define HOSTS6_BK = {{ hostvars['fw']['network6']['bk']['server'] }}
define HOSTS6_INFRA = {{ hostvars['fw']['network6']['infra']['server'] }}
define HOSTS6_AUTH = {{ hostvars['fw']['network6']['auth']['server'] }}
define HOSTS6_APP = {{ hostvars['fw']['network6']['app']['server'] }}
define HOSTS6_NAS = {{ hostvars['fw']['network6']['nas']['client'] }}
define PORTS_SSH = 22
define PORTS_WEB = { 80, 443 }
@@ -50,7 +51,6 @@ define PORTS_NTP = 123
define PORTS_VPN = 11290
define PORTS_CROWDSEC = 8080
define PORTS_NAS = { 5000, 5001 }
define PORTS_KOPIA = 51515
table inet nat {
chain prerouting {
@@ -114,12 +114,12 @@ table inet filter {
iifname "lo" accept comment "allow local connection: FW > FW"
udp dport $PORTS_VPN accept comment "allow vpn connection: > FW"
iifname { $IF_CLIENT, $IF_SERVER, $IF_USER, $IF_WG } meta l4proto { icmp, icmpv6 } accept comment "allow icmp, icmpv6 connection: CLIENT/SERVER/USER/WG > FW"
iifname { $IF_CLIENT, $IF_SERVER, $IF_WG } udp dport $PORTS_NTP accept comment "allow ntp connection: CLIENT/SERVER/USER/WG > FW"
iifname { $IF_CLIENT, $IF_SERVER, $IF_WG } udp dport $PORTS_NTP accept comment "allow ntp connection: CLIENT/SERVER/WG > FW"
# Global chain contains "WAN !> :SSH_PORT"
ip saddr $HOSTS4_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv4 ssh connection: CONSOLE > FW"
ip6 saddr $HOSTS6_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv6 ssh connection: CONSOLE > FW"
ip saddr { $HOSTS4_VMM, $HOSTS4_INFRA, $HOSTS4_AUTH, $HOSTS4_APP } tcp dport $PORTS_CROWDSEC accept comment "allow ipv4 crowdsec lapi connection: SERVER > FW"
ip6 saddr { $HOSTS6_VMM, $HOSTS6_INFRA, $HOSTS6_AUTH, $HOSTS6_APP } tcp dport $PORTS_CROWDSEC accept comment "allow ipv6 crowdsec lapi connection: SERVER > FW"
ip saddr { $HOSTS4_VMM, $HOSTS4_BK, $HOSTS4_INFRA, $HOSTS4_AUTH, $HOSTS4_APP } tcp dport $PORTS_CROWDSEC accept comment "allow ipv4 crowdsec lapi connection: SERVER > FW"
ip6 saddr { $HOSTS6_VMM, $HOSTS6_BK, $HOSTS6_INFRA, $HOSTS6_AUTH, $HOSTS6_APP } tcp dport $PORTS_CROWDSEC accept comment "allow ipv6 crowdsec lapi connection: SERVER > FW"
# Global chain contains "WAN !> :DNS_PORT"
ip daddr $HOSTS4_BLOCKY udp dport $PORTS_DNS accept comment "allow ipv4 udp dns connection: !WAN > SERVER BLOCKY(FW)"
ip daddr $HOSTS4_BLOCKY tcp dport $PORTS_DNS accept comment "allow ipv4 tcp dns connection: !WAN > SERVER BLOCKY(FW)"
@@ -140,15 +140,12 @@ table inet filter {
# SSH connection
ip saddr $HOSTS4_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv4 ssh connection: CONSOLE > FW >"
ip6 saddr $HOSTS6_CONSOLE tcp dport $PORTS_SSH accept comment "allow ipv6 ssh connection: CONSOLE > FW >"
# Reverse proxy (WAN)
# Reverse proxy for all
oifname $IF_SERVER ip daddr $HOSTS4_AUTH tcp dport $PORTS_WEB accept comment "allow ipv4 web connection: > FW > SERVER AUTH"
oifname $IF_SERVER ip6 daddr $HOSTS6_AUTH tcp dport $PORTS_WEB accept comment "allow ipv6 web connection: > FW > SERVER AUTH"
# Reverse proxy (SERVER)
oifname $IF_SERVER ip saddr $HOSTS4_CONSOLE ip daddr { $HOSTS4_INFRA, $HOSTS4_APP } tcp dport $PORTS_WEB accept comment "allow ipv4 web connection: CONSOLE > FW > SERVER INFRA/APP"
oifname $IF_SERVER ip6 saddr $HOSTS6_CONSOLE ip6 daddr { $HOSTS6_INFRA, $HOSTS6_APP } tcp dport $PORTS_WEB accept comment "allow ipv6 web connection: CONSOLE > FW > SERVER INFRA/APP"
# Kopia/NAS Console > NAS
oifname $IF_CLIENT ip saddr $HOSTS4_CONSOLE ip daddr $HOSTS4_NAS tcp dport { $PORTS_NAS, $PORTS_KOPIA } accept comment "allow ipv4 web connection (DSM, KOPIA): CONSOLE > FW > CLIENT NAS"
oifname $IF_CLIENT ip6 saddr $HOSTS6_CONSOLE ip6 daddr $HOSTS6_NAS tcp dport { $PORTS_NAS, $PORTS_KOPIA } accept comment "allow ipv6 web connection (DSM, KOPIA): CONSOLE > FW > CLIENT NAS"
# Reverse proxy (SERVER) and Kopia (SERVER) for CONSOLE
oifname $IF_SERVER ip saddr $HOSTS4_CONSOLE ip daddr { $HOSTS4_BK, $HOSTS4_INFRA, $HOSTS4_APP } tcp dport $PORTS_WEB accept comment "allow ipv4 web connection: CONSOLE > FW > SERVER BK/INFRA/APP"
oifname $IF_SERVER ip6 saddr $HOSTS6_CONSOLE ip6 daddr { $HOSTS6_BK, $HOSTS6_INFRA, $HOSTS6_APP } tcp dport $PORTS_WEB accept comment "allow ipv6 web connection: CONSOLE > FW > SERVER BK/INFRA/APP"
# Printer
oifname $IF_CLIENT ip saddr $HOSTS4_CONSOLE ip daddr $HOSTS4_PRINTER accept comment "allow ipv4 printer connection: CONSOLE > FW > PRINTER"
@@ -162,19 +159,13 @@ table inet filter {
return
}
chain client {
oifname $IF_WAN ip saddr { $HOSTS4_CONSOLE, $HOSTS4_NAS } accept comment "allow ipv4 internet connection: CLIENT CONSOLE/NAS > FW > WAN"
oifname $IF_WAN ip6 saddr { $HOSTS6_CONSOLE, $HOSTS6_NAS } accept comment "allow ipv6 internet connection: CLIENT CONSOLE/NAS > FW > WAN"
oifname $IF_WAN ip saddr { $HOSTS4_PRINTER, $HOSTS4_SWITCH } drop comment "deny ipv4 internet connection: CLIENT PRINTER/SWITCH !> FW > WAN"
oifname $IF_WAN accept comment "allow internet connection: CLIENT > FW > WAN"
return
}
chain server {
# reverse proxy AUTH > NAS
oifname $IF_CLIENT ip saddr $HOSTS4_AUTH ip daddr $HOSTS4_NAS tcp dport $PORTS_NAS accept comment "allow ipv4 web connection(DSM): SERVER AUTH > FW > CLIENT NAS"
oifname $IF_CLIENT ip6 saddr $HOSTS6_AUTH ip6 daddr $HOSTS6_NAS tcp dport $PORTS_NAS accept comment "allow ipv6 web connection(DSM): SERVER AUTH > FW > CLIENT NAS"
# Kopia INFRA, APP > NAS
oifname $IF_CLIENT ip saddr { $HOSTS4_INFRA, $HOSTS4_APP } ip daddr $HOSTS4_NAS tcp dport $PORTS_KOPIA accept comment "allow ipv4 web connection(kopia): SERVER INFRA/APP > FW > CLIENT NAS"
oifname $IF_CLIENT ip6 saddr { $HOSTS6_INFRA, $HOSTS6_APP } ip6 daddr $HOSTS6_NAS tcp dport $PORTS_KOPIA accept comment "allow ipv6 web connection(kopia): SERVER INFRA/APP > FW > CLIENT NAS"
oifname $IF_WAN ip saddr { $HOSTS4_VMM, $HOSTS4_INFRA, $HOSTS4_AUTH, $HOSTS4_APP } accept comment "allow ipv4 internet connection: SERVER VMM/INFRA/AUTH/APP > FW > WAN"
oifname $IF_WAN ip6 saddr { $HOSTS6_VMM, $HOSTS6_INFRA, $HOSTS6_AUTH, $HOSTS6_APP } accept comment "allow ipv6 internet connection: SERVER VMM/INFRA/AUTH/APP > FW > WAN"
oifname $IF_WAN ip saddr { $HOSTS4_VMM, $HOSTS4_BK, $HOSTS4_INFRA, $HOSTS4_AUTH, $HOSTS4_APP } accept comment "allow ipv4 internet connection: SERVER VMM/BK/INFRA/AUTH/APP > FW > WAN"
oifname $IF_WAN ip6 saddr { $HOSTS6_VMM, $HOSTS6_BK, $HOSTS6_INFRA, $HOSTS6_AUTH, $HOSTS6_APP } accept comment "allow ipv6 internet connection: SERVER VMM/BK/INFRA/AUTH/APP > FW > WAN"
return
}
chain user {
+48 -16
View File
@@ -7,11 +7,13 @@ sudo:
console: ENC[AES256_GCM,data:RdvzU4VOU+ww1OVvLanD9KbCYom/yPHQ1DdjUBfG6l9nPHEapYqqvHpr6Oi3nDRfc2n5cR4R40i0DHCh01Eo,iv:X8RxKloim0jsDj8U/aY+rHARtrlTrvwNMqMcTmdPfes=,tag:eaMrWFdyU9jifh3SvBrE+A==,type:str]
vmm: ENC[AES256_GCM,data:CqEAeHMgRkmsFYFGOyHic2TGYXlw/1VO4mMAaPSHqzGDoW6u6Gz29uho9EY=,iv:5fc0LkIQ/gVFTK7YEjnmA5ye2EEyh5/61VvTVarGOQA=,tag:yLwK2HchZvGZBFL4ZnxNaw==,type:str]
fw: ENC[AES256_GCM,data:Iz9zt+M+ug6vxzjMAdGuSBSQ16+JrrXpfbsWUpoA184JEV+nC0xZi4eawLI=,iv:4lbQCgFdo5WzY5vFbn9dbkLN6iCoJNJ/5UXw74qOv7s=,tag:eCRh07yL5/D3Kzh6xgmDWg==,type:str]
bk: ENC[AES256_GCM,data:mtpcOaoQzQzweKZG9toTXJ1C2wujyakkEP2axKCNXizKcQX575G42Ye3glU=,iv:SUZJFQbuxUoSB/fyk8NZZplCLG49dIFtnYqcxbXoJsg=,tag:Yy8yF/CwqLZP1cyOrTPJCA==,type:str]
infra: ENC[AES256_GCM,data:2KEo16JyMbcySxkPUA1qvQZP5YnfKlT/lJS8FnFWSoXEL508dwiWslSDPfI=,iv:xLah1BYaNuMlSWUufhdWupzPaXbLVgVwVfXO7dKSCcY=,tag:wqkJdWNf2h5ecSBDf3T8xg==,type:str]
auth: ENC[AES256_GCM,data:Gv73925NEyxQEXmVC3YImOfzj99ZVDH+Jt3iEl0yEN69E/1VeqH67uG5kyQ=,iv:+Dd4kY0nDZIBnB+Ft7e8GD1v9a9NjRjU/QnwnXJLJ/w=,tag:X/hOwq1fxHPPhp5VtENWbw==,type:str]
app: ENC[AES256_GCM,data:/e7LF628qBL1+dPtfqV0myEp9MYBx4s1R6LG+HD9P/7LSsF2qG4eUl885GE=,iv:oU23xu+QTtYbT9/7Z8Q+CrCrpOJXs+Cgstvf8RASEZc=,tag:AvYzJuTKFt+dHm/uca+TGg==,type:str]
hash:
fw: ENC[AES256_GCM,data:cI7ZkkxkNypzTRtChxC0HVp0YxwUHXDlcs+Mm1A2OxuGekr4t2IoXnF3OieQVB8LfjH14iJDhBAQxAjww9O3dsaKy1QQx8zCZ5IabDmBfLEsdhJVGHxTBw6PJHzhlW4+1QAYnxXcWqH6Tg==,iv:s/A8I30BP4PYBf+5lBxBXySx2atYn1MYdclA1Cb/VOY=,tag:tKBs1udztZV0T3/UfAiL5A==,type:str]
bk: ENC[AES256_GCM,data:tdZOY/Cyj0leqPpRqOpoudTLW00RQpzREz9gZ+wvwhcKR2tPA7IrDSSaJTt5EOYrfInwBL1x/Zq+JZ4b9+VWW/9+f8JWN0AW8IuCv+5aZ8b2/TKFM95P9a95n+JpkriwPp9Ca0ZQ8BdqpQ==,iv:NCXVJP0ZYQwNo7BmIeyKnHSIcQuUpQmccFLFRYZ+a1k=,tag:avOW/Xyy1tGSJbtp2vlvnw==,type:str]
infra: ENC[AES256_GCM,data:Ifk0g0CZOIzyTeTgSVlOj5J0TsIBNQTtZYsM+MNsZ/BGhNb/NADotrBRwpgK7Lts13TQ9eNZtcm4VgWVyWVIcM2HJ+1hAKMHcgHsOlaZNZa3vbiGS/X6xvD01vFeNWC1P0A7GDsvNS0B0A==,iv:PbwSvKNl4EmLBJ/la3ePx7hzJodtLGn57XvgM3X3xtc=,tag:Qahp1EARyRqmWr2BZ/pLJw==,type:str]
auth: ENC[AES256_GCM,data:ezk7MNsSRkF/dI8YhKB9CX+M8j876cRCkDqx64mZPaYNIWD7/tpbwqSTuTvoFsGJ9ikh3ZbNA4n3qMxMSsWstckHfcSgjUF56Bz9lCRvNqNCk/tDyj5aqxeHPXrbad4lwrgl0dPNFuPqxg==,iv:JEJnP+HnaxaLWpgsVmsLuTcxTMIA28l/ESCDnJcM1oQ=,tag:flDCxuD3L5IEfAX5BGu+YA==,type:str]
app: ENC[AES256_GCM,data:xRr+kc1cvB2BK6DhQZQ21TvsgIgAxAJlWVMM7fbFTn9HObUIuz+a9MROGnduHKe8dp5bOkL97jlwZvk4qT8n51rYp4YvgKU/CQ7n89MqQO248hJp2MRMRWmom9Gh6HlzY4IHeMhceb/oYQ==,iv:BG4tPfwp5Qa9/H0h517fHjaE/ZIMy4vnAAQ4Fvqj2H8=,tag:meE8ZxXB8E4Pi/bS4dKMZA==,type:str]
@@ -25,8 +27,8 @@ ca:
crt: ENC[AES256_GCM,data:t+yAyGjsbGUFfa/3wKqAHVgV15trtFrFHXRgBN5y1iJNXvqsHzdt4OO07o/d8lT2bh/UXZEHQDmx0VW+gKSouVzr+ZF2ENgXAW+d1cWWup7cLW+oC2LOf22gP3js53dGNS4+qRhPTi5M7XZztsAJgaI3+LAiDMoxoZn8jZZ2OKOB2038w8ZHCDGMUmvCMXJkk5u4o9zf8qLggdPk/MoAtQ4g4/5Q/gJsf5v6+ufZbSXnWWfwtBce7FStb3Bg06RIGG3LtDGzBxvMFpDJe3z0bidJtOlYyAJhOZF/knOTUwVlEt/Jhd9KoxRKYCMETj0eKO88bIyxZx9gmli7OuETWRyK+A8Y7xAdAoZxgeDJZjX4kcPsMy3vkGKSGRKDg+FMwX/PGwUDQmG+UyfmGqZUyc5fJWHKI8eY55jZoHR2U47tRvV3DcRlvfq8x1Ror1aMtaHfAYcTtxmhQxaWTBhpEQDMSBfMS85UMtNATQd9FXaIh3L6NB1hXCEa/3Tb03ULjkV9Ij92l041CdVUGhRbgz7iC7gy2aGap8fK4PopuqPeEsz306S1Mc1+FLrBz0Y4+545k+cNcEueKRXkColtdsTGU0JhftF3tXxynmx8W0kGKfG3o12VAN4Q8+yYPvE4XtTNVoJtzPeC+ksCem+w57gIAP+PbSdmnaEjsM/GrfTfGzJzcuCUKVDtSl8jh3B8f7f6dk0GSXftMnofdHlSXcAq3rG8oHoqCzS+k/VMz8o22uqcVHTiE0HKVK9h0jghDkkEYcGyiBsLPMlUxmkBmE8uwA==,iv:FyUTqDvjMajaHljcwnQZ9usEaL46hpvGOjNvIVUTMEA=,tag:K3h0hZfr0yjVhxpaG6bi3Q==,type:str]
password: ENC[AES256_GCM,data:ElXnqYyR9qAiBjPqR0HVsMcknp/qqCsBbs1zLfRVqYHSX68RkNVDWOWhrIo=,iv:xiBAV2J52aQbaA9VxdUvjiYg7JTnQyeuBukTFfDCjig=,tag:lZXVek8TSTcNtcu5VyUMUQ==,type:str]
intermediate:
key: ENC[AES256_GCM,data:xZB2VgzLgTleX/E27lie9Cy0Ham6iGZtSfmw/wfOqH/h/HBv754uYV5XPQyenj6Mst06WiiNl/gvzCZjNAoLiKPkGWvpWZeUjeclsb3QuWjIIArarjcOaA1XX0wxsizfAZ79FU4qjY+alJlN2EnWgiyYIq/bCdhMorYi9nn3z63Q2CetzDpMILfacv4aUzIqu6iX9sgJ4cwpzlAdl0vs7Q4jUZ1eNu00zb9Lbf3NSFUuafzDNSn4FFJ5H4nFqC/okLtFjfMrWv+XPXKR/EqA77cS2gW0g8bGtpZX51cNGYlIMdXj9Coet5tgj+UG4KzJSMoTFdiXHdR1jFAvmYUcFbX1wtbGtIqK3vKcVT1UBtkxQnj8dURzuetyj21qABR6WfPXVOfgIQrTdG4bUZEw1LZbEYd8w6L57NE=,iv:nspy2dXUIGvuyTkETzMxmeEPh0u4saxc5jHfsUMC93M=,tag:v7mMF2+asasRwqIyTSlgQQ==,type:str]
crt: ENC[AES256_GCM,data:4RrGKeUS+9oZN08WINaW61C7yGs6Atu/vA/w9GNGhC21wMWHoB3FXl6sxDkNAO+qZn0YhINXW6MF6VvmPh0Lu2nQ5wdLlhsgFat1fXrWcatqvHoD08Q6pWSVTXwN9ZLM038ucOj26Xs4C2mBCl2wddII59YzCKRcXIYoK7GWF3m3x5uWY+sP+8IZ2fQiHEaITFX1hg9tNItmWoRh7gCQ15yec6KTOuM9Ph2HI32oP1Lm4h3fW2SvIcgJqHcFObtequGFXpvZbSedesdR3v+gNVSr5oTVsEGoGwyALt/gGpK3zptTDsavARzsQPUXu3+QILyuNYHXP653g+VnSLMwEMuFdeQJaBeG4TF0Zo76dPuy8lPSawhGK1BJsEgUb++23q87nW/O5UdJsUJ7gIejrQQpH266nq8cw1Thyskypk0TrsMdrTWwOniWOLSUVf+5GZi9mLqNse89Qe9xyJlvocTdpZg7Bl9kDVFjLRO2IC8+r7NqD8PeOjxT8ojJNYXG3iDbdqyQtHtpETjLUN4ZHCo8EOdGIBAqnutV0gAxnKIcran86gtk6QIeYL62x0AgkDDEClCrQMZEOIMbhzPMml+4Y0SdcoA8DErrFiPeufDxak5gNLRJxOT/okOBi5/QK4deJ2Cbx0lSxB79d69Ol9OAKUNW855o/lcGD+Tx3bNXYzN+EaFmpku3Sv8Bvl3QloVQ4832w0bbwcqEoviuB8hDMqsmWXZS8D+cqL9uYZVObRJS8rR8QGPfPkSPy8NePWsuR1vznlyBT35eyRcKwVp+qweDIjHHgAIcSvSAVPoQ3EYT01WCpRPsiTIxh4IbLIYeqImFrb6Vn7S6tMmv3E3eCnuL/LfxiV1t3Q==,iv:bUDkP5wRWEqi9n9QEN9ETHFeWdAiEk8GUwTD4X7asRA=,tag:xYYYPlGQfhcK97tSPtM7dQ==,type:str]
key: ENC[AES256_GCM,data:DmzFVd4v7eAMXaTKXCJRJUq5efPOIndVUiuUxHZgS6mqkF3TfC9eaaf1E+cuaLqtjifGjc+j5AuTbNs8S76EV3sB+j9H5e+hiVSsECnrEYMFeOyWTS8Tr2CUjZUWlmvAKFYdqWnR7J9bMyaJwyxJKXSkgAocUQ5Ctd/Fyjacod2H5FTuzaS5XdWTEH1rOFv3lYZSqejjCUXASvbk5v2xEygHi6Y9vBUx8XGHiZs5QGhWAfEN/FJlozmhTOsDrzx37ozZTKnTECYEZZhGuM6oKmPWlSijsZVymFrh2LxSzYGJj5Cr9s6DJsraOuHS4A6K2gxhUNXFF7LSjnUOZxnCq9tleAUUsAwKrFfBaZA+74if2kvNouVo5T9EHnkb2zAbW4K5PFrteqMI+cvzxbsO0iOQkO5sCr+B0XE=,iv:Ip8BUzd/eNeYclMet4MlRhTaQ8Z8i3/nwFmFNp4SjHM=,tag:Ac+cCufRry7vDtKwdeItYQ==,type:str]
crt: ENC[AES256_GCM,data:nVzxH8m9/ShlGpfFZjbmP2ViRmm7b15rjXbG7ODwqiiGZE0peXiNs6LimTUZw2H69iEyI77eakW7hGjQbre9T97R1QKwmaQzSb1UNi+bj37B8KncxXxXxoKzCKa0Ebz//caA5xJTcuC8e9VwYwZUzuUJL24OP2QRUHClqZ7RYchyNjiK0ooig1nKqEg/nTpReg6Hz8aoIBBVkOxpOKjF3nFJXMZ96ID5caWU5mWdeO8s/gmAzcpr3HYAhV2HC4urybiP5/+N/jLc84JbnjQJu+5o5EJjkl/8zNrjaJxDr1WRduM2RlH6Gr6LcKiMfi6BVl5gu/W66c+ZjhGXED/uZ6IqRXcuasKe2hTW/QtBtLt+nEwzzkIj0fr+SY1FPjjlqX6tLyF9L1XI6n7bQWydljli4Bavnn5PX1cCwddfyuot5R4Zow4Y9xrEqT7e0dgnwRu/LEDdX2hpVmehK5ysrPyVFkg0GO7WwrqLbs8dGDGuz/cTXiL+LOygitPguSpmnJcBOQYxR3orPYTIk6uJOiV0X4tc1vt3iz5fg5K6OFLM7s+qV71IRXCJ1zLCY/1o4Vi6ADdkpBUheK1VY2QSH6ZftZk6Ti/Hgr+WcPSVzjrvQSqfUakXaB4o5Gbi5Px1jNzqE7/iTbpUYDMNohA91WqEnFu6Bl2OB5lZIo3uBCzzAa/1h/DogOMEwyUa+RQd88U2sDuPATTxnGJIjVYRbFqqFEf4WN1tYFOGA4dMx8mjpJAOOUxbjIJdeMFmErjVGC+ZNf362xO6kWHWcYPwspCNYEnso9+UAPnAPwUakwc8p4ak9oBVGDmXVHCa0hAiK7Hv7prKjcmzZlnnP2DYEy3lmwuCVADm,iv:tHC5Ss9zbm3Uy5leWj4oOcvCBoFXzCUjy0YnQl8TIN0=,tag:WgCzCTwWzuOTRLQpga5g1Q==,type:str]
password: ENC[AES256_GCM,data:aijMXt3hTo19xVChh1gplU8sUQjmX4Y+dSjS69Kw2cEqmEJB/pelihLH96c=,iv:zP1yDLGsbL0sxJq2PF+qe3c8rNT7Knz6vKaGwMVB2xs=,tag:Oazypz0OfAYI/eptqgaINw==,type:str]
acme_key: ENC[AES256_GCM,data:vir3SKMdU8FsZ3fBNoSMrev3vreFG4Vsex7R4hIR7Ty68x9wqsO/YpJSNkw=,iv:e7AqVbmCOkVIaje6xF8QzISU0E3JfyU4GM7/361ybdc=,tag:HC/kMa/jmVqBwCsvR31WHQ==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
@@ -58,6 +60,8 @@ wireguard:
#
#ENC[AES256_GCM,data:+kiy17Z7q3EiPgo4Ykk=,iv:vOi54XHHLNIO9U0ybpU9Cn68Ymj5W4vtQ2OhTkGAmRo=,tag:h3wdCo5UfNGlHqMpeeBf/A==,type:comment]
kopia:
key: ENC[AES256_GCM,data:7hdg6CoaIWPmfabk1ANXwn6VfpgGNpHsICeuxYfyiMsTiXF7PYIIl/gVY+bmkwIYUwTJ7Otip9eFKN56bej1qKSw0ukR1ZAB4mVGgazXPMBYoPkFTGE0mPWQbZVtlWlvIW25e6WpLkD7h0D3d+kV4GLK2vSHyNDfGZOQrU2qgd1PM0jgI2/HVcEYOIZ5k8QBDZ4rQjlt6pSkQUs2vO+NJxtqFziH89ONz05EYBpQnhsMC1zUQuiMKC+C0HCtVlRKiHBK0n+35mvxNDfUopX7GwycGenzC4ZZ4ExpfRkqDtZRmKU=,iv:4kI2bbCkq8R+zf8+RYBeC+78+4Rz3bLH5YMnuCFsrUA=,tag:i2YEcZoOhgcNP/LkGeja/g==,type:str]
crt: ENC[AES256_GCM,data:8BtSrcSFuJebJIKmLZPx1dspN+IqKAF76ulSZnN3t5PsSnD7sVNem/2wrLqILnH1M++ojzrqH6Y6/YRPPSuWITXgYQjKVmdE4k4C6hSSdsUYxzNgyT9VQRSr2BfVWFXrFWFTHDNQ8zBxA6dbSXm97TbqX85CKKutCB6wmvfIwmyrpfb3bKkJCh6orl+xobHKAKnSx1vyuzFljQc+7nvQwGq3sEiZSswjfS7Mdx+MfDfj0Ge6Fpbfck1mGKmj0MTFHUYGSKMWEShwk9fWBYjhzoDt2fDGRR4EIG9AIC7lO1JHDTjOIjePK0HD5EoOEUEKpXy/iDl8lzVbFudiUFRtg7X73hq05dFV8cb1BXFhjaxv55NrYiTNg6K18h+x5gouiCaGYbSd2CazkiQst2CXlRpNGuqnfEthRTYHZHwO33mI/4S53sZA2rhVD8gj/PheS4uP4aHztLOFBK3yLV4QnjkeUKtkG5SCOd46xg1+rbHrPl8ExWOQAqTJ/2m0GeBcAI+K2jZDdyJpATEWrvdW44jAytEQuzgACIUs8gOMdnEY8f1ck/JqG6OstTnataqvTnYOoKDa4RwrrzjXTP07WCQpBHShSYLFuEf1XhdZSfrFMqAhJjvCTvAvJkfzfC51u4/7wDLiyswtFIEc6rTMZTVXqWsb0Ca8xwsH+4yKUdsBAyWnhwq2ySzfg7ogxS7L9GMKe4b3y4+cmznleafZgq4UdPelT4coEiEFvDOXO/wzEwxzzo875Pqmm28TlTUDQSSDGq3UlGA758YH0UYA0457qjOFSF2kZqVde6KILxVAmu3kO390WXJbLCBd91euwOUvvgGWuu4Iz4PYjhQVXX9DcAHT7tXFGJLzexLjh/vSwYNniy+TgnkDeW7Ivr+mJW3gp8dHTk4Zp/LfeFUi4bnvAcetoAbo7czEGTNVHY6ob3Lw42vWvUk=,iv:d4UcuKoG/ysu0hl8hDPjOD4oDMkboG34Ul2Yh4TM7DY=,tag:tibe0jmza3V/gi2HXqkr9Q==,type:str]
repository: ENC[AES256_GCM,data:16V50bVVtecbF+TJcXHCx46ZPGNMgAhUOA0YbZHVTsCrOWQvTvcK2k8Iq3E=,iv:/wNbrE5GaFiQPNEp8wZ0JehbXkmah/gPm5ywNE6WVVM=,tag:vW82KTKtJ44++tqXON/SYw==,type:str]
users:
console: ENC[AES256_GCM,data:v1QY7h6vMmzYq/PTmnLg3M5Iwc/Ljr/1M3nJGuUJMfPN2ATY+GIMUyhYu+0=,iv:32c4YrZfTiu7sv/c9tDItmAE8G1lvc1sm6JD1GRkYr0=,tag:kj1a32+bMUsRdusadoXDvg==,type:str]
@@ -66,15 +70,26 @@ kopia:
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:OK1SKcHWst0rO4MCrEU/,iv:1z4/DaZoVTfQtM/dtKUNHjMmv0UDYayyYwv2yoE1YXQ=,tag:2emhffuIuwdlP/xUYk4bbA==,type:comment]
rclone:
google_drive:
oidc:
id: ENC[AES256_GCM,data:Z4LnW6WMwBbVf1n1sD6GWCQXUSuPe3PcKhSb3aJ7UhtzCaD9DUNn5m/2f+WaTbkv6EhrCDeGrQBnfxoSGvniE8VZ2BltSeDu,iv:CjcP+E4wyOWcghjcSHGPhbKcxfTD97uhVzRTUJVq75U=,tag:TpCr3eApSE2MsanJ5pu72w==,type:str]
secret: ENC[AES256_GCM,data:n81fc7PwTP9BSxjplwlf+A7tXhwkToHv6lDuav+fleBMC1o=,iv:ndFJpWINRVCL7wcZZ8KjEgs1YPgR6wtWig+3VR4Okgo=,tag:HGOTF4i5mhA4mr4Xnf0UTA==,type:str]
refresh_token: ENC[AES256_GCM,data:EEE8ExDhoBqPNM/t+q07ng0UYTU2AGNJEWL3ydZ5A10yOrHrdjMC0YtblQzlsA6tdkLCemaUZQXz9kEIHfTQr901AQALkfb5C9OoEnlXz579OXhDnuv9BJUi7tv+Hny5z5DEeVm/Fw==,iv:LyTHhKqzmO530zFqB0+dKlgi+/NEE/WaJw1CdMTLFls=,tag:nJVszyqv3kEQ7ehlBDe44w==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:gfsEXIOqhoTsiCI4uDjDeEY=,iv:SfJwBdIFY8Z4Nt2CxjuSY8PYKebQvKcg9n2xL3w7rsU=,tag:yrTNPBjrvnxydGK2X32Zlg==,type:comment]
crowdsec:
key: ENC[AES256_GCM,data:C7bMfJikh20PFQTe7UtP48SDUlSKKDMOul7dt9USZIVKq7LLBJV83vqbOc3xbteqPzwIYqBEndfqypSevpy/cLAPstQXL634fQGbel6trqbCApq/xjucUvnaqRKyUjk/TH3oRP9qXG8XDsgQ1o+I4b4E1alxLljMPN12wd2KIHfDGp+TURcC9DkK+NESIyQMLQg40cBaps1HcxXvg7FB1oJImM6AjqP9iybDfgkCaNAqUHxLyCHJI4rdYFflALHFkorl8jaQTazqWfuSu70x8j5tUJREksOS8arN3YIwXp4UGHQ=,iv:sbF2ZD6xS3Z3EYc6/5m3oKMghkR1q/IDzVwBX0OuTTI=,tag:NIuO8x3rP47oE/Y0EiNOvw==,type:str]
crt: ENC[AES256_GCM,data:0Olvh8chXMx0tri9NeiWhka/bl5XRwdAQf55EwhVa2kTedETOBIjdgDqdsXhoREhannWK/1cXcxP7vpvRFMt5e06FsmPh8mSuuyRboVs9ys5p8KPYQcnlOZxfBU1J6DzE8Zq/tOf9cpdlcUVpPJ038tI7cokqhBvPGZIR2Q4/2BKWQnL8d0GvQoEfXOpNw63qQDynSvaSs/asfZf7yeYH6v3Tc4tNRBB5nl8sCq8GjTvm4xleRWax9qS0oroB/uY7S+5OCGLtJFo0A4eweOcEwWuqvpGqsOgs6s8bToeaSmPOUgsCQ2abiMtBWnyCxMxMCFNB+79lpByasf1iGRWlrhjQeUpIoWWAWU6oUbd4pMRy+j+rKV1/Cq4wF7cc7kSTh8BlFAK3Yr8DIDppiUUo53utPAl179ZkycNUzHY5Auh/jA9ZU1UZRbtIOBlIN6mVr/Y53EzigtVYlZRQ6aaQFutM8jGBnegfPhp3m+XY2WanYk1GKKj46Wh11g1YqIWVYpk5hlR1RdQoPsgZPf0yChblbdfQ14iRJqw/0pVPBvX6bKS6P2OaDHpCIBSpTE98VSEjXTkK0XU0Mu5YzyUWxIe5/BoRiF9gRFGHEDWwFWprTuFk3eUEaEZ9qoyWr0igmX73Be7y8Bre0jX1xSMjepSLV/0dSUnimhfAvT1TlhwNlxq+FGR0ZMsu8QmbYp1CqLcH93rQxoloHIs2T1+KfKAENwsjm2PshdqmOOoUkzANUmdilYCelkbXyUKXkLqFb8qnSV0jK6hG/ZVeXTzZPnODJ9Tow9bXKdxOwk16XpuMSHjCoIol2E0yD/0YKVBHAnYuz/Uz8Dl/ko//3+OaDD4TudV/nRyy4Ge4JI/kckiovMLma4dJSmADgOAXa+Ao+3N/vfUMF1F1DyRXUXsqHMygtlZZpGiswabq/MrjEvBPomzFWUmu9clWiPJ,iv:SQrBnbzApJscyGuNPE40uKsUdQCf4W73uUwanfyCGbU=,tag:KK+a0tLB+mmn4YWl5Yc0tQ==,type:str]
key: ENC[AES256_GCM,data:50i2yh0vkAhAb1tYsU5ijd+QSkCQ/w3Mv4hHubixCmUBx/zR8YwkLCnCdSd+UMTYvm51KvIxN4FsNj0nrbfqBJ6pSoC0UXrxgF4cGRWC+dNtEJyDO+J7yFwp+8lYMO5TqTSqYwxu0RpqEbe5V9p4uYruFwfVY79f3+KGt3nOsktPIw/rpdvD8mlpM48ANi30813miwpAhn/x54QVj21deHxWuzauYI+wmOIPZKgLkLNokX50oq9SCJntuuYLaR8S4B9ji3BPEOS0CX6aanm8Mu4Pl6hnGcmyEXhvlYZWgohp3fI=,iv:0oKtj/PUAznINv3t6K0iJec7/ytqYF218p3XBIZOLZ0=,tag:V4wywnJeEZsf01l2f3LVJQ==,type:str]
crt: ENC[AES256_GCM,data:zcnyni/BcXXx+/9F0tBnSqffJhumnv/ob8kNRVdyhx1KU5arwPzhlBh98BBl6dTOy9Eo/pz9UiEmuPs46IqE1QGFb3bmzloq92pxOrkQwxwTPL9WROAHbd148fHmHVa2NC7VBpVuuM5t/3os6vJzdPgYHZC1k23SKKjCHAorqiIDso3ZBHfVJ12fZnEnnIWh/wWyvw4ap3plmkEHZC6zHfMBvH/IAFN6Fzzn9nNmOB54f0xT4cYcActH/lpVxDx3l5KOkM5aKXKbTxzSPUugm2X3rSKZlBgfqPBfA9dWUBqK7bfKIgYETdadP/1K2vJmscnNLPJ4DlZe9R9p/i8hdmx9Ls2xeIk6TOMxpnQ/iXKu2mSVD2L6A8cbid5kTjGEqcl8osQCsyOV8dR68beBFHdQlt/PvJjTKIAGilZPk9U+W9p70RaA9ibQU/WJJxrZvMgpJEMdbq5WLeu2c/TYXWEKyfNrpU3kLgzln+6bBEXEPk1MYWUfQJHqsWeXQzMpALGGNUPys0oaAKvFBlaEEetK8dk7tUcU4M2N6Sxn5+lLE/wSa4ZMMEFDdLEvxbfB7OyChUEzn5fXYZayEAmBgSJlWBkhbXLocfASjzluF4mrBHtSKbIZOnpFtB/mHubgeMZlWVD4FwYwkzoBuLz3quLq1zhBVvJTQDfL5lu3d6CQlGiv/Hl10Fy4Ne9QJ1bpfBaj6Ih+0WuYwtFfjwQdKvO4CZ1C5Fy4E9GiSxRQnixjG3JD/7JwnnmQriPyaWpiYumhwqrVktHcUHu3n7zWBb3P+UFMw0+JsOWaEjU3eg9m/S/z3y0eN8bd//a0fbPuBiakLODaSHbEK6ooKyc7qDzhuQMYBgdxHgC7iBhtMDiqZ2VHVaSigagE3494ObxFXq7Nc/ymWoIjhVoz0hKQ85eQosyiusCjThxT70DaAUOWvDJkDjPP+M123I3UjKT3Lg==,iv:+4LUsHW3T9cdqOQZXq9eEb3gH1VHCXts0jyBV+HMiIU=,tag:+E9sWyUYhzTYwv6f/3KAMA==,type:str]
bouncer:
fw: ENC[AES256_GCM,data:wplc8z6YNuQi7bN6r27wSOmx7alVDWUBgU+pgvnH0exmRhHCHjbS9zX+4P0=,iv:fbBg0AlqOtbKgYjbiSPopJ0QwEryAEXfcc+YaUWI6xI=,tag:Ul5+MwKZyMevBIyvZH/hAQ==,type:str]
caddy: ENC[AES256_GCM,data:fRyTH9/7OZ7oaz2n3o7DtXJCnH5l89fGSZtM3FH8sWzw8Iqj14t/qXsX5Vs=,iv:YQbozgWTmU+facWKeC+2ab2fvm47CwLbrprRiWKDRyg=,tag:Ki3hGXPJ4lWMXJYaDF0kaA==,type:str]
machine:
vmm: ENC[AES256_GCM,data:CF49CU6iwC8LiUMPaGMFMNGjF0n9gpSplnGkkJNk14qEkvYFTgfoUPqc964=,iv:Qi6cTieniO++EltyUkabXqsPAEb0AEmdR1ramlisthI=,tag:G85rEh6STS9pDr1zbuNUdg==,type:str]
bk: ENC[AES256_GCM,data:+paUsZrY/lPv6xTcPMCeTOL4aIpTsnNHo1jVL6LyXky8UyDtsrt5xr6xQ0g=,iv:oI4BoR40rZN3jHFCx3jANdyVMAeO2Kt4EonUdIixveo=,tag:x/sHdgx7S0Aus0+QiopEyw==,type:str]
fw: ENC[AES256_GCM,data:uA3AfA394F8b0a2jGjU8ABHvKGNTsZM0O1woQc7gLdHte5Cn8SgfuyNgWu0=,iv:TCZ4NwJuTVPAbiWn6QWM7cRta2uXODHb/41Q1qslJsU=,tag:A7fGrietJuxyEUik3eVZLQ==,type:str]
infra: ENC[AES256_GCM,data:rNpo77YAdoRedzJonScaT6Fhr18+T6u6/bKEzC7PYOIzEpuKh/MqzIS6Np4=,iv:D8QND39D2eqBG0USHhcc2UCxpTSuGn4SCpTCOBO0RAE=,tag:1X8IK8DNsD2m6DdtxMQTyA==,type:str]
auth: ENC[AES256_GCM,data:neUWugEoUU+LrXijmUjd6063pvtyMuvBhiLtKR0BdAqkl126LN776x4cibs=,iv:OSu3S3AQNqk0PRR2kITKgLcniEsNhrNbpPIix3UIwDw=,tag:5OpA77ayasIQAYx7tYFD9Q==,type:str]
@@ -97,15 +112,15 @@ bind:
#
#ENC[AES256_GCM,data:FNnIRyidUh2urEgGgM85,iv:w6Z7/VLY9BtH4CNqchc/3GkWb4ez2Nb0TxdZF6RdND4=,tag:14vEc2qpBoOApdnci8ZTug==,type:comment]
blocky:
key: ENC[AES256_GCM,data:3hIpfdbwUzIvFhhVOl9TYoLvVLtFS0jdKSC9FGT2dSmYJP9f0qqrKEtH9+52GF3YUXvf0fkPoqAxxDf3iNh2ynAS7PMBOth3LS5mnY+NpRCZuod8PkyQOWCzkAeVla/JeAD3n3da4f8layWHY47u37XgeojQpyijCWKifEnpbjJdXVBKi440mSaFaORgDJkvSMRGcgF1o1HWz8/YricUIfv3xniQA3Ke8gg4/L9gHHFAqAZiKUBDDppHzuY5ua82FDWTgipPtoNdi+9xyeTmNdunrVAF3OrHwkNQqfLJrGOfrhU=,iv:TGbO2lfqjnsu6ZzcO5qAeQKSgE5qr2lQZi+3YuAlAGs=,tag:tE8bTnPpBXwDXYyB9YsOPA==,type:str]
crt: ENC[AES256_GCM,data:vvT9evMqMW8isY3N0tvQCW0XFWIW98vVz0aO0o3habCbLJlB5wjF6TMA1u1xbJ9zQWX8kHTt62BxBngCHXtKFnnVN2q1hNFK/sGFpAJz/WXD/sJW8kHOAv00dwGl2/r1josB9hFmn52omxpryY2h3ZTgqLxoCpzYPoB1LvJzPKo0bS4ig+/qYbAyPe9pNbTmPzGjXutoGbOKXKAQvfZg0YNsR/nbdd0Y5eEdHL+0syH2wEZZ1ZBBNxnd9wiEmRoQHDDkDJ6VHAWfcgZcuX47jKak6LFBAOfj2lVIza78o7MSQsCT1M5DJMZu/K5mV9jJhftljnZ8MB9rX3rFcApHnFX9luLn5b7pHRwieWCNHM71gy3wHS9DFZXjYRpFqFTiXmAxtKxgL3wDIu7M3Ajrc1o1JMlsWMGVUa/InfF73NIMJMKrAPgSWXb4JgwF9HmGjbO5GiSAjRXvuFFmQBArvA7NRn6XFaRyHsig8T/5nQ4t+E0C1HjCkzMFVnUwq5C/3aEJij+cv2YaT5jVTZ5tvX/x0lykMLxd5Psy2Mqvw1++NjS1eOBmg/4q0Pero6zOl+s83fyoSCiXD+K8XHHe24M2IBITABC/Wf4L4yNrwE5S8qn7M6vbFZns1C4BpnIUqbtGeMbAx5reM30nXTWO9pI8Xl5zAPrb4gd7zdl5c6HLge3k8wdNqw2kBD/z4EDCNCPar3g4f1XMhcA1aB3FrHuLvHOkCkCmNbvZ4D4+Nams8HDQ7aNNStshLGt2Rdh8fzQ/KlQslJvgk0oLupnGuZzNWP+TO4tuR4DC/pfX9Uw3+1SDeeTFH/X4iaoSJJxqwC0D3pTvu4SUXilZ+MBE+mDEkB+PbNTuyuFkbUi9OyRFy3W175Mbr9BAdlSIAd70SpUiARqn7k3S5twV5pomxYyOaK+cuAJub/Gp8I0Y102P2rNqcASmA/Ocnogp,iv:m3r2kOi1XUVzLcKo/x9gWT22IDrnOlDYFaGxY2wd6Oo=,tag:6qOgMHBC4mrOTRJ61jhGAQ==,type:str]
key: ENC[AES256_GCM,data:6VmmgxShlwwDdfYIr5TOX7uBmnNCambqFVJ8E1FalRUBVr+HFt9cUdVror78UyPwmH6beNI94EC2R06zQmNVxhaSYtt2Ise825jbx2zthm6sP6jVK1PpU33bF9+5VSoDU448ZvQRDyW9bv8Gz1MeKMthzn6LAkg1lE1sZ5lp45QRal3IUw44SXa/pxdc7tn+VcYRMQ3a1oVUTR2kBnqLUw7OdzIZpu9PUcCoVyN28fJmlZnIx23wOfDsCb31VpbDpj9vylVZpqX+clNmwNuh0HcxUdXF6U849XOou/c3bK3WbwE=,iv:V3IiC1Po5+nZmpUsFdcw1aUiPteBXJ9vWJztR4wi+/4=,tag:ucJhuIfnS14bfPixjAB6oQ==,type:str]
crt: ENC[AES256_GCM,data:9pxHw0OkY3i3qPPsJnSYgU+u6lDFtboDKpRr4mOgInAmKyMmOB4AOBoQ7KVXzJnS4crzNs96ipYmt+n813VOL2R4G6WVXtuvNra4leSZ5gvtVy4qNQHK7IUSeOu1iqGyMF++bvT9Q6mHBiN/QwylIWJu7LJULn57o2Rfl6akH2zlzgevK2gYC76dji1e9y1jhRY3VnwEhWgOYftDie4Em1StYn2xi4GcO1OuW9lcCggVTi1TruddcoIV9AYbjV3Y30RAKtZiYXnOTvOmsYm0Ea31955k2owxNM+NGwQDs94iZ1c0MG4cV+cxe8aT5xY5+n7ZmKQVsFAlkC+3aHqKxKzr/LUiFAXYoERoU3n/fAGuuP6OPsGMRZTyW1Y9VEsNzpzANWDw3V4QS/6PXqPWxHkW/UbazNu+BD0lfMEUAG6nYx7mAi70LOYwsKqT7pdIs9i7fC5G51cD41keDFnkxfQpBlrMWydravM08Oqj3sgrjnaY5JIdkrwH0g5P6P9d9fhc24iq6VnTC1CxzMU1W5hRHTEebMlJ6DvfGxgS6xTLWbFgzZi1BLY2meU6C0NVkbjIcRd0BQMfo10gS1LDFLZhpr1yqFr7QuGIYi0m+CIE6cGQXJCyzrOt7tQFedKlpy2o0yejdFEHmsx95bNdMvMsXNcw61r0QdrpKaWPShmKtpCWh7oFB8M9XnwzxI4ZKDWnAnP+j2Qp39pKK4mS/lNFqCwJ0mIhE5d4GjDlHA/NkhKYop17ZmxPORsKJ6kB5DSLx4VNTIcQoScH6+X3YU9cx82Betky4VzaGCp9DVZv65tA1dvJqdhROc1xtYr+5ooRc9PrQ4vvKE3rNn5XSR5CqM+udDh5x4okNrF7L9qkL4Z7kmHM51CPpuQl/HavKfsClBNUPcJPz3GtaPB8BvFlUh2NW3mrP6AeRZu90qq5nffMeVwkTcNgpVXv,iv:hjyOmwysUjIyopOeDo6qdD5MrUCnA2byPL5PizqqZCs=,tag:seg+fIrhd5dYrf2jCMloDQ==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:xUxguG12uiQAzJGMXaaqdgTfCw==,iv:kh2nqSVtBXaAhEywAdDPbbG0p2ljX4mtjVKYXCNL20c=,tag:XGaBtxPBHSaLRYZa97+kmw==,type:comment]
postgresql:
key: ENC[AES256_GCM,data:XCBWU7Y/BG5kOeKqmNpqwyjePWrMzXUW2RUc+d3r8kfTMqVT2Hxe3Hh8qSCilsZOKoeEfraDKCMKBLmmaX/WGXtjnhOF87Y43i11jDPeMv4g0b3f3oh+ekckxG43pFpPUR8/IW3xB3otTdrp7Sw93xh1s6BcYVutL+opOVLwdB6JNlnx6ZAi+ppjmlHxinEa1UpnOkLC0JNIyp0i5xp5KMizNs4iNcduI3eWWZmj1z/lW1Q7dkH6rPIjtNQt0Ce8wpdsuJvPaZHHAZZ31KkSj/6n/sNhSQsgAvJNYBIsqZRG5lw=,iv:/1keHEeQzFuxaZD/Pw51IBIaBy3J/5fK7BcgF+A41Mc=,tag:Cx2AX0sPTwkmHODbuZNu3A==,type:str]
crt: ENC[AES256_GCM,data:cnvAtbrzS3xpa+RQKhSO72xsPLAiloQZrbUL5asql6Zf2B4aNzepfZa9vlTObd4pG/nw1lIFIY4N1oh+L22j9snDXRX4BX5tANJBCiZrnqhvfDkT97xaK/86D9J6kyWmIYjlQ6bXBkvDhDII0eqigV3A/NDl8V9YApGHyAMuryWB69I4wjvVnlok2HSOgZxq0F/3Epp4hk8BuUTNvAcSeIAfpWePVHYtHRk0X3IT3KhR+Q16Ahm3fqDXFkAhs86NBaCl+kEFunGLyBqxecU8uYuU7gmofISqSDytI5ZGVt+oygHSkcQKrW/NBxY2yYs++8m5N0sLPfmtE8NFIMktpjHrUGEKnZmdUEjg7sFFRGhvpGyo77x8TPC3bD1rZJVgDwYCmKjyCO3VT9jPonV66rVOq5S8svm4pQlaT+BDPZXIqDQn4UHD7grKOdUA+zgvdBuVsUnngR/D34cPPm0Y2x4CLPmz4PBGZQu5Q4sCU+8vq/n8kNX1z2T1K/d40YBPlSunOZIZk6H5XMoy4Njftty2sIjraVy7udYEwqmUwfm9XlYqBa6QeofoxY7BaUrRjVUIcPrKLUZ13qddOoPV//gLtEDwcD37kyLw5ZbPKVdR/5rU48ENvP1UPFC47HDyhtA9/FzYweEZ1jEXSuxr6r5xJVcH5/2eyhW3Eo6ewwWeT26oVPj0zm6faeY6WVPXJX3fhMwNonAtTHzkfZCfFlDgizMtSMsMadzzTut8rlziEea9cuMS0EacIdzafoZgQsYq2ZrRhsYDbQNGAE/DzK3Hkrtp8eqJDNyRyCIsn1+B0A0ci8hADSHPS3q4tQD2JPb5zj6/0PNt0oIIBRcYuXgJW1nathtP718i3Hhi+cmqvZtza7XKUkxNYNYLO9hGnVTNY+mHD8HW94QdAQ8ikfF8tSBv0N4/ebyVIkaS9Nes8MyOQJbzfGhmmpeuo8jineQsf6w=,iv:6gXqS7C8lypzUvjExURswSwnUXG1bTC2+BxUkK0D36w=,tag:cd6URiL6TxM8GxyYirvYDw==,type:str]
key: ENC[AES256_GCM,data:Q7B1R5FaFG5sTFE33vKPCxTEE1QaA3uaICSVjWfUKkJPGfqOsvl7JIkwDN2O3UOy6IGXnxlAVUoN3DtH1H55jDDrR0miJHpuEkHn8hSAh2V9iiFjI+DtuliiBdXyQVwlKj3nlAD7stUTBE5oofAYaj43Ijv5BdslcnNOTaFaFo0UvbnehcBAkMnv9YyExto9dCw2ZzOexQs4Lzo2yybCNiTG9t1kJC1LyVcmjxMOSCcay4L+6o02/fJXJ4R/W7EYUYfjWIf8FeC/HVkPsU5TvxA7/uRFUUYro/1hOGkLfPGWDtw=,iv:T84T1dKJD7GPWNz+gPvzWsnebaw7cMjIWimQiIBixmY=,tag:NEvucfSk/ApdGZYB2LbLjA==,type:str]
crt: ENC[AES256_GCM,data:JK3XeegWuX8Yr6jiskiQGLpT3tLmVFboCIUmpaikEiV3HXy3dsqeBwONoQO7AB06TWbG9UPKaPbXkMrnARESR560OvSkw8cCLqZSdEyYjvapEbhB68cmLM1ldETQfMWP6O4HXjZQStU2cj3jP7IDmCUiDPfOKosH9dFLpXDyafg5BayDkDjYk57TGhUIg48HGlQfgyGr2xqty/cdLAjVrOHbjJq9hoLHnPyz7Uv5AnqnGZmaRupFHRX2GojAJxJHu7aFacjfg9QeH5KwnfzZeY6umXb/Vq1cIEJdN2v8T9MvDODgweATaBup0LC3kLR2FuhyTn6fdjzFhXQPVCcYsUTYOaVctQm0HfGLRsR5Co63Um04dMHSslhvyx95JNyF/sn/Q2WwJ6famAo8ckdtGFuqX69K/ksI3I5hk2EIwp6DLTUH1LRHa8VoZl089adsLS/fiDGSfUFbzdY/Ki+J5MJHdoRsr0crQv2UXIpqjr68B4jfRBshk4IooJHTrDO/erg+NhC/S4thfPNibsEglowjbbYnz0vtr6pzchtNU/S5BUsL7uYh7vUv+ik/D8/tyLoboVN8dRd+wyCCpNX1sfwubPdobcvdtAjT51liv76VSTDKqY6hkEVuYQeqC5vTAL6xaD53IK/y5yVtwo99wAjj9gpXKw3Q76yAnJiWtd0xZzE7Gly4eKifTDK1zre5KnHmdI4ctktPX6a4w17TXuklQfAyBJWs/416vSYD5FEXFs1C8asPEBslO8DbAna0uGp591r/IBxPi6ulUd5RiQzeVRiEXKvAUFKf+wp/gjbBM1iQ6zVhTe2j+rmg5/AiqpSDFPbz4fAoo0fcjnwT+Y4WMVoaWKF2ml/fzoqB5Ot59TnNfDpwOuhZsImnbKTBL/WDG6B3AqXZozoP2TbJ5sqIm4We0uqAQ71MrfE2IaDvgukrbidcxq5ovxdmimgTSjYM93c=,iv:Ho10WtdWiwxMQqkW3LZcmCce/fCxqtK1L41yG0DPdmY=,tag:zf5yOLzGG1CxlJI/S3ppWg==,type:str]
#ENC[AES256_GCM,data:Zu3CNVkCUlW6BKGh/rhTTqrGv/7H2BG7,iv:vW4cAVvla/Ya0S89DTvo9k3do9517F+AHMLb36Ey19A=,tag:neDpBA97R77p88Im92xn8w==,type:comment]
password:
postgres: ENC[AES256_GCM,data:XlpsKT0/3VgNesMdifvHlYZ2dh7mlMEnxi0MJpQZiQ+1tZ0/3zMxQ7+vwec=,iv:akcPRmDZQfFLxelG7V87UnNB8ez28PPLCmY1MYfzStU=,tag:14jvIKP7Htfh+/oMwLYC2Q==,type:str]
@@ -122,13 +137,14 @@ postgresql:
outline: ENC[AES256_GCM,data:0eJFCfHgS4olWXHOnUPixs4Dl5PoxJa+2H1mk3YgC4GUv+nX8gTPmzKTMVM=,iv:HGxX7aLODUc2nVLC8YXdet9qphU/wxMapWhk93YmHDM=,tag:6/2uckW/a24LgVFRH6E85Q==,type:str]
memos: ENC[AES256_GCM,data:BaNTnZFZH4oMTnDkgrf2/fMTkO43rc9bSsgnDWrHJ/Z6+MZdMitpIMkrMN4=,iv:JsG3B0YweZYO96MW9NmoSsIG++gFq+jRUsozwJtWl3M=,tag:orLTUiW0inOJdD4YSp4baQ==,type:str]
mealie: ENC[AES256_GCM,data:XbYHQv6HBWCXUBRmcbQZN5Y1Gw+V+Wra28Mp+m+ZGl8VRXsKMu5507vGHQM=,iv:zp4ZarqG9KshiBV8F8UksfI2QXmXHwqPw631r/5bYy8=,tag:jvsvXrUsCvpQGIbLV9jTYQ==,type:str]
homebox: ENC[AES256_GCM,data:EhNnltOBwLai5vEMt7Dih5Ug6h3ojrdbwhq4Vr0tg/p/0nqI9L7O/Le0B4w=,iv:14oyl916t0inr+W/lztQCjYiSQ0vIIwC9NmbezDuvvY=,tag:IiXZd3NU+dUvDrHpxJXFew==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:VuOcF5INAvCNDvXhcAc=,iv:zdTFTKaII606RjjDJbdUyv8jGba8abL7lJNTsPH5C74=,tag:cYSefZHKIuvHmAzoxierQg==,type:comment]
ldap:
key: ENC[AES256_GCM,data:OCje8b2x62vVQDV2DXvy28UxY9IIMtRoSLyXVFwNe+sRWA5prW67Vp0XcwwL00nP6hagkXGNRxy0eXx0VYXiktQBeQQe7TC5bUpzJlDCyGmi9BsbDhzShSoSM0v/TxX4UYZ16PbdVxyEIxlD5J/SpbQdg/VvVA6oJgLU4SfZCYmKRZ/gC3I4cKGzFJAT7NLAnC74/Wngz2nE6ipzXiQaDHnSuitLHF/RGoAgE5XyelyXag5wGaB8EAUgmuM8woI5g7RES9885bPOdt/Q1MUNNmEz7jIBH4AVGo8stxRioYmAcZ0=,iv:6Fi0DyvzmrQN+OVclRFZZyXCW8v1nfizj0saGkV7qTY=,tag:AkMtsUaRIgxNKZG1oYPTTg==,type:str]
crt: ENC[AES256_GCM,data:1SJlTokgS71mW3rEQG1fTLs1cp1Mraww58stg87Bf2PryFsKw88va4HB+bsKzkCRgykQoj8iDSqM7tc7Wze1HWz+bgFTgIqluRQpZtbcmfYVlqefdIUcJPAntqe/eEaA8Hj3ozTNMlKFspv13vPwjSJHGrHcektwlcEI58+S3lhu/IrIfdxryBm97E2UQczznRE3zuP3MiBYUV0oj9Bcm8KWDKqa3DLwudp5wwyw/OG6DGg48isEvNxaMJhuEkiMwMNR+zZXgAvYcYteMaW0p1UXNFHzxMpOWshU1zYL6U9vZkKfpVMoMoTPW3+SO+vxlJVafevStEipUFd48XmOo31uMQH9zWh/hbLM8j/fiTaW+kY89XwkQ9ZUOx8+YXgcfBb04FAbtLEXMbC/eF0meVdjAHv0TEBPF1a/F4m9Kqv1GkJ4RA8jHKFng7vWsRGl7WhmxrCxjrJrYu9ptP7iMvbaaqMWG1YOSwwZUe2HvrM7WTlJxeRukwvW3rOglAUdFODJhsWbATpobuu716fKLqDcprKa+vA5l7VMghqLf7tWRGXmT3O3RgTGDwqjokx0q0+4ib10cgzrRCkwuTBDiLYRdxd2eZFWumLOlKKQyVAMJ9YZmUnH2VNULazcN7Tznpqm3vdA8pC6Em7ELTxYTUvkaye27jRVyilkZvoX8oLiWyEpMpPAajoVntmPnGTF5ppChQ0EmyuFXlv0tsU3K+bxmM5VYVNy4S+1cMOvEwGDuSGJ9nZfS0BFm8NXDp+BbEIAa/DVc6yOjVEwi1Wh0khxbplkW9feWfkLqrdlxF2khNOTePIyEDMzs5/CRq+kzLy0U+7BeyX28jleRpeAQL6nlOZ6Ha3AyQUhxC1/KVOK/kgAlGVi0gyaxZy0yuF4whFKN7+oXB+CrJoCyX3mjrOVqQIOeNIK6tLac3hW/cATu43uDQ==,iv:BmdsX6o02n/ekhnTQrpTUgM3lqXCyQNYtMbexI1Ziho=,tag:7AI4lOFSXDxR7n1zoqcE1g==,type:str]
key: ENC[AES256_GCM,data:PwQ/AtsQbbM454iM0wpUjGRfxyxNY5lGDARv/XOdk0yMCHRsf9qHKaW37EwcqNe+MVTJH+sx8lHDdxRYF1gO1yj6kasEIbMeo9cIaGQHvey5dYdber/qUkcjuAXyhSZGSUXI3PR1CuAsiqNUEruh/eKyDZAvHTTY0Rc5jk4HLFzPqqBjBOMkA6VQXJ2I+v4s8W0T8/JEQ5l6avwYbaaInIIY+uPV+sY0mBczAuaHLyeA3V7VQNTxQ9lUgdfH/TpgBn9EBH2wWqIGk2zQ6XmVrmOarCo/+JqwLQVEyY5TTrNuBto=,iv:iYzoRXjH8NjPnX5nBhU2gTQXOekXMirjTGhmpGt3YAs=,tag:IxwtGNXx1i54Jnhn1aZx3w==,type:str]
crt: ENC[AES256_GCM,data:ulOZOUV23qfU2RATrXYFKPnPNW2vHShJe3O90XXoSA4bkUBA5Jz8uuAzCiVOZfx/Ni2uG6y5pP2x4ZnJADSrkfHD5P5IF3iUWWSgRo4NDDCY00r+fuPFP5DpOhwHl4LzdiHl8SBJ6YWBbhtT8sXVXFgmQIFLppuUD7WzJ4U2c251vF6G7Q0XemlkkNFq4fbkClTwogUyTRs2Dj0e187uilDkfCBS83oxMU5pB5tBvaFbLxaJmtNpaFRp0U2fgJqJrM7avZJeNUDr1hHttAw/TnANNK3X3mgFAruluTUGTS0dt8KPUlaUCf00I0t1F6l+cNktaTlnUtcp/HdWf0Rtg+tYBfFNgsGl3iB2z1utpab+6WsI2+LZYGezOLKojbFeYGNmVLkSucCSVAtuj48fqKFeObdLcBjihXilI151ie4m5BR+37h1Laob64C/n8lhNSm8QpTuIipV/3koyBKwIimMYYO8j8sHGQNJpdDuZXmYCEiXsdXZH2tFngWtlJ+39yV+GCjhKRYPkAWFKw+EtTSnPposC8fHPMLMZnB1hpsVmSy6VBDuZ2Qq96H+njgzWXIwhWcZteTyxp2fS6dNSG3zTYD2cp9qVrA4KhTlAjVojXdlRAv5Ayob9R89hD3WNKexe3QD/Nj1Hva7GyjEHqHijodYDNXCWsMgXdgItgwUz28vh+H8o5sSHCsfUDM2s7Y4w1rmKOkD4XpxTcyGM5zrNBacvhS1FzuDFYZ8BnN+STaQ1pvscVGtrys6EtXDBfrWbrIFnIhMGSp/GGUMhrT30gPV5VmzUqixRJzpKvYF1ljr8ybRu9Sf4xaprQNeFTTWq2KXonwK8JonbZJNwQQ6oDbp7AyuQD4EJD8GoXJUAy9H5TjnpjJsZH+RsTR6O19K+O45HQCqvZpEHfUUXu022VqrjmzRHnmB4bJKgb+BqE6YpQ==,iv:Vh9eqEun0E73s57RV53rHbjP5AWXn4NPjlEPwVDQ5VY=,tag:fxpzhMHk8Jmb60GHOHnYvw==,type:str]
seed_key: ENC[AES256_GCM,data:q6kodsHzwDuf0UHPSk9VbEI9pCwePJWcA7o6dQiZqyM=,iv:EbozH0UpLThEXjmWkNhkpHQxabH/K3/lJ8xki3lLGZo=,tag:rKO8csevxpH5kzzFWQoang==,type:str]
jwt_secret: ENC[AES256_GCM,data:AfPcqKAwdsHfiYHrZ6yKqOxD93gBcLEvZ4N3M8nhWMQ=,iv:awfSXZbc7Tzg8sRqUkp71TXivLb663ZjaxNkskE1Heg=,tag:e541/ejk7x+GOhJeoUPMGA==,type:str]
#ENC[AES256_GCM,data:/5Po+8hjJg74ZBepaNWTzZy7JrTjiwo2DNFT76hZTUFKEdchNd4sfdy3tbVwL5y9QPC/kC3kvBZL2oA0+WzcUmd51Ht4/xS6CnUCpodBrSWNHu4H+ohdmz/jMplREeJVWAFHuTZROqB9v3hw3rYJ5Gb+5XROAuzT4pKfrPoJ1B+ysGUpuvybhrC1O5Tg6IPs5fvIiR3yExGcIFuywZBiiA==,iv:gupWDnXb2TD1N8GhHwpiYuz0sDciLX8V0ww9cFKtFNI=,tag:7eDx5RZOrNXOUshvdijV5w==,type:comment]
@@ -145,17 +161,24 @@ ldap:
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:6RVyF1gPUfKEvkV3RQ==,iv:0MTKlm5kE0kZi3E013Kk/Wo642BizBRmcHXR+Zwf/W8=,tag:SaQJ7lvFUuALxXWf41C24A==,type:comment]
mail:
key: ENC[AES256_GCM,data:2iT93HHmX3eCTtKb8/C5mI2uEwdck7R6TCXoLuWprfs6KMza8mwd+qDeYGauUHUojEplmopycWup1cVWyQRN2B3udxd7nR9YXcdL0ZNvjmEcjRKDicHEhIw3DVb7N5GuGkFDhc4dEx4gen5Lwu3c+b2hpL58fvZNmrldKV1Qs2jnyi4LcFxEaldPNAiJJA5CYC3M/FFrEDw+Zh1VP6rliz0wYvEoqsIanKgDs5sVkSw7IT9iBidg8EagPrlSEcYJOSHlNVJU2a28P45az0G1drw4BWiXASmCWk6CNr2WT26vz24=,iv:8h45V8wA83AHrej7ajzV3z3KUS2eu0fegjPJk7l8YoQ=,tag:kD+QVBEur6xSI0iLEthWeg==,type:str]
crt: ENC[AES256_GCM,data:v1nr+IQowiph7eRyENALTAXHt7NPoEdbyBcb3lVxUp9Ga6nGs7IPSyzMTfc3Am4cZ/6kRpXJG30cjINWpfhFkruS6SCPqMUcsxkVw6oLArbN62wV4umws6xh0pQNajTGY8ZNhya4RudWSLAtmIlbI0aneJKxrypPQKXeX+nML48TLcV8PicAhr4gyEk967IaHLCPK3GAuDwrD/b5NsaU3sVb5bHwcSsUKUBzdAYdHTDwsuOVwbrYT0egQ+Bcn44bY/EWw8SbIa90A+iqU6R4G9V2+/eHCSdqaNX27Yp+h3YgRJH+NqItiMlgeJ99bjvYjdSaOucmUE9gcUt7BFHpEuCI6nBGduFu/PEsFd1vNB/jKjLidN/wjcHHXXfmLy1vw8ut8ns9cl3Ufyk2VcwTfexKTuZbcAIlDdc2HGB03Z8bju8Ci96+j6cZEgv04eUZSl/5erRuyrIrA/erC09J3gqz3ojMTPsGxmQs8Rbkv6ZWM2mrsBizZq3K3XMCAP/eC1072pnOjgE91PzDUmQwBSm2xxyvjDDjJxp0M5EVdTQ/GiJXtV/Fwq8NvhI1P3nkQoDOEz+i3I1ZNrsRPLoaC+HKd+DdnO24slTUXXYO2/j9/Y3z+gjj25WR3Q1iFzKXcVH5pPVToDavMKLhlchcXI0FvqjKPYyqAY1uNEg1oVlqcyD/CcOUur1ugNQGriXPf+//nb3WZgxfcZiCwyee4oCAa3OxSMIaENn3QQ6QVGNocNanvulcA9LwIMU8qXp0+AixtFvbTlM/LHBWy10YDH2LKg7kZ9ePd6JnSFYIdB8gKhnbolrtLVIpC9NDcyzYosFrZ1NsvGgEiy+5+tLl+XERaZunulZ3VBG+4w583/PfeIZ4l7kRde1HWkym5oI0IluQG9MagyIv2a2fSScZEIClDPkOJqFaL6WszVViAgB3pAL3dEr8+48=,iv:bByFvLj8L+yZ2ErlkDBHGRhnNVQsFOe7q3WwyGVWQOU=,tag:VK8iwa8m0thEBNyTvEZOZw==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:NronfCPkbPQLilb38VmliNn0Yg==,iv:AbC7xk46CJ5dqU55IbyPX+OZVEROz+OGnJfxoZmaKwE=,tag:GEsziOOFhDF16Vjqx59SNw==,type:comment]
prometheus:
key: ENC[AES256_GCM,data:MNmUv4J5qVRB2NH3vFcIPGsx8ZB7ggYLK/4HyI2hBE+KhBGgh/9GeIauN6VDxO0xPqVe6xsY41vMDE8K5wmQyvTOi2fyXzL6LDxPHKVZVUKMjRgiYKJJh9hghzVeQwKwTjfZ+eXFgSKf+ENpSgh4+zbFqm0eVhhZy6EQ38494IlwS/UsojC831xg0OeeHNF5QrE7rIG8zo+rjsntpWZbJzSM9BgzFAAfW38Ywi03lstbJi8IEq/gFmZjQxU3CFHicD0X/jKY/uBxZB8lu8Km2ZevGDLFGwGX3zpcYxou3667m9M=,iv:1Px2X4RKD1sbQ9NhFW9rf0Q8cu0ex8P2RjmR5wUmFbA=,tag:rKkYDav55LfUwUueaA9Qfg==,type:str]
crt: ENC[AES256_GCM,data:0eNafjanGCWtqCiavj5vAQKi9nczPShx0w9dfULmP03sPZTh74FCZwZvu2vQ8i0GbKNZdHMKYAP/Utyd1XdttaVr4Kvf6ggkQXj5a0AOakRmTOjkO2WhZtWUVd/fJSfcsifa1gG+x5O32yVZXx4pWSVtcV57g+BnksTyt5JBnPz+u5pEBK0VZKRSjIjlvanLM+l/Dp1tyrmKarFX+28bCDUxB3I6wZUEnF35wUNbu6pVMcR8JofQG6KrmsjixMWPRDz+u1w8b/+nTGOXyykUUjxH7a6wEf7/RTGNBwT44CrSACxybJMCed2MBjY42jA0Lm4ISwE8epvOkpptu7NRyEgMiVke5mNSH9ZGaUe6mCBpbUVkL+tUQG5rwclsL6ijaCyqsWSD5bb2i6wGhmPe5/ERKxmLjOWNeAQSf1HiJc/bSXOSpt15DuGgwwJ4jfebOqdshUy7CMc7KclQj7ppqhTYhRHbM1mJwCJr6QGVDh5xSzFDSH1qbCRdeXkPEkm7WxRJbAtzY73UmO4eqhsp5n6+PtiFNNqQg66mOGF8N7PlEKh2vcoxgtjnTTVHVlgiAHuT2TMWNuhO3qwJeF6G3zApFSPuJWcEH7PGAeezL+qUF2TzHVthOHQMUNFr1m8i4ms/hCE9DGSv5oo+X7YKO6xcIe00xNQEzMNk+HqmAKed9pl/eBHhD62UvQNZh66e1VjWpOE/ig0hP6vgKSwyQL6xUtJWWzYl184KcWokw+O9Vyw2FCR++0iu+Zn+PIYYQg4Vwpqd5VM0cj0tIB7W1+aLz8hGIOgQKDjQUshWeMxEjESILgb3+7lNyZkKT/y48ocHL/dedHRLXuxUcSQ6ZUGhWf33so7763Gimo2hK3jmIaprnx4LGyiBKPPxkSl+Bt7seUiFJ041olDmO+fUDvi0ko166CA+3mUxeoeNRh74J2RPHR8dmy1okp1NLEQztyHw4Ps=,iv:14bMOT+Fn8j2kFDQxTuwtsTyYnp4fIF0WRpQntz4afE=,tag:fWnktLbiQzG8UExa3nspUA==,type:str]
key: ENC[AES256_GCM,data:3/x/3FSbr/3uv1ziG79E/eQ7ysV8G6+tyGWC57fJjLSlNpTFsp+oc3lYWJbaTkDdazWz53CmrBrT3r2Lo4gMb6+CsxQjZWBCs7M7eJXB1p+a1ZwWOVndN0IWWnB9HkK2cmFh260s06FmPRNsnaBx3TvscWlr0+qcnmBxHE9tfT/k3ud3euhlMNovrKFnjSaPtMtDmBHFnr23ogcOBoe8/b29wtVC9ILh3B8qU30Rqsnu1kQhAYCPVrZhwYzAoVjX83uou8QV/GjaTgvbaRS3J/mK9kxRzspgb0zKxC/TPjptrxM=,iv:+Ly48Af4d/SCw5/WUKmzrkMzDXFRfc+RaCjiS7BEg5I=,tag:tUdZdfgY8cPz2M1RmtskZQ==,type:str]
crt: ENC[AES256_GCM,data:Vs69VLwR7Gw+pPuyXmyX8wRgS07fPO9+pgQOdWfTSFNcPY3M7IK08qDCxfZad3Y2/hVtW0afTq6RJWYXTr5faW1GYcp4ur1sPliU5qOYod9yJhZlxDhHPVaYfTrY1y8zy4ynW0C9tprx3AD9kGXUyXEPO2lQBgdfd50YIN0VUvTH8PC5AX+zJyytfGrfGHZlJDVBbQu3jOiuaDuyV6dszQf09PnVNaF8w67T0VUBS/01BJz46JPU/Enkzixz/ZH70wU4kHqG+77Pq4SXs1sg4kTK17Vb7px6HaOiqDy+si7nSU4Y0yQ1rKu3aqGfaz0EmKlmrxFx6RphJ1GMY+gm8k53PJubYb90DF5b/T+DYCGICYmLt9p4SEIYu3FXXcBvUkMn/Y5GGDfkbIOJLo/VqzUaGrLzy6uv90c4iGCG0FvuM+FWJAXiIC533MZt+zeEZilB5QHNqsIdLahi5kN2LCFhKHtYJVUohCt59cU0Ir3V1XTFJH8PsYAqZIwJQOhPUYmnm7GSPAWWK4i3UNH20wQY7h09MxyEgZhjcAUjtsdtZErfDykvDU4Vc+qQrH9vKGEHRberP3a/logWcYNBn3PiTPEqW3aOsVz8PdabUyFDsRymi+0eBKk5mQX4ttsPTTFZ80/t2lHoefe/SdVxGig2X+d3buUwCgXf/DZ/3KfYcZEw6Ge+HIPjYPbiJRureugy4bmL1HmhrsQCGen/mTubPH31eE33W/X4ewOgRGLjVBq6GNiJDya7jKLDn68AFX5PV+Gi7+iQAmm6zczfGWN4MR5UX14GhBNUs3NBHxx07/0Z8F+4JLMNprR+Kr/PpBP8fsOeDlbRKauQeuTaHmPkESTYtTmdzoBzV/sCwH6w2pbNoYrGLEjElhhOk3slBwaUJwQ/O+jTvw69SsmRjFrOzty7tVUEp1amNMJ976k2rYeMZehlTMk6KAewTNdI/XivEIg=,iv:J+6oHMZTFZmsDcyizC7vdJ1D1aHRJ9lcgIsCPx1fj9U=,tag:XtyFeB50RPRaW7j1JYu2jA==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:8Pk3De3AWQPtW+o18w==,iv:O05B1JyftUqN/0/pCi5HReEOltp5jJ6GRER5+NXkoMY=,tag:Tl4duRnv+EoZ4xWVwMfEWA==,type:comment]
loki:
key: ENC[AES256_GCM,data:gtQ3j59WJd3YxreCMoR70VZBJubaR8UfoeAEYgZR8KGIYkEGPnBCMEmBlu/L4qZO0fHg0DLSN/ixLbmvbOluTMNF1rbj/5W4F0O79YI2RztXZ3oxrtYf4dRPxIoCuOQi9PtD5VrduBUGGhvDW9yae9QxzQ+Ca7KwMHgOLu/5+VW6YdQRBdgEQycSDsdMvRTmFwnQuSxuQhWxhu9GqR2WGx3cinQIZ8ewr147aOiZNgUjnHLfYjtzRXqQtK0HEvjNVKBWPuOWLYRa+h10TQlKMQMZn7x7QJlDZhsF/aRqVRNwYc0=,iv:0vVdSLKOgFTwvUbW1uU2vINYwnXMIYIzOl+2BqsJ7Vk=,tag:nm34OwNXseUTzO/KUmT2Vg==,type:str]
crt: ENC[AES256_GCM,data:O1if/oi5wJQNhHftNEwRhSvdnloaiE8EIJ0dlY9DO2Lml1rz5+2LfwGnN/mHGFOkIZri2AhizPUqXios5kKYgdh7ib4jD2C4DC8crKO9XiNiiChzqE0WqP/sda9UJL9zzmmm3WtkGljcL1iyr4zuCvH0uuIMX6W+NohIu19JT9wqamwMHBCEYuQ6gFAVOBnsz5Rsa6v2LBsIsUBadAzngUcrIztT1UO0cHiplBq5s+TkC96wWrlAUiaV1Lcf1aABDrI6o2u4uv6Vwv/d4AQrEpaNvH1N3un/M3lOIek53atwMYw598/BAioJfZXRTQ4qeqcv9+WxFJSpG6XrWxx941St6N3ITJcAMVmKQFfupj257oyazHF4oqyJKsgXQBTXlMdLPDLH/HTRVfv5k1qD5RQ6AXJrMy4NQrE4BlLBPkLU1wxyg2REqd6eKMgw1PQdqTeZHZrQCGzxWeY+rv/0v5jwSFzC/YY+hOZRz3BRpfufWC9mZQZpNoH8yccVhPybVT22YMMVljLUEg/0BICm9Q4mSMpc9hEJ7VwHZo5pT4DuNiVjComTpcnnrEgK3yYGBB6jsAAkU+zNmOZ6AkCvKAma2DVbOhwUx1ZuBruc4GsfJcHGWqWogr4zBpgDg9FOtLF28yQpfKDWZEdx1eq1Bu9gFhK1FoBxrzd7mX5ADn0XeRbFDn/G+C24ShA+D+rmhARAdCtz6M4uk5IZ3w+L8NA/qWyZ+bgaXX8KrpDOV+/7KFStD4FXkVk20+IaMpb6OSYskqPq1ICI0MIFsONajO8gCd026B6WD/hLsGvkLL/58IwtAd+bRBceXHgoqXeTjyqNgKhhiqmKejZUphzrIsMx+GWdP70GOdcj41I5PSN0GbMnXQTRPGJo/GYN4pP3TbtmVrEvs5rMuGe0WiDiXQGM2hAreGMnt43Ge7DjyB+BPAx6ZA==,iv:1E1u18aWSRZFKewRnH+pJKN9xCpFUcJ0Dy4kq4yMprY=,tag:nmI9X0IrT545vUWhoIdhHg==,type:str]
key: ENC[AES256_GCM,data:9D6ZrmJTAMUf6B2k1Kzl64KgTWwe+kDTTSH9d+Ho++hHnCMKr9ORytJUsaglSip7/X6guRXh2v6s9PuTlEvEhrsAcvj1gFU1nilmq8plOzWh52U1xtpNh8Lf0z5qUYUoG4XjR2kG80B431n/iov/N5/1QWx3PauUAdSWK8ljlZ/1rPBtNjdXWCQzI7zD6p7qKUvXifp2j6r4hlkb9tJH8/t5rlD0LRU7vA7xWg06UayUw94dfgCLVDfB5zqtwPKhot4BkELMUVaUPmJoW3p1ifhZ/JOgLbB0wgt1DMmu6iI8cuw=,iv:9iJBus8P+LnhOc0YDIzwiaKRbp2kc52rW7pyayKDPKs=,tag:pDjlecxjJtdI2OY/x8SpcA==,type:str]
crt: ENC[AES256_GCM,data:cl6K1gEzaKpwr/hksXhjtQty1UGbhAIqB4YNJccfFAOx65YOc1VQ3pIMn+L/IvDQMmuJRRm7zlGdYBnrexhWwuNlcnEi6e0Q25ulneJbrkg77v7rB+ap9VWZsafTRusRlObGTA01liV21UsaDA/Kknt+PFZTAdO8EC6KXegYgAVDNIhiEKUDsaZMrdVFkTSJ00qK9zfwDJ5mcTYM5A14o+3HyK/nMXKYqR+yxwQqLVYVvD6M8SjLbe9VJfBJlOsOe6toTOHUGglBnQr7x0lsEdBvk9UZ3+S2bKpU1qqY7OIxSSjeOZ7YWUC7r8D8lhz2yIOKRFvzJPm10ZxSDq4sn4jIs/hPXIgRTfO4h33pjOhqeTM5q6SRslYpKNsudYYrI++vog2B3b8qhg5II5yJ1Xhe3MqfH4pJeIZcYS8+uIrSMPujhR5q6jVhXwoCKVpifktmz+vopmEFfXzdnaboXYtj//bbhgn0wRxONBYv3dOfQMAVDtptqNJC9iskiZTeZdNmrMTko8BhU/cZF3VML0fTVo8XAQiMvFuk5FaaJaMVFW0sbuyoR3izCm0HGMsjyHmwRov3AarzHkjj7CTuMmZ0kDPrmzPHylgasfuh7W4VTOKN9Ld6umWR2YpBYKSrrnnr7rfag2Ll7ywEcl/Sj1ZymIlsixokiMGwE564AN1MqnsYc5uiCXfo6aUg6nnbUlEg8U0j6pQWICTkFCSjKpD9RgL/FqhcsY+qiGL+pPLHBYXUVo/uARup545+2MG1PA/vCGP/WTp36siXskx3wrQywHg1TIuhTsB0rh499lJhtXP4uStX4vHbE9dDjSOss56FFENn6kc84PInv1ulWcMLKFqEfI/KTgzH2E8NImcJORne+vWkMYOpoCfHTpIQizj6/+5W/5OPKgknpMUH2MaLusopbGNDk2uC5m1my1uirT+AGg==,iv:eTcuF44+5wemJmnTQWKMQnsVH40UlyNtQPLCOklMzdY=,tag:Um5r0IUKw4/VQR7HxKaaZg==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
@@ -279,6 +302,15 @@ mealie:
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:b74wua/tFSoef2fq5jZhNQ==,iv:RDtz7Y6eFQKiVVnNvoLAfzE4xo8t7RFiKjLTbd0xo80=,tag:iUS7ofvuVj6ZTsy8zA6KSg==,type:comment]
homebox:
api_secret: ENC[AES256_GCM,data:BT6m0D9Xwa1tnQQRzpUnEF4bjLFne297oS3p8HM8Q/1Vakrf+uFcfS0C4MU=,iv:5WeWyfe8kt+AWwsV3YXxfLVuSEE8lYmNFxrXyRZzISc=,tag:cYd6U3BxqyGst+UsMpRypA==,type:str]
oidc:
secret: ENC[AES256_GCM,data:26flutXrbmP4UqyfQs4PmROQGwrJhmkTw2Ujro0jg+q5nfy1mN5i5zKcnAg=,iv:a/fwjeE7MVByIG/L8Tq2ORjF4aSOwWSusER0c3ySBV8=,tag:kkx4hAlfff42Qghe+l9uLg==,type:str]
hash: ENC[AES256_GCM,data:CRdr7WWotsMujhH5PFEFNNF/y0xLjLu6TZc93/eIZyzFZPp9kdHcVyMs8pnyQTMT5r3ynWRsT17N8s7X84uZNqPCu1o4KGvNR5UioiSecHdov1LoJbE1mYCCm2H34PEFwpjSMP013ipz0ybuaYqMTASOn1B/lOUt2bP6yj9NnlBVPUg=,iv:K4iH1ZykdgcL9a0eqCB6PPCU/8jGpX9NEt3yljbOVpY=,tag:EpWG3NEwioRietOi/IbvmA==,type:str]
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
#
#
#ENC[AES256_GCM,data:T4Wtn49AAxPd2QUFTR+q,iv:bH5goGWBDqumAat9dUv2OwfCUJUpuVqncTMqMBZUXhI=,tag:G+W6hHA+yftQ+4RJpXrxHg==,type:comment]
switch:
admin:
@@ -310,7 +342,7 @@ sops:
k+uyWPPi+enFkVaceSwMFrA66uaWWrwAj11sXEB7yzvGFPrnAGezjQ==
-----END AGE ENCRYPTED FILE-----
recipient: age120wuwcmsm845ztsvsz46pswj5je53uc2n35vadklrfqudu6cxuusxetk7y
lastmodified: "2026-06-12T13:44:23Z"
mac: ENC[AES256_GCM,data:FMOY7qMDSCzvOUL44zmwLwUVAXfYkat1JbiNeBVQFuUrC4lAwuukxW9MCPs5plj84LOAUMVgMofyudFJh5VKTR6Y0EnB5ft0NnbGgwMwzQnQn7d9Mcg9xPca3ccSbSGNx0UrC+8lTjXuzhoawMQ5ZXZ3ZK0k7JrFQ2El7UUwxEA=,iv:+oj0bvsDo8gidUn6m0FOeWsJOxIXPHhzvtiIbQ1VgcY=,tag:8xLcthizKqWjqg2eQvJlRA==,type:str]
lastmodified: "2026-06-23T00:53:05Z"
mac: ENC[AES256_GCM,data:30XGjw0JCuUTo5nbcAYkIeMyJ2H50f8F9hdTArYLxAlRz0iffx0n7BXH/LLtI/5FKcOmfhoHP/CT22wcSFxKAPzWD5Ce/xNGAFNoTftodU4CE+/VHn7VFhWYaXVjnQs2duuy4QHQvcDa8uMo+c7ZunqrkZO0zU5fKSYOaJMLloQ=,iv:MyMgnJ96P9RoDYxsbJVZnnK1q82o5AB/hueMXeQhKqQ=,tag:1s288++ruxdUfSnyP8/ecA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.1
@@ -15,7 +15,7 @@ HostName=gitea
PublishPort={{ services['gitea']['ports']['http'] }}:3000/tcp
Volume=%h/data/containers/gitea:/data:rw
Volume=%h/data/containers/gitea/data:/data:rw
Volume=%h/containers/gitea/ssl:/etc/ssl/gitea:ro
# General
@@ -23,7 +23,7 @@ Environment="TZ={{ timezone }}"
Environment="GITEA__server__DISABLE_SSH=true"
# Database
Environment="GITEA__database__DB_TYPE=postgres"
Environment="GITEA__database__HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}"
Environment="GITEA__database__HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}"
Environment="GITEA__database__NAME=gitea_db"
Environment="GITEA__database__USER=gitea"
Secret=GITEA__database__PASSWD,type=env
@@ -34,7 +34,7 @@ Environment="GITEA__oauth2_client__ACCOUNT_LINKING=auto"
# OIDC configuration
Environment="GITEA__openid__ENABLE_OPENID_SIGNIN=false"
Environment="GITEA__openid__ENABLE_OPENID_SIGNUP=true"
Environment="GITEA__openid__WHITELISTED_URIS={{ services['authelia']['domain'] }}.{{ domain['public'] }}"
Environment="GITEA__openid__WHITELISTED_URIS={{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}"
# automatic create user via authelia
Environment="GITEA__service__DISABLE_REGISTRATION=false"
Environment="GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION=true"
@@ -42,7 +42,7 @@ Environment="GITEA__service__SHOW_REGISTRATION_BUTTON=false"
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -0,0 +1,49 @@
[Quadlet]
DefaultDependencies=false
[Unit]
Description=Homebox
[Container]
Image=ghcr.io/sysadminsmedia/homebox:{{ version['containers']['homebox'] }}
ContainerName=homebox
HostName=homebox
PublishPort={{ services['homebox']['ports']['http'] }}:7745
# Volumes
Volume=%h/data/containers/homebox/data:/data:rw
Volume=%h/containers/homebox/ssl:/etc/ssl/homebox:ro
# Environment
Environment="TZ={{ timezone }}"
Secret=HBOX_AUTH_API_KEY_PEPPER,type=env
# OIDC
Environment="HBOX_OIDC_ENABLED=true"
Environment="HBOX_OIDC_ISSUER_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}"
Environment="HBOX_OIDC_CLIENT_ID=homebox"
Environment="HBOX_OIDC_SCOPE=openid profile email groups"
Environment="HBOX_OPTIONS_TRUST_PROXY=true"
Environment="HBOX_OIDC_ALLOWED_GROUPS=admins,users"
Environment="HBOX_OPTIONS_ALLOW_LOCAL_LOGIN=false"
Secret=HBOX_OIDC_CLIENT_SECRET,type=env
# Database
Environment="HBOX_DATABASE_DRIVER=postgres"
Environment="HBOX_DATABASE_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="HBOX_DATABASE_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="HBOX_DATABASE_USERNAME=homebox"
Environment="HBOX_DATABASE_DATABASE=homebox_db"
Environment="HBOX_DATABASE_SSL_MODE=verify-full"
Environment="HBOX_DATABASE_SSL_ROOT_CERT=/etc/ssl/homebox/{{ root_cert_filename }}"
Secret=HBOX_DATABASE_PASSWORD,type=env
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
[Install]
WantedBy=default.target
@@ -20,7 +20,7 @@ AddDevice=/dev/dri:/dev/dri
PodmanArgs=--group-add keep-groups
# Volumes
Volume=%h/data/containers/immich:/data:rw
Volume=%h/data/containers/immich/data:/data:rw
Volume=%h/containers/immich/ssl:/etc/ssl/immich:ro
# Environment
@@ -34,7 +34,7 @@ Environment="REDIS_PORT={{ services['immich']['ports']['redis'] }}"
Environment="REDIS_DBINDEX=0"
# Database
Environment="DB_HOSTNAME={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="DB_HOSTNAME={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="DB_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="DB_USERNAME=immich"
Environment="DB_DATABASE_NAME=immich_db"
@@ -44,7 +44,7 @@ Environment="NODE_EXTRA_CA_CERTS=/etc/ssl/immich/{{ root_cert_filename }}"
Secret=IMMICH_DB_PASSWORD,target=/run/secrets/DB_PASSWORD
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -21,7 +21,7 @@ Environment="ALLOW_PASSWORD_LOGIN=false"
# Database (PostgreSQL)
Environment="DB_ENGINE=postgres"
Environment="POSTGRES_SERVER={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="POSTGRES_SERVER={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="POSTGRES_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="POSTGRES_USER=mealie"
Environment="POSTGRES_DB=mealie_db"
@@ -32,7 +32,7 @@ Secret=MEALIE_POSTGRES_PASSWORD,type=env,target=POSTGRES_PASSWORD
# OIDC
Environment="OIDC_AUTH_ENABLED=true"
Environment="OIDC_SIGNUP_ENABLED=true"
Environment="OIDC_CONFIGURATION_URL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/.well-known/openid-configuration"
Environment="OIDC_CONFIGURATION_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/.well-known/openid-configuration"
Environment="OIDC_CLIENT_ID=mealie"
Environment="OIDC_AUTO_REDIRECT=false"
Environment="OIDC_ADMIN_GROUP=admins"
@@ -40,7 +40,7 @@ Environment="OIDC_USER_GROUP=users"
Secret=MEALIE_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -25,7 +25,7 @@ Environment="MEMOS_DRIVER=postgres"
Secret=MEMOS_DSN,type=env
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -4,6 +4,9 @@ DefaultDependencies=false
[Unit]
Description=Nextcloud
After=redis_nextcloud.service
Wants=redis_nextcloud.service
[Container]
Image=docker.io/library/nextcloud:{{ version['containers']['nextcloud'] }}
ContainerName=nextcloud
@@ -29,7 +29,7 @@ Secret=OUTLINE_SECRET_KEY,type=env,target=SECRET_KEY
Secret=OUTLINE_UTILS_SECRET,type=env,target=UTILS_SECRET
# Database
Environment="DATABASE_HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="DATABASE_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="DATABASE_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="DATABASE_NAME=outline_db"
Environment="DATABASE_USER=outline"
@@ -47,9 +47,9 @@ Environment="FILE_STORAGE_UPLOAD_MAX_SIZE=262144000"
# OIDC
Environment="OIDC_CLIENT_ID=outline"
Environment="OIDC_AUTH_URI=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/api/oidc/authorization"
Environment="OIDC_TOKEN_URI=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/api/oidc/token"
Environment="OIDC_USERINFO_URI=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/api/oidc/userinfo"
Environment="OIDC_AUTH_URI=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/api/oidc/authorization"
Environment="OIDC_TOKEN_URI=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/api/oidc/token"
Environment="OIDC_USERINFO_URI=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/api/oidc/userinfo"
Environment="OIDC_USERNAME_CLAIM=preferred_username"
Environment="OIDC_DISPLAY_NAME=Authelia"
Environment="OIDC_SCOPES=openid offline_access profile email"
@@ -69,7 +69,7 @@ Secret=OUTLINE_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET
Environment="LOG_LEVEL=warn"
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -35,7 +35,7 @@ Secret=PAPERLESS_SECRET_KEY,type=env
Environment="PAPERLESS_REDIS=redis://host.containers.internal:{{ services['paperless']['ports']['redis'] }}"
# Database
Environment="PAPERLESS_DBHOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="PAPERLESS_DBHOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="PAPERLESS_DBPORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="PAPERLESS_DBNAME=paperless_db"
Environment="PAPERLESS_DBUSER=paperless"
@@ -50,7 +50,7 @@ Environment="PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS=true"
Secret=PAPERLESS_SOCIALACCOUNT_PROVIDERS,type=env
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -29,7 +29,7 @@ Secret=SURE_SECRET_KEY_BASE,type=env,target=SECRET_KEY_BASE
# PostgreSQL
Environment="POSTGRES_USER=sure"
Environment="POSTGRES_DB=sure_db"
Environment="DB_HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="DB_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="DB_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="PGSSLMODE=verify-full"
Environment="PGSSLROOTCERT=/etc/ssl/sure/{{ root_cert_filename }}"
@@ -40,7 +40,7 @@ Environment="REDIS_URL=redis://host.containers.internal:{{ services['sure']['por
# OIDC - Authelia
Environment="OIDC_CLIENT_ID=sure"
Environment="OIDC_ISSUER=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}"
Environment="OIDC_ISSUER=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}"
Environment="OIDC_REDIRECT_URI=https://{{ services['sure']['domain']['public'] }}.{{ domain['public'] }}/auth/openid_connect/callback"
Secret=SURE_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET
Environment="OIDC_BUTTON_LABEL=Sign in with Authelia"
@@ -58,7 +58,7 @@ Environment="EXCHANGE_RATE_PROVIDER=yahoo_finance"
Environment="SECURITIES_PROVIDER=yahoo_finance"
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -29,7 +29,7 @@ Secret=SURE_SECRET_KEY_BASE,type=env,target=SECRET_KEY_BASE
# PostgreSQL
Environment="POSTGRES_USER=sure"
Environment="POSTGRES_DB=sure_db"
Environment="DB_HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}"
Environment="DB_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}"
Environment="DB_PORT={{ services['postgresql']['ports']['tcp'] }}"
Environment="PGSSLMODE=verify-full"
Environment="PGSSLROOTCERT=/etc/ssl/sure/{{ root_cert_filename }}"
@@ -40,7 +40,7 @@ Environment="REDIS_URL=redis://host.containers.internal:{{ services['sure']['por
# OIDC - Authelia
Environment="OIDC_CLIENT_ID=sure"
Environment="OIDC_ISSUER=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}"
Environment="OIDC_ISSUER=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}"
Environment="OIDC_REDIRECT_URI=https://{{ services['sure']['domain']['public'] }}.{{ domain['public'] }}/auth/openid_connect/callback"
Secret=SURE_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET
Environment="OIDC_BUTTON_LABEL=Sign in with Authelia"
@@ -58,7 +58,7 @@ Environment="EXCHANGE_RATE_PROVIDER=yahoo_finance"
Environment="SECURITIES_PROVIDER=yahoo_finance"
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -15,7 +15,7 @@ HostName=vaultwarden
PublishPort={{ services['vaultwarden']['ports']['http'] }}:80/tcp
Volume=%h/data/containers/vaultwarden:/data:rw
Volume=%h/data/containers/vaultwarden/data:/data:rw
Volume=%h/containers/vaultwarden/ssl:/etc/ssl/vaultwarden:ro
Environment="TZ={{ timezone }}"
@@ -25,7 +25,7 @@ Secret=VW_ADMIN_TOKEN,type=env,target=ADMIN_TOKEN
Secret=VW_DATABASE_URL,type=env,target=DATABASE_URL
[Service]
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
Restart=always
RestartSec=10s
TimeoutStopSec=120
@@ -56,9 +56,9 @@ Exec=--config /config/authelia.yaml
# Wait for dependency
# They run as rootless podman container, so their port is not opened until they are normally running
# Check their ports with nc command
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
# services['ldap']['ports']['ldaps'] is 6360, but nftables works on 636 the original port
ExecStartPre=/usr/bin/nc -zv {{ services['ldap']['domain'] }}.{{ domain['internal'] }} 636
ExecStartPre=/usr/bin/nc -zv {{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }} 636
ExecStartPre=sleep 5
Restart=always
RestartSec=10s
@@ -30,12 +30,12 @@ identity_validation:
# Authentication backend provider configuration
authentication_backend:
ldap:
# ldaps uses 636 -> NAT automatically change port 636 in output packet -> 2636 which lldap server uses.
address: 'ldaps://{{ services['ldap']['domain'] }}.{{ domain['internal'] }}'
# ldaps uses 636 -> NAT automatically change port 636 in output packet -> 6360 which lldap server uses.
address: 'ldaps://{{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}'
implementation: 'lldap'
# tls configuration, it uses certificates_directory's /etc/ssl/authelia/{{ root_cert_filename }}
tls:
server_name: '{{ services['ldap']['domain'] }}.{{ domain['internal'] }}'
server_name: '{{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}'
skip_verify: false
# LLDAP base DN
base_dn: '{{ domain['dc'] }}'
@@ -53,7 +53,7 @@ access_control:
default_policy: 'deny'
rules:
# authelia portal
- domain: '{{ services['authelia']['domain'] }}.{{ domain['public'] }}'
- domain: '{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}'
policy: 'bypass'
# - domain: 'test.ilnmors.com'
# policy: 'one_factor'
@@ -68,7 +68,7 @@ session:
cookies:
- name: 'authelia_public_session'
domain: '{{ domain['public'] }}'
authelia_url: 'https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}'
authelia_url: 'https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}'
same_site: 'lax'
# This authelia doesn't use Redis.
@@ -77,12 +77,12 @@ session:
storage:
encryption_key: '' # $AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE is designated in container file
postgres:
address: 'tcp://{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}'
address: 'tcp://{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}'
database: 'authelia_db'
username: 'authelia'
password: '' # $AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE is designated in container file
tls:
server_name: '{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}'
server_name: '{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}'
skip_verify: false
# Notification provider
@@ -94,47 +94,18 @@ notifier:
identity_providers:
oidc:
hmac_secret: '' # $AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE
jwks:{% raw %}
jwks:
- algorithm: 'RS256'
use: 'sig'
key: {{ secret "/run/secrets/AUTHELIA_JWKS_RS256" | mindent 10 "|" | msquote }}
key: {% raw %}{{ secret "/run/secrets/AUTHELIA_JWKS_RS256" | mindent 10 "|" | msquote }}{% endraw %}{{ '' }}
- algorithm: 'ES256'
use: 'sig'
key: {{ secret "/run/secrets/AUTHELIA_JWKS_ES256" | mindent 10 "|" | msquote }}{% endraw %}
key: {% raw %}{{ secret "/run/secrets/AUTHELIA_JWKS_ES256" | mindent 10 "|" | msquote }}{% endraw %}{{ '' }}
lifespans:
access_token: '1h'
id_token: '1h'
refresh_token: '720h'
clients:
# https://www.authelia.com/integration/openid-connect/clients/synology-dsm/
- client_id: 'dsm'
client_name: 'dsm'
consent_mode: 'implicit'
# It depends on application
# hash value generate:
# podman exec -it authelia sh
# authelia crypto hash generate pbkdf2 --password 'password'
client_secret: '{{ hostvars['console']['dsm']['oidc']['hash'] }}'
# If there were not client secret, public should be `true` [true | false]
public: false
authorization_policy: 'one_factor'
require_pkce: false
pkce_challenge_method: ''
redirect_uris:
- 'https://{{ services['nas']['domain'] }}.{{ domain['internal'] }}:{{ services['nas']['ports']['https'] }}'
scopes:
- 'openid'
- 'profile'
- 'groups'
- 'email'
response_types:
- 'code'
grant_types:
- 'authorization_code'
access_token_signed_response_alg: 'none'
userinfo_signed_response_alg: 'none'
# [ client_secret_post | client_secret_basic ]
token_endpoint_auth_method: 'client_secret_post'
# https://www.authelia.com/integration/openid-connect/clients/gitea/
- client_id: 'gitea'
client_name: 'gitea'
@@ -319,3 +290,24 @@ identity_providers:
access_token_signed_response_alg: 'none'
userinfo_signed_response_alg: 'none'
token_endpoint_auth_method: 'client_secret_basic'
# https://www.authelia.com/integration/openid-connect/clients/homebox/
- client_id: 'homebox'
client_name: 'homebox'
consent_mode: 'implicit'
client_secret: '{{ hostvars['console']['homebox']['oidc']['hash'] }}'
public: false
authorization_policy: 'one_factor'
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://{{ services['homebox']['domain']['public'] }}.{{ domain['public'] }}/api/v1/users/login/oidc/callback'
scopes:
- 'openid'
- 'groups'
- 'email'
- 'profile'
response_types:
- 'code'
grant_types:
- 'authorization_code'
token_endpoint_auth_method: 'client_secret_basic'
@@ -19,9 +19,9 @@ Image={{ domain['internal'] }}/{{ node['name'] }}/caddy:{{ version['containers']
ContainerName=caddy_{{ node['name'] }}
HostName=caddy_{{ node['name'] }}
{% if node['name'] == 'infra' %}
AddHost={{ services['ca']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['prometheus']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['loki']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['ca']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['prometheus']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['loki']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
{% endif %}
PublishPort={{ services['caddy']['ports']['http'] }}:80/tcp
@@ -0,0 +1,92 @@
{% if node['name'] == 'auth' %}
{
# CrowdSec LAPI connection
crowdsec {
api_url https://{{ services['crowdsec']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}
api_key "{file./run/secrets/CADDY_CROWDSEC_KEY}"
}
}
# Snippets
# CrowdSec log for parser
(crowdsec_log) {
log {
output file /log/access.log {
mode 0644
roll_size 100MiB
roll_keep 1
}
format json
}
}
{% endif %}
{% if node['name'] == 'app' %}
{
servers {
# Only accept packets from auth main caddy
trusted_proxies static {{ hostvars['fw']['network4']['auth']['server'] }} {{ hostvars['fw']['network6']['auth']['server'] }}
}
}
{% endif %}
# Private TLS ACME with DNS-01-challenge
(private_tls) {
tls {
issuer acme {
dir https://{{ services['ca']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}/acme/acme@{{ domain['internal'] }}/directory
dns rfc2136 {
server {{ services['bind']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['bind']['ports']['dns'] }}
key_name acme-key
key_alg hmac-sha256
key "{file./run/secrets/CADDY_ACME_KEY}"
}
resolvers {{ services['bind']['domain']['internal'] }}.{{ domain['internal'] }}
}
}
}
# Node domain
{{ node['name'] }}.{{ domain['internal'] }} {
import private_tls
metrics
}
# Applications
{% for item in caddy_services %}
{% set service = services[item] %}
{% set web_type = service['dependency']['web'] %}
{% if node['name'] == 'auth' %}
{% if service['domain']['public'] == '@' %}
{{ domain['public'] }} {
{% else %}
{{ service['domain']['public'] }}.{{ domain['public'] }} {
{% endif %}
import crowdsec_log
route {
crowdsec
{% if web_type == 'auth-public' %}
reverse_proxy host.containers.internal:{{ service['ports']['http'] }}
{% else %}
reverse_proxy https://{{ service['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
{% endif %}
}
}
{% elif node['name'] == 'infra' %}
{{ service['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
route {
{% if service['ports']['https'] is defined %}
reverse_proxy https://{{ service['domain']['internal'] }}.{{ domain['internal'] }}:{{ service['ports']['https'] }}
{% else %}
reverse_proxy host.containers.internal:{{ service['ports']['http'] }}
{% endif %}
}
}
{% elif node['name'] == 'app' %}
{{ service['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ service['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{% endif %}
{% endfor %}{{ '' }}
@@ -1,91 +0,0 @@
{
servers {
# Only accept packets from auth main caddy
trusted_proxies static {{ hostvars['fw']['network4']['auth']['server'] }} {{ hostvars['fw']['network6']['auth']['server'] }}
}
}
# Private TLS ACME with DNS-01-challenge
(private_tls) {
tls {
issuer acme {
dir https://{{ services['ca']['domain'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}/acme/acme@{{ domain['internal'] }}/directory
dns rfc2136 {
server {{ services['bind']['domain'] }}.{{ domain['internal'] }}:{{ services['bind']['ports']['dns'] }}
key_name acme-key
key_alg hmac-sha256
key "{file./run/secrets/CADDY_ACME_KEY}"
}
resolvers {{ services['bind']['domain'] }}.{{ domain['internal'] }}
}
}
}
{{ node['name'] }}.{{ domain['internal'] }} {
import private_tls
metrics
}
# test.app.ilnmors.internal {
# import private_tls
# root * /usr/share/caddy
# file_server
# }
{{ services['vaultwarden']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['vaultwarden']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['gitea']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['gitea']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['immich']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['immich']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['paperless']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['paperless']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['nextcloud']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['nextcloud']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['collabora']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['collabora']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['sure']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['sure']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['outline']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['outline']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['memos']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['memos']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
{{ services['mealie']['domain']['internal'] }}.{{ domain['internal'] }} {
import private_tls
reverse_proxy host.containers.internal:{{ services['mealie']['ports']['http'] }} {
header_up Host {http.request.header.X-Forwarded-Host}
}
}
@@ -1,162 +0,0 @@
{
# CrowdSec LAPI connection
crowdsec {
api_url https://{{ services['crowdsec']['domain'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}
api_key "{file./run/secrets/CADDY_CROWDSEC_KEY}"
}
}
# Snippets
# CrowdSec log for parser
(crowdsec_log) {
log {
output file /log/access.log {
mode 0644
roll_size 100MiB
roll_keep 1
}
format json
}
}
# Private TLS ACME with DNS-01-challenge
(private_tls) {
tls {
issuer acme {
dir https://{{ services['ca']['domain'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}/acme/acme@{{ domain['internal'] }}/directory
dns rfc2136 {
server {{ services['bind']['domain'] }}.{{ domain['internal'] }}:{{ services['bind']['ports']['dns'] }}
key_name acme-key
key_alg hmac-sha256
key "{file./run/secrets/CADDY_ACME_KEY}"
}
resolvers {{ services['bind']['domain'] }}.{{ domain['internal'] }}
}
}
}
# Public domain
{{ services['authelia']['domain'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy host.containers.internal:{{ services['authelia']['ports']['http'] }}
}
}
# test.ilnmors.com {
# import crowdsec_log
# route {
# crowdsec
# forward_auth host.containers.internal:9091 {
# # Authelia Forward Auth endpoint URI
# uri /api/authz/forward-auth
# copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
# }
# root * /usr/share/caddy
# file_server
# }
# }
# test.app.ilnmors.com {
# import crowdsec_log
# route {
# crowdsec
# reverse_proxy https://test.app.ilnmors.internal {
# header_up Host {http.reverse_proxy.upstream.host}
# }
# }
# }
{{ services['vaultwarden']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{ services['vaultwarden']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['gitea']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{ services['gitea']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['immich']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{ services['immich']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['paperless']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{ services['paperless']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['nextcloud']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['collabora']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['collabora']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['sure']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['sure']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['outline']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['outline']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['memos']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['memos']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
{{ services['mealie']['domain']['public'] }}.{{ domain['public'] }} {
import crowdsec_log
route {
crowdsec
reverse_proxy https://{{services['mealie']['domain']['internal'] }}.{{ domain['internal'] }} {
header_up Host {http.reverse_proxy.upstream.host}
}
}
}
# Internal domain
{{ node['name'] }}.{{ domain['internal'] }} {
import private_tls
metrics
}
@@ -1,41 +0,0 @@
# Private TLS ACME with DNS-01-challenge
(private_tls) {
tls {
issuer acme {
dir https://{{ services['ca']['domain'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}/acme/acme@{{ domain['internal'] }}/directory
dns rfc2136 {
server {{ services['bind']['domain'] }}.{{ domain['internal'] }}:{{ services['bind']['ports']['dns'] }}
key_name acme-key
key_alg hmac-sha256
key "{file./run/secrets/CADDY_ACME_KEY}"
}
resolvers {{ services['bind']['domain'] }}.{{ domain['internal'] }}
}
}
}
{{ node['name'] }}.{{ domain['internal'] }} {
import private_tls
metrics
}
{{ services['ldap']['domain'] }}.{{ domain['internal'] }} {
import private_tls
route {
reverse_proxy host.containers.internal:{{ services['ldap']['ports']['http'] }}
}
}
{{ services['prometheus']['domain'] }}.{{ domain['internal'] }} {
import private_tls
route {
reverse_proxy https://{{ services['prometheus']['domain'] }}.{{ domain['internal'] }}:{{ services['prometheus']['ports']['https'] }}
}
}
{{ services['grafana']['domain'] }}.{{ domain['internal'] }} {
import private_tls
route {
reverse_proxy host.containers.internal:{{ services['grafana']['ports']['http'] }}
}
}
@@ -6,7 +6,7 @@
"address": ":9000",
"insecureAddress": "",
"dnsNames": [
"{{ services['ca']['domain'] }}.{{ domain['internal'] }}"
"{{ services['ca']['domain']['internal'] }}.{{ domain['internal'] }}"
],
"logger": {
"format": "text"
@@ -1,6 +1,6 @@
{
"ca-url": "https://{{ services['ca']['domain'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}",
"ca-url": "https://{{ services['ca']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['ca']['ports']['https'] }}",
"ca-config": "/home/step/config/ca.json",
"fingerprint": "215c851d2d0d2dbf90fc3507425207c29696ffd587c640c94a68dddb1d84d8e8",
"fingerprint": "",
"root": "/home/step/certs/{{ root_cert_filename }}"
}
@@ -8,13 +8,13 @@ provisioning = /etc/grafana/provisioning
[server]
protocol = http
http_port = {{ services['grafana']['ports']['http'] }}
domain = {{ services['grafana']['domain'] }}.{{ domain['internal'] }}
root_url = http://{{ services['grafana']['domain'] }}.{{ domain['internal'] }}/
domain = {{ services['grafana']['domain']['internal'] }}.{{ domain['internal'] }}
root_url = https://{{ services['grafana']['domain']['internal'] }}.{{ domain['internal'] }}/
router_logging = false
[database]
type = postgres
host = {{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}
host = {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}
name = grafana_db
user = grafana
password = $__file{/run/secrets/GF_DB_PASSWORD}
@@ -1,6 +1,6 @@
# https://github.com/lldap/lldap/blob/main/example_configs/grafana_ldap_config.toml
[[servers]]
host = "{{ services['ldap']['domain'] }}.{{ domain['internal'] }}"
host = "{{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}"
port = {{ services['ldap']['ports']['ldaps'] }}
# Activate STARTTLS or LDAPS
use_ssl = true
@@ -4,7 +4,7 @@ apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: https://{{ services['prometheus']['domain'] }}.{{ domain['internal'] }}:{{ services['prometheus']['ports']['https'] }}
url: https://{{ services['prometheus']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['prometheus']['ports']['https'] }}
access: proxy
isDefault: true
jsonData:
@@ -16,7 +16,7 @@ datasources:
- name: Loki
type: loki
url: https://{{ services['loki']['domain'] }}.{{ domain['internal'] }}:{{ services['loki']['ports']['https'] }}
url: https://{{ services['loki']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['loki']['ports']['https'] }}
access: proxy
jsonData:
tlsAuth: false
@@ -13,10 +13,10 @@ Image=docker.io/grafana/grafana:{{ version['containers']['grafana'] }}
ContainerName=grafana
HostName=grafana
AddHost={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['ldap']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['prometheus']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['loki']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['ldap']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['prometheus']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['loki']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
PublishPort={{ services['grafana']['ports']['http'] }}:3000/tcp
@@ -13,7 +13,7 @@ Image=docker.io/lldap/lldap:{{ version['containers']['ldap'] }}
ContainerName=ldap
HostName=ldap
# They are at the same host (for Pasta, it is needed)
AddHost={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:host-gateway
AddHost={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:host-gateway
# For LDAPS - 636 > 6360 nftables
PublishPort={{ services['ldap']['ports']['ldaps'] }}:6360/tcp
# Web UI
@@ -1,6 +1,5 @@
# @authcomment@
# TYPE DATABASE USER ADDRESS METHOD
# Local host `trust`
local all all trust
@@ -11,8 +11,12 @@ StandardOutput=journal
StandardError=journal
ExecStartPre=/usr/bin/podman exec postgresql sh -c "mkdir -p /backups/cluster && chown postgres:root /backups/cluster && chmod 770 /backups/cluster"
ExecStartPre=/usr/bin/podman exec postgresql sh -c "mkdir -p /backups/sql && chown postgres:root /backups/sql && chmod 770 /backups/sql"
# Run the script
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'pg_dumpall -U postgres --schema-only | grep -v -E "CREATE ROLE postgres" > /backups/cluster/pg_cluster_$(date "+%%Y-%%m-%%d").sql'
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c "find /backups/cluster -maxdepth 1 -type f -mtime +7 -delete"
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'cp /backups/cluster/pg_cluster_$(date "+%%Y-%%m-%%d").sql /backups/sql/pg_cluster.sql'
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c "find /backups/cluster -maxdepth 1 -type f -name 'pg_cluster_*.sql' -mtime +7 -delete"
ExecStart=/usr/bin/podman exec postgresql sh -c "chown -R postgres:root /backups/cluster && chmod 660 /backups/cluster/*"
ExecStart=/usr/bin/podman exec postgresql sh -c "chown -R postgres:root /backups/sql && chmod 660 /backups/sql/pg_cluster.sql"
@@ -11,6 +11,7 @@ StandardOutput=journal
StandardError=journal
ExecStartPre=/usr/bin/podman exec postgresql sh -c "mkdir -p /backups/%i && chown postgres:root /backups/%i && chmod 770 /backups/%i"
ExecStartPre=/usr/bin/podman exec postgresql sh -c "mkdir -p /backups/sql && chown postgres:root /backups/sql && chmod 770 /backups/sql"
# Run the script
## DB Connection
@@ -21,5 +22,7 @@ ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'printf "SET session
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'pg_dump -U postgres -d %i_db --data-only >> /backups/%i/pg_%i_$(date "+%%Y-%%m-%%d").sql'
## reset session replication role
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'printf "SET session_replication_role = origin;\n" >> /backups/%i/pg_%i_$(date "+%%Y-%%m-%%d").sql'
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c "find /backups/%i -maxdepth 1 -type f -mtime +7 -delete"
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'cp -a /backups/%i/pg_%i_$(date "+%%Y-%%m-%%d").sql /backups/sql/pg_%i.sql'
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c "find /backups/%i -maxdepth 1 -type f -name 'pg_%i_*.sql' -mtime +7 -delete"
ExecStart=/usr/bin/podman exec postgresql sh -c "chown -R postgres:root /backups/%i && chmod 660 /backups/%i/*"
ExecStart=/usr/bin/podman exec postgresql sh -c "chown -R postgres:root /backups/sql && chmod 660 /backups/sql/pg_%i.sql"
@@ -24,7 +24,7 @@ scrape_configs:
scheme: "https"
tls_config:
ca_file: "/etc/ssl/prometheus/{{ root_cert_filename }}"
server_name: "{{ services['prometheus']['domain'] }}.{{ domain['internal'] }}"
server_name: "{{ ['internal'] }}.{{ domain['internal'] }}"
static_configs:
- targets: ["localhost:9090"]
# The label name is added as a label `label_name=<label_value>` to any timeseries scraped from this config.
@@ -1,7 +1,7 @@
groups:
- name: node_exporters_heartbeat
rules:
{% for instance in ['vmm', 'fw', 'infra', 'auth', 'app'] %}
{% for instance in ["vmm", "fw", "infra", "auth", "app"] %}
- alert: {{ instance }}_node_exporter_down
expr: |
(present_over_time(up{instance="{{ instance }}"}[5m]) or on() vector(0)) == 0
@@ -25,7 +25,7 @@ groups:
description: "postgresql node is down for 5 mins."
- name: Certificate_expiry_check
rules:
{% for filename in ['root.crt', 'intermediate.crt', 'crowdsec.crt', 'blocky.crt', 'postgresql.crt', 'ldap.crt', 'prometheus.crt', 'loki.crt', 'dsm.crt'] %}
{% for filename in ["root.crt", "intermediate.crt", "crowdsec.crt", "blocky.crt", "kopia.crt", "postgresql.crt", "ldap.crt", "prometheus.crt", "loki.crt"] %}
- alert: {{ filename | replace('.', '_') }}_is_expired_soon
expr: |
max(x509_cert_not_after{filename="{{ filename }}"}) - time() < 2592000
@@ -0,0 +1,13 @@
[gdrive]
type = drive
client_id = {{ hostvars['console']['rclone']['google_drive']['oidc']['id'] }}
client_secret = {{ hostvars['console']['rclone']['google_drive']['oidc']['secret'] }}
scope = drive
token = {{
{
"access_token":"expired",
"token_type":"Bearer",
"refresh_token":hostvars['console']['rclone']['google_drive']['refresh_token'],
"expiry":"1970-01-01T00:00:00Z",
} | to_json
}}
@@ -0,0 +1,28 @@
[Unit]
Description=Daily Kopia rclone backup service
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
# logging
StandardOutput=journal
StandardError=journal
Environment="RCLONE_CONFIG=/etc/secrets/{{ node['uid'] }}/rclone/rclone.config"
ExecStart=/usr/bin/rclone copy %h/data/kopia/ gdrive:kopia \
--transfers=4 \
--checkers=4 \
--tpslimit=8 \
--tpslimit-burst=4 \
--drive-chunk-size=64M \
--retries=20 \
--low-level-retries=10 \
--retries-sleep=30s \
--timeout=3m \
--contimeout=30s \
--drive-stop-on-upload-limit \
--stats=30s \
--log-level=INFO
@@ -0,0 +1,10 @@
[Unit]
Description=Daily Kopia rclone backup timer
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
@@ -6,7 +6,7 @@
//// Metric ouput
prometheus.remote_write "prometheus" {
endpoint {
url = "https://{{ services['prometheus']['domain'] }}.{{ domain['internal'] }}:{{ services['prometheus']['ports']['https'] }}/api/v1/write"
url = "https://{{ services['prometheus']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['prometheus']['ports']['https'] }}/api/v1/write"
}
}
@@ -71,8 +71,8 @@ prometheus.scrape "system" {
////// For Crowdsec metrics
prometheus.scrape "crowdsec" {
targets = [
{ "__address__" = "{{ services['crowdsec']['domain'] }}.{{ domain['internal'] }}:6060", "job" = "crowdsec" },
{ "__address__" = "{{ services['crowdsec']['domain'] }}.{{ domain['internal'] }}:60601", "job" = "crowdsec-bouncer" },
{ "__address__" = "{{ services['crowdsec']['domain']['internal'] }}.{{ domain['internal'] }}:6060", "job" = "crowdsec" },
{ "__address__" = "{{ services['crowdsec']['domain']['internal'] }}.{{ domain['internal'] }}:60601", "job" = "crowdsec-bouncer" },
]
honor_labels = true
forward_to = [prometheus.relabel.default_label.receiver]
@@ -83,7 +83,7 @@ prometheus.scrape "crowdsec" {
////// For postgresql metrics
prometheus.exporter.postgres "postgresql" {
data_source_names = [
"postgres://alloy@{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}/postgres?sslmode=verify-full",
"postgres://alloy@{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}/postgres?sslmode=verify-full",
]
}
prometheus.scrape "postgresql" {
@@ -99,7 +99,7 @@ prometheus.scrape "x509" {
}
{% endif %}
{% if node['name'] in ['infra', 'auth', 'app'] %}
{% if node['name'] in ["infra", "auth", "app"] %}
////// For Input Caddy metrics
prometheus.scrape "caddy" {
targets = [
@@ -114,7 +114,7 @@ prometheus.scrape "caddy" {
//// Logs output
loki.write "loki" {
endpoint {
url = "https://{{ services['loki']['domain'] }}.{{ domain['internal'] }}:{{ services['loki']['ports']['https'] }}/loki/api/v1/push"
url = "https://{{ services['loki']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['loki']['ports']['https'] }}/loki/api/v1/push"
tenant_id = "{{ domain['internal'] }}"
}
}
@@ -8,7 +8,7 @@ log_compression: true
log_max_size: 100
log_max_backups: 3
log_max_age: 30
api_url: "https://{{ services['crowdsec']['domain'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}"
api_url: "https://{{ services['crowdsec']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}"
api_key: "{{ hostvars['console']['crowdsec']['bouncer']['fw'] }}"
insecure_skip_verify: false
disable_ipv6: false
@@ -1,3 +1,3 @@
url: https://{{ services['crowdsec']['domain'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}
url: https://{{ services['crowdsec']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['crowdsec']['ports']['https'] }}
login: {{ node['name'] }}
password: {{ hostvars['console']['crowdsec']['machine'][node['name']] }}
@@ -28,8 +28,10 @@ BindPaths=/etc/secrets/{{ kopia_uid }}
BindPaths=/var/cache/kopia
EnvironmentFile=/etc/secrets/{{ kopia_uid }}/kopia.env
ExecStartPre=/usr/bin/nc -zv {{ services['kopia']['domain']['internal'] }}.{{ domain['internal'] }} 443
ExecStartPre=/usr/bin/kopia repository connect server \
--url=https://{{ services['kopia']['domain'] }}.{{ domain['internal'] }}:{{ services['kopia']['ports']['https'] }} \
--url=https://{{ services['kopia']['domain']['internal'] }}.{{ domain['internal'] }}:443 \
--override-username={{ node['name'] }} \
--override-hostname={{ node['name'] }}.{{ domain['internal'] }}
@@ -0,0 +1,24 @@
[Unit]
Description=Kopia repository server
Wants=network-online.target
After=network-online.target
[Service]
# logging
StandardOutput=journal
StandardError=journal
EnvironmentFile=/etc/secrets/{{ node['uid'] }}/kopia.env
ExecStartPre=/usr/bin/kopia repository connect filesystem \
--path={{ node['home_path'] }}/data/kopia/repository
ExecStart=/usr/bin/kopia server start \
--no-ui \
--tls-cert-file=/etc/kopia/ssl/kopia.crt \
--tls-key-file=/etc/kopia/ssl/kopia.key \
--address=0.0.0.0:{{ services['kopia']['ports']['https'] }} \
--log-level=info
[Install]
WantedBy=default.target
@@ -1,4 +1,8 @@
{% if node['name'] == "bk" %}
KOPIA_PASSWORD={{ hostvars['console']['kopia']['repository'] }}
{% elif node['name'] in ["infra", "app"] %}
KOPIA_PASSWORD={{ hostvars['console']['kopia']['users'][node['name']] }}
{% endif %}
KOPIA_CONFIG_PATH=/etc/kopia/repository.config
KOPIA_CACHE_DIRECTORY=/var/cache/kopia
KOPIA_LOG_DIR=/var/cache/kopia/logs
@@ -9,5 +9,4 @@ $TTL 86400
)
IN NS bind.ilnmors.internal.
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR fw.ilnmors.internal.
1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR nas.ilnmors.internal.
0.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR console.ilnmors.internal.
@@ -9,5 +9,6 @@ $TTL 86400
)
IN NS bind.ilnmors.internal.
1 IN PTR fw.ilnmors.internal.
11 IN PTR nas.ilnmors.internal.
2 IN PTR switch.ilnmors.internal.
20 IN PTR console.ilnmors.internal.
101 IN PTR printer.ilnmors.internal.
@@ -11,6 +11,7 @@ $TTL 86400
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR fw.ilnmors.internal.
2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR blocky.ilnmors.internal.
3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR bind.ilnmors.internal.
9.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR bk.ilnmors.internal.
0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR vmm.ilnmors.internal.
1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR infra.ilnmors.internal.
2.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR auth.ilnmors.internal.

Some files were not shown because too many files have changed in this diff Show More