diff --git a/ansible/inventory/group_vars/all.yaml b/ansible/inventory/group_vars/all.yaml index 4c980b8..0c58a59 100644 --- a/ansible/inventory/group_vars/all.yaml +++ b/ansible/inventory/group_vars/all.yaml @@ -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" diff --git a/ansible/inventory/group_vars/hypervisor.yaml b/ansible/inventory/group_vars/hypervisor.yaml index 3cafb42..19783e4 100644 --- a/ansible/inventory/group_vars/hypervisor.yaml +++ b/ansible/inventory/group_vars/hypervisor.yaml @@ -21,13 +21,40 @@ hardware: bus: "0x00" slot: "0x02" function: "0x0" - hdd: + disk: data: - hdd0: "WMC6N0E617S8" - hdd1: "P6HVTL0W" - hdd2: "P2GH354H" - hdd3: "P6GJKSMP" - btrfs: - label: "DATA" - level: "raid10" - mount_point: "{{ node['libvirt_path'] }}/data" + serials: + - "WMC6N0E617S8" + - "P6HVTL0W" + - "P2GH354H" + - "P6GJKSMP" + fs: + type: "btrfs" + label: "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 diff --git a/ansible/inventory/host_vars/app.yaml b/ansible/inventory/host_vars/app.yaml index fd5e841..9fb4c48 100644 --- a/ansible/inventory/host_vars/app.yaml +++ b/ansible/inventory/host_vars/app.yaml @@ -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" diff --git a/ansible/inventory/host_vars/auth.yaml b/ansible/inventory/host_vars/auth.yaml index bbf70f6..50abbff 100644 --- a/ansible/inventory/host_vars/auth.yaml +++ b/ansible/inventory/host_vars/auth.yaml @@ -12,12 +12,12 @@ vm: name: "auth" cpu: 2 cpu_pin: | - - - + + + shares: 1024 memory: 2 - storage: 64 + storage: 32 lan_mac: "0a:49:6e:4d:02:00" lan_net: "lan-net" lan_group: "vlan10-access" diff --git a/ansible/inventory/host_vars/bk.yaml b/ansible/inventory/host_vars/bk.yaml new file mode 100644 index 0000000..624fc94 --- /dev/null +++ b/ansible/inventory/host_vars/bk.yaml @@ -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: | + + + + 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" diff --git a/ansible/inventory/host_vars/console.yaml b/ansible/inventory/host_vars/console.yaml index f94b01e..7b32ec9 100644 --- a/ansible/inventory/host_vars/console.yaml +++ b/ansible/inventory/host_vars/console.yaml @@ -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'] }}" diff --git a/ansible/inventory/host_vars/fw.yaml b/ansible/inventory/host_vars/fw.yaml index 690c7f6..a5284a6 100644 --- a/ansible/inventory/host_vars/fw.yaml +++ b/ansible/inventory/host_vars/fw.yaml @@ -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]' diff --git a/ansible/inventory/host_vars/infra.yaml b/ansible/inventory/host_vars/infra.yaml index 2d9961a..98b9aba 100644 --- a/ansible/inventory/host_vars/infra.yaml +++ b/ansible/inventory/host_vars/infra.yaml @@ -15,11 +15,11 @@ vm: cpu_pin: | - - - + + + shares: 2048 - memory: 6 + memory: 4 storage: 256 lan_mac: "0a:49:6e:4d:01:00" lan_net: "lan-net" diff --git a/ansible/inventory/hosts.ini b/ansible/inventory/hosts.ini index 8fcf9fe..942cee7 100644 --- a/ansible/inventory/hosts.ini +++ b/ansible/inventory/hosts.ini @@ -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 diff --git a/ansible/playbooks/app/site.yaml b/ansible/playbooks/app/site.yaml index 6526248..342e565 100644 --- a/ansible/playbooks/app/site.yaml +++ b/ansible/playbooks/app/site.yaml @@ -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" diff --git a/ansible/playbooks/auth/site.yaml b/ansible/playbooks/auth/site.yaml index fb56629..d5cbc52 100644 --- a/ansible/playbooks/auth/site.yaml +++ b/ansible/playbooks/auth/site.yaml @@ -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" diff --git a/ansible/playbooks/bk/site.yaml b/ansible/playbooks/bk/site.yaml new file mode 100644 index 0000000..2d2d4a9 --- /dev/null +++ b/ansible/playbooks/bk/site.yaml @@ -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"] diff --git a/ansible/playbooks/console/create_cert.yaml b/ansible/playbooks/console/create_cert.yaml new file mode 100644 index 0000000..2eee4ba --- /dev/null +++ b/ansible/playbooks/console/create_cert.yaml @@ -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 diff --git a/ansible/playbooks/console/site.yaml b/ansible/playbooks/console/site.yaml index a79afac..1ac3eab 100644 --- a/ansible/playbooks/console/site.yaml +++ b/ansible/playbooks/console/site.yaml @@ -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"] diff --git a/ansible/playbooks/infra/site.yaml b/ansible/playbooks/infra/site.yaml index a0bd5b7..71ef5db 100644 --- a/ansible/playbooks/infra/site.yaml +++ b/ansible/playbooks/infra/site.yaml @@ -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" diff --git a/ansible/playbooks/vmm/create_vm.yaml b/ansible/playbooks/vmm/create_vm.yaml index 4b01eae..edba912 100644 --- a/ansible/playbooks/vmm/create_vm.yaml +++ b/ansible/playbooks/vmm/create_vm.yaml @@ -19,6 +19,7 @@ vars: valid_vm_names: - "fw" + - "bk" - "infra" - "auth" - "app" diff --git a/ansible/playbooks/vmm/site.yaml b/ansible/playbooks/vmm/site.yaml index ee28469..4b49a09 100644 --- a/ansible/playbooks/vmm/site.yaml +++ b/ansible/playbooks/vmm/site.yaml @@ -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"] diff --git a/ansible/roles/app/handlers/main.yaml b/ansible/roles/app/handlers/main.yaml index f7e9736..4d8fdbb 100644 --- a/ansible/roles/app/handlers/main.yaml +++ b/ansible/roles/app/handlers/main.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_collabora.yaml b/ansible/roles/app/tasks/services/set_collabora.yaml index a1ae5bd..9fd80e0 100644 --- a/ansible/roles/app/tasks/services/set_collabora.yaml +++ b/ansible/roles/app/tasks/services/set_collabora.yaml @@ -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" diff --git a/ansible/roles/app/tasks/services/set_gitea.yaml b/ansible/roles/app/tasks/services/set_gitea.yaml index 4daa3b3..b53fbe9 100644 --- a/ansible/roles/app/tasks/services/set_gitea.yaml +++ b/ansible/roles/app/tasks/services/set_gitea.yaml @@ -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" diff --git a/ansible/roles/app/tasks/services/set_homebox.yaml b/ansible/roles/app/tasks/services/set_homebox.yaml new file mode 100644 index 0000000..213a2b3 --- /dev/null +++ b/ansible/roles/app/tasks/services/set_homebox.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_immich.yaml b/ansible/roles/app/tasks/services/set_immich.yaml index 3630863..ef1d696 100644 --- a/ansible/roles/app/tasks/services/set_immich.yaml +++ b/ansible/roles/app/tasks/services/set_immich.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_mealie.yaml b/ansible/roles/app/tasks/services/set_mealie.yaml index b44ce3c..c9539b5 100644 --- a/ansible/roles/app/tasks/services/set_mealie.yaml +++ b/ansible/roles/app/tasks/services/set_mealie.yaml @@ -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" diff --git a/ansible/roles/app/tasks/services/set_memos.yaml b/ansible/roles/app/tasks/services/set_memos.yaml index cb10d6c..fffc9fb 100644 --- a/ansible/roles/app/tasks/services/set_memos.yaml +++ b/ansible/roles/app/tasks/services/set_memos.yaml @@ -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" diff --git a/ansible/roles/app/tasks/services/set_nextcloud.yaml b/ansible/roles/app/tasks/services/set_nextcloud.yaml index 7fbecda..2a4a774 100644 --- a/ansible/roles/app/tasks/services/set_nextcloud.yaml +++ b/ansible/roles/app/tasks/services/set_nextcloud.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_outline.yaml b/ansible/roles/app/tasks/services/set_outline.yaml index 3e7690e..4f1975c 100644 --- a/ansible/roles/app/tasks/services/set_outline.yaml +++ b/ansible/roles/app/tasks/services/set_outline.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_paperless.yaml b/ansible/roles/app/tasks/services/set_paperless.yaml index c135461..372697a 100644 --- a/ansible/roles/app/tasks/services/set_paperless.yaml +++ b/ansible/roles/app/tasks/services/set_paperless.yaml @@ -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: diff --git a/ansible/roles/app/tasks/services/set_sure.yaml b/ansible/roles/app/tasks/services/set_sure.yaml index 9f2fd49..32873cd 100644 --- a/ansible/roles/app/tasks/services/set_sure.yaml +++ b/ansible/roles/app/tasks/services/set_sure.yaml @@ -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 diff --git a/ansible/roles/app/tasks/services/set_vaultwarden.yaml b/ansible/roles/app/tasks/services/set_vaultwarden.yaml index cdef9df..e5affba 100644 --- a/ansible/roles/app/tasks/services/set_vaultwarden.yaml +++ b/ansible/roles/app/tasks/services/set_vaultwarden.yaml @@ -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" diff --git a/ansible/roles/auth/tasks/services/set_authelia.yaml b/ansible/roles/auth/tasks/services/set_authelia.yaml index f735cc8..077e97f 100644 --- a/ansible/roles/auth/tasks/services/set_authelia.yaml +++ b/ansible/roles/auth/tasks/services/set_authelia.yaml @@ -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" diff --git a/ansible/roles/bk/tasks/services/set_rclone.yaml b/ansible/roles/bk/tasks/services/set_rclone.yaml new file mode 100644 index 0000000..1a8c77c --- /dev/null +++ b/ansible/roles/bk/tasks/services/set_rclone.yaml @@ -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 diff --git a/ansible/roles/common/handlers/main.yaml b/ansible/roles/common/handlers/main.yaml index c254c8f..90daacd 100644 --- a/ansible/roles/common/handlers/main.yaml +++ b/ansible/roles/common/handlers/main.yaml @@ -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 diff --git a/ansible/roles/app/tasks/node/set_data_volume.yaml b/ansible/roles/common/tasks/node/set_data_volume.yaml similarity index 62% rename from ansible/roles/app/tasks/node/set_data_volume.yaml rename to ansible/roles/common/tasks/node/set_data_volume.yaml index bb42766..75500e5 100644 --- a/ansible/roles/app/tasks/node/set_data_volume.yaml +++ b/ansible/roles/common/tasks/node/set_data_volume.yaml @@ -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" diff --git a/ansible/roles/common/tasks/node/set_timesyncd.yaml b/ansible/roles/common/tasks/node/set_timesyncd.yaml index 6b78325..d376bdb 100644 --- a/ansible/roles/common/tasks/node/set_timesyncd.yaml +++ b/ansible/roles/common/tasks/node/set_timesyncd.yaml @@ -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" diff --git a/ansible/roles/common/tasks/services/set_alloy.yaml b/ansible/roles/common/tasks/services/set_alloy.yaml index 732d7ad..f02eb89 100644 --- a/ansible/roles/common/tasks/services/set_alloy.yaml +++ b/ansible/roles/common/tasks/services/set_alloy.yaml @@ -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 diff --git a/ansible/roles/common/tasks/services/set_caddy.yaml b/ansible/roles/common/tasks/services/set_caddy.yaml index b32b9c9..c5d02de 100644 --- a/ansible/roles/common/tasks/services/set_caddy.yaml +++ b/ansible/roles/common/tasks/services/set_caddy.yaml @@ -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: diff --git a/ansible/roles/common/tasks/services/set_crowdsec.yaml b/ansible/roles/common/tasks/services/set_crowdsec.yaml index 23f3c51..eef946d 100644 --- a/ansible/roles/common/tasks/services/set_crowdsec.yaml +++ b/ansible/roles/common/tasks/services/set_crowdsec.yaml @@ -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" diff --git a/ansible/roles/common/tasks/services/set_kopia.yaml b/ansible/roles/common/tasks/services/set_kopia.yaml index 41c8875..5a568ca 100644 --- a/ansible/roles/common/tasks/services/set_kopia.yaml +++ b/ansible/roles/common/tasks/services/set_kopia.yaml @@ -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 diff --git a/ansible/roles/console/tasks/node/load_secret_vars.yaml b/ansible/roles/console/tasks/node/load_secret_vars.yaml index 3380b7c..93c6024 100644 --- a/ansible/roles/console/tasks/node/load_secret_vars.yaml +++ b/ansible/roles/console/tasks/node/load_secret_vars.yaml @@ -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 diff --git a/ansible/roles/infra/tasks/services/set_ca_server.yaml b/ansible/roles/infra/tasks/services/set_ca_server.yaml index 967c266..69f0916 100644 --- a/ansible/roles/infra/tasks/services/set_ca_server.yaml +++ b/ansible/roles/infra/tasks/services/set_ca_server.yaml @@ -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" diff --git a/ansible/roles/infra/tasks/services/set_grafana.yaml b/ansible/roles/infra/tasks/services/set_grafana.yaml index 744ac21..f24b07d 100644 --- a/ansible/roles/infra/tasks/services/set_grafana.yaml +++ b/ansible/roles/infra/tasks/services/set_grafana.yaml @@ -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" diff --git a/ansible/roles/infra/tasks/services/set_ldap.yaml b/ansible/roles/infra/tasks/services/set_ldap.yaml index 52d78af..1f2e2cb 100644 --- a/ansible/roles/infra/tasks/services/set_ldap.yaml +++ b/ansible/roles/infra/tasks/services/set_ldap.yaml @@ -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" diff --git a/ansible/roles/infra/tasks/services/set_loki.yaml b/ansible/roles/infra/tasks/services/set_loki.yaml index 9910872..e070aa8 100644 --- a/ansible/roles/infra/tasks/services/set_loki.yaml +++ b/ansible/roles/infra/tasks/services/set_loki.yaml @@ -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" diff --git a/ansible/roles/infra/tasks/services/set_postgresql.yaml b/ansible/roles/infra/tasks/services/set_postgresql.yaml index e956fb9..ccf872b 100644 --- a/ansible/roles/infra/tasks/services/set_postgresql.yaml +++ b/ansible/roles/infra/tasks/services/set_postgresql.yaml @@ -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: diff --git a/ansible/roles/infra/tasks/services/set_prometheus.yaml b/ansible/roles/infra/tasks/services/set_prometheus.yaml index 95df464..1e6094b 100644 --- a/ansible/roles/infra/tasks/services/set_prometheus.yaml +++ b/ansible/roles/infra/tasks/services/set_prometheus.yaml @@ -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" diff --git a/ansible/roles/infra/tasks/services/set_x509-exporter.yaml b/ansible/roles/infra/tasks/services/set_x509-exporter.yaml index a1496aa..9989483 100644 --- a/ansible/roles/infra/tasks/services/set_x509-exporter.yaml +++ b/ansible/roles/infra/tasks/services/set_x509-exporter.yaml @@ -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" diff --git a/ansible/roles/vmm/tasks/node/set_disk.yaml b/ansible/roles/vmm/tasks/node/set_disk.yaml new file mode 100644 index 0000000..a2d6b08 --- /dev/null +++ b/ansible/roles/vmm/tasks/node/set_disk.yaml @@ -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 diff --git a/ansible/roles/vmm/tasks/node/set_libvirt.yaml b/ansible/roles/vmm/tasks/node/set_libvirt.yaml index f3ca54c..bf57366 100644 --- a/ansible/roles/vmm/tasks/node/set_libvirt.yaml +++ b/ansible/roles/vmm/tasks/node/set_libvirt.yaml @@ -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" diff --git a/ansible/roles/vmm/tasks/node/set_raid.yaml b/ansible/roles/vmm/tasks/node/set_raid.yaml deleted file mode 100644 index c4f0ccb..0000000 --- a/ansible/roles/vmm/tasks/node/set_raid.yaml +++ /dev/null @@ -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 diff --git a/ansible/roles/vmm/tasks/vm/create_seed.yaml b/ansible/roles/vmm/tasks/vm/create_seed.yaml index f5c2135..277062a 100644 --- a/ansible/roles/vmm/tasks/vm/create_seed.yaml +++ b/ansible/roles/vmm/tasks/vm/create_seed.yaml @@ -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 \ diff --git a/ansible/roles/vmm/tasks/vm/deploy_vm_init.yaml b/ansible/roles/vmm/tasks/vm/deploy_vm_init.yaml index 0536c3d..103d7cd 100644 --- a/ansible/roles/vmm/tasks/vm/deploy_vm_init.yaml +++ b/ansible/roles/vmm/tasks/vm/deploy_vm_init.yaml @@ -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 diff --git a/ansible/roles/vmm/tasks/vm/register_vm.yaml b/ansible/roles/vmm/tasks/vm/register_vm.yaml index 1d0f6af..5456b6b 100644 --- a/ansible/roles/vmm/tasks/vm/register_vm.yaml +++ b/ansible/roles/vmm/tasks/vm/register_vm.yaml @@ -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 diff --git a/config/node/bk/nftables.conf.j2 b/config/node/bk/nftables.conf.j2 new file mode 100644 index 0000000..5b403ba --- /dev/null +++ b/config/node/bk/nftables.conf.j2 @@ -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; + } +} diff --git a/config/node/common/hosts.j2 b/config/node/common/hosts.j2 index 2d07a90..4d06e61 100644 --- a/config/node/common/hosts.j2 +++ b/config/node/common/hosts.j2 @@ -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) diff --git a/config/node/common/resolved/global.conf.j2 b/config/node/common/resolved/global.conf.j2 index 8c1bfbc..4f855aa 100644 --- a/config/node/common/resolved/global.conf.j2 +++ b/config/node/common/resolved/global.conf.j2 @@ -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 diff --git a/config/node/common/timesyncd/local-ntp.conf b/config/node/common/timesyncd/local-ntp.conf.j2 similarity index 100% rename from config/node/common/timesyncd/local-ntp.conf rename to config/node/common/timesyncd/local-ntp.conf.j2 diff --git a/config/node/fw/nftables.conf.j2 b/config/node/fw/nftables.conf.j2 index dc9299b..e2c6161 100644 --- a/config/node/fw/nftables.conf.j2 +++ b/config/node/fw/nftables.conf.j2 @@ -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 { diff --git a/config/secrets/secrets.yaml b/config/secrets/secrets.yaml index ca3f812..f48568f 100644 --- a/config/secrets/secrets.yaml +++ b/config/secrets/secrets.yaml @@ -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 diff --git a/config/services/containers/app/gitea/gitea.container.j2 b/config/services/containers/app/gitea/gitea.container.j2 index 4abb252..eafc847 100644 --- a/config/services/containers/app/gitea/gitea.container.j2 +++ b/config/services/containers/app/gitea/gitea.container.j2 @@ -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 diff --git a/config/services/containers/app/homebox/homebox.container.j2 b/config/services/containers/app/homebox/homebox.container.j2 new file mode 100644 index 0000000..b8cf450 --- /dev/null +++ b/config/services/containers/app/homebox/homebox.container.j2 @@ -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 diff --git a/config/services/containers/app/immich/immich.container.j2 b/config/services/containers/app/immich/immich.container.j2 index 47c379a..7ee05c8 100644 --- a/config/services/containers/app/immich/immich.container.j2 +++ b/config/services/containers/app/immich/immich.container.j2 @@ -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 diff --git a/config/services/containers/app/mealie/mealie.container.j2 b/config/services/containers/app/mealie/mealie.container.j2 index d8ed020..5b41a33 100644 --- a/config/services/containers/app/mealie/mealie.container.j2 +++ b/config/services/containers/app/mealie/mealie.container.j2 @@ -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 diff --git a/config/services/containers/app/memos/memos.container.j2 b/config/services/containers/app/memos/memos.container.j2 index a9eba88..51db31e 100644 --- a/config/services/containers/app/memos/memos.container.j2 +++ b/config/services/containers/app/memos/memos.container.j2 @@ -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 diff --git a/config/services/containers/app/nextcloud/nextcloud.container.j2 b/config/services/containers/app/nextcloud/nextcloud.container.j2 index c1d6fa9..4973c6c 100644 --- a/config/services/containers/app/nextcloud/nextcloud.container.j2 +++ b/config/services/containers/app/nextcloud/nextcloud.container.j2 @@ -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 diff --git a/config/services/containers/app/outline/outline.container.j2 b/config/services/containers/app/outline/outline.container.j2 index 1b76e1b..65874b7 100644 --- a/config/services/containers/app/outline/outline.container.j2 +++ b/config/services/containers/app/outline/outline.container.j2 @@ -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 diff --git a/config/services/containers/app/paperless/paperless.container.j2 b/config/services/containers/app/paperless/paperless.container.j2 index db18abf..534997b 100644 --- a/config/services/containers/app/paperless/paperless.container.j2 +++ b/config/services/containers/app/paperless/paperless.container.j2 @@ -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 diff --git a/config/services/containers/app/sure/sure-web.container.j2 b/config/services/containers/app/sure/sure-web.container.j2 index e745809..f0a1f1c 100644 --- a/config/services/containers/app/sure/sure-web.container.j2 +++ b/config/services/containers/app/sure/sure-web.container.j2 @@ -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 diff --git a/config/services/containers/app/sure/sure-worker.container.j2 b/config/services/containers/app/sure/sure-worker.container.j2 index 03589c7..0d6f958 100644 --- a/config/services/containers/app/sure/sure-worker.container.j2 +++ b/config/services/containers/app/sure/sure-worker.container.j2 @@ -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 diff --git a/config/services/containers/app/vaultwarden/vaultwarden.container.j2 b/config/services/containers/app/vaultwarden/vaultwarden.container.j2 index 963a420..e750e6f 100644 --- a/config/services/containers/app/vaultwarden/vaultwarden.container.j2 +++ b/config/services/containers/app/vaultwarden/vaultwarden.container.j2 @@ -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 diff --git a/config/services/containers/auth/authelia/authelia.container.j2 b/config/services/containers/auth/authelia/authelia.container.j2 index 9673780..c9a6a07 100644 --- a/config/services/containers/auth/authelia/authelia.container.j2 +++ b/config/services/containers/auth/authelia/authelia.container.j2 @@ -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 diff --git a/config/services/containers/auth/authelia/config/authelia.yaml.j2 b/config/services/containers/auth/authelia/config/authelia.yaml.j2 index 3e7424d..b273234 100644 --- a/config/services/containers/auth/authelia/config/authelia.yaml.j2 +++ b/config/services/containers/auth/authelia/config/authelia.yaml.j2 @@ -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' diff --git a/config/services/containers/common/caddy/caddy.container.j2 b/config/services/containers/common/caddy/caddy.container.j2 index 63dd00c..4c1902d 100644 --- a/config/services/containers/common/caddy/caddy.container.j2 +++ b/config/services/containers/common/caddy/caddy.container.j2 @@ -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 diff --git a/config/services/containers/common/caddy/etc/Caddyfile.j2 b/config/services/containers/common/caddy/etc/Caddyfile.j2 new file mode 100644 index 0000000..4238acd --- /dev/null +++ b/config/services/containers/common/caddy/etc/Caddyfile.j2 @@ -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 %}{{ '' }} diff --git a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 b/config/services/containers/common/caddy/etc/app/Caddyfile.j2 deleted file mode 100644 index 03f9dc3..0000000 --- a/config/services/containers/common/caddy/etc/app/Caddyfile.j2 +++ /dev/null @@ -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} - } -} diff --git a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 b/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 deleted file mode 100644 index e9fd55f..0000000 --- a/config/services/containers/common/caddy/etc/auth/Caddyfile.j2 +++ /dev/null @@ -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 -} diff --git a/config/services/containers/common/caddy/etc/infra/Caddyfile.j2 b/config/services/containers/common/caddy/etc/infra/Caddyfile.j2 deleted file mode 100644 index 6420833..0000000 --- a/config/services/containers/common/caddy/etc/infra/Caddyfile.j2 +++ /dev/null @@ -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'] }} - } -} diff --git a/config/services/containers/infra/ca/config/ca.json.j2 b/config/services/containers/infra/ca/config/ca.json.j2 index 1241cb7..17f9ca2 100644 --- a/config/services/containers/infra/ca/config/ca.json.j2 +++ b/config/services/containers/infra/ca/config/ca.json.j2 @@ -6,7 +6,7 @@ "address": ":9000", "insecureAddress": "", "dnsNames": [ - "{{ services['ca']['domain'] }}.{{ domain['internal'] }}" + "{{ services['ca']['domain']['internal'] }}.{{ domain['internal'] }}" ], "logger": { "format": "text" diff --git a/config/services/containers/infra/ca/config/defaults.json.j2 b/config/services/containers/infra/ca/config/defaults.json.j2 index 5525b2e..ea76ad7 100644 --- a/config/services/containers/infra/ca/config/defaults.json.j2 +++ b/config/services/containers/infra/ca/config/defaults.json.j2 @@ -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 }}" } diff --git a/config/services/containers/infra/grafana/etc/grafana.ini.j2 b/config/services/containers/infra/grafana/etc/grafana.ini.j2 index c1c3f7f..f554bd2 100644 --- a/config/services/containers/infra/grafana/etc/grafana.ini.j2 +++ b/config/services/containers/infra/grafana/etc/grafana.ini.j2 @@ -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} diff --git a/config/services/containers/infra/grafana/etc/ldap.toml.j2 b/config/services/containers/infra/grafana/etc/ldap.toml.j2 index e9a14ea..c7e513d 100644 --- a/config/services/containers/infra/grafana/etc/ldap.toml.j2 +++ b/config/services/containers/infra/grafana/etc/ldap.toml.j2 @@ -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 diff --git a/config/services/containers/infra/grafana/etc/provisioning/datasources/datasources.yaml.j2 b/config/services/containers/infra/grafana/etc/provisioning/datasources/datasources.yaml.j2 index 44630a3..1e52584 100644 --- a/config/services/containers/infra/grafana/etc/provisioning/datasources/datasources.yaml.j2 +++ b/config/services/containers/infra/grafana/etc/provisioning/datasources/datasources.yaml.j2 @@ -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 @@ -26,4 +26,4 @@ datasources: maxLines: 1000 secureJsonData: tlsCACert: "$__file{/etc/ssl/grafana/{{ root_cert_filename }}}" - httpHeaderValue1: "{{ domain['internal'] }} " + httpHeaderValue1: "{{ domain['internal'] }}" diff --git a/config/services/containers/infra/grafana/grafana.container.j2 b/config/services/containers/infra/grafana/grafana.container.j2 index 409fe85..5319fa7 100644 --- a/config/services/containers/infra/grafana/grafana.container.j2 +++ b/config/services/containers/infra/grafana/grafana.container.j2 @@ -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 diff --git a/config/services/containers/infra/ldap/ldap.container.j2 b/config/services/containers/infra/ldap/ldap.container.j2 index c13f6ab..ffe0d92 100644 --- a/config/services/containers/infra/ldap/ldap.container.j2 +++ b/config/services/containers/infra/ldap/ldap.container.j2 @@ -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 diff --git a/config/services/containers/infra/postgresql/config/pg_hba.conf.j2 b/config/services/containers/infra/postgresql/config/pg_hba.conf.j2 index 888f3b3..79fce4e 100644 --- a/config/services/containers/infra/postgresql/config/pg_hba.conf.j2 +++ b/config/services/containers/infra/postgresql/config/pg_hba.conf.j2 @@ -1,29 +1,28 @@ # @authcomment@ -# TYPE DATABASE USER ADDRESS METHOD - +# TYPE DATABASE USER ADDRESS METHOD # Local host `trust` -local all all trust +local all all trust # Local monitoring connection (host - infra VM) `trust` -hostssl postgres alloy {{ hostvars['fw']['network4']['infra']['server'] }}/32 trust -hostssl postgres alloy {{ hostvars['fw']['network6']['infra']['server'] }}/128 trust -hostssl postgres alloy {{ hostvars['fw']['network4']['subnet']['lla'] }} trust -hostssl postgres alloy {{ hostvars['fw']['network6']['subnet']['lla'] }} trust +hostssl postgres alloy {{ hostvars['fw']['network4']['infra']['server'] }}/32 trust +hostssl postgres alloy {{ hostvars['fw']['network6']['infra']['server'] }}/128 trust +hostssl postgres alloy {{ hostvars['fw']['network4']['subnet']['lla'] }} trust +hostssl postgres alloy {{ hostvars['fw']['network6']['subnet']['lla'] }} trust # Local connection (in postgresql container) needs password (127.0.0.1 - container loopback) -host all all 127.0.0.1/32 scram-sha-256 -host all all ::1/128 scram-sha-256 +host all all 127.0.0.1/32 scram-sha-256 +host all all ::1/128 scram-sha-256 # Local connection (host - infra VM) needs password (169.254.1.0/24 - link_local subnet for containers in pasta mode) -hostssl all all {{ hostvars['fw']['network4']['infra']['server'] }}/32 scram-sha-256 -hostssl all all {{ hostvars['fw']['network6']['infra']['server'] }}/128 scram-sha-256 -hostssl all all {{ hostvars['fw']['network4']['subnet']['lla'] }} scram-sha-256 -hostssl all all {{ hostvars['fw']['network6']['subnet']['lla'] }} scram-sha-256 +hostssl all all {{ hostvars['fw']['network4']['infra']['server'] }}/32 scram-sha-256 +hostssl all all {{ hostvars['fw']['network6']['infra']['server'] }}/128 scram-sha-256 +hostssl all all {{ hostvars['fw']['network4']['subnet']['lla'] }} scram-sha-256 +hostssl all all {{ hostvars['fw']['network6']['subnet']['lla'] }} scram-sha-256 # auth VM -hostssl all all {{ hostvars['fw']['network4']['auth']['server'] }}/32 scram-sha-256 -hostssl all all {{ hostvars['fw']['network6']['auth']['server'] }}/128 scram-sha-256 +hostssl all all {{ hostvars['fw']['network4']['auth']['server'] }}/32 scram-sha-256 +hostssl all all {{ hostvars['fw']['network6']['auth']['server'] }}/128 scram-sha-256 # app VM (Applications, 192.168.10.13) -hostssl all all {{ hostvars['fw']['network4']['app']['server'] }}/32 scram-sha-256 -hostssl all all {{ hostvars['fw']['network6']['app']['server'] }}/128 scram-sha-256 +hostssl all all {{ hostvars['fw']['network4']['app']['server'] }}/32 scram-sha-256 +hostssl all all {{ hostvars['fw']['network6']['app']['server'] }}/128 scram-sha-256 diff --git a/config/services/containers/infra/postgresql/config/postgresql.conf.j2 b/config/services/containers/infra/postgresql/config/postgresql.conf.j2 index 1911f45..de38079 100644 --- a/config/services/containers/infra/postgresql/config/postgresql.conf.j2 +++ b/config/services/containers/infra/postgresql/config/postgresql.conf.j2 @@ -41,4 +41,4 @@ autovacuum_vacuum_scale_factor = 0.1 autovacuum_analyze_scale_factor = 0.05 autovacuum_vacuum_cost_limit = 1000 effective_io_concurrency = 200 -random_page_cost = 1.2 \ No newline at end of file +random_page_cost = 1.2 diff --git a/config/services/containers/infra/postgresql/services/postgresql-cluster-backup.service b/config/services/containers/infra/postgresql/services/postgresql-cluster-backup.service index 91405d3..b6e8a15 100644 --- a/config/services/containers/infra/postgresql/services/postgresql-cluster-backup.service +++ b/config/services/containers/infra/postgresql/services/postgresql-cluster-backup.service @@ -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" diff --git a/config/services/containers/infra/postgresql/services/postgresql-data-backup@.service b/config/services/containers/infra/postgresql/services/postgresql-data-backup@.service index 2a7f5a2..76b1723 100644 --- a/config/services/containers/infra/postgresql/services/postgresql-data-backup@.service +++ b/config/services/containers/infra/postgresql/services/postgresql-data-backup@.service @@ -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" diff --git a/config/services/containers/infra/prometheus/etc/prometheus.yaml.j2 b/config/services/containers/infra/prometheus/etc/prometheus.yaml.j2 index f6cd455..34d6075 100644 --- a/config/services/containers/infra/prometheus/etc/prometheus.yaml.j2 +++ b/config/services/containers/infra/prometheus/etc/prometheus.yaml.j2 @@ -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=` to any timeseries scraped from this config. diff --git a/config/services/containers/infra/prometheus/etc/rules.yaml.j2 b/config/services/containers/infra/prometheus/etc/rules.yaml.j2 index 808c0db..fbb157f 100644 --- a/config/services/containers/infra/prometheus/etc/rules.yaml.j2 +++ b/config/services/containers/infra/prometheus/etc/rules.yaml.j2 @@ -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 diff --git a/config/services/systemd/bk/rclone/config/rclone.config.j2 b/config/services/systemd/bk/rclone/config/rclone.config.j2 new file mode 100644 index 0000000..30d0f4f --- /dev/null +++ b/config/services/systemd/bk/rclone/config/rclone.config.j2 @@ -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 +}} diff --git a/config/services/systemd/bk/rclone/rclone.service.j2 b/config/services/systemd/bk/rclone/rclone.service.j2 new file mode 100644 index 0000000..8c71c2a --- /dev/null +++ b/config/services/systemd/bk/rclone/rclone.service.j2 @@ -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 diff --git a/config/services/systemd/bk/rclone/rclone.timer.j2 b/config/services/systemd/bk/rclone/rclone.timer.j2 new file mode 100644 index 0000000..1353ecd --- /dev/null +++ b/config/services/systemd/bk/rclone/rclone.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Daily Kopia rclone backup timer + +[Timer] +OnCalendar=*-*-* 09:00:00 +Persistent=true +RandomizedDelaySec=300 + +[Install] +WantedBy=timers.target diff --git a/config/services/systemd/common/alloy/config.alloy.j2 b/config/services/systemd/common/alloy/config.alloy.j2 index 3f7b220..f531716 100644 --- a/config/services/systemd/common/alloy/config.alloy.j2 +++ b/config/services/systemd/common/alloy/config.alloy.j2 @@ -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'] }}" } } diff --git a/config/services/systemd/common/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml.j2 b/config/services/systemd/common/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml.j2 index f2201b6..c91f986 100644 --- a/config/services/systemd/common/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml.j2 +++ b/config/services/systemd/common/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml.j2 @@ -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 diff --git a/config/services/systemd/common/crowdsec/etc/local_api_credentials.yaml.j2 b/config/services/systemd/common/crowdsec/etc/local_api_credentials.yaml.j2 index 752ba69..93f6054 100644 --- a/config/services/systemd/common/crowdsec/etc/local_api_credentials.yaml.j2 +++ b/config/services/systemd/common/crowdsec/etc/local_api_credentials.yaml.j2 @@ -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']] }} diff --git a/config/services/systemd/common/kopia/kopia-backup.service.j2 b/config/services/systemd/common/kopia/kopia-backup.service.j2 index 96bce14..7cf0606 100644 --- a/config/services/systemd/common/kopia/kopia-backup.service.j2 +++ b/config/services/systemd/common/kopia/kopia-backup.service.j2 @@ -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'] }} diff --git a/config/services/systemd/common/kopia/kopia-server.service.j2 b/config/services/systemd/common/kopia/kopia-server.service.j2 new file mode 100644 index 0000000..f80857e --- /dev/null +++ b/config/services/systemd/common/kopia/kopia-server.service.j2 @@ -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 diff --git a/config/services/systemd/common/kopia/kopia.env.j2 b/config/services/systemd/common/kopia/kopia.env.j2 index 70477b1..6e27a2e 100644 --- a/config/services/systemd/common/kopia/kopia.env.j2 +++ b/config/services/systemd/common/kopia/kopia.env.j2 @@ -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 diff --git a/config/services/systemd/fw/bind/lib/db.1.00df.ip6.arpa b/config/services/systemd/fw/bind/lib/db.1.00df.ip6.arpa index 62c6181..a4fd193 100644 --- a/config/services/systemd/fw/bind/lib/db.1.00df.ip6.arpa +++ b/config/services/systemd/fw/bind/lib/db.1.00df.ip6.arpa @@ -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. diff --git a/config/services/systemd/fw/bind/lib/db.1.168.192.in-addr.arpa b/config/services/systemd/fw/bind/lib/db.1.168.192.in-addr.arpa index e011c72..a6d7501 100644 --- a/config/services/systemd/fw/bind/lib/db.1.168.192.in-addr.arpa +++ b/config/services/systemd/fw/bind/lib/db.1.168.192.in-addr.arpa @@ -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. diff --git a/config/services/systemd/fw/bind/lib/db.10.00df.ip6.arpa b/config/services/systemd/fw/bind/lib/db.10.00df.ip6.arpa index 0d9bd6c..0663dac 100644 --- a/config/services/systemd/fw/bind/lib/db.10.00df.ip6.arpa +++ b/config/services/systemd/fw/bind/lib/db.10.00df.ip6.arpa @@ -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. diff --git a/config/services/systemd/fw/bind/lib/db.10.168.192.in-addr.arpa b/config/services/systemd/fw/bind/lib/db.10.168.192.in-addr.arpa index dea0da8..c024ea7 100644 --- a/config/services/systemd/fw/bind/lib/db.10.168.192.in-addr.arpa +++ b/config/services/systemd/fw/bind/lib/db.10.168.192.in-addr.arpa @@ -11,6 +11,7 @@ $TTL 86400 1 IN PTR fw.ilnmors.internal. 2 IN PTR blocky.ilnmors.internal. 3 IN PTR bind.ilnmors.internal. +9 IN PTR bk.ilnmors.internal. 10 IN PTR vmm.ilnmors.internal. 11 IN PTR infra.ilnmors.internal. 12 IN PTR auth.ilnmors.internal. diff --git a/config/services/systemd/fw/bind/lib/db.ilnmors.com b/config/services/systemd/fw/bind/lib/db.ilnmors.com index f9e03d4..308a9c1 100644 --- a/config/services/systemd/fw/bind/lib/db.ilnmors.com +++ b/config/services/systemd/fw/bind/lib/db.ilnmors.com @@ -8,5 +8,7 @@ $TTL 86400 86400 ; minimum (1 day) ) IN NS bind.ilnmors.internal. +@ IN A 192.168.10.12 +@ IN AAAA fd00:10::12 * IN A 192.168.10.12 * IN AAAA fd00:10::12 diff --git a/config/services/systemd/fw/bind/lib/db.ilnmors.internal b/config/services/systemd/fw/bind/lib/db.ilnmors.internal index 4c45d46..9e1c2d3 100644 --- a/config/services/systemd/fw/bind/lib/db.ilnmors.internal +++ b/config/services/systemd/fw/bind/lib/db.ilnmors.internal @@ -14,6 +14,8 @@ fw IN A 192.168.10.1 fw IN AAAA fd00:10::1 blocky IN A 192.168.10.2 blocky IN AAAA fd00:10::2 +bk IN A 192.168.10.9 +bk IN AAAA fd00:10::9 vmm IN A 192.168.10.10 vmm IN AAAA fd00:10::10 infra IN A 192.168.10.11 @@ -23,13 +25,12 @@ auth IN AAAA fd00:10::12 app IN A 192.168.10.13 app IN AAAA fd00:10::13 switch IN A 192.168.1.2 -nas IN A 192.168.1.11 -nas IN AAAA fd00:1::11 console IN A 192.168.1.20 console IN AAAA fd00:1::20 printer IN A 192.168.1.101 ntp IN CNAME fw.ilnmors.internal. crowdsec IN CNAME fw.ilnmors.internal. +kopia IN CNAME bk.ilnmors.internal. ca IN CNAME infra.ilnmors.internal. postgresql IN CNAME infra.ilnmors.internal. ldap IN CNAME infra.ilnmors.internal. diff --git a/config/services/systemd/fw/blocky/etc/config.yaml.j2 b/config/services/systemd/fw/blocky/etc/config.yaml.j2 index e7a7bdf..36736a8 100644 --- a/config/services/systemd/fw/blocky/etc/config.yaml.j2 +++ b/config/services/systemd/fw/blocky/etc/config.yaml.j2 @@ -5,14 +5,14 @@ connectIPVersion: dual ports: dns: - - "{{ hostvars['fw']['network4']['blocky']['server'] }}:53" - - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:53" + - "{{ hostvars['fw']['network4']['blocky']['server'] }}:{{ services['blocky']['ports']['dns'] }}" + - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:{{ services['blocky']['ports']['dns'] }}" tls: - - "{{ hostvars['fw']['network4']['blocky']['server'] }}:853" - - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:853" + - "{{ hostvars['fw']['network4']['blocky']['server'] }}:{{ services['blocky']['ports']['dot'] }}" + - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:{{ services['blocky']['ports']['dot'] }}" https: - - "{{ hostvars['fw']['network4']['blocky']['server'] }}:443" - - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:443" + - "{{ hostvars['fw']['network4']['blocky']['server'] }}:{{ services['blocky']['ports']['https'] }}" + - "[{{ hostvars['fw']['network6']['blocky']['server'] }}]:{{ services['blocky']['ports']['https'] }}" log: level: info @@ -31,13 +31,13 @@ upstreams: conditional: fallbackUpstream: false mapping: - ilnmors.internal: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" - ilnmors.com: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" + {{ domain['internal'] }}: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" + {{ domain['public'] }}: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" 1.168.192.in-addr.arpa: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" 10.168.192.in-addr.arpa: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" 0.0.0.0.0.0.0.0.1.0.0.0.0.0.d.f.ip6.arpa: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" 0.0.0.0.0.0.0.0.0.1.0.0.0.0.d.f.ip6.arpa: "{{ hostvars['fw']['network4']['bind']['server'] }}, {{ hostvars['fw']['network6']['bind']['server'] }}" - vpn.ilnmors.com: "tcp-tls:1.1.1.1:853, tcp-tls:1.0.0.1:853, tcp-tls:[2606:4700:4700::1111]:853, tcp-tls:[2606:4700:4700::1001]:853" + vpn.{{ domain['public'] }}: "tcp-tls:1.1.1.1:853, tcp-tls:1.0.0.1:853, tcp-tls:[2606:4700:4700::1111]:853, tcp-tls:[2606:4700:4700::1001]:853" blocking: blockType: nxDomain diff --git a/config/services/systemd/fw/ddns/ddns.sh b/config/services/systemd/fw/ddns/ddns.sh index f564544..8ef2a31 100644 --- a/config/services/systemd/fw/ddns/ddns.sh +++ b/config/services/systemd/fw/ddns/ddns.sh @@ -17,25 +17,27 @@ CURRENT_IP="" # usage() function usage() { - echo "Usage: $0 -d \"domain\" [-t \"ttl\"] [-p] [-r]" - echo "-d : Specify the domain to update" - echo "-t : Specify the TTL(Time to live)" - echo "-p: Specify the cloudflare proxy to use" - echo "-r: Delete the DNS record" - exit 1 + echo "Usage: $0 -d \"domain\" [-t \"ttl\"] [-p] [-r]" + echo "-d : Specify the domain to update" + echo "-t : Specify the TTL(Time to live)" + echo "-p: Specify the cloudflare proxy to use" + echo "-r: Delete the DNS record" + exit 1 } # Log function log() { - local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + local timestamp local level="$1" local msg="$2" - echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"ddns.sh\"">&2 + + timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"ddns.sh\"" >&2 } # getopts to get arguments while getopts "d:t:pr" opt; do - case $opt in + case "$opt" in d) DOMAIN="$OPTARG" ;; @@ -60,26 +62,26 @@ while getopts "d:t:pr" opt; do done # Get option and move to parameters - This has no functional thing, because it only use arguments with parameters -shift $((OPTIND - 1)) +shift "$((OPTIND - 1))" # Check necessary options -if [ -z "$DOMAIN" ]; then +if [[ -z "$DOMAIN" ]]; then log "error" "-d option is required" usage fi -if ! [[ "$TTL" =~ ^[0-9]+$ ]] || [ "$TTL" -le 0 ]; then +if ! [[ "$TTL" =~ ^[0-9]+$ ]] || [[ "$TTL" -le 0 ]]; then log "error" "-t option (ttl) requires a number above 0." usage fi # Check necessary environment variables (Injected by systemd or shell) -if [ -z "$ZONE_ID" ]; then +if [[ -z "$ZONE_ID" ]]; then log "error" "ZONE_ID is required via environment variable." exit 1 fi -if [ -z "$API_KEY" ]; then +if [[ -z "$API_KEY" ]]; then log "error" "API_KEY is required via environment variable." exit 1 fi @@ -89,6 +91,7 @@ if ! command -v curl >/dev/null; then log "error" "curl is required" exit 1 fi + if ! command -v jq >/dev/null; then log "error" "jq is required" exit 1 @@ -100,32 +103,61 @@ CONTENT_TYPE="Content-Type: application/json" AUTHORIZATION="Authorization: Bearer $API_KEY" # Current IP -CURRENT_IP=$( ip address show dev wan | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 ) +CURRENT_IP="$(ip address show dev wan | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)" + # Get current IP from external server when IP is private IP if [[ -z "$CURRENT_IP" || "$CURRENT_IP" =~ ^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|127\.) ]]; then log "info" "IP from interface is private or empty. Fetching public IP..." - CURRENT_IP=$(curl -sf "https://ifconfig.me") ||\ - CURRENT_IP=$(curl -sf "https://ifconfig.kr") ||\ - CURRENT_IP=$(curl -sf "https://api.ipify.org") + CURRENT_IP="$(curl -sf "https://ifconfig.me")" || \ + CURRENT_IP="$(curl -sf "https://ifconfig.kr")" || \ + CURRENT_IP="$(curl -sf "https://api.ipify.org")" fi -if [ "$CURRENT_IP" == "" ]; then + +if [[ -z "$CURRENT_IP" ]]; then log "error" "Can't get an IP" exit 1 fi # DNS functions -# get_dns_record() function -get_dns_record() -{ +# build_dns_payload() function +build_dns_payload() { local type="$1" local name="$2" + local ttl="$3" + local comment="$4" + local content="$5" - local response="$( - curl -s "$URL?type=$type&name=$name"\ - -H "$CONTENT_TYPE"\ - -H "$AUTHORIZATION")" - if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then + jq -n \ + --arg name "$name" \ + --argjson ttl "$ttl" \ + --arg type "$type" \ + --arg comment "$comment" \ + --arg content "$content" \ + --argjson proxied "$PROXIED" \ + '{ + name: $name, + ttl: $ttl, + type: $type, + comment: $comment, + content: $content, + proxied: $proxied + }' +} + +# get_dns_record() function +get_dns_record() { + local type="$1" + local name="$2" + local response + + response="$( + curl -s "$URL?type=$type&name=$name" \ + -H "$CONTENT_TYPE" \ + -H "$AUTHORIZATION" + )" + + if [[ "$(jq -r '.success' <<< "$response")" == "false" ]]; then log "error" "Can't get dns record by $response" exit 1 else @@ -135,27 +167,26 @@ get_dns_record() } # create_dns_record() function -create_dns_record() -{ +create_dns_record() { local type="$1" local name="$2" local ttl="$3" local comment="$4" local content="$5" - local response="$( - curl -s "$URL"\ - -X POST\ - -H "$CONTENT_TYPE"\ - -H "$AUTHORIZATION"\ - -d "{ - \"name\": \"$name\", - \"ttl\": $ttl, - \"type\": \"$type\", - \"comment\": \"$comment\", - \"content\": \"$content\", - \"proxied\": $PROXIED - }")" - if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then + local payload + local response + + payload="$(build_dns_payload "$type" "$name" "$ttl" "$comment" "$content")" + + response="$( + curl -s "$URL" \ + -X POST \ + -H "$CONTENT_TYPE" \ + -H "$AUTHORIZATION" \ + -d "$payload" + )" + + if [[ "$(jq -r '.success' <<< "$response")" == "false" ]]; then log "error" "Can't create dns record by $response" exit 1 else @@ -165,49 +196,49 @@ create_dns_record() } # update_dns_record() function -update_dns_record() -{ +update_dns_record() { local type="$1" local name="$2" local ttl="$3" local comment="$4" local content="$5" local id="$6" - local response=$( - curl -s "$URL/$id"\ - -X PUT\ - -H "$CONTENT_TYPE"\ - -H "$AUTHORIZATION"\ - -d "{ - \"name\": \"$name\", - \"ttl\": $ttl, - \"type\": \"$type\", - \"comment\": \"$comment\", - \"content\": \"$content\", - \"proxied\": $PROXIED - }") - if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then + local payload + local response + + payload="$(build_dns_payload "$type" "$name" "$ttl" "$comment" "$content")" + + response="$( + curl -s "$URL/$id" \ + -X PUT \ + -H "$CONTENT_TYPE" \ + -H "$AUTHORIZATION" \ + -d "$payload" + )" + + if [[ "$(jq -r '.success' <<< "$response")" == "false" ]]; then log "error" "Can't update dns record by $response" exit 1 else - #return + # return echo "$response" fi } # delete_dns_record() function -delete_dns_record() -{ +delete_dns_record() { local type="$1" local id="$2" + local response - local response=$( - curl -s "$URL/$id"\ - -X DELETE\ - -H "$CONTENT_TYPE"\ + response="$( + curl -s "$URL/$id" \ + -X DELETE \ + -H "$CONTENT_TYPE" \ -H "$AUTHORIZATION" - ) - if [ "$(echo "$response" | jq -r '.success')" == "false" ]; then + )" + + if [[ "$(jq -r '.success' <<< "$response")" == "false" ]]; then log "error" "Can't delete dns record by $response" exit 1 else @@ -217,44 +248,50 @@ delete_dns_record() } # Get DNS A, and CNAME record -A_DNS_RECORD=$(get_dns_record "A" "$DOMAIN") -S_DNS_RECORD=$(get_dns_record "CNAME" "*.$DOMAIN") -W_DNS_RECORD=$(get_dns_record "CNAME" "www.$DOMAIN") +A_DNS_RECORD="$(get_dns_record "A" "$DOMAIN")" +S_DNS_RECORD="$(get_dns_record "CNAME" "*.$DOMAIN")" +W_DNS_RECORD="$(get_dns_record "CNAME" "www.$DOMAIN")" # Delete DNS record with Delete flag -if [ "$DELETE_FLAG" == "true" ]; then +if [[ "$DELETE_FLAG" == "true" ]]; then FLAG="false" - if [ "$(echo $A_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then - A_DNS_ID="$(echo $A_DNS_RECORD | jq -r '.result[0].id')" + + if [[ "$(jq -r '.result | length' <<< "$A_DNS_RECORD")" -eq 1 ]]; then + A_DNS_ID="$(jq -r '.result[0].id' <<< "$A_DNS_RECORD")" delete_dns_record "A" "$A_DNS_ID" log "info" "root DNS record is deleted" FLAG="true" fi - if [ "$(echo $S_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then - S_DNS_ID="$(echo $S_DNS_RECORD | jq -r '.result[0].id')" + + if [[ "$(jq -r '.result | length' <<< "$S_DNS_RECORD")" -eq 1 ]]; then + S_DNS_ID="$(jq -r '.result[0].id' <<< "$S_DNS_RECORD")" delete_dns_record "CNAME" "$S_DNS_ID" log "info" "sub DNS record is deleted" FLAG="true" fi - if [ "$(echo $W_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then - W_DNS_ID="$(echo $W_DNS_RECORD | jq -r '.result[0].id')" + + if [[ "$(jq -r '.result | length' <<< "$W_DNS_RECORD")" -eq 1 ]]; then + W_DNS_ID="$(jq -r '.result[0].id' <<< "$W_DNS_RECORD")" delete_dns_record "CNAME" "$W_DNS_ID" log "info" "www DNS record is deleted" FLAG="true" fi - if [ "$FLAG" == "false" ]; then + + if [[ "$FLAG" == "false" ]]; then log "info" "Nothing is Deleted. There are no DNS records" fi + exit fi # Create or update DNS A record -if [ "$(echo $A_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # root DNS record exist - A_DNS_ID="$(echo $A_DNS_RECORD | jq -r '.result[0].id')" - A_DNS_CONTENT="$(echo $A_DNS_RECORD | jq -r '.result[0].content')" - A_DNS_TTL="$(echo $A_DNS_RECORD | jq -r '.result[0].ttl')" - A_DNS_PROXIED="$(echo $A_DNS_RECORD | jq -r '.result[0].proxied')" - if [ "$A_DNS_CONTENT" != $CURRENT_IP -o "$A_DNS_TTL" != "$TTL" -o "$A_DNS_PROXIED" != "$PROXIED" ]; then +if [[ "$(jq -r '.result | length' <<< "$A_DNS_RECORD")" -eq 1 ]]; then # root DNS record exist + A_DNS_ID="$(jq -r '.result[0].id' <<< "$A_DNS_RECORD")" + A_DNS_CONTENT="$(jq -r '.result[0].content' <<< "$A_DNS_RECORD")" + A_DNS_TTL="$(jq -r '.result[0].ttl' <<< "$A_DNS_RECORD")" + A_DNS_PROXIED="$(jq -r '.result[0].proxied' <<< "$A_DNS_RECORD")" + + if [[ "$A_DNS_CONTENT" != "$CURRENT_IP" || "$A_DNS_TTL" != "$TTL" || "$A_DNS_PROXIED" != "$PROXIED" ]]; then update_dns_record "A" "$DOMAIN" "$TTL" "$(date "+%Y-%m-%d %H:%M:%S"): root domain from ddns.sh" "$CURRENT_IP" "$A_DNS_ID" log "info" "Root DNS record is successfully changed Domain: $DOMAIN IP: $A_DNS_CONTENT to $CURRENT_IP TTL: $A_DNS_TTL to $TTL proxied: $A_DNS_PROXIED to $PROXIED" else @@ -266,12 +303,13 @@ else # root DNS record does not exist fi # Create or update DNS CNAME records -if [ "$(echo $S_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # sub DNS record exist - S_DNS_ID="$(echo $S_DNS_RECORD | jq -r '.result[0].id')" - S_DNS_CONTENT="$(echo $S_DNS_RECORD | jq -r '.result[0].content')" - S_DNS_TTL="$(echo $S_DNS_RECORD | jq -r '.result[0].ttl')" - S_DNS_PROXIED="$(echo $S_DNS_RECORD | jq -r '.result[0].proxied')" - if [ "$S_DNS_CONTENT" != "$DOMAIN" -o "$S_DNS_TTL" != "$C_TTL" -o "$S_DNS_PROXIED" != "$PROXIED" ]; then +if [[ "$(jq -r '.result | length' <<< "$S_DNS_RECORD")" -eq 1 ]]; then # sub DNS record exist + S_DNS_ID="$(jq -r '.result[0].id' <<< "$S_DNS_RECORD")" + S_DNS_CONTENT="$(jq -r '.result[0].content' <<< "$S_DNS_RECORD")" + S_DNS_TTL="$(jq -r '.result[0].ttl' <<< "$S_DNS_RECORD")" + S_DNS_PROXIED="$(jq -r '.result[0].proxied' <<< "$S_DNS_RECORD")" + + if [[ "$S_DNS_CONTENT" != "$DOMAIN" || "$S_DNS_TTL" != "$C_TTL" || "$S_DNS_PROXIED" != "$PROXIED" ]]; then update_dns_record "CNAME" "*.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): sub domain from ddns.sh" "$DOMAIN" "$S_DNS_ID" log "info" "Sub DNS record is successfully changed Domain: $S_DNS_CONTENT to *.$DOMAIN CNAME: $DOMAIN TTL: $S_DNS_TTL to $C_TTL proxied: $S_DNS_PROXIED to $PROXIED" else @@ -282,12 +320,13 @@ else # sub DNS record does not exist log "info" "Sub DNS record is successfully created Domain: *.$DOMAIN CNAME: $DOMAIN TTL: $C_TTL proxied: $PROXIED" fi -if [ "$(echo $W_DNS_RECORD | jq -r '.result | length')" -eq 1 ]; then # www DNS record exist - W_DNS_ID="$(echo $W_DNS_RECORD | jq -r '.result[0].id')" - W_DNS_CONTENT="$(echo $W_DNS_RECORD | jq -r '.result[0].content')" - W_DNS_TTL="$(echo $W_DNS_RECORD | jq -r '.result[0].ttl')" - W_DNS_PROXIED="$(echo $W_DNS_RECORD | jq -r '.result[0].proxied')" - if [ "$W_DNS_CONTENT" != "$DOMAIN" -o "$W_DNS_TTL" != "$C_TTL" -o "$W_DNS_PROXIED" != "$PROXIED" ]; then +if [[ "$(jq -r '.result | length' <<< "$W_DNS_RECORD")" -eq 1 ]]; then # www DNS record exist + W_DNS_ID="$(jq -r '.result[0].id' <<< "$W_DNS_RECORD")" + W_DNS_CONTENT="$(jq -r '.result[0].content' <<< "$W_DNS_RECORD")" + W_DNS_TTL="$(jq -r '.result[0].ttl' <<< "$W_DNS_RECORD")" + W_DNS_PROXIED="$(jq -r '.result[0].proxied' <<< "$W_DNS_RECORD")" + + if [[ "$W_DNS_CONTENT" != "$DOMAIN" || "$W_DNS_TTL" != "$C_TTL" || "$W_DNS_PROXIED" != "$PROXIED" ]]; then update_dns_record "CNAME" "www.$DOMAIN" "$C_TTL" "$(date "+%Y-%m-%d %H:%M:%S"): www domain from ddns.sh" "$DOMAIN" "$W_DNS_ID" log "info" "www DNS record is successfully changed Domain: $W_DNS_CONTENT to www.$DOMAIN CNAME: $DOMAIN TTL: $W_DNS_TTL to $C_TTL proxied: $W_DNS_PROXIED to $PROXIED" else diff --git a/config/services/systemd/fw/kea/kea-dhcp4.conf.j2 b/config/services/systemd/fw/kea/kea-dhcp4.conf.j2 index 65e2952..04f1acf 100644 --- a/config/services/systemd/fw/kea/kea-dhcp4.conf.j2 +++ b/config/services/systemd/fw/kea/kea-dhcp4.conf.j2 @@ -28,15 +28,10 @@ "ip-address": "{{ hostvars['fw']['network4']['switch']['client'] }}", "hostname": "switch" }, - { - "hw-address": "90:09:d0:65:a9:db", - "ip-address": "{{ hostvars['fw']['network4']['nas']['client'] }}", - "hostname": "nas" - }, { "hw-address": "d8:e2:df:ff:1b:d5", "ip-address": "{{ hostvars['fw']['network4']['console']['client'] }}", - "hostname": "surface" + "hostname": "console" }, { "hw-address": "38:ca:84:94:5e:06", diff --git a/config/services/systemd/vmm/btrfs/btrfs-scrub-backup.service.j2 b/config/services/systemd/vmm/btrfs/btrfs-scrub-backup.service.j2 new file mode 100644 index 0000000..8f9fce6 --- /dev/null +++ b/config/services/systemd/vmm/btrfs/btrfs-scrub-backup.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=BTRFS backup auto scrub +ConditionPathIsMountPoint={{ hardware['disk']['backup']['fs']['mount_point'] }} +RequiresMountsFor={{ hardware['disk']['backup']['fs']['mount_point'] }} + +[Service] +Type=oneshot +ExecStart=/usr/bin/btrfs scrub start -Bd {{ hardware['disk']['backup']['fs']['mount_point'] }} +Nice=19 +IOSchedulingClass=idle diff --git a/config/services/systemd/vmm/btrfs/btrfs-scrub.timer.j2 b/config/services/systemd/vmm/btrfs/btrfs-scrub-backup.timer.j2 similarity index 100% rename from config/services/systemd/vmm/btrfs/btrfs-scrub.timer.j2 rename to config/services/systemd/vmm/btrfs/btrfs-scrub-backup.timer.j2 diff --git a/config/services/systemd/vmm/btrfs/btrfs-scrub-data.service.j2 b/config/services/systemd/vmm/btrfs/btrfs-scrub-data.service.j2 new file mode 100644 index 0000000..3727c6a --- /dev/null +++ b/config/services/systemd/vmm/btrfs/btrfs-scrub-data.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=BTRFS data auto scrub +ConditionPathIsMountPoint={{ hardware['disk']['data']['fs']['mount_point'] }} +RequiresMountsFor={{ hardware['disk']['data']['fs']['mount_point'] }} + +[Service] +Type=oneshot +ExecStart=/usr/bin/btrfs scrub start -Bd {{ hardware['disk']['data']['fs']['mount_point'] }} +Nice=19 +IOSchedulingClass=idle diff --git a/config/services/systemd/vmm/btrfs/btrfs-scrub-data.timer.j2 b/config/services/systemd/vmm/btrfs/btrfs-scrub-data.timer.j2 new file mode 100644 index 0000000..5b9216b --- /dev/null +++ b/config/services/systemd/vmm/btrfs/btrfs-scrub-data.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Monthly BTRFS auto scrub + +[Timer] +OnCalendar=*-*-01 04:00:00 +Persistent=true +RandomizedDelaySec=300 + +[Install] +WantedBy=timers.target diff --git a/config/services/systemd/vmm/btrfs/btrfs-scrub.service.j2 b/config/services/systemd/vmm/btrfs/btrfs-scrub.service.j2 deleted file mode 100644 index 9ee6e12..0000000 --- a/config/services/systemd/vmm/btrfs/btrfs-scrub.service.j2 +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=BTRFS auto scrub -ConditionPathIsMountPoint={{ hardware['btrfs']['mount_point'] }} -RequiresMountsFor={{ hardware['btrfs']['mount_point'] }} - -[Service] -Type=oneshot -ExecStart=/usr/bin/btrfs scrub start -Bd {{ hardware['btrfs']['mount_point'] }} -Nice=19 -IOSchedulingClass=idle diff --git a/config/services/systemd/vmm/libvirt/services/app.service b/config/services/systemd/vmm/libvirt/services/app.service.j2 similarity index 59% rename from config/services/systemd/vmm/libvirt/services/app.service rename to config/services/systemd/vmm/libvirt/services/app.service.j2 index 24f814e..2983dae 100644 --- a/config/services/systemd/vmm/libvirt/services/app.service +++ b/config/services/systemd/vmm/libvirt/services/app.service.j2 @@ -1,7 +1,9 @@ [Unit] Description=app vm -After=network-online.target libvirtd.service fw.service infra.service auth.service -Wants=fw.service infra.service auth.service +After=network-online.target libvirtd.service fw.service bk.service infra.service auth.service +Wants=fw.service bk.service infra.service auth.service +ConditionPathIsMountPoint={{ hardware['disk']['data']['fs']['mount_point'] }} +RequiresMountsFor={{ hardware['disk']['data']['fs']['mount_point'] }} [Service] Type=oneshot diff --git a/config/services/systemd/vmm/libvirt/services/auth.service b/config/services/systemd/vmm/libvirt/services/auth.service.j2 similarity index 100% rename from config/services/systemd/vmm/libvirt/services/auth.service rename to config/services/systemd/vmm/libvirt/services/auth.service.j2 diff --git a/config/services/systemd/vmm/libvirt/services/bk.service.j2 b/config/services/systemd/vmm/libvirt/services/bk.service.j2 new file mode 100644 index 0000000..0a8bad2 --- /dev/null +++ b/config/services/systemd/vmm/libvirt/services/bk.service.j2 @@ -0,0 +1,25 @@ +[Unit] +Description=bk vm +After=network-online.target libvirtd.service fw.service +Wants=fw.service +ConditionPathIsMountPoint={{ hardware['disk']['backup']['fs']['mount_point'] }} +RequiresMountsFor={{ hardware['disk']['backup']['fs']['mount_point'] }} + +[Service] +Type=oneshot +RemainAfterExit=yes +TimeoutStopSec=360 + +ExecStart=/usr/bin/virsh -c qemu:///system start bk + +ExecStartPost=/bin/sleep 30 + +ExecStop=/bin/bash -c '\ + /usr/bin/virsh -c qemu:///system shutdown bk; \ + while /usr/bin/virsh -c qemu:///system list --state-running --name | grep -q "bk"; do \ + echo "Waiting for bk to shutdown..."; \ + sleep 2; \ + done' + +[Install] +WantedBy=default.target diff --git a/config/services/systemd/vmm/libvirt/services/fw.service b/config/services/systemd/vmm/libvirt/services/fw.service.j2 similarity index 100% rename from config/services/systemd/vmm/libvirt/services/fw.service rename to config/services/systemd/vmm/libvirt/services/fw.service.j2 diff --git a/config/services/systemd/vmm/libvirt/services/infra.service b/config/services/systemd/vmm/libvirt/services/infra.service.j2 similarity index 82% rename from config/services/systemd/vmm/libvirt/services/infra.service rename to config/services/systemd/vmm/libvirt/services/infra.service.j2 index 714e2c9..55207bd 100644 --- a/config/services/systemd/vmm/libvirt/services/infra.service +++ b/config/services/systemd/vmm/libvirt/services/infra.service.j2 @@ -1,7 +1,7 @@ [Unit] Description=infra vm -After=network-online.target libvirtd.service fw.service -Wants=fw.service +After=network-online.target libvirtd.service fw.service bk.service +Wants=fw.service bk.service [Service] Type=oneshot diff --git a/config/services/systemd/vmm/libvirt/xml/storages/backup-pool.xml b/config/services/systemd/vmm/libvirt/xml/storages/backup-pool.xml new file mode 100644 index 0000000..40178e7 --- /dev/null +++ b/config/services/systemd/vmm/libvirt/xml/storages/backup-pool.xml @@ -0,0 +1,8 @@ + + + + backup-pool + + /var/lib/libvirt/disk/backup + + diff --git a/config/services/systemd/vmm/libvirt/xml/storages/data-pool.xml b/config/services/systemd/vmm/libvirt/xml/storages/data-pool.xml index 8bfd284..ee58a2f 100644 --- a/config/services/systemd/vmm/libvirt/xml/storages/data-pool.xml +++ b/config/services/systemd/vmm/libvirt/xml/storages/data-pool.xml @@ -3,6 +3,6 @@ data-pool - /var/lib/libvirt/data + /var/lib/libvirt/disk/data diff --git a/config/services/systemd/vmm/libvirt/xml/vms/vms.xml.j2 b/config/services/systemd/vmm/libvirt/xml/vms/vms.xml.j2 index fef60f1..f2041a0 100644 --- a/config/services/systemd/vmm/libvirt/xml/vms/vms.xml.j2 +++ b/config/services/systemd/vmm/libvirt/xml/vms/vms.xml.j2 @@ -70,10 +70,14 @@
+{% endif %} +{% if hostvars[target_vm]['vm']['data_storage'] is defined %} +{% set pool = hostvars[target_vm]['vm']['data_storage']['pool'] %} +{% set disk = hostvars['vmm']['hardware']['disk'][pool]['fs'] %} - - + + {% endif %} diff --git a/config/services/containers/infra/postgresql/init/.gitkeep b/data/sql/postgresql/.gitkeep similarity index 100% rename from config/services/containers/infra/postgresql/init/.gitkeep rename to data/sql/postgresql/.gitkeep diff --git a/docs/archives/services/app/actual-budget/actual-budget.container.j2 b/docs/archives/services/app/actual-budget/actual-budget.container.j2 index 23a4001..f23700c 100644 --- a/docs/archives/services/app/actual-budget/actual-budget.container.j2 +++ b/docs/archives/services/app/actual-budget/actual-budget.container.j2 @@ -14,7 +14,7 @@ PublishPort={{ services['actualbudget']['ports']['http'] }}:5006 Volume=%h/data/containers/actual-budget:/data:rw Environment="TZ={{ timezone }}" -Environment="ACTUAL_OPENID_DISCOVERY_URL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}/.well-known/openid-configuration" +Environment="ACTUAL_OPENID_DISCOVERY_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/.well-known/openid-configuration" Environment="ACTUAL_OPENID_CLIENT_ID=actual-budget" Environment="ACTUAL_OPENID_SERVER_HOSTNAME=https://{{ services['actualbudget']['domain']['public'] }}.{{ domain['public'] }}" Environment="ACTUAL_OPENID_AUTH_METHOD=oauth2" diff --git a/docs/archives/services/app/affine/affine.container.j2 b/docs/archives/services/app/affine/affine.container.j2 index c3dea8f..e0ab508 100644 --- a/docs/archives/services/app/affine/affine.container.j2 +++ b/docs/archives/services/app/affine/affine.container.j2 @@ -40,7 +40,7 @@ Environment="AFFINE_INDEXER_ENABLED=true" Environment="AFFINE_INDEXER_SEARCH_ENDPOINT=http://host.containers.internal:{{ services['affine']['ports']['manticore'] }}" [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 diff --git a/docs/archives/services/app/affine/set_affine.yaml b/docs/archives/services/app/affine/set_affine.yaml index 440f72a..c1616c8 100644 --- a/docs/archives/services/app/affine/set_affine.yaml +++ b/docs/archives/services/app/affine/set_affine.yaml @@ -112,7 +112,7 @@ value: "{{ hostvars['console']['affine']['secret_key'] }}" - name: "AFFINE_DATABASE_URL" value: "postgresql://affine:{{ hostvars['console']['postgresql']['password']['affine'] | urlencode | replace('/', '%2F') }}\ - @{{ services['postgresql']['domain'] }}.{{ domain['internal'] }}/affine_db?sslmode=verify-full&\ + @{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}/affine_db?sslmode=verify-full&\ sslrootcert=/etc/ssl/affine/{{ root_cert_filename }}" notify: "notification_restart_affine" no_log: true @@ -166,14 +166,17 @@ ansible.builtin.stat: path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar" loop: - - image: "docker.io/manticoresearch/manticore:{{ version['containers']['manticore'] }}" + - name: "manticore" + image: "docker.io/manticoresearch/manticore:{{ version['containers']['manticore'] }}" file: "docker.io_manticoresearch_manticore_{{ version['containers']['manticore'] }}" - - 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/toeverything/affine:{{ version['containers']['affine'] }}" + - name: affine + image: "ghcr.io/toeverything/affine:{{ version['containers']['affine'] }}" file: "ghcr.io_toeverything_affine_{{ version['containers']['affine'] }}" loop_control: - label: "{{ item.file }}" + label: "{{ item.name }}" register: container_archive_images - name: Save container archive images diff --git a/docs/archives/services/app/ezbookkeeping/ezbookkeeping.container.j2 b/docs/archives/services/app/ezbookkeeping/ezbookkeeping.container.j2 index 15a8f66..4d6c759 100644 --- a/docs/archives/services/app/ezbookkeeping/ezbookkeeping.container.j2 +++ b/docs/archives/services/app/ezbookkeeping/ezbookkeeping.container.j2 @@ -25,7 +25,7 @@ Environment="EBK_LOG_MODE=console" # Database Environment="EBK_DATABASE_TYPE=postgres" -Environment="EBK_DATABASE_HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}" +Environment="EBK_DATABASE_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}" Environment="EBK_DATABASE_NAME=ezbookkeeping_db" Environment="EBK_DATABASE_USER=ezbookkeeping" Secret=EBK_DATABASE_PASSWD,type=env @@ -38,7 +38,7 @@ Environment="EBK_AUTH_OAUTH2_PROVIDER=oidc" Environment="EBK_AUTH_OAUTH2_CLIENT_ID=ezbookkeeping" Secret=EBK_AUTH_OAUTH2_CLIENT_SECRET,type=env Environment="EBK_AUTH_OAUTH2_USE_PKCE=true" -Environment="EBK_AUTH_OIDC_PROVIDER_BASE_URL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +Environment="EBK_AUTH_OIDC_PROVIDER_BASE_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" Environment="EBK_AUTH_ENABLE_OIDC_DISPLAY_NAME=true" Environment="EBK_AUTH_OIDC_CUSTOM_DISPLAY_NAME=Authelia" @@ -52,7 +52,7 @@ Environment="EBK_MCP_ENABLE_MCP=false" Environment="EBK_LLM_TRANSACTION_FROM_AI_IMAGE_RECOGNITION=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 diff --git a/docs/archives/services/app/opencloud/etc/csp.yaml.j2 b/docs/archives/services/app/opencloud/etc/csp.yaml.j2 index 9ebc5eb..3419085 100644 --- a/docs/archives/services/app/opencloud/etc/csp.yaml.j2 +++ b/docs/archives/services/app/opencloud/etc/csp.yaml.j2 @@ -6,7 +6,7 @@ directives: - 'blob:' - 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps' - 'https://update.opencloud.eu' - - 'https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}' + - 'https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}' # default-src: # - '''none''' font-src: diff --git a/docs/archives/services/app/opencloud/etc/proxy.yaml.j2 b/docs/archives/services/app/opencloud/etc/proxy.yaml.j2 index 86cc7eb..cd59e28 100644 --- a/docs/archives/services/app/opencloud/etc/proxy.yaml.j2 +++ b/docs/archives/services/app/opencloud/etc/proxy.yaml.j2 @@ -3,11 +3,11 @@ role_assignment: oidc_role_mapper: role_claim: "preferred_username" role_mapping: -{% for admin_user in ['il'] %} +{% for admin_user in ["il"] %} - role_name: "admin" claim_value: "{{ admin_user }}" {% endfor %} -{% for general_user in ['morsalin', 'eunkyoung'] %} +{% for general_user in ["morsalin", "eunkyoung"] %} - role_name: "user" claim_value: "{{ general_user }}" {% endfor %} diff --git a/docs/archives/services/app/opencloud/opencloud.container.j2 b/docs/archives/services/app/opencloud/opencloud.container.j2 index ddc45f2..18572a9 100644 --- a/docs/archives/services/app/opencloud/opencloud.container.j2 +++ b/docs/archives/services/app/opencloud/opencloud.container.j2 @@ -27,7 +27,7 @@ Environment="OC_URL=https://{{ services['opencloud']['domain']['public'] }}.{{ d ## CSP file location: allow authelia public domain Environment="PROXY_CSP_CONFIG_FILE_LOCATION=/etc/opencloud/csp.yaml" # OIDC -Environment="OC_OIDC_ISSUER=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +Environment="OC_OIDC_ISSUER=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" Environment="PROXY_OIDC_REWRITE_WELLKNOWN=true" ## OIDC CLIENT CONFIGURATION and SCOPES Environment="WEB_OIDC_CLIENT_ID=opencloud" @@ -41,8 +41,8 @@ Environment="GRAPH_USERNAME_MATCH=none" # OIDC standard link environments -#Environment="WEB_OIDC_AUTHORITY=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" -#Environment="WEBFINGER_OIDC_ISSUER=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +#Environment="WEB_OIDC_AUTHORITY=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" +#Environment="WEBFINGER_OIDC_ISSUER=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" #Environment="OC_OIDC_CLIENT_ID=opencloud" #Environment="OC_OIDC_CLIENT_SCOPES=openid profile email groups" #Environment="WEBFINGER_ANDROID_OIDC_CLIENT_ID=opencloud" diff --git a/docs/archives/services/app/trilium/trilium.container.j2 b/docs/archives/services/app/trilium/trilium.container.j2 index 9033ae4..276f805 100644 --- a/docs/archives/services/app/trilium/trilium.container.j2 +++ b/docs/archives/services/app/trilium/trilium.container.j2 @@ -26,13 +26,13 @@ Environment="TRILIUM_NO_UPLOAD_LIMIT=true" ## Short Alias doesn't work now. #Environment="TRILIUM_OAUTH_BASE_URL=https://{{ services['trilium']['domain']['public'] }}.{{ domain['public'] }}" #Environment="TRILIUM_OAUTH_CLIENT_ID=trilium" -#Environment="TRILIUM_OAUTH_ISSUER_BASE_URL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +#Environment="TRILIUM_OAUTH_ISSUER_BASE_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" #Environment="TRILIUM_OAUTH_ISSUER_NAME=Authelia" #Environment="TRILIUM_OAUTH_ISSUER_ICON=https://www.authelia.com/images/branding/logo-cropped.png" #Secret="TRILIUM_OAUTH_CLIENT_SECRET",type=env Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHBASEURL=https://{{ services['trilium']['domain']['public'] }}.{{ domain['public'] }}" Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHCLIENTID=trilium" -Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERBASEURL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERBASEURL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERNAME=Authelia" Environment="TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHISSUERICON=https://www.authelia.com/images/branding/logo-cropped.png" Secret="TRILIUM_OAUTH_CLIENT_SECRET",type=env,target=TRILIUM_MULTIFACTORAUTHENTICATION_OAUTHCLIENTSECRET diff --git a/docs/archives/services/app/vikunja/vikunja.container.j2 b/docs/archives/services/app/vikunja/vikunja.container.j2 index f6c1c9f..93613c4 100644 --- a/docs/archives/services/app/vikunja/vikunja.container.j2 +++ b/docs/archives/services/app/vikunja/vikunja.container.j2 @@ -28,7 +28,7 @@ Secret=VIKUNJA_SERVICE_JWTSECRET,type=env # Database Environment="VIKUNJA_DATABASE_TYPE=postgres" -Environment="VIKUNJA_DATABASE_HOST={{ services['postgresql']['domain'] }}.{{ domain['internal'] }}" +Environment="VIKUNJA_DATABASE_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}" Environment="VIKUNJA_DATABASE_USER=vikunja" Environment="VIKUNJA_DATABASE_DATABASE=vikunja_db" Environment="VIKUNJA_DATABASE_SSLMODE=verify-full" @@ -39,7 +39,7 @@ Secret=VIKUNJA_DATABASE_PASSWORD,type=env # OIDC Environment="VIKUNJA_AUTH_OPENID_ENABLED=true" Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_NAME=Authelia" -Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_AUTHURL=https://{{ services['authelia']['domain'] }}.{{ domain['public'] }}" +Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_AUTHURL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_CLIENTID=vikunja" # Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_SCOPE=" default value = openid email profile # Vikunja doesn't support OIDC and local dual login. @@ -48,7 +48,7 @@ Environment="VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_CLIENTID=vikunja" Secret=VIKUNJA_AUTH_OPENID_PROVIDERS_authelia_CLIENTSECRET,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 diff --git a/docs/archives/services/app/wikijs/wikijs.container.j2 b/docs/archives/services/app/wikijs/wikijs.container.j2 index a8738c6..103e7c1 100644 --- a/docs/archives/services/app/wikijs/wikijs.container.j2 +++ b/docs/archives/services/app/wikijs/wikijs.container.j2 @@ -23,7 +23,7 @@ Environment="TZ={{ timezone }}" # Database Environment="DB_TYPE=postgres" -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="DB_USER=wikijs" Environment="DB_NAME=wikijs_db" @@ -32,7 +32,7 @@ Environment="NODE_EXTRA_CA_CERTS=/etc/ssl/wiki/{{ root_cert_filename }}" Secret=WIKIJS_DB_PASS,type=env,target=DB_PASS [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