2026-JUL-31
This commit is contained in:
@@ -45,6 +45,7 @@ services:
|
||||
internal: "kopia"
|
||||
ports:
|
||||
https: "51515"
|
||||
uid: "951"
|
||||
dependency:
|
||||
cert: true
|
||||
caddy:
|
||||
@@ -333,26 +334,26 @@ version:
|
||||
## maddy
|
||||
maddy: "0.9.5"
|
||||
x509-exporter: "4.1.0"
|
||||
prometheus: "v3.13.0"
|
||||
loki: "3.7.3"
|
||||
grafana: "13.1.0"
|
||||
prometheus: "v3.13.2"
|
||||
loki: "3.7.4"
|
||||
grafana: "13.1.1"
|
||||
# Auth
|
||||
authelia: "4.39.20"
|
||||
# App
|
||||
vaultwarden: "1.36.0"
|
||||
gitea: "1.26.4"
|
||||
redis: "8.8.0"
|
||||
immich: "v3.0.1"
|
||||
nextcloud: "34.0.1"
|
||||
collabora: "26.04.2.1.1"
|
||||
vaultwarden: "1.37.1"
|
||||
gitea: "1.27.1"
|
||||
redis: "8.10.0"
|
||||
immich: "v3.1.0"
|
||||
nextcloud: "34.0.2"
|
||||
collabora: "26.04.2.4.1"
|
||||
sure: "0.7.2"
|
||||
outline: "1.8.1"
|
||||
memos: "0.29.1"
|
||||
mealie: "v3.20.1"
|
||||
outline: "1.9.2"
|
||||
memos: "0.30.0"
|
||||
mealie: "v3.22.0"
|
||||
homebox: "0.26.2"
|
||||
homepage: "v1.13.2"
|
||||
snappymail: "v2.38.2"
|
||||
mastodon: "v4.6.3"
|
||||
mastodon: "v4.6.4"
|
||||
booklogr: "v1.11.1"
|
||||
papra: "26.6.1-rootless"
|
||||
romm: "5.0.0-beta.2"
|
||||
romm: "5.1.0"
|
||||
|
||||
@@ -85,11 +85,6 @@
|
||||
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"
|
||||
@@ -97,7 +92,14 @@
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ node['group'] }}"
|
||||
mode: "0770"
|
||||
when: not is_kopia_repository.stat.exists
|
||||
|
||||
- name: Check repository directory empty
|
||||
ansible.builtin.find:
|
||||
path: "{{ node['home_path'] }}/data/kopia/repository/"
|
||||
file_type: "any"
|
||||
hidden: true
|
||||
recurse: false
|
||||
register: "is_kopia_repository_empty"
|
||||
|
||||
- name: Create kopia repository
|
||||
ansible.builtin.shell: |
|
||||
@@ -106,11 +108,9 @@
|
||||
set +a
|
||||
/usr/bin/kopia repository create filesystem \
|
||||
--path="{{ node['home_path'] }}/data/kopia/repository" \
|
||||
--description="{{ domain['internal'] }} kopia repository" \
|
||||
--create-only
|
||||
when: not is_kopia_repository.stat.exists
|
||||
--description="{{ domain['internal'] }} kopia repository"
|
||||
when: is_kopia_repository_empty.matched == 0
|
||||
changed_when: true
|
||||
register: "is_repository_created"
|
||||
no_log: true
|
||||
|
||||
- name: Connect to kopia repository
|
||||
@@ -122,10 +122,11 @@
|
||||
--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
|
||||
when: is_kopia_repository_empty.matched > 0
|
||||
changed_when: false
|
||||
failed_when:
|
||||
- is_kopia_repository_empty.matched > 0
|
||||
- is_repository_connected.rc != 0
|
||||
register: "is_repository_connected"
|
||||
no_log: true
|
||||
|
||||
@@ -144,10 +145,27 @@
|
||||
password: "{{ hostvars['console']['kopia']['users']['infra'] }}"
|
||||
- name: "app@app.{{ domain['internal'] }}"
|
||||
password: "{{ hostvars['console']['kopia']['users']['app'] }}"
|
||||
when:
|
||||
- not is_kopia_repository.stat.exists
|
||||
- is_repository_connected.rc == 0
|
||||
changed_when: true
|
||||
when: is_kopia_repository_empty.matched == 0
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Update kopia user
|
||||
ansible.builtin.shell: |
|
||||
set -a
|
||||
. /etc/secrets/{{ node['uid'] }}/kopia.env
|
||||
set +a
|
||||
/usr/bin/kopia server users set \
|
||||
--user-password {{ item.password | quote }} \
|
||||
{{ item.name }}
|
||||
loop:
|
||||
- name: "console@console.{{ domain['internal'] }}"
|
||||
password: "{{ hostvars['console']['kopia']['users']['console'] }}"
|
||||
- name: "infra@infra.{{ domain['internal'] }}"
|
||||
password: "{{ hostvars['console']['kopia']['users']['infra'] }}"
|
||||
- name: "app@app.{{ domain['internal'] }}"
|
||||
password: "{{ hostvars['console']['kopia']['users']['app'] }}"
|
||||
when: is_kopia_repository_empty.matched > 0
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set kopia console ACL
|
||||
@@ -158,17 +176,15 @@
|
||||
/usr/bin/kopia server acl add \
|
||||
--user console@console.{{ domain['internal'] }} \
|
||||
--target {{ item }} \
|
||||
--access FULL
|
||||
--access FULL \
|
||||
--overwrite
|
||||
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
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set kopia infra ACL
|
||||
@@ -179,7 +195,8 @@
|
||||
/usr/bin/kopia server acl add \
|
||||
--user infra@infra.{{ domain['internal'] }} \
|
||||
--target {{ item.target }} \
|
||||
--access {{ item.access }}
|
||||
--access {{ item.access }} \
|
||||
--overwrite
|
||||
loop:
|
||||
- target: "type=snapshot"
|
||||
access: "FULL"
|
||||
@@ -189,10 +206,7 @@
|
||||
access: "FULL"
|
||||
- target: "type=content"
|
||||
access: "APPEND"
|
||||
when:
|
||||
- not is_kopia_repository.stat.exists
|
||||
- is_repository_connected.rc == 0
|
||||
changed_when: true
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set kopia app ACL
|
||||
@@ -203,7 +217,8 @@
|
||||
/usr/bin/kopia server acl add \
|
||||
--user app@app.{{ domain['internal'] }} \
|
||||
--target {{ item.target }} \
|
||||
--access {{ item.access }}
|
||||
--access {{ item.access }} \
|
||||
--overwrite
|
||||
loop:
|
||||
- target: "type=snapshot"
|
||||
access: "FULL"
|
||||
@@ -213,10 +228,7 @@
|
||||
access: "FULL"
|
||||
- target: "type=content"
|
||||
access: "APPEND"
|
||||
when:
|
||||
- not is_kopia_repository.stat.exists
|
||||
- is_repository_connected.rc == 0
|
||||
changed_when: true
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set kopia policy
|
||||
@@ -248,10 +260,7 @@
|
||||
--keep-weekly=4 \
|
||||
--keep-monthly=6 \
|
||||
--keep-annual=1
|
||||
when:
|
||||
- not is_kopia_repository.stat.exists
|
||||
- is_repository_connected.rc == 0
|
||||
changed_when: true
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set kopia parameters
|
||||
@@ -261,10 +270,7 @@
|
||||
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
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Deploy kopia server service file
|
||||
@@ -289,10 +295,6 @@
|
||||
- name: Set infra/app backup kopia
|
||||
when: node['name'] in ["infra", "app"]
|
||||
block:
|
||||
- name: Set kopia uid
|
||||
ansible.builtin.set_fact:
|
||||
kopia_uid: 951
|
||||
|
||||
- name: Download kopia deb file (x86_64)
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/kopia/kopia/releases/download/v{{ version['packages']['kopia'] }}/\
|
||||
@@ -318,14 +320,14 @@
|
||||
- name: Create kopia group
|
||||
ansible.builtin.group:
|
||||
name: "kopia"
|
||||
gid: "{{ kopia_uid }}"
|
||||
gid: "{{ services['kopia']['uid'] }}"
|
||||
state: "present"
|
||||
become: true
|
||||
|
||||
- name: Create kopia user
|
||||
ansible.builtin.user:
|
||||
name: "kopia"
|
||||
uid: "{{ kopia_uid }}"
|
||||
uid: "{{ services['kopia']['uid'] }}"
|
||||
group: "kopia"
|
||||
shell: "/usr/sbin/nologin"
|
||||
password_lock: true
|
||||
@@ -359,8 +361,8 @@
|
||||
- name: Deploy kopia env
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/systemd/common/kopia/kopia.env.j2"
|
||||
dest: "/etc/secrets/{{ kopia_uid }}/kopia.env"
|
||||
owner: "{{ kopia_uid }}"
|
||||
dest: "/etc/secrets/{{ services['kopia']['uid'] }}/kopia.env"
|
||||
owner: "{{ services['kopia']['uid'] }}"
|
||||
group: "root"
|
||||
mode: "0400"
|
||||
become: true
|
||||
@@ -413,7 +415,7 @@
|
||||
become: true
|
||||
when: (is_kopia_connected.rc | default(1)) == 0
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
failed_when: is_snapshot_exist.rc != 0
|
||||
register: "is_snapshot_exist"
|
||||
no_log: true
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
- "containers/postgresql/build"
|
||||
become: true
|
||||
|
||||
- name: Check postgresql .init marker
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/containers/postgresql/.init"
|
||||
register: "is_postgresql_init_marker"
|
||||
|
||||
- name: Deploy containerfile for build
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/infra/postgresql/build/postgresql.containerfile.j2"
|
||||
@@ -95,38 +100,56 @@
|
||||
notify: "notification_reload_postgresql"
|
||||
no_log: true
|
||||
|
||||
- name: Check postgresql .init marker
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/containers/postgresql/.init"
|
||||
register: "is_postgresql_init_marker"
|
||||
- name: Initiating Postgresql
|
||||
when: not is_postgresql_init_marker.stat.exists
|
||||
block:
|
||||
- name: Check initiating SQL files
|
||||
ansible.builtin.find:
|
||||
path: "{{ node['home_path'] }}/data/containers/postgresql/backups/sql/"
|
||||
file_type: "file"
|
||||
patterns:
|
||||
- "pg_*.sql"
|
||||
hidden: true
|
||||
recurse: false
|
||||
become: true
|
||||
register: "postgresql_init_sql_files"
|
||||
|
||||
- name: Check initiating SQL file
|
||||
ansible.builtin.find:
|
||||
path: "{{ node['home_path'] }}/data/containers/postgresql/backups/sql/"
|
||||
file_type: "file"
|
||||
patterns:
|
||||
- "pg_*.sql"
|
||||
hidden: true
|
||||
recurse: false
|
||||
become: true
|
||||
register: "postgresql_init_sql_files"
|
||||
- name: Set initiating SQL file lists
|
||||
ansible.builtin.set_fact:
|
||||
postgresql_init_sql_file_list: >-
|
||||
{{
|
||||
postgresql_init_sql_files.files
|
||||
| map(attribute='path')
|
||||
| map('basename')
|
||||
| map('regex_replace', '^pg_', '')
|
||||
| map('regex_replace', '\\.sql$', '')
|
||||
| list
|
||||
}}
|
||||
|
||||
- name: Remote copy the initiating SQL
|
||||
ansible.builtin.copy:
|
||||
src: "{{ node['home_path'] }}/data/containers/postgresql/backups/sql/pg_{{ item }}.sql"
|
||||
remote_src: true
|
||||
dest: "{{ node['home_path'] }}/containers/postgresql/init/{{ '%02d' | format(index_num) }}_pg_{{ item }}.sql"
|
||||
owner: "{{ services['postgresql']['subuid'] }}"
|
||||
group: "{{ node['group'] }}"
|
||||
mode: "0600"
|
||||
loop: "{{ ['cluster'] + connected_services }}"
|
||||
loop_control:
|
||||
index_var: index_num
|
||||
become: true
|
||||
when:
|
||||
- (connected_services | length) > 0
|
||||
- not is_postgresql_init_marker.stat.exists
|
||||
- postgresql_init_sql_files.matched == (connected_services | length) + 1
|
||||
- name: Fail on invalid initiating SQL file set
|
||||
ansible.builtin.fail:
|
||||
when:
|
||||
- postgresql_init_sql_files.matched > 0
|
||||
- ((['cluster'] + connected_services)
|
||||
| symmetric_difference(postgresql_init_sql_file_list)
|
||||
| length) > 0
|
||||
|
||||
- name: Remote copy the initiating SQL
|
||||
ansible.builtin.copy:
|
||||
src: "{{ node['home_path'] }}/data/containers/postgresql/backups/sql/pg_{{ item }}.sql"
|
||||
remote_src: true
|
||||
dest: "{{ node['home_path'] }}/containers/postgresql/init/{{ '%02d' | format(index_num) }}_pg_{{ item }}.sql"
|
||||
owner: "{{ services['postgresql']['subuid'] }}"
|
||||
group: "{{ node['group'] }}"
|
||||
mode: "0600"
|
||||
loop: "{{ ['cluster'] + connected_services }}"
|
||||
loop_control:
|
||||
index_var: index_num
|
||||
become: true
|
||||
when:
|
||||
- ((['cluster'] + connected_services)
|
||||
| symmetric_difference(postgresql_init_sql_file_list)
|
||||
| length) == 0
|
||||
|
||||
- name: Deploy container file
|
||||
ansible.builtin.template:
|
||||
|
||||
@@ -112,6 +112,7 @@ table inet filter {
|
||||
jump global comment "set global condition"
|
||||
iifname "lo" accept comment "allow local connection: FW > FW"
|
||||
udp dport $PORTS_VPN accept comment "allow vpn connection: > FW"
|
||||
ip6 saddr $NET6_LLA meta l4proto icmpv6 accept comment "allow icmpv6 connection: ipv6 LLA > 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/WG > FW"
|
||||
# Global chain contains "WAN !> :SSH_PORT"
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ ExecStartPre=/usr/bin/podman exec postgresql sh -c "mkdir -p /backups/sql && cho
|
||||
|
||||
|
||||
# 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 'pg_dumpall -U postgres --schema-only > /backups/cluster/pg_cluster_$(date "+%%Y-%%m-%%d").sql'
|
||||
ExecStart=/usr/bin/podman exec -u postgres postgresql sh -c 'sed -i \'/CREATE ROLE postgres/d\' /backups/cluster/pg_cluster_$(date "+%%Y-%%m-%%d").sql'
|
||||
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/*"
|
||||
|
||||
@@ -346,7 +346,7 @@ loki.process "journal_parser" {
|
||||
}
|
||||
}
|
||||
stage.match {
|
||||
selector = "{level=~\"info|debug\", job!~\"loki|prometheus|grafana\"} |~ \"(?i)(\\berror\\b|\\berr\\b|\\bfailed\\b|\\bfailure\\b|\\bexception\\b|traceback|exit-code|container died|permission denied|access denied|connection refused|connection reset|timed out|timeout exceeded|no such file|authentication failed|sqlstate|duplicate key|relation .* does not exist|column .* does not exist|database .* does not exist|relation .* already exists|column .* already exists|x509|unknown authority|certificate .*expired|tls handshake|bad gateway|upstream .*failed|upstream .*error)\""
|
||||
selector = "{level=~\"info|debug\", job!~\"loki|prometheus|grafana\"} |~ \"(?i)(\\berror\\b|\\berr\\b|\\bfailed\\b|\\bfailure\\b|\\bexception\\b|traceback|exit-code|container died|permission denied|access denied|connection refused|connection reset|timed out|timeout exceeded|no such file|authentication failed|sqlstate|duplicate key|relation .* does not exist|column .* does not exist|database .* does not exist|relation .* already exists|column .* already exists|\bx509:|unknown authority|certificate .*expired|tls handshake|bad gateway|upstream .*failed|upstream .*error)\""
|
||||
stage.static_labels {
|
||||
values = {
|
||||
level = "error",
|
||||
|
||||
@@ -24,9 +24,9 @@ BindReadOnlyPaths={{ node['home_path'] }}/data
|
||||
|
||||
# In root namescope, %u always bring 0
|
||||
BindPaths=/etc/kopia
|
||||
BindPaths=/etc/secrets/{{ kopia_uid }}
|
||||
BindPaths=/etc/secrets/{{ services['kopia']['uid'] }}
|
||||
BindPaths=/var/cache/kopia
|
||||
EnvironmentFile=/etc/secrets/{{ kopia_uid }}/kopia.env
|
||||
EnvironmentFile=/etc/secrets/{{ services['kopia']['uid'] }}/kopia.env
|
||||
|
||||
ExecStartPre=/usr/bin/nc -zv {{ services['kopia']['domain']['internal'] }}.{{ domain['internal'] }} 443
|
||||
|
||||
|
||||
Reference in New Issue
Block a user