mirror of
https://github.com/fscotto/infra.git
synced 2026-10-04 13:59:52 +00:00
Deploy temporary Atlas Nextcloud and ONLYOFFICE stack
This commit is contained in:
21
AGENTS.md
21
AGENTS.md
@@ -66,6 +66,8 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora
|
|||||||
`ansible-playbook ansible/site.yml --limit atlas --tags gitea --check --diff`
|
`ansible-playbook ansible/site.yml --limit atlas --tags gitea --check --diff`
|
||||||
- Atlas canonical Gitea domain (restarts only Gitea on a real configuration change):
|
- Atlas canonical Gitea domain (restarts only Gitea on a real configuration change):
|
||||||
`ansible-playbook ansible/site.yml --limit atlas --tags gitea_public_domain --check --diff`
|
`ansible-playbook ansible/site.yml --limit atlas --tags gitea_public_domain --check --diff`
|
||||||
|
- Atlas Nextcloud/ONLYOFFICE steady state:
|
||||||
|
`ansible-playbook ansible/site.yml --limit atlas --tags nextcloud --check --diff`
|
||||||
- Atlas iCloudPD storage and boot-started Quadlet:
|
- Atlas iCloudPD storage and boot-started Quadlet:
|
||||||
`ansible-playbook ansible/site.yml --limit atlas --tags icloudpd --check --diff`
|
`ansible-playbook ansible/site.yml --limit atlas --tags icloudpd --check --diff`
|
||||||
- Ongoing Gitea proxy configuration:
|
- Ongoing Gitea proxy configuration:
|
||||||
@@ -363,10 +365,21 @@ successfully. The first monthly scrub remains a runtime check.
|
|||||||
- [x] Validate Gitea login and write via HTTPS. On 2026-10-03 the operator confirmed
|
- [x] Validate Gitea login and write via HTTPS. On 2026-10-03 the operator confirmed
|
||||||
authenticated web login and Git clone/pull/push through the public HTTPS endpoint. Do not
|
authenticated web login and Git clone/pull/push through the public HTTPS endpoint. Do not
|
||||||
restart the stale source Gitea after Atlas has accepted writes.
|
restart the stale source Gitea after Atlas has accepted writes.
|
||||||
- [ ] Design and deploy Nextcloud as another explicitly temporary Atlas service before Uranus. Give it
|
- [x] Design and deploy the empty temporary Atlas Nextcloud/ONLYOFFICE stack on 2026-10-03.
|
||||||
separate persistent application, database, and cache storage; keep credentials in Vault; publish it only
|
The operator explicitly authorized empty internal service startup before the first scrub;
|
||||||
through NPM over the Prometheus--Aegis gateway; and define backup, upgrade, and eventual Uranus-migration
|
this does not close the scrub or protection checks. Four rootless Quadlets, separate
|
||||||
procedures before exposing user data. Do not deploy Nextcloud before the data-protection checklist is complete.
|
component datasets, pinned images/apps, Vault secrets, standard fabio/chiara users, a
|
||||||
|
separate application admin and the Famiglia folder are deployed. Cron and internal Office
|
||||||
|
connection checks succeeded; repeat deployment changed nothing. See `docs/atlas-nextcloud.md`.
|
||||||
|
- [x] Complete the authorized empty-stack public cutover on 2026-10-03 after operator
|
||||||
|
DNS/NPM configuration. Both hostnames passed TLS and HTTPS redirects; authenticated
|
||||||
|
web login, WebDAV, private-file isolation, Famiglia cross-user create/read/update/delete and
|
||||||
|
CalDAV/CardDAV discovery passed. The Office connector and public health/API asset passed.
|
||||||
|
Temporary test files were removed; no iCloud data was imported.
|
||||||
|
- [ ] Complete Nextcloud desktop/mobile editing and synchronization acceptance, and
|
||||||
|
application-consistent backup/restore validation. Close the first actual scrub and
|
||||||
|
protection checks before importing family data.
|
||||||
|
iCloud migration and future Uranus transfer remain separate operations, not playbook flags.
|
||||||
- [x] Move Gitea canonical HTTPS and SSH hostname to `git.fscotto.co` on
|
- [x] Move Gitea canonical HTTPS and SSH hostname to `git.fscotto.co` on
|
||||||
2026-10-03 through Ansible. Only Gitea restarted; second run changed nothing.
|
2026-10-03 through Ansible. Only Gitea restarted; second run changed nothing.
|
||||||
HTTPS and authenticated SSH reads returned the same repository HEAD.
|
HTTPS and authenticated SSH reads returned the same repository HEAD.
|
||||||
|
|||||||
@@ -49,6 +49,38 @@ atlas_zfs_backup_reservation: 500G
|
|||||||
atlas_zfs_dataset_photobook: media/photobook
|
atlas_zfs_dataset_photobook: media/photobook
|
||||||
atlas_mount_root: /zpool
|
atlas_mount_root: /zpool
|
||||||
atlas_manage_storage: true
|
atlas_manage_storage: true
|
||||||
|
atlas_manage_nextcloud: true
|
||||||
|
atlas_nextcloud_domain: cloud.fscotto.co
|
||||||
|
atlas_onlyoffice_domain: office.fscotto.co
|
||||||
|
# Resolved official amd64 images on 2026-10-03; updates are deliberate.
|
||||||
|
atlas_nextcloud_image: docker.io/library/nextcloud:33.0.9-apache@sha256:a97666d6ae931bde78a80cfba8abdf46d436d7b540f31895803f6fb0a012d689
|
||||||
|
atlas_nextcloud_postgres_image: docker.io/library/postgres:17-bookworm@sha256:639ab7ceb90e13123085b741fb31ef493fba25463002f6da665352e7b534b652
|
||||||
|
atlas_nextcloud_redis_image: docker.io/library/redis:7.4-bookworm@sha256:c6eabf748fc7a61dbb5a705c78bcf3d6377b1127a97d0ce965c11c44ba46896f
|
||||||
|
atlas_onlyoffice_image: docker.io/onlyoffice/documentserver:9.4.0.1@sha256:3ab6ebc7c605e5a32b7ae3ff19daed4925090245acc8100ce2230bd766c88212
|
||||||
|
atlas_nextcloud_users:
|
||||||
|
- username: fabio
|
||||||
|
display_name: Fabio
|
||||||
|
password: "{{ vault_nextcloud_fabio_password }}"
|
||||||
|
- username: chiara
|
||||||
|
display_name: Chiara
|
||||||
|
password: "{{ vault_nextcloud_chiara_password }}"
|
||||||
|
atlas_nextcloud_apps:
|
||||||
|
- id: groupfolders
|
||||||
|
version: 21.0.9
|
||||||
|
url: https://github.com/nextcloud-releases/groupfolders/releases/download/v21.0.9/groupfolders-v21.0.9.tar.gz
|
||||||
|
checksum: sha256:d8b95f0778425f646f2311ba5b42d8e2fcfdf37dc2fd35fcac8d3f01bde38a21
|
||||||
|
- id: onlyoffice
|
||||||
|
version: 10.2.1
|
||||||
|
url: https://github.com/ONLYOFFICE/onlyoffice-nextcloud/releases/download/v10.2.1/onlyoffice.tar.gz
|
||||||
|
checksum: sha256:144998af0610ccd17ee8d7025e2f8001472da03f6dab90ff38039247825e3a9b
|
||||||
|
- id: contacts
|
||||||
|
version: 8.9.1
|
||||||
|
url: https://github.com/nextcloud-releases/contacts/releases/download/v8.9.1/contacts-v8.9.1.tar.gz
|
||||||
|
checksum: sha256:a25cdf448b192631b8e8eb7addc31b40382b33871b10521f4308ac5a6e0457bf
|
||||||
|
- id: calendar
|
||||||
|
version: 6.6.2
|
||||||
|
url: https://github.com/nextcloud-releases/calendar/releases/download/v6.6.2/calendar-v6.6.2.tar.gz
|
||||||
|
checksum: sha256:7e83632d4436d3037a34d1c73cbc06d5ccb6e8fc10f096a86a43a0515588529c
|
||||||
# Rootless Gitea was restored from the stopped-source export before production activation.
|
# Rootless Gitea was restored from the stopped-source export before production activation.
|
||||||
atlas_manage_gitea: true
|
atlas_manage_gitea: true
|
||||||
atlas_gitea_production_enabled: true
|
atlas_gitea_production_enabled: true
|
||||||
|
|||||||
@@ -1,5 +1,29 @@
|
|||||||
---
|
---
|
||||||
atlas_manage_storage: false
|
atlas_manage_storage: false
|
||||||
|
atlas_manage_nextcloud: false
|
||||||
|
atlas_nextcloud_root: "{{ atlas_app_data_mountpoint }}/nextcloud"
|
||||||
|
atlas_nextcloud_dataset: "{{ atlas_zfs_pool }}/services/data/nextcloud"
|
||||||
|
atlas_nextcloud_domain: ""
|
||||||
|
atlas_onlyoffice_domain: ""
|
||||||
|
atlas_nextcloud_http_port: 8080
|
||||||
|
atlas_onlyoffice_http_port: 8081
|
||||||
|
atlas_nextcloud_network_subnet: 10.90.10.0/24
|
||||||
|
atlas_nextcloud_network_gateway: 10.90.10.1
|
||||||
|
atlas_nextcloud_quadlet_dir: "{{ atlas_admin_home }}/.config/containers/systemd"
|
||||||
|
atlas_nextcloud_private_dir: "{{ atlas_admin_home }}/.config/atlas-nextcloud"
|
||||||
|
atlas_nextcloud_app_cache: "{{ atlas_admin_home }}/.cache/atlas-nextcloud-apps"
|
||||||
|
atlas_nextcloud_image: ""
|
||||||
|
atlas_nextcloud_postgres_image: ""
|
||||||
|
atlas_nextcloud_redis_image: ""
|
||||||
|
atlas_onlyoffice_image: ""
|
||||||
|
atlas_nextcloud_admin: admin
|
||||||
|
atlas_nextcloud_users: []
|
||||||
|
atlas_nextcloud_apps: []
|
||||||
|
atlas_nextcloud_services:
|
||||||
|
- atlas-nextcloud-db.service
|
||||||
|
- atlas-nextcloud-redis.service
|
||||||
|
- atlas-nextcloud.service
|
||||||
|
- atlas-onlyoffice.service
|
||||||
atlas_manage_sharing: false
|
atlas_manage_sharing: false
|
||||||
# Destructive first-boot action; normally false once the pool exists.
|
# Destructive first-boot action; normally false once the pool exists.
|
||||||
atlas_create_pool: false
|
atlas_create_pool: false
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
- name: Import the declared Atlas Gitea public domain
|
- name: Import the declared Atlas Gitea public domain
|
||||||
ansible.builtin.import_tasks: gitea_public_domain.yml
|
ansible.builtin.import_tasks: gitea_public_domain.yml
|
||||||
|
|
||||||
|
- name: Import Atlas Nextcloud steady-state stack
|
||||||
|
ansible.builtin.import_tasks: nextcloud.yml
|
||||||
|
|
||||||
- name: Import Atlas iCloudPD storage and boot-started Quadlet tasks
|
- name: Import Atlas iCloudPD storage and boot-started Quadlet tasks
|
||||||
ansible.builtin.import_tasks: icloudpd.yml
|
ansible.builtin.import_tasks: icloudpd.yml
|
||||||
|
|
||||||
|
|||||||
309
ansible/roles/profile_atlas/tasks/nextcloud.yml
Normal file
309
ansible/roles/profile_atlas/tasks/nextcloud.yml
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
---
|
||||||
|
- name: Manage the empty Atlas Nextcloud and ONLYOFFICE stack
|
||||||
|
tags: [atlas, nextcloud]
|
||||||
|
when: atlas_manage_nextcloud | bool
|
||||||
|
block:
|
||||||
|
- name: Validate dedicated paths, domains and pinned images
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- atlas_manage_storage | bool
|
||||||
|
- atlas_manage_firewall | bool
|
||||||
|
- atlas_nextcloud_root == atlas_app_data_mountpoint ~ '/nextcloud'
|
||||||
|
- atlas_nextcloud_dataset == atlas_zfs_pool ~ '/services/data/nextcloud'
|
||||||
|
- atlas_nextcloud_domain is match('^[a-z0-9.-]+$')
|
||||||
|
- atlas_onlyoffice_domain is match('^[a-z0-9.-]+$')
|
||||||
|
- atlas_nextcloud_domain != atlas_onlyoffice_domain
|
||||||
|
- atlas_nextcloud_http_port | int > 1024
|
||||||
|
- atlas_onlyoffice_http_port | int > 1024
|
||||||
|
- atlas_nextcloud_http_port != atlas_onlyoffice_http_port
|
||||||
|
- "['calendar', 'contacts', 'onlyoffice', 'groupfolders'] | difference(atlas_nextcloud_apps | map(attribute='id') | list) | length == 0"
|
||||||
|
- atlas_nextcloud_users | length > 0
|
||||||
|
- atlas_nextcloud_admin not in (atlas_nextcloud_users | map(attribute='username') | list)
|
||||||
|
- atlas_nextcloud_users | map(attribute='username') | unique | list | length == atlas_nextcloud_users | length
|
||||||
|
- item is search('@sha256:[0-9a-f]{64}$')
|
||||||
|
loop:
|
||||||
|
- "{{ atlas_nextcloud_image }}"
|
||||||
|
- "{{ atlas_nextcloud_postgres_image }}"
|
||||||
|
- "{{ atlas_nextcloud_redis_image }}"
|
||||||
|
- "{{ atlas_onlyoffice_image }}"
|
||||||
|
|
||||||
|
- name: Require dedicated Vault secrets without exposing them
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item | default('') is match('^[a-zA-Z0-9]{32,}$')
|
||||||
|
loop: >-
|
||||||
|
{{ [vault_nextcloud_database_password | default(''),
|
||||||
|
vault_nextcloud_redis_password | default(''),
|
||||||
|
vault_nextcloud_admin_password | default(''),
|
||||||
|
vault_nextcloud_onlyoffice_jwt | default('')] +
|
||||||
|
(atlas_nextcloud_users | map(attribute='password') | list) }}
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Verify the existing application-data parent is mounted
|
||||||
|
community.general.zfs_facts:
|
||||||
|
name: "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_app_data }}"
|
||||||
|
properties: name,mounted,mountpoint
|
||||||
|
register: atlas_nextcloud_parent
|
||||||
|
|
||||||
|
- name: Require the verified application-data parent
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- atlas_nextcloud_parent.ansible_facts.ansible_zfs_datasets | length == 1
|
||||||
|
- atlas_nextcloud_parent.ansible_facts.ansible_zfs_datasets[0].mounted == 'yes'
|
||||||
|
- atlas_nextcloud_parent.ansible_facts.ansible_zfs_datasets[0].mountpoint == atlas_app_data_mountpoint
|
||||||
|
|
||||||
|
- name: Create the dedicated Nextcloud namespace and component datasets
|
||||||
|
community.general.zfs:
|
||||||
|
name: "{{ atlas_nextcloud_dataset }}{{ item }}"
|
||||||
|
state: present
|
||||||
|
extra_zfs_properties:
|
||||||
|
compression: zstd
|
||||||
|
mountpoint: "{{ atlas_nextcloud_root }}{{ item }}"
|
||||||
|
loop: ['', /app, /files, /database, /cache, /office]
|
||||||
|
|
||||||
|
- name: Inspect component directories before seeding ownership
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ atlas_nextcloud_root }}{{ item }}"
|
||||||
|
follow: false
|
||||||
|
get_checksum: false
|
||||||
|
loop: [/app, /files, /database, /cache, /office]
|
||||||
|
register: atlas_nextcloud_component_paths
|
||||||
|
|
||||||
|
- name: Seed only root-owned new dataset roots without recursive ownership changes
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.stat.path }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0700"
|
||||||
|
loop: "{{ atlas_nextcloud_component_paths.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
|
when:
|
||||||
|
- item.stat.exists
|
||||||
|
- item.stat.uid | default(-1) | int == 0
|
||||||
|
|
||||||
|
- name: Ensure private rootless stack configuration directories exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.path }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "{{ item.mode }}"
|
||||||
|
loop:
|
||||||
|
- {path: "{{ atlas_nextcloud_private_dir }}", mode: "0700"}
|
||||||
|
- {path: "{{ atlas_nextcloud_app_cache }}", mode: "0755"}
|
||||||
|
- {path: "{{ atlas_nextcloud_quadlet_dir }}", mode: "0700"}
|
||||||
|
- {path: "{{ atlas_admin_home }}/.config/systemd/user", mode: "0700"}
|
||||||
|
|
||||||
|
- name: Inspect the dedicated ONLYOFFICE bind directories
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ atlas_nextcloud_root }}/office/{{ item }}"
|
||||||
|
follow: false
|
||||||
|
get_checksum: false
|
||||||
|
loop: [data, lib, logs, database]
|
||||||
|
register: atlas_onlyoffice_bind_paths
|
||||||
|
|
||||||
|
- name: Create ONLYOFFICE bind directories only when absent
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item.invocation.module_args.path }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0700"
|
||||||
|
loop: "{{ atlas_onlyoffice_bind_paths.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item }}"
|
||||||
|
when: not item.stat.exists
|
||||||
|
|
||||||
|
- name: Store private mounted password files inside a restricted host directory
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "{{ item.value }}\n"
|
||||||
|
dest: "{{ atlas_nextcloud_private_dir }}/{{ item.name }}"
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
loop:
|
||||||
|
- {name: postgres-password, value: "{{ vault_nextcloud_database_password }}"}
|
||||||
|
- {name: redis-password, value: "{{ vault_nextcloud_redis_password }}"}
|
||||||
|
- {name: admin-password, value: "{{ vault_nextcloud_admin_password }}"}
|
||||||
|
- {name: onlyoffice-jwt, value: "{{ vault_nextcloud_onlyoffice_jwt }}"}
|
||||||
|
no_log: true
|
||||||
|
diff: false
|
||||||
|
register: atlas_nextcloud_secret_files
|
||||||
|
|
||||||
|
- name: Render private Redis and ONLYOFFICE configuration
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ atlas_nextcloud_private_dir }}/{{ item.dest }}"
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "{{ item.mode }}"
|
||||||
|
loop:
|
||||||
|
- {src: atlas-nextcloud-redis.conf.j2, dest: redis.conf, mode: "0644"}
|
||||||
|
- {src: atlas-onlyoffice.env.j2, dest: onlyoffice.env, mode: "0600"}
|
||||||
|
no_log: true
|
||||||
|
diff: false
|
||||||
|
register: atlas_nextcloud_private_configuration
|
||||||
|
|
||||||
|
- name: Download checksum-pinned compatible application releases
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ item.url }}"
|
||||||
|
dest: "{{ atlas_nextcloud_app_cache }}/{{ item.id }}-{{ item.version }}.tar.gz"
|
||||||
|
checksum: "{{ item.checksum }}"
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
loop: "{{ atlas_nextcloud_apps }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.id }} {{ item.version }}"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Admit only the Aegis gateway to the Nextcloud and Office HTTP listeners
|
||||||
|
ansible.posix.firewalld:
|
||||||
|
rich_rule: >-
|
||||||
|
rule family="ipv4" source address="{{ atlas_aegis_ip }}"
|
||||||
|
port port="{{ item }}" protocol="tcp" accept
|
||||||
|
zone: "{{ atlas_firewalld_zone }}"
|
||||||
|
state: enabled
|
||||||
|
permanent: true
|
||||||
|
immediate: true
|
||||||
|
loop: ["{{ atlas_nextcloud_http_port }}", "{{ atlas_onlyoffice_http_port }}"]
|
||||||
|
|
||||||
|
- name: Enable lingering for the declared rootless owner
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [loginctl, enable-linger, "{{ atlas_admin_username }}"]
|
||||||
|
creates: "/var/lib/systemd/linger/{{ atlas_admin_username }}"
|
||||||
|
|
||||||
|
- name: Render Nextcloud component and network Quadlets
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: "{{ atlas_nextcloud_quadlet_dir }}/{{ item }}"
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
loop:
|
||||||
|
- atlas-nextcloud.network
|
||||||
|
- atlas-nextcloud-db.container
|
||||||
|
- atlas-nextcloud-redis.container
|
||||||
|
- atlas-nextcloud.container
|
||||||
|
- atlas-onlyoffice.container
|
||||||
|
register: atlas_nextcloud_quadlets
|
||||||
|
|
||||||
|
- name: Render recurring Nextcloud cron user units
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: "{{ atlas_admin_home }}/.config/systemd/user/{{ item }}"
|
||||||
|
owner: "{{ atlas_admin_username }}"
|
||||||
|
group: "{{ atlas_admin_group }}"
|
||||||
|
mode: "0644"
|
||||||
|
loop: [atlas-nextcloud-cron.service, atlas-nextcloud-cron.timer]
|
||||||
|
register: atlas_nextcloud_cron_units
|
||||||
|
|
||||||
|
- name: Manage and verify rootless Nextcloud services
|
||||||
|
become_user: "{{ atlas_admin_username }}"
|
||||||
|
environment:
|
||||||
|
XDG_RUNTIME_DIR: "/run/user/{{ atlas_admin_uid }}"
|
||||||
|
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ atlas_admin_uid }}/bus"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
block:
|
||||||
|
- name: Pull the pinned images before starting services
|
||||||
|
containers.podman.podman_image:
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
loop:
|
||||||
|
- "{{ atlas_nextcloud_image }}"
|
||||||
|
- "{{ atlas_nextcloud_postgres_image }}"
|
||||||
|
- "{{ atlas_nextcloud_redis_image }}"
|
||||||
|
- "{{ atlas_onlyoffice_image }}"
|
||||||
|
|
||||||
|
- name: Reload the user manager to generate component units
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
scope: user
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Start the declared Nextcloud and ONLYOFFICE services
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
scope: user
|
||||||
|
name: "{{ item }}"
|
||||||
|
state: >-
|
||||||
|
{{ 'restarted' if (atlas_nextcloud_quadlets is changed or
|
||||||
|
atlas_nextcloud_private_configuration is changed or
|
||||||
|
atlas_nextcloud_secret_files is changed) else 'started' }}
|
||||||
|
loop: "{{ atlas_nextcloud_services }}"
|
||||||
|
|
||||||
|
- name: Wait for the application configuration directory to be initialized
|
||||||
|
become: true
|
||||||
|
become_user: root
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
path: "{{ atlas_nextcloud_root }}/app/config/config.php"
|
||||||
|
timeout: 600
|
||||||
|
|
||||||
|
- name: Derive container web-user host IDs from the actual rootless maps
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- podman
|
||||||
|
- unshare
|
||||||
|
- python3
|
||||||
|
- -c
|
||||||
|
- >-
|
||||||
|
import json;
|
||||||
|
print(json.dumps({k: next(int(b)+33-int(a) for a,b,n in
|
||||||
|
(l.split() for l in open('/proc/self/'+k+'_map'))
|
||||||
|
if int(a)<=33<int(a)+int(n)) for k in ['uid','gid']}))
|
||||||
|
register: atlas_nextcloud_web_mapping
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Read the current application SELinux label without changing it
|
||||||
|
become: true
|
||||||
|
become_user: root
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [stat, -c, '%C', "{{ atlas_nextcloud_root }}/app/config"]
|
||||||
|
register: atlas_nextcloud_config_label
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Maintain the managed Nextcloud configuration include
|
||||||
|
become: true
|
||||||
|
become_user: root
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: atlas-nextcloud.config.php.j2
|
||||||
|
dest: "{{ atlas_nextcloud_root }}/app/config/atlas.config.php"
|
||||||
|
owner: "{{ (atlas_nextcloud_web_mapping.stdout | from_json).uid }}"
|
||||||
|
group: "{{ (atlas_nextcloud_web_mapping.stdout | from_json).gid }}"
|
||||||
|
mode: "0640"
|
||||||
|
seuser: "{{ atlas_nextcloud_config_label.stdout.split(':')[0] }}"
|
||||||
|
serole: "{{ atlas_nextcloud_config_label.stdout.split(':')[1] }}"
|
||||||
|
setype: "{{ atlas_nextcloud_config_label.stdout.split(':')[2] }}"
|
||||||
|
selevel: "{{ atlas_nextcloud_config_label.stdout.split(':')[3:] | join(':') }}"
|
||||||
|
diff: false
|
||||||
|
|
||||||
|
- name: Wait for Nextcloud to complete its initial installation
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, status, --output=json]
|
||||||
|
register: atlas_nextcloud_status
|
||||||
|
changed_when: false
|
||||||
|
retries: 60
|
||||||
|
delay: 10
|
||||||
|
until: >-
|
||||||
|
atlas_nextcloud_status.rc == 0 and
|
||||||
|
atlas_nextcloud_status.stdout.startswith('{') and
|
||||||
|
(atlas_nextcloud_status.stdout | from_json).installed | default(false)
|
||||||
|
|
||||||
|
- name: Import declared ongoing application and account configuration
|
||||||
|
ansible.builtin.include_tasks: nextcloud_application.yml
|
||||||
|
|
||||||
|
- name: Enable and start the recurring Nextcloud cron timer
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
scope: user
|
||||||
|
name: atlas-nextcloud-cron.timer
|
||||||
|
state: "{{ 'restarted' if atlas_nextcloud_cron_units is changed else 'started' }}"
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
- name: Verify ONLYOFFICE local health without publishing the domain
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:{{ atlas_onlyoffice_http_port }}/healthcheck"
|
||||||
|
return_content: true
|
||||||
|
register: atlas_onlyoffice_health
|
||||||
|
retries: 60
|
||||||
|
delay: 10
|
||||||
|
until: atlas_onlyoffice_health.status | default(0) == 200 and atlas_onlyoffice_health.content | default('') | trim == 'true'
|
||||||
171
ansible/roles/profile_atlas/tasks/nextcloud_application.yml
Normal file
171
ansible/roles/profile_atlas/tasks/nextcloud_application.yml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
- name: Inspect installed application state
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, app:list, --output=json]
|
||||||
|
register: atlas_nextcloud_current_apps
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Record enabled and disabled application versions
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
atlas_nextcloud_installed_apps: >-
|
||||||
|
{{ (atlas_nextcloud_current_apps.stdout | from_json).enabled |
|
||||||
|
combine((atlas_nextcloud_current_apps.stdout | from_json).disabled) }}
|
||||||
|
|
||||||
|
- name: Refuse implicit application upgrades or downgrades
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.id not in atlas_nextcloud_installed_apps or atlas_nextcloud_installed_apps[item.id] == item.version
|
||||||
|
fail_msg: Application versions must be changed in a deliberate upgrade window.
|
||||||
|
loop: "{{ atlas_nextcloud_apps }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.id }}"
|
||||||
|
|
||||||
|
- name: Install only absent checksum-verified application archives
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- podman
|
||||||
|
- exec
|
||||||
|
- --user
|
||||||
|
- '33'
|
||||||
|
- atlas-nextcloud
|
||||||
|
- tar
|
||||||
|
- -xzf
|
||||||
|
- "/mnt/atlas-apps/{{ item.id }}-{{ item.version }}.tar.gz"
|
||||||
|
- -C
|
||||||
|
- /var/www/html/custom_apps
|
||||||
|
loop: "{{ atlas_nextcloud_apps }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.id }}"
|
||||||
|
when: item.id not in atlas_nextcloud_installed_apps
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Enable the declared applications
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, app:enable, "{{ item.id }}"]
|
||||||
|
loop: "{{ atlas_nextcloud_apps }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.id }}"
|
||||||
|
when: item.id not in (atlas_nextcloud_current_apps.stdout | from_json).enabled
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Inspect existing application users without exposing passwords
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, user:list, --output=json]
|
||||||
|
register: atlas_nextcloud_current_users
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Ensure the two standard users exist without resetting existing passwords
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- podman
|
||||||
|
- exec
|
||||||
|
- --user
|
||||||
|
- '33'
|
||||||
|
- --env
|
||||||
|
- OC_PASS
|
||||||
|
- atlas-nextcloud
|
||||||
|
- php
|
||||||
|
- occ
|
||||||
|
- user:add
|
||||||
|
- --password-from-env
|
||||||
|
- --display-name
|
||||||
|
- "{{ item.display_name }}"
|
||||||
|
- "{{ item.username }}"
|
||||||
|
environment:
|
||||||
|
OC_PASS: "{{ item.password }}"
|
||||||
|
loop: "{{ atlas_nextcloud_users }}"
|
||||||
|
when: item.username not in (atlas_nextcloud_current_users.stdout | from_json)
|
||||||
|
changed_when: true
|
||||||
|
no_log: true
|
||||||
|
diff: false
|
||||||
|
|
||||||
|
- name: Inspect standard-user group membership and quota
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, user:info, --output=json, "{{ item.username }}"]
|
||||||
|
loop: "{{ atlas_nextcloud_users }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.username }}"
|
||||||
|
register: atlas_nextcloud_user_info
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Require that family users are not administrators
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- "'admin' not in (item.stdout | from_json).groups"
|
||||||
|
loop: "{{ atlas_nextcloud_user_info.results }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Maintain unlimited initial standard-user quotas
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, user:setting, "{{ item.item.username }}", files, quota, none]
|
||||||
|
loop: "{{ atlas_nextcloud_user_info.results }}"
|
||||||
|
when: (item.stdout | from_json).quota != 'none'
|
||||||
|
changed_when: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Inspect the family group
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, group:list, --output=json]
|
||||||
|
register: atlas_nextcloud_groups
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Ensure the family group exists
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, group:add, famiglia]
|
||||||
|
when: "'famiglia' not in (atlas_nextcloud_groups.stdout | from_json)"
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Ensure both standard users belong to the family group
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, group:adduser, famiglia, "{{ item.username }}"]
|
||||||
|
loop: "{{ atlas_nextcloud_users }}"
|
||||||
|
when: item.username not in ((atlas_nextcloud_groups.stdout | from_json).get('famiglia', []))
|
||||||
|
changed_when: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Inspect configured family folders
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, groupfolders:list, --output=json]
|
||||||
|
register: atlas_nextcloud_folders_before
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Ensure a shared Famiglia folder exists
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, groupfolders:create, Famiglia]
|
||||||
|
when: >-
|
||||||
|
(atlas_nextcloud_folders_before.stdout | from_json |
|
||||||
|
selectattr('mountPoint', 'equalto', 'Famiglia') | list | length) == 0
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Inspect the resulting family folder
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, groupfolders:list, --output=json]
|
||||||
|
register: atlas_nextcloud_folders_after
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Select the existing family folder without changing unrelated folders
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
atlas_nextcloud_family_folder: >-
|
||||||
|
{{ atlas_nextcloud_folders_after.stdout | from_json |
|
||||||
|
selectattr('mountPoint', 'equalto', 'Famiglia') | first }}
|
||||||
|
|
||||||
|
- name: Maintain family read, create, write and delete permissions
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, groupfolders:group,
|
||||||
|
"{{ atlas_nextcloud_family_folder.id }}", famiglia, write, delete]
|
||||||
|
when: (atlas_nextcloud_family_folder.groups_list | default({}, true)).get('famiglia', 0) | int != 15
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Inspect the background job mode
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, config:app:get, core, backgroundjobs_mode]
|
||||||
|
register: atlas_nextcloud_background_mode
|
||||||
|
changed_when: false
|
||||||
|
failed_when: atlas_nextcloud_background_mode.rc not in [0, 1]
|
||||||
|
|
||||||
|
- name: Maintain cron background processing
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: [podman, exec, --user, '33', atlas-nextcloud, php, occ, background:cron]
|
||||||
|
when: atlas_nextcloud_background_mode.stdout | trim != 'cron'
|
||||||
|
changed_when: true
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Atlas recurring Nextcloud background jobs
|
||||||
|
Requires=atlas-nextcloud.service
|
||||||
|
After=atlas-nextcloud.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/bin/podman exec --user 33 atlas-nextcloud php -f /var/www/html/cron.php
|
||||||
|
TimeoutStartSec=15min
|
||||||
|
NoNewPrivileges=true
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run Nextcloud background jobs every five minutes
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=5min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
Unit=atlas-nextcloud-cron.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Atlas Nextcloud PostgreSQL
|
||||||
|
RequiresMountsFor={{ atlas_nextcloud_root }}/database
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=atlas-nextcloud-db
|
||||||
|
Image={{ atlas_nextcloud_postgres_image }}
|
||||||
|
Network=atlas-nextcloud.network
|
||||||
|
NetworkAlias=atlas-nextcloud-db
|
||||||
|
Environment=POSTGRES_DB=nextcloud
|
||||||
|
Environment=POSTGRES_USER=nextcloud
|
||||||
|
Environment=POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/postgres-password:/run/secrets/postgres-password:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/database:/var/lib/postgresql/data:Z
|
||||||
|
PodmanArgs=--memory=1g
|
||||||
|
HealthCmd=pg_isready -U nextcloud -d nextcloud
|
||||||
|
HealthInterval=30s
|
||||||
|
HealthStartPeriod=60s
|
||||||
|
NoNewPrivileges=true
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
bind 0.0.0.0
|
||||||
|
protected-mode yes
|
||||||
|
port 6379
|
||||||
|
requirepass {{ vault_nextcloud_redis_password }}
|
||||||
|
maxmemory 128mb
|
||||||
|
maxmemory-policy noeviction
|
||||||
|
save ""
|
||||||
|
appendonly no
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Atlas Nextcloud private Redis
|
||||||
|
RequiresMountsFor={{ atlas_nextcloud_root }}/cache
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=atlas-nextcloud-redis
|
||||||
|
Image={{ atlas_nextcloud_redis_image }}
|
||||||
|
Network=atlas-nextcloud.network
|
||||||
|
NetworkAlias=atlas-nextcloud-redis
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/redis.conf:/usr/local/etc/redis/atlas.conf:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/cache:/data:Z
|
||||||
|
Exec=redis-server /usr/local/etc/redis/atlas.conf
|
||||||
|
PodmanArgs=--memory=256m
|
||||||
|
NoNewPrivileges=true
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
// Managed ongoing application settings; never import or migrate user data.
|
||||||
|
$CONFIG = [
|
||||||
|
'trusted_domains' => ['{{ atlas_nextcloud_domain }}', 'atlas-nextcloud'],
|
||||||
|
'trusted_proxies' => ['{{ atlas_aegis_ip }}', '{{ atlas_nextcloud_network_gateway }}'],
|
||||||
|
'overwrite.cli.url' => 'https://{{ atlas_nextcloud_domain }}',
|
||||||
|
'overwritehost' => '{{ atlas_nextcloud_domain }}',
|
||||||
|
'overwriteprotocol' => 'https',
|
||||||
|
'allow_local_remote_servers' => true,
|
||||||
|
'default_quota' => 'none',
|
||||||
|
'skeletondirectory' => '',
|
||||||
|
'maintenance_window_start' => 1,
|
||||||
|
'default_phone_region' => 'IT',
|
||||||
|
'twofactor_enforced' => false,
|
||||||
|
'onlyoffice' => [
|
||||||
|
'DocumentServerUrl' => 'https://{{ atlas_onlyoffice_domain }}/',
|
||||||
|
'DocumentServerInternalUrl' => 'http://atlas-onlyoffice/',
|
||||||
|
'StorageUrl' => 'http://atlas-nextcloud/',
|
||||||
|
'jwt_secret' => trim(file_get_contents('/run/secrets/onlyoffice-jwt')),
|
||||||
|
'jwt_header' => 'AuthorizationJwt',
|
||||||
|
'allow_local_address' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Atlas Nextcloud
|
||||||
|
Requires=atlas-nextcloud-db.service atlas-nextcloud-redis.service
|
||||||
|
After=atlas-nextcloud-db.service atlas-nextcloud-redis.service
|
||||||
|
RequiresMountsFor={{ atlas_nextcloud_root }}/app {{ atlas_nextcloud_root }}/files
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=atlas-nextcloud
|
||||||
|
Image={{ atlas_nextcloud_image }}
|
||||||
|
Network=atlas-nextcloud.network
|
||||||
|
NetworkAlias=atlas-nextcloud
|
||||||
|
PublishPort={{ ansible_host }}:{{ atlas_nextcloud_http_port }}:80
|
||||||
|
PublishPort=127.0.0.1:{{ atlas_nextcloud_http_port }}:80
|
||||||
|
Environment=POSTGRES_HOST=atlas-nextcloud-db
|
||||||
|
Environment=POSTGRES_DB=nextcloud
|
||||||
|
Environment=POSTGRES_USER=nextcloud
|
||||||
|
Environment=POSTGRES_PASSWORD_FILE=/run/secrets/postgres-password
|
||||||
|
Environment=NEXTCLOUD_ADMIN_USER={{ atlas_nextcloud_admin }}
|
||||||
|
Environment=NEXTCLOUD_ADMIN_PASSWORD_FILE=/run/secrets/admin-password
|
||||||
|
Environment="NEXTCLOUD_TRUSTED_DOMAINS={{ atlas_nextcloud_domain }} atlas-nextcloud"
|
||||||
|
Environment=REDIS_HOST=atlas-nextcloud-redis
|
||||||
|
Environment=REDIS_HOST_PASSWORD_FILE=/run/secrets/redis-password
|
||||||
|
Environment=APACHE_DISABLE_REWRITE_IP=1
|
||||||
|
Environment=PHP_MEMORY_LIMIT=512M
|
||||||
|
Environment=PHP_UPLOAD_LIMIT=2G
|
||||||
|
Volume={{ atlas_nextcloud_root }}/app:/var/www/html:Z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/files:/var/www/html/data:Z
|
||||||
|
Volume={{ atlas_nextcloud_app_cache }}:/mnt/atlas-apps:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/postgres-password:/run/secrets/postgres-password:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/admin-password:/run/secrets/admin-password:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/redis-password:/run/secrets/redis-password:ro,z
|
||||||
|
Volume={{ atlas_nextcloud_private_dir }}/onlyoffice-jwt:/run/secrets/onlyoffice-jwt:ro,z
|
||||||
|
PodmanArgs=--memory=2g
|
||||||
|
NoNewPrivileges=true
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Managed by Ansible: private rootless application network, no host services.
|
||||||
|
[Network]
|
||||||
|
NetworkName=atlas-nextcloud
|
||||||
|
Subnet={{ atlas_nextcloud_network_subnet }}
|
||||||
|
Gateway={{ atlas_nextcloud_network_gateway }}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Atlas ONLYOFFICE Docs Community
|
||||||
|
RequiresMountsFor={{ atlas_nextcloud_root }}/office
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=atlas-onlyoffice
|
||||||
|
Image={{ atlas_onlyoffice_image }}
|
||||||
|
Network=atlas-nextcloud.network
|
||||||
|
NetworkAlias=atlas-onlyoffice
|
||||||
|
PublishPort={{ ansible_host }}:{{ atlas_onlyoffice_http_port }}:80
|
||||||
|
PublishPort=127.0.0.1:{{ atlas_onlyoffice_http_port }}:80
|
||||||
|
EnvironmentFile={{ atlas_nextcloud_private_dir }}/onlyoffice.env
|
||||||
|
Volume={{ atlas_nextcloud_root }}/office/data:/var/www/onlyoffice/Data:Z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/office/lib:/var/lib/onlyoffice:Z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/office/logs:/var/log/onlyoffice:Z
|
||||||
|
Volume={{ atlas_nextcloud_root }}/office/database:/var/lib/postgresql:Z
|
||||||
|
PodmanArgs=--memory=4g --shm-size=256m
|
||||||
|
NoNewPrivileges=true
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStartSec=1200
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
JWT_ENABLED=true
|
||||||
|
JWT_SECRET={{ vault_nextcloud_onlyoffice_jwt }}
|
||||||
|
JWT_HEADER=AuthorizationJwt
|
||||||
|
ALLOW_PRIVATE_IP_ADDRESS=true
|
||||||
|
ALLOW_META_IP_ADDRESS=false
|
||||||
|
USE_UNAUTHORIZED_STORAGE=false
|
||||||
|
WOPI_ENABLED=false
|
||||||
105
docs/atlas-nextcloud-design.md
Normal file
105
docs/atlas-nextcloud-design.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Nextcloud on Atlas — design draft
|
||||||
|
|
||||||
|
Status: the empty stack was deployed on 2026-10-03, explicitly before the first
|
||||||
|
scrub. The operator configured DNS/NPM and authorized public cutover; public TLS,
|
||||||
|
DAV and cross-user file checks passed. Client editing/sync acceptance and consistent
|
||||||
|
backup/restore validation remain open before family data. iCloud import remains a
|
||||||
|
separate operation. See `docs/atlas-nextcloud.md` for observed runtime state.
|
||||||
|
|
||||||
|
## Confirmed requirements
|
||||||
|
|
||||||
|
- Three family members are the eventual scope; provision only two standard user
|
||||||
|
accounts initially, `fabio` and `chiara`, with the third family user deferred.
|
||||||
|
Each initial user has a private file space and no administrator privileges.
|
||||||
|
- Add a separate Nextcloud application administrator account named `admin`, for
|
||||||
|
administration rather than daily document use. This is distinct from Atlas'
|
||||||
|
host account of the same name; credentials must not be reused.
|
||||||
|
- 2FA is optional, not enforced for the accounts. Offer enrollment and recovery
|
||||||
|
codes; encourage it for the administrator without silently imposing it.
|
||||||
|
- The three application accounts have been created in the empty deployment.
|
||||||
|
- No SMTP service is available. Initial deployment will not configure outbound
|
||||||
|
email or provision a mail server. Email notifications and email-based password
|
||||||
|
recovery are unavailable until SMTP is explicitly added. Document administrator-
|
||||||
|
assisted recovery for standard users and a private host-side admin recovery
|
||||||
|
procedure; do not expose a recovery endpoint or store plaintext passwords.
|
||||||
|
- Both initial users may add, edit and delete files in the shared `Famiglia`
|
||||||
|
folder. This does not imply sharing personal calendars or contacts.
|
||||||
|
- No initial per-user Nextcloud storage quota for `fabio` or `chiara`. Available
|
||||||
|
space is still bounded by the physical pool and any separately approved dataset
|
||||||
|
limits; monitor capacity and do not describe this as unlimited physical storage.
|
||||||
|
- Files, calendars, contacts and Office document editing in the browser.
|
||||||
|
- iPhone/iPad, Windows and Linux clients.
|
||||||
|
- Migrate iCloud Drive files, calendars and contacts. The operator estimates
|
||||||
|
approximately 50 GB of iCloud Drive files, excluding iCloudPD photos; this is
|
||||||
|
an estimate, not a measured inventory. The files include a mix of Fabio's and
|
||||||
|
Chiara's data. Migration is explicitly deferred to a separate later operation;
|
||||||
|
initial deployment must not import iCloud files, calendars or contacts.
|
||||||
|
Per-account mapping will be decided at migration time. Do not assume ongoing
|
||||||
|
two-way synchronization with iCloud or extend this scope to iCloud Photos.
|
||||||
|
- ONLYOFFICE is the chosen editor: browser editing on desktop and the existing
|
||||||
|
ONLYOFFICE app on iPhone/iPad. Mobile browser editing is not required.
|
||||||
|
- Temporary Atlas hosting, with eventual migration to Uranus.
|
||||||
|
- Completed one-time imports/migrations stay outside the steady-state playbook.
|
||||||
|
|
||||||
|
## Implemented architecture — public acceptance pending
|
||||||
|
|
||||||
|
- Nextcloud application with Files, Calendar, Contacts and an Office connector.
|
||||||
|
- PostgreSQL database and Redis for locking/cache; deployed versions and pinned
|
||||||
|
image digests are declared in Atlas host vars and documented in the runbook.
|
||||||
|
- Dedicated ONLYOFFICE Docs service and its Nextcloud connector. Test real
|
||||||
|
DOCX/XLSX/PPTX files in desktop browsers and opening/editing/saving through
|
||||||
|
the mobile ONLYOFFICE app before acceptance. Community Edition is deployed;
|
||||||
|
internal connector checks passed, but browser/mobile acceptance is still pending.
|
||||||
|
- Explicit Podman Quadlets managed by Ansible, preferably rootless like existing
|
||||||
|
Atlas services, subject to image/user namespace/SELinux validation.
|
||||||
|
- Separate persistent application/configuration, user files, database and cache
|
||||||
|
storage in the service namespace. Do not expose the managed Nextcloud data
|
||||||
|
directory as a writable SMB share or let Syncthing modify it directly.
|
||||||
|
- Approved names: `cloud.fscotto.co` for Nextcloud and `office.fscotto.co` for
|
||||||
|
ONLYOFFICE Docs. The operator configured DNS, certificates and NPM hosts;
|
||||||
|
public endpoint and routing checks passed on 2026-10-03.
|
||||||
|
- Public HTTPS through Prometheus NPM and the existing Aegis gateway only.
|
||||||
|
No public database/cache ports or directly exposed administrative interfaces.
|
||||||
|
- Office/Nextcloud callback routing, WebSockets, trusted proxies, JWT authentication
|
||||||
|
and upload limits must be tested end to end before publication.
|
||||||
|
- Credentials remain in Vault; never enter passwords or private keys in chat.
|
||||||
|
|
||||||
|
## Office decision
|
||||||
|
|
||||||
|
The operator already uses ONLYOFFICE on mobile and desktop and selected it for
|
||||||
|
this project. Desktop browser editing will use ONLYOFFICE Docs integrated with
|
||||||
|
Nextcloud; mobile editing will use the existing ONLYOFFICE app. The limitation
|
||||||
|
on Community mobile web editors does not conflict with that requirement.
|
||||||
|
App integration, permissions, document fidelity and reliable saves still require
|
||||||
|
acceptance tests; the app is not treated as proof of server-side compatibility.
|
||||||
|
|
||||||
|
## Data protection and rollout gates
|
||||||
|
|
||||||
|
- The operator explicitly authorized this empty deployment before the first scrub.
|
||||||
|
Close the data-protection checks before accepting live family data; this limited
|
||||||
|
exception does not mark the scrub or recovery checks complete.
|
||||||
|
- Re-check free RAM/CPU/storage and existing workload before choosing limits or quotas.
|
||||||
|
- Design consistent backups covering configuration, custom apps/themes, user files
|
||||||
|
and the database. ZFS snapshots alone do not establish application consistency.
|
||||||
|
- Define a coordinated maintenance/background-job pause and database dump/snapshot
|
||||||
|
procedure for recurring backups, with failure cleanup and monitoring.
|
||||||
|
- Confirm ZFS/Borg/USB coverage and independently restore into an isolated environment
|
||||||
|
before importing family data.
|
||||||
|
- Define deliberate upgrades and rollback boundaries; do not roll back a database
|
||||||
|
independently of its matching application/data backup.
|
||||||
|
- Start with a test account and representative documents; migrate iCloud content
|
||||||
|
explicitly only after client, sharing, Office and recovery tests pass.
|
||||||
|
- Plan Uranus transfer separately; do not add permanent one-time migration flags.
|
||||||
|
|
||||||
|
## Next decisions, one at a time
|
||||||
|
|
||||||
|
1. Validate desktop Office editing/saving, calendar/contact synchronization and
|
||||||
|
mobile ONLYOFFICE app integration; public empty-stack cutover is verified.
|
||||||
|
2. Complete protection gates and application-consistent backup/recovery tests.
|
||||||
|
3. Plan the deferred iCloud migration when explicitly requested.
|
||||||
|
|
||||||
|
## Primary references
|
||||||
|
|
||||||
|
- [Nextcloud Office installation](https://docs.nextcloud.com/server/stable/admin_manual/office/installation.html)
|
||||||
|
- [ONLYOFFICE mobile web editor restrictions](https://helpcenter.onlyoffice.com/mobile/android/mobile-web-editors/overview.aspx)
|
||||||
|
- [Nextcloud backup requirements](https://docs.nextcloud.com/server/stable/admin_manual/maintenance/backup.html)
|
||||||
127
docs/atlas-nextcloud.md
Normal file
127
docs/atlas-nextcloud.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# Atlas Nextcloud — public empty-stack cutover
|
||||||
|
|
||||||
|
## Observed state, 2026-10-03
|
||||||
|
|
||||||
|
The operator explicitly approved an empty deployment before the first monthly
|
||||||
|
scrub, and subsequently authorized public cutover. No iCloud files, calendars
|
||||||
|
or contacts have been imported. Public empty-stack validation is not acceptance
|
||||||
|
of production data before the outstanding protection and recovery checks.
|
||||||
|
|
||||||
|
Ansible manages the steady state through `profile_atlas` and the host-local
|
||||||
|
`atlas_manage_nextcloud: true` declaration. No migration/import flags or helpers
|
||||||
|
were added. An actual repeat run returned `changed=0`, with no failures.
|
||||||
|
|
||||||
|
- Rootless `admin` Quadlets: Nextcloud 33.0.9, PostgreSQL 17.11, Redis 7.4.11 and
|
||||||
|
ONLYOFFICE Docs Community 9.4.0.129 (image tag 9.4.0.1), on a dedicated network.
|
||||||
|
- Images are pinned by digest; Calendar 6.6.2, Contacts 8.9.1, ONLYOFFICE connector
|
||||||
|
10.2.1 and Team Folders 21.0.9 archives are pinned by version and SHA-256.
|
||||||
|
- Dedicated ZFS namespace: `zpool/services/data/nextcloud`, with separate `app`,
|
||||||
|
`files`, `database`, `cache` and `office` datasets. No writable SMB/Syncthing
|
||||||
|
access to the Nextcloud-managed file namespace is provided.
|
||||||
|
- The `admin` Nextcloud account is an application administrator, distinct from
|
||||||
|
the host account. `fabio` and `chiara` are standard users in `famiglia`, each
|
||||||
|
with no initial quota. Team folder `Famiglia` has unlimited quota and group
|
||||||
|
permission mask 15 (read/create/update/delete, not additional re-sharing).
|
||||||
|
- Optional TOTP is available; 2FA is not enforced. SMTP is not configured.
|
||||||
|
- The five-minute user cron timer is active; a manual service run succeeded.
|
||||||
|
Its `Type=oneshot` means a recurring short-lived job, not a one-time migration.
|
||||||
|
- Component memory ceilings are Nextcloud 2 GiB, ONLYOFFICE 4 GiB, PostgreSQL
|
||||||
|
1 GiB and Redis 256 MiB; these are ceilings, not reserved memory or load-test results.
|
||||||
|
|
||||||
|
Nextcloud reported installed, no maintenance mode and no pending DB upgrade.
|
||||||
|
PostgreSQL was healthy; ONLYOFFICE `/healthcheck` returned `true`. The connector's
|
||||||
|
`onlyoffice:documentserver --check` succeeded using internal routing. JWT is
|
||||||
|
enabled and matches the dedicated secret; neither privileged containers nor
|
||||||
|
container-engine socket mounts are used.
|
||||||
|
|
||||||
|
NPM on Prometheus reached both upstreams through the Aegis gateway. Direct LAN
|
||||||
|
connections from Ikaros to 8080/8081 were blocked, and PostgreSQL/Redis had no
|
||||||
|
published host ports. Existing Git, Music and Syncthing HTTPS returned 200 with
|
||||||
|
valid TLS. NPM and its backup export timer stayed active; the pool remained healthy.
|
||||||
|
|
||||||
|
After operator DNS/NPM configuration, both public hostnames resolved to the VPS.
|
||||||
|
HTTPS and HTTP-to-HTTPS redirects passed with valid certificates. Both Proxy Hosts
|
||||||
|
were enabled with Force SSL and WebSocket support. Public Office health and its
|
||||||
|
browser API asset returned 200; the connector check also passed. Actual browser
|
||||||
|
editing/saving and native mobile client use remain operator acceptance tests.
|
||||||
|
|
||||||
|
Public session-based web login and authenticated WebDAV succeeded for admin,
|
||||||
|
fabio and chiara. CalDAV/CardDAV
|
||||||
|
discovery redirected to the DAV endpoint; Fabio's calendar/address-book collections
|
||||||
|
answered PROPFIND. A uniquely named private test file was inaccessible to Chiara.
|
||||||
|
Fabio created a test file in Famiglia; Chiara read, edited and deleted it, and Fabio
|
||||||
|
read the updated contents. All temporary test files were removed. These are HTTP
|
||||||
|
protocol checks, not device synchronization or large-upload acceptance evidence.
|
||||||
|
|
||||||
|
## Operator DNS and NPM configuration
|
||||||
|
|
||||||
|
Namecheap: add CNAMEs `cloud` and `office` to `fscotto.co`. Do not change the blog,
|
||||||
|
mail records or apex IP.
|
||||||
|
|
||||||
|
| NPM hostname | Scheme | Upstream | Port |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| cloud.fscotto.co | http | 192.168.178.55 | 8080 |
|
||||||
|
| office.fscotto.co | http | 192.168.178.55 | 8081 |
|
||||||
|
|
||||||
|
For each host, obtain a certificate for its hostname, enable Force SSL and
|
||||||
|
WebSocket support. Keep NPM administration loopback-only; do not expose port 81.
|
||||||
|
Nextcloud's declared upload ceiling is 2 GiB; align the proxy request-size and
|
||||||
|
timeout settings rather than claiming large uploads work before testing them.
|
||||||
|
Verify CalDAV/CardDAV `.well-known` redirects to `/remote.php/dav/` through NPM.
|
||||||
|
Never disable certificate verification to make Office work.
|
||||||
|
|
||||||
|
The browser-facing Office URL is `https://office.fscotto.co/`; server-side routes
|
||||||
|
use `http://atlas-onlyoffice/` and `http://atlas-nextcloud/` on the private network.
|
||||||
|
These internal routes require explicit local-address permission in the connector
|
||||||
|
and ONLYOFFICE. Metadata-address access remains disabled. Nextcloud trusts only
|
||||||
|
the declared Aegis address and rootless network gateway, not arbitrary proxies.
|
||||||
|
|
||||||
|
## Secrets and administration
|
||||||
|
|
||||||
|
Six unique secrets were generated into the existing encrypted `secrets/vault.yml`:
|
||||||
|
database, Redis, Office JWT and initial passwords for `admin`, `fabio`, `chiara`.
|
||||||
|
Use the local Vault editor to retrieve them; do not paste them in chat.
|
||||||
|
Account provisioning never resets an existing user's password. After a user
|
||||||
|
changes it, the initial Vault password is not necessarily their current password.
|
||||||
|
Database secret rotation needs a coordinated role-password update, not just an
|
||||||
|
edited initialization file. Image/app upgrades likewise require a deliberate window.
|
||||||
|
|
||||||
|
Host configuration lives below `/home/admin/.config/atlas-nextcloud` with a 0700
|
||||||
|
parent. Mounted individual secret files are readable by their container consumers,
|
||||||
|
but a different host user was verified unable to read them through the parent.
|
||||||
|
Nextcloud's managed PHP include inherits the live container SELinux category;
|
||||||
|
neither global relabeling nor disabling SELinux is used.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook ansible/site.yml --limit atlas --tags nextcloud --check --diff
|
||||||
|
ansible-playbook ansible/site.yml --limit atlas --tags nextcloud
|
||||||
|
```
|
||||||
|
|
||||||
|
Dry-run skips initial downloads, image pulls and runtime account/app commands;
|
||||||
|
it is not proof of an installed or healthy stack. The deployed repeat run is
|
||||||
|
the current idempotence evidence.
|
||||||
|
|
||||||
|
## Gates before family data and full client acceptance
|
||||||
|
|
||||||
|
- Verify the first actual scrub and the outstanding protection checks.
|
||||||
|
- Public TLS, redirects, web login and WebDAV passed. Complete calendar/contact
|
||||||
|
synchronization and Office editing/saving from a desktop.
|
||||||
|
- Test opening, editing and saving from the iPhone/iPad ONLYOFFICE app; mobile
|
||||||
|
browser editing is not a requirement. No such client test is claimed yet.
|
||||||
|
- Private-space isolation and cross-user shared writes/deletes passed the public
|
||||||
|
smoke test above; complete normal client acceptance as well.
|
||||||
|
- Integrate and test application-consistent database/files backups before import.
|
||||||
|
The new datasets fall beneath existing recursive snapshot/backup scope, but
|
||||||
|
that alone does not verify a new Borg/USB version or a consistent Nextcloud restore.
|
||||||
|
- For a consistent backup, coordinate pending Office saves, pause cron and writes,
|
||||||
|
take a verified PostgreSQL dump and matching application/files snapshot, and
|
||||||
|
resume services promptly even on failure. Extend recurring backup procedures,
|
||||||
|
not the steady-state playbook with one-time migration tasks. Restore into an
|
||||||
|
isolated environment using matching image/app versions, config, files and DB.
|
||||||
|
- Confirm encrypted Vault/recovery material is available offline. Without SMTP,
|
||||||
|
recovery for standard accounts is administrator-assisted; a forgotten admin
|
||||||
|
password can be reset through the private host-side `occ` CLI.
|
||||||
|
- Select versions deliberately for upgrades. Do not downgrade the application
|
||||||
|
against an upgraded database; use matching tested backups for recovery.
|
||||||
|
- Future Uranus migration and iCloud import are separate, explicitly authorized
|
||||||
|
operations. No source data deletion or automatic cross-system cutover is provided.
|
||||||
@@ -1,71 +1,101 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
37646664613266633436346262613633613830623366383138613432366365373765353230333134
|
36616436366637373963326235323736623235633666353235383933663230616532613131636466
|
||||||
3332333764313337396637323133623937343738373133370a333930356365653034323235643230
|
3132633562663861353835633633653764376634636638620a636662316234316164626635646539
|
||||||
36633864343161653833356636373931383761663864663334336236373733326266386639366335
|
63343233373531373833626437656630363330363932353136653834313830646431343961386237
|
||||||
3131313661313637320a313937633361646333333962303335333233346166343831373039663964
|
3166323135376665620a383334616634356361326134313930613266333136393238366566343233
|
||||||
33366532386135663463643965363766643063616436316463666232666138323236346231303537
|
33366335353639316164346239336539636335393130663261333065363733323163613437396332
|
||||||
34303535333866376430363063623934623761373865656231656661383935393866353566346430
|
35663339623338363737383332396238346430353730356632623964323134663434336363613564
|
||||||
64333434613432376436343438343561383235366631623730653533633535326237666265653439
|
63306464623331643738666234343162643630353061363231313933633733626165333763653461
|
||||||
34366264653665643063663361313339663034323932326233366636326336323432303434373765
|
39663066376637623939383964333663306137663433313334313132323465623534666133393533
|
||||||
36316532316265343434383438623239666232373633626330333464303361643630303635643834
|
36633036376538623764363165663861383135663437343230366165636530663165643538376161
|
||||||
39313136623830303762313462343637633763626333393033346637663931663238653734626131
|
62653335666463653538356635333339353165336333306462373233316438386539613361383039
|
||||||
38393963646563333732353531653239643330326539643538323164343934356166343034316565
|
34663336636565343035626238633139356638636535373239386463663738633036383861633062
|
||||||
33346431333735636537613930383331393265313962626234363237373562313231393061326439
|
35313735333530306666313966333061326338393533333936633634633136353237643464376563
|
||||||
64363765323935316661353531366165343139633963336139313737306332613364643031666161
|
35626266363237613037663934666538356639366637643037386336316131343965616137336330
|
||||||
30386362643930316265616564306336633133303166363665333462316265313364393939306162
|
64623432663033653066353661613366313065366264663138643965346363626562366433326461
|
||||||
31303639313933356337386134623934663461643161306666633261653538633232343036653833
|
65316661346631343330633033326630306536633831366231363066323861366662363861666364
|
||||||
66316466636233343136393765636333353230353738313833333265663238303730313936326664
|
38623438633235646430613935363932386237303132343236303439633939373862366565313864
|
||||||
38373239353162363438323964333030666563346161643437326335666162356264396135393532
|
35306332636562636466333739343663343762343163343738646234353638303134643763636639
|
||||||
63363862373136346532653734336335616132386237303031363433663132343861633937386130
|
36613662633135303733376333613235333637646661326235373732306139363363623632666262
|
||||||
30633938616364303462303030303966303939633066393264303462393730363233373937356439
|
38366436613733316465623438343334333861313161363131376132613232376663623230623533
|
||||||
36663533376232663737613734653532313136343939663539373866333638396266666163383864
|
38303061386639616631383636303966666338353865626464363434353661393665613862303130
|
||||||
63613532393334373539346338616163383637633237666234613437663966653733616361353830
|
62303664653362336433356239626661353864363537346234613331376331313038633138363565
|
||||||
61656666376133363330633863346637376266343134633037313132313361366638616261363839
|
31616232653265343430646537373835643163396530353832366337663363386635306665643432
|
||||||
39393062396237666333303937363536346561343763663133323236393037383532396465336138
|
66393838363266383230363633313235376130356436633137636637666562383165643862313931
|
||||||
35613463356532376534386433626337613030343266353332306462306463336336343830666138
|
63346633343334333662363334373865653232623938363162363362646361383961376532626339
|
||||||
33656138363837633337393865643633623261613335366263643162663637623636666162653632
|
30643537356436346161353161626232303962396463323037653235343633643261396134373061
|
||||||
30626238616266323332616234393838343330663662393433366630393566316336636530303165
|
61323463653962363639373531366130326431353635346463396434393336313730373431316334
|
||||||
38343665623437356636643236393734396264356632326133623264633862633333626330336663
|
37313032666231383536363535326239383363346137363037653930373261326338303936663234
|
||||||
61376263656665653731636133316161653635323138303866623862303065366232633736623336
|
36326635346465316233363266383337343335653239393830356262346530363734383532303936
|
||||||
39656666386435343062656138313061616661313966326432663236626631316162623961616636
|
38633065633135666438333832333336636365326430656534313332356662356165616563333035
|
||||||
35343939613262303066626537396164616666316265643065373638663436643961336138313862
|
35363833363636346430356461306337396561366536326139623131303638333733616663653336
|
||||||
39666163646538356338356631346534633139643636393866646462646533363265663234633761
|
65623062626366386364343036386633626236363638393565323163623936663930363864656264
|
||||||
65363661336138353239656165393836386134666331663036653132306433343764643666306333
|
61323566376464356532316366633663623031613439653635323339363730366231326531303163
|
||||||
33623661626565633333306337303263633335386632386330353730316436313931326164363862
|
62313638393962653064303934663436376335663763333965366230323466646463653665656466
|
||||||
37616265653161633632353865346639653961653836353962303762336535666266386535363165
|
62643164616331636464613934376335353437653662363433363533613633633536346662656339
|
||||||
39653138646663376634323131613463333035326639313266613830616431316131383464353533
|
62353565303464373438383234353237636239313062643036383161303735386539613533383334
|
||||||
62656634346637636164626461613137303461633761336232373133653532323566303136663030
|
63383065613236316633623936383130353466383865376336393733663434663636333463336334
|
||||||
30633337346534636566343934306662356238396365306563336666623435353731613136333036
|
37356233306333366463303839643363393463636630306632326339646661643162323334633331
|
||||||
36343436373932323265306639363761353364383635333136366231373166613861633032343233
|
66613731313733646362396534356236363361363330383230303731356261333336653930303161
|
||||||
61376338616630343639333964356162613332323835333730333135356665383431626138643534
|
35353336326438376563616534616361353233373232303034623465656261326664393962326632
|
||||||
66393966666465303763316230386538393863303063386564303165303962346139373338303436
|
36373936393261616338396630383034323462646664623566663064316438363065646330353362
|
||||||
39373032663538323532323766353864643338326561313564373562616430326264386362666532
|
32633566666263333863383264363762323964356430336539623633643537336538353037396566
|
||||||
36613132306462336631363035343732636465343562643430343035373961366566383130656165
|
63396537626465363531393161653939633461366231326234663161646364616338636236313332
|
||||||
64613938393265343037633161653937323933646637653036306532366237313838346361333932
|
35646664333763623532306637383961623538643164633939303561316262316463646665353633
|
||||||
34663565653264626137323239336532643262356166633665313761336162303635346666383863
|
66313164646134646132653338356531303435623130343864326236353939356433396164336236
|
||||||
61383930383033626337383366353766393536653135383062656639323361353539356232613736
|
36623066396435323532356663326163636637346463626235616132353932326438303233393830
|
||||||
63646235663363333333623463313961326533653236363938383765663439613832653039386436
|
64326563303365646664376337303539643032363537633139623665346130636631386662373762
|
||||||
38393734633536313731323437336332353564363564333736663037386530333639326338656561
|
66336565303334303561386134343730306566303036313933613134366238303636316238363165
|
||||||
66336637353238383231613666313261383234336531666132396230373931623363323832633064
|
66373531633430363730376236353939626137323862623538356233616363376330366433633032
|
||||||
39613036346166393936613939363865616135653830366435643538336365353333613831353962
|
37363538393331376665623230623233343065653139313431323966326238636663633030393734
|
||||||
36623537363434373137633063373934383439333462646361613737303239643834303535366138
|
32643635326266646636663539353537623062633130386532373638396366353038663861333033
|
||||||
34656465316431656461373737643537303936636539383934373831616438343965373765373535
|
63343031383238306139653932366433346564643233323937316134306666623030623137313736
|
||||||
63653164363731653030303466646539636361383664343763646163663238383435653035653666
|
39623537346564623236353131656465326632303038366261626661333931323265396262636661
|
||||||
63383165626365653261303834333234626534396333353231303261396361616233363334383336
|
35313739306432323034643832623831373831666231643862613736393135383561386365323835
|
||||||
33316462636133336132656364613439396131613565646565396365316238323962353462653736
|
35623863396339653038316262303263313262616361666666343331393666663530363764643639
|
||||||
64386139313266663963643962363133386133393166306163626632646463333363323830306164
|
31353564633835323031303835636261613839353031373334366335323465326536323762626633
|
||||||
35353936653137383761326132373739306163613764386531613032313235373331303530383633
|
37343633376666323963336436623533346261396438343336663630366434383961393738383263
|
||||||
64646533313434653734366233633535323564386431306538633666383661303038613330653832
|
65383036333031643336393835303835363733663634653463313639313939636539386634663464
|
||||||
34643463396137643034353439653334653836333161396130363637326339383363303037306330
|
32643034383835333533343434656234343134313934323462643631653337383536363165613835
|
||||||
61353635633334343432646461396439393439383639336139316161373737333961653731393333
|
63393437653261653966313237633939626330316631633335386235346465663332336337613865
|
||||||
61636164343838346365373736356161386430356533303331333838333732363233613931613863
|
30626332353130326430316266363062353636356663663439346662313461393835663864656561
|
||||||
66633662383466306332366563373865323861323833353238356563363635313463366333653432
|
62633131323937656239383531373863393865386265663038346535616463326630646565663463
|
||||||
65303839653963376566383737346231343663363363313332383365646363373737323839613564
|
64393861366635656130386434633431393661656438333832633366333730643639333036613935
|
||||||
34613362303335316363363661653639386538326337386537333765643161613961316531613563
|
31363764396466333964343136363630386530343662656362316137306634383032363962616530
|
||||||
38386564636637643762643830666138383361396233303339643665343261356462393830376662
|
38393731346236353530626263336366376466343430316235653363396565656435323531393438
|
||||||
32656334346536636536343263336565333234353831616565366538393661353561376538346334
|
61356464333539636637363632626661663634333331643734316230663736333134383664376231
|
||||||
61396135623230366433303932396130636331333263316333643861626564343330386636613063
|
37613339613266663831613030633466326439323635626638343430383230333639333561646431
|
||||||
32383061616435643736653264313839363232346332343565336464353138396339623533393237
|
66313634373365373137613134373635333535333164353134623937633066613330393430633438
|
||||||
38353632646565323735643462626239663736643033643231613464663866663262366632353434
|
35366261633739623963623331373262313865326264386334633630653263343637343633313366
|
||||||
37363866343239363131633464316133396462353336613962306332343563333962333934616330
|
33303036623333633365373830333465333931633761636366323939363463303239363461333139
|
||||||
3536356634376131633039373834376533633065303533653333
|
33396663376335646137393436323463383461336233646236306331636361653964346536666637
|
||||||
|
36376133326431333234613435613535316263313364396362386537343565393533356564633338
|
||||||
|
64363261323838343531326234343138303133626636653732313234383662326131313431323332
|
||||||
|
32636539323033376434323939666437373936383763323762636439323836656432303833363362
|
||||||
|
35646235653839303838363936613166643662393131373438633265316136663264316332663638
|
||||||
|
32613535643565303566376530373065646333356136613462666465396566313933323261663736
|
||||||
|
66396565396261306139396364393962393361326363666439333566376561366466626335363461
|
||||||
|
34356232353664346234316330363962656332396236383136353461333234313662633234346565
|
||||||
|
34376365356133396239383333643163386133316461633032373035323131663139336339346633
|
||||||
|
32373633663231373361393762396632383738616330333038646439336532303461663430613133
|
||||||
|
37633833393762303035353566633736353130663136626666613061383732326233303831386466
|
||||||
|
38313162623836373533326361313031303636393564656634393263306262376336303663363933
|
||||||
|
30643266626632366333323434383063356363646264363133306566316533356438633135336130
|
||||||
|
62663335386335636364313234633965373961353135373339316337626665323761336133653364
|
||||||
|
33393733383330656432356231313236646163666565373666633637373765346636336235316534
|
||||||
|
64613536333433626261646333373539383862366334376137373862323232653362346431386164
|
||||||
|
36643536613133396162653132616134663538393566323363353038383464663638303865376336
|
||||||
|
30333833313764643130366533646234343339356562663036373137356565643762306261316632
|
||||||
|
33323134633562303263383931623565383766653536353565303266353862643234346637653132
|
||||||
|
63646130646339663035333963323366373331616462613236623133646239363134333165646133
|
||||||
|
64643433373134656161653130323537613361643731653938623036383331633861666332376361
|
||||||
|
39373962653630326561323662303664636161386461383833363865663935303132353637386633
|
||||||
|
37346566626439323863393064643765636337616231363066636539306439356632633032663065
|
||||||
|
66363839616430666233373033376362623862383066396565633632306534623036626335393039
|
||||||
|
32343132616465383961373432336233376339393863663136663435303266333038333566313665
|
||||||
|
61303561376366306331633730616265343662333833633533643465373663663634636632666234
|
||||||
|
31373332323234376430306538386138316431623133626636633034333735303337663335646461
|
||||||
|
61653439653663653930666332313334623264323539613037323534666137616165373865306531
|
||||||
|
36306137663164316534373738383865363333316363323538356139646139363064383666626536
|
||||||
|
66653363393433316335623063633436353761313065636631623366646633353735326362366162
|
||||||
|
64613736343363333834
|
||||||
|
|||||||
@@ -11,4 +11,10 @@ vault_atlas_icloudpd_apple_id: "REPLACE_ME"
|
|||||||
vault_atlas_admin_password_hash: "REPLACE_WITH_A_SHADOW_COMPATIBLE_HASH"
|
vault_atlas_admin_password_hash: "REPLACE_WITH_A_SHADOW_COMPATIBLE_HASH"
|
||||||
vault_atlas_samba_password: "REPLACE_ME"
|
vault_atlas_samba_password: "REPLACE_ME"
|
||||||
vault_atlas_immich_db_password: "REPLACE_ME"
|
vault_atlas_immich_db_password: "REPLACE_ME"
|
||||||
|
vault_nextcloud_database_password: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
|
vault_nextcloud_redis_password: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
|
vault_nextcloud_onlyoffice_jwt: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
|
vault_nextcloud_admin_password: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
|
vault_nextcloud_fabio_password: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
|
vault_nextcloud_chiara_password: "REPLACE_WITH_A_UNIQUE_RANDOM_ALPHANUMERIC_SECRET"
|
||||||
vault_atlas_borg_passphrase: "REPLACE_WITH_A_STRONG_UNIQUE_PASSPHRASE"
|
vault_atlas_borg_passphrase: "REPLACE_WITH_A_STRONG_UNIQUE_PASSPHRASE"
|
||||||
|
|||||||
Reference in New Issue
Block a user