Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9be4276676 | |||
| 1256122081 | |||
| f28661e664 | |||
| a09712c142 | |||
| a7e2320b21 | |||
| 24c83029e9 | |||
| ac64b3c04e | |||
| 26d696f813 | |||
| 1096981ef2 | |||
| e1936b494d | |||
| 0afc841b69 | |||
| a39122eb4b | |||
| 0f4da0bb53 |
@@ -2,7 +2,16 @@
|
||||
|
||||
This homelab project implements single-node On-premise IaaS system. The homelab contains virtual machines which are divided by their roles, such as private firewall, DNS, PKI, LDAP and database, SSO(OIDC). The standard domain is used to implement this system without specific vendors. All components are defined as code and initiated by IaC (Ansible) except hypervisor initial configuration.
|
||||
|
||||
## RTO times
|
||||
## RTO and RPO
|
||||
|
||||
### RPO
|
||||
- Each backup guarantees 24 hours RPO
|
||||
- DB dumps are backed up at 12:00 AM
|
||||
- Stateful data in app vm is backed up at 03:00 AM
|
||||
- The maximum inconsistency window between DB dumps and stateful data can be 27 hours.
|
||||
- The different backup time.
|
||||
|
||||
### RTO
|
||||
- Feb/25/2026 - Reprovisioning Hypervisor and vms
|
||||
- RTO: 1 hour 30 min - verified
|
||||
- Manual install and set vmm: 20 min
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
tags:
|
||||
- "always"
|
||||
- "init"
|
||||
- "upgrade"
|
||||
- "update"
|
||||
|
||||
- "site"
|
||||
- "[service_name]"
|
||||
# when: "'tags' is not in ansible_run_tags"
|
||||
|
||||
@@ -151,6 +151,21 @@ services:
|
||||
http: "3001"
|
||||
redis: "6383"
|
||||
subuid: "100999"
|
||||
outline:
|
||||
domain:
|
||||
public: "outline"
|
||||
internal: "outline.app"
|
||||
ports:
|
||||
http: "3002"
|
||||
redis: "6384"
|
||||
subuid: "101000"
|
||||
memos:
|
||||
domain:
|
||||
public: "memos"
|
||||
internal: "memos.app"
|
||||
ports:
|
||||
http: "3004"
|
||||
subuid: "110000"
|
||||
|
||||
version:
|
||||
packages:
|
||||
@@ -187,3 +202,5 @@ version:
|
||||
nextcloud: "33.0.3"
|
||||
collabora: "25.04.9.4.1"
|
||||
sure: "0.7.0-hotfix.2"
|
||||
outline: "1.7.1"
|
||||
memos: "0.28.0"
|
||||
|
||||
@@ -142,8 +142,8 @@
|
||||
name: "common"
|
||||
tasks_from: "services/set_alloy"
|
||||
apply:
|
||||
tags: ["init", "update", "alloy"]
|
||||
tags: ["init", "update", "alloy"]
|
||||
tags: ["init", "alloy"]
|
||||
tags: ["init", "alloy"]
|
||||
|
||||
- name: Set kopia
|
||||
ansible.builtin.include_role:
|
||||
@@ -225,6 +225,22 @@
|
||||
tags: ["site", "sure"]
|
||||
tags: ["site", "sure"]
|
||||
|
||||
- name: Set outline
|
||||
ansible.builtin.include_role:
|
||||
name: "app"
|
||||
tasks_from: "services/set_outline"
|
||||
apply:
|
||||
tags: ["site", "outline"]
|
||||
tags: ["site", "outline"]
|
||||
|
||||
- name: Set memos
|
||||
ansible.builtin.include_role:
|
||||
name: "app"
|
||||
tasks_from: "services/set_memos"
|
||||
apply:
|
||||
tags: ["site", "memos"]
|
||||
tags: ["site", "memos"]
|
||||
|
||||
- name: Flush handlers right now
|
||||
ansible.builtin.meta: "flush_handlers"
|
||||
|
||||
|
||||
@@ -162,8 +162,8 @@
|
||||
name: "fw"
|
||||
tasks_from: "services/set_bind"
|
||||
apply:
|
||||
tags: ["init", "update", "bind"]
|
||||
tags: ["init", "update", "bind"]
|
||||
tags: ["init", "bind"]
|
||||
tags: ["init", "bind"]
|
||||
|
||||
- name: Set blocky
|
||||
ansible.builtin.include_role:
|
||||
|
||||
@@ -102,3 +102,26 @@
|
||||
changed_when: false
|
||||
listen: "notification_restart_sure"
|
||||
ignore_errors: true # noqa: ignore-errors
|
||||
|
||||
- name: Restart outline
|
||||
ansible.builtin.systemd:
|
||||
name: "outline.service"
|
||||
state: "restarted"
|
||||
enabled: true
|
||||
scope: "user"
|
||||
daemon_reload: true
|
||||
when: is_outline_init.stat.exists
|
||||
changed_when: false
|
||||
listen: "notification_restart_outline"
|
||||
ignore_errors: true # noqa: ignore-errors
|
||||
|
||||
- name: Restart memos
|
||||
ansible.builtin.systemd:
|
||||
name: "memos.service"
|
||||
state: "restarted"
|
||||
enabled: true
|
||||
scope: "user"
|
||||
daemon_reload: true
|
||||
changed_when: false
|
||||
listen: "notification_restart_memos"
|
||||
ignore_errors: true # noqa: ignore-errors
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
- name: Create memos directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ node['home_path'] }}/{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ services['memos']['subuid'] }}"
|
||||
group: "svadmins"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "data/containers/memos"
|
||||
- "data/containers/memos/data"
|
||||
- "containers/memos"
|
||||
- "containers/memos/ssl"
|
||||
become: true
|
||||
|
||||
- name: Deploy root certificate
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
{{ hostvars['console']['ca']['root']['crt'] }}
|
||||
dest: "{{ node['home_path'] }}/containers/memos/ssl/{{ root_cert_filename }}"
|
||||
owner: "{{ services['memos']['subuid'] }}"
|
||||
group: "svadmins"
|
||||
mode: "0440"
|
||||
become: true
|
||||
notify: "notification_restart_memos"
|
||||
no_log: true
|
||||
|
||||
- name: Register secret value to podman secret
|
||||
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&\
|
||||
sslrootcert=/etc/ssl/memos/{{ root_cert_filename }}"
|
||||
state: "present"
|
||||
force: true
|
||||
notify: "notification_restart_memos"
|
||||
|
||||
- name: Deploy memos.container file
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/memos/memos.container.j2"
|
||||
dest: "{{ node['home_path'] }}/.config/containers/systemd/memos.container"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
mode: "0644"
|
||||
notify: "notification_restart_memos"
|
||||
|
||||
- name: Enable memos.service
|
||||
ansible.builtin.systemd:
|
||||
name: "memos.service"
|
||||
state: "started"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
scope: "user"
|
||||
|
||||
- name: Check container archive images
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/archives/containers/docker.io_neosmemo_memos_{{ version['containers']['memos'] }}.tar"
|
||||
register: container_archive_images
|
||||
|
||||
- name: Save container archive images
|
||||
containers.podman.podman_save:
|
||||
image:
|
||||
- "docker.io/neosmemo/memos:{{ version['containers']['memos'] }}"
|
||||
dest: "{{ node['home_path'] }}/archives/containers/docker.io_neosmemo_memos_{{ version['containers']['memos'] }}.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/docker.io_neosmemo_memos_{{ version['containers']['memos'] }}.tar"
|
||||
dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/"
|
||||
flat: true
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
- name: Set redis service name
|
||||
ansible.builtin.set_fact:
|
||||
redis_service: "outline"
|
||||
|
||||
- name: Create redis_outline directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ node['home_path'] }}/{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ services['redis']['subuid'] }}"
|
||||
group: "svadmins"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "containers/redis"
|
||||
- "containers/redis/{{ redis_service }}"
|
||||
- "containers/redis/{{ redis_service }}/data"
|
||||
become: true
|
||||
|
||||
- name: Deploy redis config file
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.conf.j2"
|
||||
dest: "{{ node['home_path'] }}/containers/redis/{{ redis_service }}/redis.conf"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
mode: "0644"
|
||||
register: "is_redis_conf"
|
||||
|
||||
- name: Deploy redis container file
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.container.j2"
|
||||
dest: "{{ node['home_path'] }}/.config/containers/systemd/redis_{{ redis_service }}.container"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
mode: "0644"
|
||||
register: "is_redis_containerfile"
|
||||
|
||||
- name: Enable (Restart) redis service
|
||||
ansible.builtin.systemd:
|
||||
name: "redis_{{ redis_service }}.service"
|
||||
state: "restarted"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
scope: "user"
|
||||
when: is_redis_conf.changed or is_redis_containerfile.changed # noqa: no-handler
|
||||
|
||||
- name: Create outline directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ node['home_path'] }}/{{ item }}"
|
||||
state: "directory"
|
||||
owner: "{{ services['outline']['subuid'] }}"
|
||||
group: "svadmins"
|
||||
mode: "0770"
|
||||
loop:
|
||||
- "data/containers/outline"
|
||||
- "data/containers/outline/data"
|
||||
- "containers/outline"
|
||||
- "containers/outline/ssl"
|
||||
become: true
|
||||
|
||||
- name: Check data directory empty
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/data/containers/outline/.init"
|
||||
register: "is_outline_init"
|
||||
|
||||
- name: Create .init file
|
||||
ansible.builtin.file:
|
||||
path: "{{ node['home_path'] }}/data/containers/outline/.init"
|
||||
state: "touch"
|
||||
mode: "0644"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
when: not is_outline_init.stat.exists
|
||||
|
||||
- name: Deploy root certificate
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
{{ hostvars['console']['ca']['root']['crt'] }}
|
||||
dest: "{{ node['home_path'] }}/containers/outline/ssl/{{ root_cert_filename }}"
|
||||
owner: "{{ services['outline']['subuid'] }}"
|
||||
group: "svadmins"
|
||||
mode: "0440"
|
||||
become: true
|
||||
notify: "notification_restart_outline"
|
||||
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: "OUTLINE_SECRET_KEY"
|
||||
value: "{{ hostvars['console']['outline']['session_secret'] }}"
|
||||
- name: "OUTLINE_UTILS_SECRET"
|
||||
value: "{{ hostvars['console']['outline']['utils_secret'] }}"
|
||||
- name: "OUTLINE_DATABASE_PASSWORD"
|
||||
value: "{{ hostvars['console']['postgresql']['password']['outline'] }}"
|
||||
- name: "OUTLINE_OIDC_CLIENT_SECRET"
|
||||
value: "{{ hostvars['console']['outline']['oidc']['secret'] }}"
|
||||
notify: "notification_restart_outline"
|
||||
no_log: true
|
||||
|
||||
- name: Deploy outline.container file
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/outline/outline.container.j2"
|
||||
dest: "{{ node['home_path'] }}/.config/containers/systemd/outline.container"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
mode: "0644"
|
||||
notify: "notification_restart_outline"
|
||||
|
||||
- name: Enable outline.service
|
||||
ansible.builtin.systemd:
|
||||
name: "outline.service"
|
||||
state: "started"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
scope: "user"
|
||||
|
||||
- name: Check container archive images
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar"
|
||||
loop:
|
||||
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
|
||||
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
|
||||
- image: "docker.io/outlinewiki/outline:{{ version['containers']['outline'] }}"
|
||||
file: "docker.io_outlinewiki_outline_{{ version['containers']['outline'] }}"
|
||||
loop_control:
|
||||
label: "{{ item.file }}"
|
||||
register: container_archive_images
|
||||
|
||||
- name: Save container archive images
|
||||
containers.podman.podman_save:
|
||||
image:
|
||||
- "{{ item.item.image }}"
|
||||
dest: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar"
|
||||
format: "oci-archive"
|
||||
force: false
|
||||
loop: "{{ container_archive_images.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.file }}"
|
||||
when: not item.stat.exists
|
||||
|
||||
- name: Fetch container archive images
|
||||
ansible.builtin.fetch:
|
||||
src: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar"
|
||||
dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/"
|
||||
flat: true
|
||||
loop: "{{ container_archive_images.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.file }}"
|
||||
@@ -57,8 +57,16 @@
|
||||
- "data/containers/paperless/consume"
|
||||
- "containers/paperless"
|
||||
- "containers/paperless/ssl"
|
||||
- "containers/paperless/build"
|
||||
become: true
|
||||
|
||||
- name: Deploy containerfile for build
|
||||
ansible.builtin.template:
|
||||
src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/paperless/build/paperless.containerfile.j2"
|
||||
dest: "{{ node['home_path'] }}/containers/paperless/build/Containerfile"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "svadmins"
|
||||
mode: "0640"
|
||||
|
||||
- name: Deploy root certificate
|
||||
ansible.builtin.copy:
|
||||
@@ -72,6 +80,18 @@
|
||||
notify: "notification_restart_paperless"
|
||||
no_log: true
|
||||
|
||||
- name: Build paperless container image
|
||||
containers.podman.podman_image:
|
||||
name: "{{ domain['internal'] }}/{{ node['name'] }}/paperless-ngx"
|
||||
# check tags from container file
|
||||
tag: "{{ version['containers']['paperless'] }}"
|
||||
state: "build"
|
||||
path: "{{ node['home_path'] }}/containers/paperless/build"
|
||||
|
||||
- name: Prune paperless dangling images
|
||||
containers.podman.podman_prune:
|
||||
image: true
|
||||
|
||||
- name: Register secret value to podman secret
|
||||
containers.podman.podman_secret:
|
||||
name: "{{ item.name }}"
|
||||
@@ -129,8 +149,8 @@
|
||||
loop:
|
||||
- image: "docker.io/library/redis:{{ version['containers']['redis'] }}"
|
||||
file: "docker.io_library_redis_{{ version['containers']['redis'] }}"
|
||||
- image: "ghcr.io/paperless-ngx/paperless-ngx:{{ version['containers']['paperless'] }}"
|
||||
file: "ghcr.io_paperless-ngx_paperless-ngx_{{ version['containers']['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 }}"
|
||||
register: container_archive_images
|
||||
|
||||
@@ -74,3 +74,10 @@
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
become: true
|
||||
|
||||
- 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
|
||||
become: true
|
||||
|
||||
@@ -100,20 +100,20 @@
|
||||
|
||||
- name: Check container archive images
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/archives/containers/ilnmors.internal_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
|
||||
path: "{{ node['home_path'] }}/archives/containers/{{ domain['internal'] }}_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
|
||||
register: container_archive_images
|
||||
|
||||
- name: Save container archive images
|
||||
containers.podman.podman_save:
|
||||
image:
|
||||
- "ilnmors.internal/{{ node['name'] }}/caddy:{{ version['containers']['caddy'] }}"
|
||||
dest: "{{ node['home_path'] }}/archives/containers/ilnmors.internal_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
|
||||
- "{{ 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
|
||||
|
||||
- name: Fetch container archive images
|
||||
ansible.builtin.fetch:
|
||||
src: "{{ node['home_path'] }}/archives/containers/ilnmors.internal_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
|
||||
src: "{{ node['home_path'] }}/archives/containers/{{ domain['internal'] }}_{{ node['name'] }}_caddy_{{ version['containers']['caddy'] }}.tar"
|
||||
dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/"
|
||||
flat: true
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
---
|
||||
- name: Register font
|
||||
ansible.builtin.shell: |
|
||||
fc-cache -f -v
|
||||
become: true
|
||||
changed_when: false
|
||||
listen: "notification_update_font"
|
||||
ignore_errors: true # noqa: ignore-errors
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/0xERR0R/blocky/releases/download/v{{ version['packages']['blocky'] }}/\
|
||||
blocky_v{{ version['packages']['blocky'] }}_Linux_x86_64.tar.gz"
|
||||
dest: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}-x86_64.tar.gz"
|
||||
dest: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}.tar.gz"
|
||||
owner: "blocky"
|
||||
group: "blocky"
|
||||
mode: "0600"
|
||||
@@ -52,16 +52,16 @@
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/0xERR0R/blocky/releases/download/v{{ version['packages']['blocky'] }}/\
|
||||
blocky_v{{ version['packages']['blocky'] }}_Linux_arm64.tar.gz"
|
||||
dest: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}-arm64.tar.gz"
|
||||
dest: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}.tar.gz"
|
||||
owner: "blocky"
|
||||
group: "blocky"
|
||||
mode: "0600"
|
||||
become: true
|
||||
when: ansible_facts['architecture'] == "aarch64"
|
||||
|
||||
- name: Deploy blocky binary file (x86_64)
|
||||
- name: Deploy blocky binary file
|
||||
ansible.builtin.unarchive:
|
||||
src: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}-x86_64.tar.gz"
|
||||
src: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}.tar.gz"
|
||||
remote_src: true
|
||||
dest: "/usr/local/bin/"
|
||||
owner: "root"
|
||||
@@ -72,23 +72,6 @@
|
||||
- "--wildcards"
|
||||
- "blocky"
|
||||
become: true
|
||||
when: ansible_facts['architecture'] == "x86_64"
|
||||
notify: "notification_restart_blocky"
|
||||
|
||||
- name: Deploy blocky binary file (aarch64)
|
||||
ansible.builtin.unarchive:
|
||||
src: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}-arm64.tar.gz"
|
||||
remote_src: true
|
||||
dest: "/usr/local/bin/"
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0755"
|
||||
extra_opts:
|
||||
- "--strip-components=0"
|
||||
- "--wildcards"
|
||||
- "blocky"
|
||||
become: true
|
||||
when: ansible_facts['architecture'] == "aarch64"
|
||||
notify: "notification_restart_blocky"
|
||||
|
||||
- name: Deploy blocky config
|
||||
@@ -141,3 +124,10 @@
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
become: true
|
||||
|
||||
- name: Fetch deb bin file
|
||||
ansible.builtin.fetch:
|
||||
src: "/home/blocky/bin/blocky-{{ version['packages']['blocky'] }}.tar.gz"
|
||||
dest: "{{ hostvars['console']['node']['data_path'] }}/bin/"
|
||||
flat: true
|
||||
become: true
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
- "affine"
|
||||
- "nextcloud"
|
||||
- "sure"
|
||||
- "outline"
|
||||
- "memos"
|
||||
|
||||
- name: Create postgresql directory
|
||||
ansible.builtin.file:
|
||||
@@ -176,15 +178,17 @@
|
||||
- name: Check container archive images
|
||||
ansible.builtin.stat:
|
||||
path: "{{ node['home_path'] }}/archives/containers/\
|
||||
ilnmors.internal_{{ node['name'] }}_postgres_pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
|
||||
{{ domain['internal'] }}_{{ node['name'] }}_postgres_\
|
||||
pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
|
||||
register: container_archive_images
|
||||
|
||||
- name: Save container archive images
|
||||
containers.podman.podman_save:
|
||||
image:
|
||||
- "ilnmors.internal/{{ node['name'] }}/postgres:pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}"
|
||||
- "{{ domain['internal'] }}/{{ node['name'] }}/postgres:pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}"
|
||||
dest: "{{ node['home_path'] }}/archives/containers/\
|
||||
ilnmors.internal_{{ node['name'] }}_postgres_pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
|
||||
{{ 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
|
||||
@@ -192,6 +196,7 @@
|
||||
- name: Fetch container archive images
|
||||
ansible.builtin.fetch:
|
||||
src: "{{ node['home_path'] }}/archives/containers/\
|
||||
ilnmors.internal_{{ node['name'] }}_postgres_pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
|
||||
{{ domain['internal'] }}_{{ node['name'] }}_postgres_\
|
||||
pg{{ version['containers']['postgresql'] }}-vectorchord{{ version['containers']['vectorchord'] }}.tar"
|
||||
dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/"
|
||||
flat: true
|
||||
|
||||
@@ -19,7 +19,11 @@ log() {
|
||||
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
local level="$1"
|
||||
local msg="$2"
|
||||
echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"edit_secret.sh\"">&2
|
||||
if [ "$level" == "error" ]; then
|
||||
echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"edit_secret.sh\"">&2
|
||||
else
|
||||
echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"edit_secret.sh\"">&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Secret file check
|
||||
@@ -58,9 +62,9 @@ cleanup() {
|
||||
trap cleanup EXIT
|
||||
|
||||
# Get GPG password from prompt
|
||||
echo -n "Enter GPG passphrase: " >&2
|
||||
echo -n "Enter GPG passphrase: " >&1
|
||||
read -s GPG_PASSPHRASE
|
||||
echo "" >&2
|
||||
echo "" >&1
|
||||
|
||||
# Decrypt age-key on the tmpfs (memory)
|
||||
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 \
|
||||
|
||||
@@ -120,6 +120,8 @@ postgresql:
|
||||
affine: ENC[AES256_GCM,data:XPXrcszsV06YqCJZ7CDqc4rCwqqNlbtLCFYfLAQ8jamLtft8L2UVrMA4WZo=,iv:vrWdBeckxB9tmEE628j4jhU+hSpE6TXYMGt0hh1Cg84=,tag:hlWwWUGht8NqWTZREMsa1Q==,type:str]
|
||||
nextcloud: ENC[AES256_GCM,data:ROsximNuWYMTZktmLJPx7W1Qol/uT+APgwoCtFO/6ZYYc3KxKvlk344eqEc=,iv:4d+MrfIHjJKAcwhvZ3g4go66uZcieuL7lngKErJd+fg=,tag:QbWOtxeCbiu62GyrE2atXg==,type:str]
|
||||
sure: ENC[AES256_GCM,data:FULJ2gjJ2gZC3s324itW+CjGRBHIP9RnOqw5TT1UaiUhb7UHAPm1na+LsZk=,iv:c0GnVZkxprJUzPPq3TCQaZvAes9QQuvDXqgVLLaiQIg=,tag:uDxy/Lkd2hNK4AWwMNMslw==,type:str]
|
||||
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]
|
||||
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
|
||||
#
|
||||
#
|
||||
@@ -251,6 +253,24 @@ sure:
|
||||
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
|
||||
#
|
||||
#
|
||||
#ENC[AES256_GCM,data:H6BtfLuevnDCEpHSmf6pxQ==,iv:9Jw0UZwVwS2It7c4e1rAN/BEiqDKp9DHD4W9xdpJ/Vs=,tag:Xj8HKvxgCA260CXBVK2S1A==,type:comment]
|
||||
outline:
|
||||
session_secret: ENC[AES256_GCM,data:A26j8/ghGA9MjDiuq+H84iTFXkHZp3l3LbGKO9w+Cgv+u1dkTF5q4zuBnOSBi+Tnz+2Y4jIoxsac+CS0dguRMg==,iv:NzJiJxVxOvud3e4mP8urnHrqugE8SmjFVnZtwfzrUzs=,tag:YR/jexb3G0HfC2qHpn6fPA==,type:str]
|
||||
utils_secret: ENC[AES256_GCM,data:ACI+NZzqHBsYeb4WwTJ1nfJcy6zNwJn2hBqA93aM5cgNWQ4KtQHDXgmS6UTwmSpxrteiaH25J3Np03H3eQoVeA==,iv:nitwxxwTPQvoQXGLVMnk2zsdC7M+JIXkTY4zJdQ3DFo=,tag:IQewG7EE9IgQk+Rfz7M5Iw==,type:str]
|
||||
oidc:
|
||||
secret: ENC[AES256_GCM,data:zR6sN205C51BIiQxLYOwC4ZbTYGr2AveiPeEbdadairmfnNJkGOtm0zT4Gg=,iv:uyYoJYuAuH/Gy2dlLr2SkkLYq+lMdwBgmzUsEj1EAKc=,tag:uhgvE6BagvPPKf7Y3NaGfA==,type:str]
|
||||
hash: ENC[AES256_GCM,data:xOibOUdmfhvUk6HABHf/MGIaUqRtrmbBpD5eTIrlt+Ll5IxuIaECMIyBufqV6KStoNsAxtlWmPqoVYROnVh7Z50ntywP6w6YNFbihMGr5tYnNBAS2ITViCq0Z7GQpNVmhNkFbmZv4bX14YOI557CS1KeDVrKrHnZ3fPgEgDwEiUdCwo=,iv:3lVQEw/gmTvJ6wteyKhAHAYUUw1AfKku8YEveddJTZU=,tag:0jimA4vof+nKOQtCOrVFdw==,type:str]
|
||||
#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment]
|
||||
#
|
||||
#
|
||||
#ENC[AES256_GCM,data:Fis0K+QOQwUfHItGI5U=,iv:kRmt4WhV6bBwN7Xl2xLrbxnKuLKTBC5nI9ZnQSIgIwo=,tag:whb1oOEX+fry9GS1+RlPrA==,type:comment]
|
||||
memos:
|
||||
oidc:
|
||||
secret: ENC[AES256_GCM,data:+rX8znej0jjXybmUjoI7DkybNkNeg15Py7bav665Uej48I5ZAZbUiKwccNo=,iv:UzcCqXxvweosRL8afhyRBB2ERm1PNs+tuxeDwL7Mcu8=,tag:+gJWSAq2BpSlSw/iwhIbpA==,type:str]
|
||||
hash: ENC[AES256_GCM,data:D7+37XTpDFRabT5YEh9nfxO1Q9Sdc98jAxPsjkPzj7bthn1H0dlLYZSJ2Fy8Ye4vjIFO+TZ6HWx8N4cSwHTfgls3EYFh1TW0FUnyCY1kYd/+ljfZB9Y6M+i5zb5nEIBHI32Sk7/B20cGnr0SUb3EKU+SmnACgZ0fjKKyUpSzGu75MH8=,iv:3RkTpgy85JPJqu6AEKrw8RY2O+L/CldPuPpN9ln54u8=,tag:Nl0DL5tVenA/fdRNOGyPkA==,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:
|
||||
password: ENC[AES256_GCM,data:qu0f9L7A0eFq/UCpaRs=,iv:W8LLOp3MSfd/+EfNEZNf91K8GgI5eUfVPoWTRES2C0Y=,tag:Q5FlAOfwqwJwPvd7k6i+0g==,type:str]
|
||||
@@ -280,7 +300,7 @@ sops:
|
||||
UmliaFNxVTBqRkI1QWJpWGpTRWxETW8KEY/8AfU73UOzCGhny1cNnd5dCNv7bHXt
|
||||
k+uyWPPi+enFkVaceSwMFrA66uaWWrwAj11sXEB7yzvGFPrnAGezjQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-09T14:26:51Z"
|
||||
mac: ENC[AES256_GCM,data:TYs08ZSS2kcO5lYuhQ/IySUSQ3DpL+ba3/uNLyszht4OttR110/W/WQLiRuu/Ql6FwtDtjq6I3iNpOhmCHSv1kMCam1l99GEIYCaPUIY+TY3Zw0j7518dFXe8p/DrKRwIVXfK5lIKLIEd+eizD50HzwXXJFmU+7YDkQ1Dx+55kw=,iv:arJKJ4wO4sdQlu3GZbtultsfM6s8vbhG93tnf2EjJDc=,tag:m95gUqvn4w85XI8qVvCZpQ==,type:str]
|
||||
lastmodified: "2026-05-16T11:08:23Z"
|
||||
mac: ENC[AES256_GCM,data:FprpwGfSw+KObEthT41tD8qgawQPpRlHw0ZxcyH74Nzk+47AbkDR3dWUPmZGPTePcQM4WI3e1IzE0ptJA7Hpcsnq/wHdo+uTW8nozYxzDU7JhjG2Xn6CPov2mV3o0SCegiCpfvE4A3bXiBOPapUIIs+ySBUMmyyW6m5ravElxsA=,iv:wt/3nFmVfodxActWBNvli2M2LLQZG8bkuagA/Fc6iuk=,tag:z1QWaMrGN/8kBrmZ8mPJUA==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.12.1
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
[Quadlet]
|
||||
DefaultDependencies=false
|
||||
|
||||
[Unit]
|
||||
Description=Memos
|
||||
|
||||
[Container]
|
||||
Image=docker.io/neosmemo/memos:{{ version['containers']['memos'] }}
|
||||
ContainerName=memos
|
||||
HostName=memos
|
||||
PublishPort={{ services['memos']['ports']['http'] }}:5230/tcp
|
||||
|
||||
# Volumes
|
||||
Volume=%h/data/containers/memos/data:/var/opt/memos:rw
|
||||
Volume=%h/containers/memos/ssl:/etc/ssl/memos:ro
|
||||
|
||||
# General
|
||||
Environment="TZ={{ timezone }}"
|
||||
Environment="MEMOS_MODE=prod"
|
||||
Environment="MEMOS_PORT=5230"
|
||||
Environment="MEMOS_INSTANCE_URL=https://{{ services['memos']['domain']['public'] }}.{{ domain['public'] }}"
|
||||
|
||||
# Database (PostgreSQL)
|
||||
Environment="MEMOS_DRIVER=postgres"
|
||||
Secret=MEMOS_DSN,type=env
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
TimeoutStopSec=120
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,77 @@
|
||||
[Quadlet]
|
||||
DefaultDependencies=false
|
||||
|
||||
[Unit]
|
||||
Description=Outline
|
||||
|
||||
After=redis_outline.service
|
||||
Wants=redis_outline.service
|
||||
|
||||
[Container]
|
||||
Image=docker.io/outlinewiki/outline:{{ version['containers']['outline'] }}
|
||||
ContainerName=outline
|
||||
HostName=outline
|
||||
PublishPort={{ services['outline']['ports']['http'] }}:3000/tcp
|
||||
|
||||
# Volumes
|
||||
Volume=%h/data/containers/outline/data:/var/lib/outline/data:rw
|
||||
Volume=%h/containers/outline/ssl:/etc/ssl/outline:ro
|
||||
|
||||
# General
|
||||
Environment="TZ={{ timezone }}"
|
||||
Environment="NODE_ENV=production"
|
||||
Environment="URL=https://{{ services['outline']['domain']['public'] }}.{{ domain['public'] }}"
|
||||
Environment="PORT=3000"
|
||||
#Environment="FORCE_HTTPS=true" #HTST function is not required
|
||||
|
||||
# Secrets
|
||||
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_PORT={{ services['postgresql']['ports']['tcp'] }}"
|
||||
Environment="DATABASE_NAME=outline_db"
|
||||
Environment="DATABASE_USER=outline"
|
||||
Environment="PGSSLMODE=verify-full"
|
||||
Environment="NODE_EXTRA_CA_CERTS=/etc/ssl/outline/{{ root_cert_filename }}"
|
||||
Secret=OUTLINE_DATABASE_PASSWORD,type=env,target=DATABASE_PASSWORD
|
||||
|
||||
# Redis
|
||||
Environment="REDIS_URL=redis://host.containers.internal:{{ services['outline']['ports']['redis'] }}"
|
||||
|
||||
# Storage
|
||||
Environment="FILE_STORAGE=local"
|
||||
Environment="FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data"
|
||||
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_USERNAME_CLAIM=preferred_username"
|
||||
Environment="OIDC_DISPLAY_NAME=Authelia"
|
||||
Environment="OIDC_SCOPES=openid offline_access profile email"
|
||||
Secret=OUTLINE_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET
|
||||
|
||||
# Mail
|
||||
#Environment="SMTP_HOST=smtp.example.com"
|
||||
#Environment="SMTP_PORT=587"
|
||||
#Environment="SMTP_USERNAME=outline@example.com"
|
||||
#Environment="SMTP_PASSWORD=change_me"
|
||||
#Environment="SMTP_FROM_EMAIL=outline@example.com"
|
||||
#Environment="SMTP_REPLY_EMAIL=outline@example.com"
|
||||
#Environment="SMTP_SECURE=false"
|
||||
|
||||
# Optional
|
||||
Environment="LOG_LEVEL=warn"
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }}
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
TimeoutStopSec=120
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM ghcr.io/paperless-ngx/paperless-ngx:{{ version['containers']['paperless'] }}
|
||||
|
||||
USER root
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
||||
&& curl -fsSL https://raw.githubusercontent.com/tesseract-ocr/tessdata_best/main/kor.traineddata \
|
||||
-o /usr/share/tesseract-ocr/5/tessdata/kor.traineddata \
|
||||
&& curl -fsSL https://raw.githubusercontent.com/tesseract-ocr/tessdata_best/main/eng.traineddata \
|
||||
-o /usr/share/tesseract-ocr/5/tessdata/eng.traineddata \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER paperless
|
||||
@@ -8,7 +8,7 @@ After=redis_paperless.service
|
||||
Wants=redis_paperless.service
|
||||
|
||||
[Container]
|
||||
Image=ghcr.io/paperless-ngx/paperless-ngx:{{ version['containers']['paperless'] }}
|
||||
Image={{ domain['internal'] }}/{{ node['name'] }}/paperless-ngx:{{ version['containers']['paperless'] }}
|
||||
ContainerName=paperless
|
||||
HostName=paperless
|
||||
PublishPort={{ services['paperless']['ports']['http'] }}:8000/tcp
|
||||
|
||||
@@ -260,3 +260,47 @@ 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/outline/
|
||||
- client_id: 'outline'
|
||||
client_name: 'Outline'
|
||||
client_secret: '{{ hostvars['console']['outline']['oidc']['hash'] }}'
|
||||
public: false
|
||||
authorization_policy: 'one_factor'
|
||||
require_pkce: false
|
||||
pkce_challenge_method: ''
|
||||
redirect_uris:
|
||||
- 'https://{{ services['outline']['domain']['public'] }}.{{ domain['public'] }}/auth/oidc.callback'
|
||||
scopes:
|
||||
- 'openid'
|
||||
- 'offline_access'
|
||||
- 'profile'
|
||||
- 'email'
|
||||
response_types:
|
||||
- 'code'
|
||||
grant_types:
|
||||
- 'authorization_code'
|
||||
- 'refresh_token'
|
||||
access_token_signed_response_alg: 'none'
|
||||
userinfo_signed_response_alg: 'none'
|
||||
token_endpoint_auth_method: 'client_secret_post'
|
||||
# https://www.authelia.com/integration/openid-connect/clients/memos/
|
||||
- client_id: 'memos'
|
||||
client_name: 'Memos'
|
||||
client_secret: '{{ hostvars['console']['memos']['oidc']['hash'] }}'
|
||||
public: false
|
||||
authorization_policy: 'one_factor'
|
||||
require_pkce: false
|
||||
pkce_challenge_method: ''
|
||||
redirect_uris:
|
||||
- 'https://{{ services['memos']['domain']['public'] }}.{{ domain['public'] }}/auth/callback'
|
||||
scopes:
|
||||
- 'openid'
|
||||
- 'profile'
|
||||
- 'email'
|
||||
response_types:
|
||||
- 'code'
|
||||
grant_types:
|
||||
- 'authorization_code'
|
||||
access_token_signed_response_alg: 'none'
|
||||
userinfo_signed_response_alg: 'none'
|
||||
token_endpoint_auth_method: 'client_secret_post'
|
||||
|
||||
@@ -77,3 +77,15 @@
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
{{ 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}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Internal domain
|
||||
{{ node['name'] }}.{{ domain['internal'] }} {
|
||||
|
||||
@@ -16,4 +16,6 @@ whitelist:
|
||||
- "evt.Meta.target_fqdn == '{{ services['immich']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status == '404' && evt.Meta.http_verb == 'GET' && evt.Meta.http_path contains '/api/assets/' && evt.Meta.http_path contains '/thumbnail'"
|
||||
# nextcloud thumbnail/preview request error false positive
|
||||
- "evt.Meta.target_fqdn == '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status == '404' && evt.Meta.http_verb == 'GET' && evt.Meta.http_path startsWith '/index.php/core/preview?'"
|
||||
# nextcloud chunks.mjs request false positive
|
||||
- "evt.Meta.target_fqdn == '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status in ['200', '304'] && evt.Meta.http_verb == 'GET' && evt.Meta.http_path contains 'chunk.mjs'"
|
||||
{% endif %}
|
||||
|
||||
+1
-1
@@ -148,4 +148,4 @@ if [ "$TYPE" == "ENV" ]; then
|
||||
log "error" "SOPS extract error"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -23,11 +23,14 @@
|
||||
- 2026-05-03: Make previous expressions annotation
|
||||
- 2026-05-07: Find the false positive case, which is not on `crowdsecurity/nextcloud-whitelist`
|
||||
- 2026-05-07: Set whitelist expression
|
||||
- 2026-05-11: Find the false positive case, which is not on `crowdsec/nextcloud-whitelist`
|
||||
- 2026-05-11: Set whitelist expression
|
||||
|
||||
## Solution
|
||||
- Install crowdsecurity/nextcloud-whitelist on auth node
|
||||
- Add expression on whitelist
|
||||
- evt.Meta.target_fqdn == '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status == '404' && evt.Meta.http_verb == 'GET' && evt.Meta.http_path startsWith '/index.php/core/preview?'
|
||||
- evt.Meta.target_fqdn == '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status == '404' && evt.Meta.http_verb == 'GET' && evt.Meta.http_path startsWith '/index.php/core/preview?'
|
||||
- evt.Meta.target_fqdn == '{{ services['nextcloud']['domain']['public'] }}.{{ domain['public'] }}' && evt.Meta.http_status in ['200', '304'] && evt.Meta.http_verb == 'GET' && evt.Meta.http_path contains 'chunk.mjs'
|
||||
|
||||
### Deprecated solution
|
||||
- Access to fw
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# Memos
|
||||
|
||||
## Prerequisite
|
||||
|
||||
### Create database
|
||||
|
||||
- Create the password with `openssl rand -base64 32`
|
||||
- Save this value in secrets.yaml in `postgresql.password.memos`
|
||||
- Access infra server to create memos_db with `podman exec -it postgresql psql -U postgres`
|
||||
|
||||
```SQL
|
||||
CREATE USER memos WITH PASSWORD 'postgresql.password.memos';
|
||||
CREATE DATABASE memos_db;
|
||||
ALTER DATABASE memos_db OWNER TO memos;
|
||||
```
|
||||
|
||||
### Create oidc secret and hash
|
||||
|
||||
- Create the secret with `openssl rand -base64 32`
|
||||
- access to auth vm
|
||||
- `podman exec -it authelia sh`
|
||||
- `authelia crypto hash generate pbkdf2 --password 'memos.oidc.secret'`
|
||||
- Save this value in secrets.yaml in `memos.oidc.secret` and `memos.oidc.hash`
|
||||
|
||||
### Add postgresql dump backup list
|
||||
|
||||
- [set_postgresql.yaml](../../../ansible/roles/infra/tasks/services/set_postgresql.yaml)
|
||||
|
||||
```yaml
|
||||
- name: Set connected services list
|
||||
ansible.builtin.set_fact:
|
||||
connected_services:
|
||||
- ...
|
||||
- "memos"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Access to memos
|
||||
|
||||
- https://memos.ilnmors.com
|
||||
- Create local-admin account
|
||||
|
||||
### Set OIDC
|
||||
|
||||
- Profile: Settings: Admin: SSO: Create
|
||||
- Type: OAUTH2
|
||||
- Template: Custom
|
||||
- Provider ID: authelia
|
||||
- Name: Authelia
|
||||
- Client ID: memos
|
||||
- Client secret: memos.oidc.secret
|
||||
- Authorization endpoint: https://authelia.ilnmors.com/api/oidc/authorization
|
||||
- Token endpoint: https://authelia.ilnmors.com/api/oidc/token
|
||||
- User endpoint: https://authelia.ilnmors.com/api/oidc/userinfo
|
||||
- Scopes: openid profile email
|
||||
- Identifier: preferred_username
|
||||
- Display Name: given_name
|
||||
- Email: email
|
||||
- Create
|
||||
|
||||
- Profile: Settings: Basic: My Account: SSO Accounts: Link
|
||||
- Login with Authelia
|
||||
- Accept
|
||||
|
||||
### Disallow local login and signup
|
||||
|
||||
- Profile: Settings: Admin: System
|
||||
- Disallow password auth: toggle
|
||||
- Save
|
||||
|
||||
### Change user name
|
||||
|
||||
- Profile: Settings: Basic: My Account: Edit
|
||||
@@ -0,0 +1,52 @@
|
||||
# Outline
|
||||
|
||||
## Prerequisite
|
||||
|
||||
### Create database
|
||||
|
||||
- Create the password with `openssl rand -base64 32`
|
||||
- Save this value in secrets.yaml in `postgresql.password.outline`
|
||||
- Access infra server to create outline_db with `podman exec -it postgresql psql -U postgres`
|
||||
|
||||
```SQL
|
||||
CREATE USER outline WITH PASSWORD 'postgresql.password.outline';
|
||||
CREATE DATABASE outline_db;
|
||||
ALTER DATABASE outline_db OWNER TO outline;
|
||||
```
|
||||
|
||||
### Create oidc secret and hash
|
||||
|
||||
- Create the secret with `openssl rand -base64 32`
|
||||
- access to auth vm
|
||||
- `podman exec -it authelia sh`
|
||||
- `authelia crypto hash generate pbkdf2 --password 'outline.oidc.secret'`
|
||||
- Save this value in secrets.yaml in `outline.oidc.secret` and `outline.oidc.hash`
|
||||
|
||||
### Create session secret and utils value
|
||||
|
||||
- Create two secrets with `openssl rand -hex 32`
|
||||
- Save this value in secrets.yaml in `outline.session_secret`
|
||||
- Save this value in secrets.yaml in `outline.utils_secret`
|
||||
|
||||
### Add postgresql dump backup list
|
||||
|
||||
- [set_postgresql.yaml](../../../ansible/roles/infra/tasks/services/set_postgresql.yaml)
|
||||
|
||||
```yaml
|
||||
- name: Set connected services list
|
||||
ansible.builtin.set_fact:
|
||||
connected_services:
|
||||
- ...
|
||||
- "outline"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Notice
|
||||
|
||||
- Outline is a BSL, not fully open-sourced stack. Always ensure they can change their policy of the outline
|
||||
|
||||
### Access to outline
|
||||
|
||||
- https://outline.ilnmors.com
|
||||
- Authelia redirect and automatically sign-up
|
||||
@@ -45,6 +45,11 @@ ALTER DATABASE paperless_db OWNER TO paperless;
|
||||
- "paperless"
|
||||
```
|
||||
|
||||
### Paperless custom build
|
||||
|
||||
- paperless-ngx uses 'tesseract_fast' model
|
||||
- building custom container to use 'tesseract_best' model to improve OCR accuracy.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Access to paperless
|
||||
|
||||
@@ -126,7 +126,10 @@
|
||||
- Link to Nextcloud
|
||||
- [x] sure
|
||||
- budget and finance
|
||||
- [ ] memos
|
||||
- [x] outline
|
||||
- Compare to affine, the whiteboard and canvas functions are not useful enough
|
||||
- [x] memos
|
||||
- Check outline and memos can be substituded for affine
|
||||
- WriteFreely or directus + frontend(Astro)
|
||||
- MediaCMS or PeerTube
|
||||
- Funkwhale or Navidrome or Jellyfin
|
||||
|
||||
Reference in New Issue
Block a user