digitalboard.core/roles/nextcloud/tasks/main.yml
Simon Bärlocher 3ace667b6c
feat(services): refine split-horizon OIDC routing and harden nextcloud patch
- authentik: address the rewrite service by compose service name instead
  of a network alias on the public FQDN, which shadowed extra_hosts pins
  and broke OIDC discovery for c-ares-based (Node) resolvers
- homarr: add homarr_extra_hosts to pin the IdP FQDN to a LAN IP so OIDC
  discovery stays in-network while the issuer matches the browser-facing URL
- opnform: add opnform_oidc_sso_redirect_root to 302 the root URL to the
  SSO path (deep-links untouched, /login?bypass=1 break-glass); restart
  ingress via container restart so envsubst re-renders nginx.conf
- nextcloud: make the UserConfig sed workaround fail loud on upstream
  drift instead of silently skipping (nextcloud/server#59629)
- gitignore: exclude the local .ansible/ collection cache
2026-06-02 13:44:08 +02:00

152 lines
5.2 KiB
YAML

#SPDX-License-Identifier: MIT-0
---
# tasks file for nextcloud
- name: Create docker compose directory
file:
path: "{{ nextcloud_docker_compose_dir }}"
state: directory
mode: '0755'
- name: Create nextcloud data directory
file:
path: "{{ nextcloud_docker_volume_dir }}/data"
state: directory
mode: '0755'
- name: Create postgres data directory
file:
path: "{{ nextcloud_docker_volume_dir }}/postgresql"
state: directory
mode: '0755'
- name: Ensure extra networks exist
community.docker.docker_network:
name: "{{ item }}"
state: present
loop: "{{ nextcloud_extra_networks }}"
- name: Create docker-compose file for nextcloud
template:
src: docker-compose.yml.j2
dest: "{{ nextcloud_docker_compose_dir }}/docker-compose.yml"
mode: '0644'
- name: Create nginx template
template:
src: nginx.conf.j2
dest: "{{ nextcloud_docker_compose_dir }}/nginx.conf"
mode: '0644'
notify: Restart nginx container
- name: Create database initialization script
template:
src: init-db.sql.j2
dest: "{{ nextcloud_docker_compose_dir }}/init-db.sql"
mode: '0644'
- name: Start nextcloud container
community.docker.docker_compose_v2:
project_src: "{{ nextcloud_docker_compose_dir }}"
state: present
# nextcloud/server#59629: UserConfig::getValueBool() passes a non-string from
# getTypedValue() into strtolower() under PHP 8.x + OPcache, throwing a
# TypeError on every authenticated request once user_ldap is involved. Fix
# is in master (PR #59646) but no stable33 backport landed before 33.0.4.
# Apply the (string) cast in-container; idempotent via grep guard. Remove
# this block once nextcloud_image >= 33.0.4.
- name: Discover nextcloud php containers needing the UserConfig patch
ansible.builtin.shell:
cmd: >-
docker ps --filter "label=com.docker.compose.project={{ nextcloud_docker_compose_dir | basename }}"
--filter "label=com.docker.compose.service=nextcloud"
--format '{% raw %}{{.Names}}{% endraw %}'
register: _nextcloud_php_containers
changed_when: false
- name: Check UserConfig.php patch status per container
ansible.builtin.shell:
# rc 0 -> already patched; rc 1 -> still the unpatched original; rc 2 ->
# neither marker present (upstream drift -> the guard task below fails loud).
cmd: >-
docker exec {{ item }} sh -c '
grep -q "strtolower((string)\$this->getTypedValue" /var/www/html/lib/private/Config/UserConfig.php && exit 0;
grep -q "strtolower(\$this->getTypedValue" /var/www/html/lib/private/Config/UserConfig.php && exit 1;
exit 2'
loop: "{{ _nextcloud_php_containers.stdout_lines }}"
register: _nextcloud_userconfig_check
changed_when: false
failed_when: false
- name: Fail if the UserConfig.php source drifted from the expected upstream line
ansible.builtin.fail:
msg: >-
Neither the patched nor the expected original strtolower($this->getTypedValue(...))
line was found in {{ item.item }}:/var/www/html/lib/private/Config/UserConfig.php.
The nextcloud/server#59629 workaround can no longer locate its target — the upstream
source likely changed. Re-verify whether the fix shipped (then drop this block) or
update the sed expression. Silently skipping would let the TypeError regress.
loop: "{{ _nextcloud_userconfig_check.results }}"
loop_control:
label: "{{ item.item }}"
when:
- item.rc | default(2) == 2
- name: Apply UserConfig::getValueBool string-cast workaround
ansible.builtin.shell:
cmd: >-
docker exec {{ item.item }}
sed -i 's|$b = strtolower($this->getTypedValue|$b = strtolower((string)$this->getTypedValue|'
/var/www/html/lib/private/Config/UserConfig.php
loop: "{{ _nextcloud_userconfig_check.results }}"
loop_control:
label: "{{ item.item }}"
when:
- item.rc | default(2) == 1
- name: Wait for Nextcloud to be ready
ansible.builtin.shell:
cmd: docker compose exec -T nextcloud php /var/www/html/occ status --output=json
chdir: "{{ nextcloud_docker_compose_dir }}"
retries: 30
delay: 5
register: nextcloud_ready
until:
- nextcloud_ready.rc == 0
- (nextcloud_ready.stdout | from_json).installed == true
changed_when: false
- name: Deploy local network config file
ansible.builtin.template:
src: local-network.config.php.j2
dest: "{{ nextcloud_docker_volume_dir }}/nextcloud/config/local-network.config.php"
owner: www-data
group: www-data
mode: '0640'
- name: Install nextcloud plugins
ansible.builtin.include_tasks: plugins.yml
- name: Configure nextcloud collabora
ansible.builtin.include_tasks: collabora.yml
when: nextcloud_enable_collabora
- name: Configure nextcloud draw.io
ansible.builtin.include_tasks: drawio.yml
when: nextcloud_enable_drawio
- name: Configure notify_push
ansible.builtin.include_tasks: notify_push.yml
when: nextcloud_enable_notify_push
- name: Configure LDAP backend
ansible.builtin.include_tasks: ldap.yml
when: nextcloud_ldap_enabled
- name: Configure OIDC providers
ansible.builtin.include_tasks: oidc.yml
when: nextcloud_oidc_providers | length > 0 or nextcloud_oidc_providers_removed | length > 0
- name: Configure Nextcloud Talk (HPB + TURN + STUN)
ansible.builtin.include_tasks: talk.yml
when: nextcloud_enable_talk