Compare commits
No commits in common. "d476bca4f5a228c54743248c62a9c72d250cd8ba" and "02d45026a559c02383ed32a3c123d02f5717650c" have entirely different histories.
d476bca4f5
...
02d45026a5
18 changed files with 21 additions and 375 deletions
|
|
@ -17,15 +17,6 @@ authentik_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ authentik_service_
|
|||
# server-to-server traffic so backend calls don't hairpin via DMZ.
|
||||
authentik_domains:
|
||||
- "authentik.local.test"
|
||||
|
||||
# Hostnames that should reach authentik but make it generate URLs (OIDC
|
||||
# issuer, password reset links, etc.) as if requested from the canonical
|
||||
# `authentik_domains[0]` instead. Used for split-horizon setups where an
|
||||
# internal FQDN (e.g. `auth.int.example.com`) keeps server-to-server
|
||||
# traffic in the LAN but the iss claim must still match the public
|
||||
# hostname that browsers see. Traefik handles each entry via a separate
|
||||
# router that rewrites the Host header before forwarding to authentik.
|
||||
authentik_host_rewrite_domains: []
|
||||
authentik_image: "ghcr.io/goauthentik/server:2026.2.2"
|
||||
authentik_port: 9000
|
||||
authentik_secret_key: "changeme-generate-a-random-string"
|
||||
|
|
|
|||
|
|
@ -20,16 +20,6 @@ entries:
|
|||
|
||||
internal_host: "{{ item.internal_host }}"
|
||||
external_host: "{{ item.external_host }}"
|
||||
{# Provider mode controls how authentik treats the proxy app:
|
||||
- proxy : the outpost itself proxies traffic to internal_host
|
||||
- forward_single : a single app behind an external reverse proxy
|
||||
(traefik forwardauth talks to authentik per-domain)
|
||||
- forward_domain : wildcard mode — one provider guards every host on a
|
||||
cookie domain; configure forward_auth_mode=domain on
|
||||
the outpost in that case. Default to forward_single
|
||||
since that's the common ForwardAuth-with-traefik
|
||||
pattern. #}
|
||||
mode: {{ item.mode | default('forward_single') }}
|
||||
|
||||
{% if item.skip_path_regex is defined and item.skip_path_regex|length > 0 %}
|
||||
skip_path_regex: |
|
||||
|
|
@ -44,20 +34,3 @@ entries:
|
|||
name: "{{ item.name | default(item.slug) }}"
|
||||
slug: {{ item.slug }}
|
||||
provider: !KeyOf proxy-provider-{{ item.slug }}
|
||||
|
||||
{% if item.allowed_groups is defined and item.allowed_groups | length > 0 %}
|
||||
{# Restrict access to listed groups: one PolicyBinding per group, all bound
|
||||
to the application. Authentik treats multiple bindings on the same target
|
||||
as OR (a user matching any binding passes), and a request from a user in
|
||||
none of the bound groups is denied. #}
|
||||
{% for group_name in item.allowed_groups %}
|
||||
- model: authentik_policies.policybinding
|
||||
identifiers:
|
||||
target: !KeyOf app-{{ item.slug }}
|
||||
order: {{ loop.index0 }}
|
||||
group: !Find [authentik_core.group, [name, "{{ group_name }}"]]
|
||||
attrs:
|
||||
enabled: true
|
||||
negate: false
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -43,19 +43,12 @@ services:
|
|||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
{{ authentik_backend_network }}: {}
|
||||
# Network alias so traefik (which shares this network) can resolve
|
||||
# the canonical FQDN to this container directly. The URL-based
|
||||
# service below uses that to send upstream traffic with a fixed
|
||||
# Host header equal to the canonical hostname.
|
||||
{{ authentik_traefik_network }}:
|
||||
aliases:
|
||||
- {{ authentik_domains[0] }}
|
||||
- {{ authentik_backend_network }}
|
||||
- {{ authentik_traefik_network }}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.docker.network={{ authentik_traefik_network }}
|
||||
- traefik.http.routers.{{ authentik_service_name }}.rule={% for d in authentik_domains %}Host(`{{ d }}`){% if not loop.last %} || {% endif %}{% endfor +%}
|
||||
- traefik.http.routers.{{ authentik_service_name }}.service={{ authentik_service_name }}
|
||||
{% if authentik_use_ssl %}
|
||||
- traefik.http.routers.{{ authentik_service_name }}.entrypoints=websecure
|
||||
- traefik.http.routers.{{ authentik_service_name }}.tls=true
|
||||
|
|
@ -66,34 +59,6 @@ services:
|
|||
- traefik.http.routers.{{ authentik_service_name }}.entrypoints=web
|
||||
{% endif %}
|
||||
- traefik.http.services.{{ authentik_service_name }}.loadbalancer.server.port={{ authentik_port }}
|
||||
{% if authentik_host_rewrite_domains | length > 0 %}
|
||||
# Server-to-server entry: a separate service points at this very
|
||||
# container by the canonical FQDN (resolved via the network alias
|
||||
# above) and disables passHostHeader so the upstream Host header
|
||||
# becomes `{{ authentik_domains[0] }}`. Authentik builds OIDC issuer
|
||||
# URLs from X-Forwarded-Host (not Host), so we also pin that header
|
||||
# via middleware. Together this keeps the iss claim aligned with
|
||||
# the public hostname browsers see during login, even when the
|
||||
# request itself arrived on an internal *.int.* FQDN.
|
||||
- traefik.http.services.{{ authentik_service_name }}-rewrite.loadbalancer.server.url=http://{{ authentik_domains[0] }}:{{ authentik_port }}
|
||||
- traefik.http.services.{{ authentik_service_name }}-rewrite.loadbalancer.passhostheader=false
|
||||
- traefik.http.middlewares.{{ authentik_service_name }}-xfh-rewrite.headers.customrequestheaders.X-Forwarded-Host={{ authentik_domains[0] }}
|
||||
{% for d in authentik_host_rewrite_domains %}
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.rule=Host(`{{ d }}`)
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.priority=100
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.service={{ authentik_service_name }}-rewrite
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.middlewares={{ authentik_service_name }}-xfh-rewrite
|
||||
{% if authentik_use_ssl %}
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.entrypoints=websecure
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.tls=true
|
||||
{% if traefik_cert_mode | default('selfsigned') == 'acme' %}
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.tls.certresolver={{ traefik_ssl_cert_resolver | default('dns') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
- traefik.http.routers.{{ authentik_service_name }}-rewrite-{{ loop.index0 }}.entrypoints=web
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
worker:
|
||||
image: {{ authentik_image }}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,4 @@ drawio_extra_hosts: []
|
|||
|
||||
# Traefik configuration
|
||||
drawio_traefik_network: "proxy"
|
||||
drawio_use_ssl: true
|
||||
|
||||
# Optional Authentik ForwardAuth (set to true and provide the URL to gate
|
||||
# drawio behind an authentik proxy provider). Expects the authentik
|
||||
# embedded outpost to expose the /outpost.goauthentik.io/auth/traefik
|
||||
# endpoint on the configured URL (typically the public auth.* FQDN).
|
||||
drawio_authentik_forward_auth: false
|
||||
drawio_authentik_forward_auth_url: ""
|
||||
drawio_use_ssl: true
|
||||
|
|
@ -22,15 +22,6 @@ services:
|
|||
{% else %}
|
||||
- traefik.http.routers.{{ drawio_service_name }}.entrypoints=web
|
||||
{% endif %}
|
||||
{% if drawio_authentik_forward_auth | default(false) %}
|
||||
# ForwardAuth via the authentik embedded outpost. Unauthenticated
|
||||
# requests get redirected to authentik to log in; authentik then
|
||||
# sets X-Authentik-* headers traefik forwards downstream.
|
||||
- traefik.http.middlewares.{{ drawio_service_name }}-authentik.forwardauth.address={{ drawio_authentik_forward_auth_url }}
|
||||
- traefik.http.middlewares.{{ drawio_service_name }}-authentik.forwardauth.trustForwardHeader=true
|
||||
- traefik.http.middlewares.{{ drawio_service_name }}-authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version
|
||||
- traefik.http.routers.{{ drawio_service_name }}.middlewares={{ drawio_service_name }}-authentik
|
||||
{% endif %}
|
||||
|
||||
networks:
|
||||
{{ drawio_traefik_network }}:
|
||||
|
|
|
|||
|
|
@ -25,20 +25,10 @@ garage_webui_domain: "console.storage.local.test"
|
|||
garage_webui_enabled: true
|
||||
garage_webui_image: "khairul169/garage-webui:latest"
|
||||
garage_webui_port: 3909
|
||||
# WebUI basic auth credentials (plaintext, will be hashed automatically).
|
||||
# Ignored when garage_webui_authentik_forward_auth is true — in that case
|
||||
# authentik handles authentication via the ForwardAuth middleware below.
|
||||
# WebUI basic auth credentials (plaintext, will be hashed automatically)
|
||||
garage_webui_username: "admin"
|
||||
garage_webui_password: "admin"
|
||||
|
||||
# Optional Authentik ForwardAuth in front of the WebUI. When true:
|
||||
# - the AUTH_USER_PASS env-var is dropped from the container so htpasswd
|
||||
# isn't enforced; authentik is the only gate.
|
||||
# - traefik attaches a ForwardAuth middleware pointing at the URL below.
|
||||
# Leave false to keep classic htpasswd protection.
|
||||
garage_webui_authentik_forward_auth: false
|
||||
garage_webui_authentik_forward_auth_url: ""
|
||||
|
||||
# Garage ports
|
||||
garage_s3_api_port: 3900
|
||||
garage_s3_web_port: 3902
|
||||
|
|
|
|||
|
|
@ -7,27 +7,21 @@
|
|||
container: "{{ garage_service_name }}"
|
||||
command: /garage node id -q
|
||||
register: _garage_node_id
|
||||
changed_when: false
|
||||
|
||||
- name: Extract short node ID
|
||||
ansible.builtin.set_fact:
|
||||
_garage_node_id_short: "{{ _garage_node_id.stdout.split('@')[0] }}"
|
||||
|
||||
- name: Extract truncated node ID (first 16 chars, matches `garage layout show` output)
|
||||
ansible.builtin.set_fact:
|
||||
_garage_node_id_truncated: "{{ _garage_node_id_short[:16] }}"
|
||||
|
||||
- name: Check if node layout is configured
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ garage_service_name }}"
|
||||
command: /garage layout show
|
||||
register: _garage_layout_show
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Check if node is in layout
|
||||
ansible.builtin.set_fact:
|
||||
_node_in_layout: "{{ (_garage_node_id_truncated in _garage_layout_show.stdout) or (_garage_node_id_short in _garage_layout_show.stdout) }}"
|
||||
_node_in_layout: "{{ _garage_node_id_short in _garage_layout_show.stdout }}"
|
||||
|
||||
- name: Configure garage node layout
|
||||
community.docker.docker_container_exec:
|
||||
|
|
|
|||
|
|
@ -26,77 +26,12 @@
|
|||
dest: "{{ garage_docker_compose_dir }}/garage.toml"
|
||||
mode: '0644'
|
||||
|
||||
- name: Set webui htpasswd activation fact
|
||||
ansible.builtin.set_fact:
|
||||
# htpasswd only runs when the WebUI is enabled AND authentik ForwardAuth
|
||||
# is not handling authentication. When authentik is in front, the
|
||||
# compose template drops AUTH_USER_PASS so no hash is needed.
|
||||
_garage_webui_htpasswd_active: >-
|
||||
{{
|
||||
garage_webui_enabled
|
||||
and not (garage_webui_authentik_forward_auth | default(false))
|
||||
}}
|
||||
|
||||
- name: Read cached webui htpasswd hash
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ garage_docker_compose_dir }}/webui.htpasswd"
|
||||
register: _garage_webui_htpasswd_cached
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
when: _garage_webui_htpasswd_active
|
||||
|
||||
- name: Verify cached webui htpasswd hash still matches password
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- htpasswd
|
||||
- -vbB
|
||||
- "{{ garage_docker_compose_dir }}/webui.htpasswd"
|
||||
- "{{ garage_webui_username }}"
|
||||
- "{{ garage_webui_password }}"
|
||||
register: _garage_webui_htpasswd_verify
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
no_log: true
|
||||
when:
|
||||
- _garage_webui_htpasswd_active
|
||||
- _garage_webui_htpasswd_cached.content is defined
|
||||
|
||||
- name: Generate bcrypt hash for webui password using htpasswd
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- htpasswd
|
||||
- -nbBC
|
||||
- "10"
|
||||
- "{{ garage_webui_username }}"
|
||||
- "{{ garage_webui_password }}"
|
||||
register: _garage_webui_password_hash_new
|
||||
changed_when: true
|
||||
when:
|
||||
- _garage_webui_htpasswd_active
|
||||
- (_garage_webui_htpasswd_cached.content is not defined)
|
||||
or (_garage_webui_htpasswd_verify.rc | default(1) != 0)
|
||||
|
||||
- name: Persist webui htpasswd hash on disk
|
||||
ansible.builtin.copy:
|
||||
content: "{{ _garage_webui_password_hash_new.stdout }}\n"
|
||||
dest: "{{ garage_docker_compose_dir }}/webui.htpasswd"
|
||||
mode: '0600'
|
||||
when:
|
||||
- _garage_webui_htpasswd_active
|
||||
- _garage_webui_password_hash_new is changed
|
||||
|
||||
- name: Load current webui htpasswd hash
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ garage_docker_compose_dir }}/webui.htpasswd"
|
||||
register: _garage_webui_htpasswd_current
|
||||
ansible.builtin.shell: |
|
||||
htpasswd -nbBC 10 "{{ garage_webui_username }}" "{{ garage_webui_password }}"
|
||||
register: _garage_webui_password_hash
|
||||
changed_when: false
|
||||
when: _garage_webui_htpasswd_active
|
||||
|
||||
- name: Expose current webui htpasswd hash to template
|
||||
ansible.builtin.set_fact:
|
||||
_garage_webui_password_hash:
|
||||
stdout: "{{ (_garage_webui_htpasswd_current.content | b64decode).strip() }}"
|
||||
when: _garage_webui_htpasswd_active
|
||||
when: garage_webui_enabled
|
||||
|
||||
- name: Create docker-compose file for garage
|
||||
template:
|
||||
|
|
|
|||
|
|
@ -4,17 +4,11 @@
|
|||
container: "{{ garage_service_name }}"
|
||||
command: /garage key list
|
||||
register: _existing_keys_output
|
||||
changed_when: false
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Parse existing key names
|
||||
ansible.builtin.set_fact:
|
||||
# `garage key list` columns: ID Created Name Expiration.
|
||||
# Data rows begin with a GK<hex> key ID; header is "ID Created ..."
|
||||
# and INFO log lines may interleave on stderr (kept separate by
|
||||
# docker_container_exec). Strip ANSI escapes defensively, filter to
|
||||
# GK-prefixed rows, then take the 3rd whitespace-separated field.
|
||||
_existing_keys: "{{ _existing_keys_output.stdout_lines | map('regex_replace', '\\x1b\\[[0-9;]*m', '') | select('match', '^GK[0-9a-fA-F]+') | map('regex_replace', '^\\S+\\s+\\S+\\s+(\\S+).*$', '\\1') | list }}"
|
||||
_existing_keys: "{{ _existing_keys_output.stdout_lines[1:] | select('match', '^GK') | map('regex_replace', '^\\S+\\s+\\S+\\s+(\\S+)\\s+.*$', '\\1') | list }}"
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Create S3 keys
|
||||
|
|
@ -33,7 +27,6 @@
|
|||
command: /garage key info {{ item.name }}
|
||||
loop: "{{ garage_s3_keys }}"
|
||||
register: _key_info_results
|
||||
changed_when: false
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Extract key IDs from info
|
||||
|
|
@ -49,21 +42,11 @@
|
|||
container: "{{ garage_service_name }}"
|
||||
command: /garage bucket list
|
||||
register: _existing_buckets_output
|
||||
changed_when: false
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Parse existing bucket names
|
||||
ansible.builtin.set_fact:
|
||||
# `garage bucket list` columns: ID Created Global aliases Local aliases
|
||||
# Data rows start with a hex bucket ID; filter to those and take the
|
||||
# third whitespace-separated field (the global alias = bucket name).
|
||||
_existing_buckets: >-
|
||||
{{
|
||||
_existing_buckets_output.stdout_lines
|
||||
| select('match', '^[0-9a-f]{16}\\s')
|
||||
| map('regex_replace', '^\\S+\\s+\\S+\\s+(\\S+).*$', '\\1')
|
||||
| list
|
||||
}}
|
||||
_existing_buckets: "{{ _existing_buckets_output.stdout_lines[2:] | map('split') | map('first') | list }}"
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Get unique bucket names
|
||||
|
|
@ -81,37 +64,12 @@
|
|||
- item not in _existing_buckets
|
||||
failed_when: false
|
||||
|
||||
- name: Get current bucket permissions
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ garage_service_name }}"
|
||||
command: /garage bucket info {{ item.1.name }}
|
||||
loop: "{{ garage_s3_keys | subelements('buckets', skip_missing=True) }}"
|
||||
loop_control:
|
||||
label: "{{ item.1.name }}"
|
||||
register: _bucket_info_results
|
||||
changed_when: false
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Set bucket permissions using key IDs
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ garage_service_name }}"
|
||||
command: /garage bucket allow {{ item.item.1.name }} {% for perm in item.item.1.permissions %}--{{ perm }} {% endfor %}--key {{ _key_id_map[item.item.0.name] }}
|
||||
loop: "{{ _bucket_info_results.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.1.name }} -> {{ item.item.0.name }}"
|
||||
when:
|
||||
- garage_s3_keys | length > 0
|
||||
- >-
|
||||
(item.stdout | regex_search(
|
||||
'(?m)^\s*' ~ _wanted_flags ~ '\s+' ~ _key_id_map[item.item.0.name]
|
||||
)) is none
|
||||
vars:
|
||||
_wanted_flags: >-
|
||||
{{
|
||||
('R' if 'read' in item.item.1.permissions else '-')
|
||||
~ ('W' if 'write' in item.item.1.permissions else '-')
|
||||
~ ('O' if 'owner' in item.item.1.permissions else '-')
|
||||
}}
|
||||
command: /garage bucket allow {{ item.1.name }} {% for perm in item.1.permissions %}--{{ perm }} {% endfor %}--key {{ _key_id_map[item.0.name] }}
|
||||
loop: "{{ garage_s3_keys | subelements('buckets', skip_missing=True) }}"
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
# Export key credentials for use by other roles
|
||||
- name: Get detailed key information for all keys
|
||||
|
|
@ -120,7 +78,6 @@
|
|||
command: /garage key info {{ item.name }} --show-secret
|
||||
loop: "{{ garage_s3_keys }}"
|
||||
register: _key_details_results
|
||||
changed_when: false
|
||||
when: garage_s3_keys | length > 0
|
||||
|
||||
- name: Build garage S3 credentials map
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ services:
|
|||
environment:
|
||||
API_BASE_URL: "http://{{ garage_service_name }}:{{ garage_admin_port }}"
|
||||
S3_ENDPOINT_URL: "http://{{ garage_service_name }}:{{ garage_s3_api_port }}"
|
||||
{% if not (garage_webui_authentik_forward_auth | default(false)) %}
|
||||
AUTH_USER_PASS: '{{ _garage_webui_password_hash.stdout | replace("$", "$$") }}'
|
||||
{% endif %}
|
||||
volumes:
|
||||
- {{ garage_docker_compose_dir }}/garage.toml:/etc/garage.toml:ro
|
||||
networks:
|
||||
|
|
@ -62,16 +60,6 @@ services:
|
|||
- traefik.http.routers.{{ garage_service_name }}-console.service={{ garage_service_name }}-console
|
||||
- traefik.http.routers.{{ garage_service_name }}-console.priority=10
|
||||
- traefik.http.services.{{ garage_service_name }}-console.loadbalancer.server.port={{ garage_webui_port }}
|
||||
{% if garage_webui_authentik_forward_auth | default(false) %}
|
||||
# ForwardAuth via the authentik embedded outpost. Unauthenticated
|
||||
# requests are redirected to authentik; authentik then forwards
|
||||
# X-Authentik-* identity headers downstream. htpasswd is disabled
|
||||
# in the env block above so authentik is the only gate.
|
||||
- traefik.http.middlewares.{{ garage_service_name }}-console-authentik.forwardauth.address={{ garage_webui_authentik_forward_auth_url }}
|
||||
- traefik.http.middlewares.{{ garage_service_name }}-console-authentik.forwardauth.trustForwardHeader=true
|
||||
- traefik.http.middlewares.{{ garage_service_name }}-console-authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-entitlements,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version
|
||||
- traefik.http.routers.{{ garage_service_name }}-console.middlewares={{ garage_service_name }}-console-authentik
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -1,55 +1,28 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# tasks file for configuring Collabora in Nextcloud
|
||||
- name: Read current richdocuments config values
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:get richdocuments {{ item }}
|
||||
loop:
|
||||
- wopi_url
|
||||
- public_wopi_url
|
||||
- disable_certificate_verification
|
||||
- wopi_allowlist
|
||||
register: _richdocuments_current
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Build map of current richdocuments config
|
||||
ansible.builtin.set_fact:
|
||||
_richdocuments_cfg: "{{ _richdocuments_cfg | default({}) | combine({item.item: (item.stdout | default('')).strip()}) }}"
|
||||
loop: "{{ _richdocuments_current.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item }}"
|
||||
|
||||
- name: Configure Collabora WOPI URL (server-to-server)
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set richdocuments wopi_url --value=https://{{ nextcloud_collabora_domain }}
|
||||
when: _richdocuments_cfg.wopi_url != ('https://' ~ nextcloud_collabora_domain)
|
||||
|
||||
- name: Configure Collabora public WOPI URL (browser-facing)
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set richdocuments public_wopi_url --value=https://{{ nextcloud_collabora_public_domain }}
|
||||
when:
|
||||
- nextcloud_collabora_public_domain is defined
|
||||
- nextcloud_collabora_public_domain != nextcloud_collabora_domain
|
||||
- _richdocuments_cfg.public_wopi_url != ('https://' ~ nextcloud_collabora_public_domain)
|
||||
when: nextcloud_collabora_public_domain is defined and nextcloud_collabora_public_domain != nextcloud_collabora_domain
|
||||
|
||||
- name: Configure certificate verification for Collabora
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set richdocuments disable_certificate_verification --value={{ nextcloud_collabora_disable_cert_verification | ternary('yes', 'no') }}
|
||||
when: _richdocuments_cfg.disable_certificate_verification != (nextcloud_collabora_disable_cert_verification | ternary('yes', 'no'))
|
||||
|
||||
- name: Set Collabora WOPI allowlist
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set richdocuments wopi_allowlist --value=''
|
||||
when: _richdocuments_cfg.wopi_allowlist | default('') != ''
|
||||
|
||||
- name: Activate richdocuments configuration (fetch discovery from Collabora)
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ richdocuments:activate-config
|
||||
changed_when: false
|
||||
command: php /var/www/html/occ richdocuments:activate-config
|
||||
|
|
@ -2,41 +2,18 @@
|
|||
---
|
||||
# tasks file for configuring draw.io in Nextcloud
|
||||
|
||||
- name: Read current drawio config values
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:get drawio {{ item }}
|
||||
loop:
|
||||
- DrawioUrl
|
||||
- DrawioTheme
|
||||
- DrawioOffline
|
||||
register: _drawio_current
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Build map of current drawio config
|
||||
ansible.builtin.set_fact:
|
||||
_drawio_cfg: "{{ _drawio_cfg | default({}) | combine({item.item: (item.stdout | default('')).strip()}) }}"
|
||||
loop: "{{ _drawio_current.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item }}"
|
||||
|
||||
- name: Configure draw.io URL
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set drawio DrawioUrl --value={{ nextcloud_drawio_url }}
|
||||
when:
|
||||
- nextcloud_drawio_url | length > 0
|
||||
- _drawio_cfg.DrawioUrl != nextcloud_drawio_url
|
||||
when: nextcloud_drawio_url | length > 0
|
||||
|
||||
- name: Configure draw.io theme
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set drawio DrawioTheme --value={{ nextcloud_drawio_theme }}
|
||||
when: _drawio_cfg.DrawioTheme != (nextcloud_drawio_theme | string)
|
||||
|
||||
- name: Configure draw.io offline mode
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:set drawio DrawioOffline --value={{ nextcloud_drawio_offline }}
|
||||
when: _drawio_cfg.DrawioOffline != (nextcloud_drawio_offline | string)
|
||||
command: php /var/www/html/occ config:app:set drawio DrawioOffline --value={{ nextcloud_drawio_offline }}
|
||||
|
|
@ -15,24 +15,6 @@
|
|||
command: php /var/www/html/occ ldap:create-empty-config
|
||||
when: "'s01' not in ldap_show_config.stdout"
|
||||
|
||||
- name: Read current LDAP config for s01
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_service_name }}-nextcloud-1"
|
||||
command: php /var/www/html/occ ldap:show-config s01 --output=json
|
||||
register: _ldap_show_s01
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Parse current LDAP config
|
||||
ansible.builtin.set_fact:
|
||||
_ldap_current: >-
|
||||
{{
|
||||
(_ldap_show_s01.stdout | from_json) if (
|
||||
(_ldap_show_s01.stdout | default('') | trim) is match('^[\\[{]')
|
||||
) else {}
|
||||
}}
|
||||
when: _ldap_show_s01.rc | default(1) == 0
|
||||
|
||||
- name: Configure LDAP settings
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_service_name }}-nextcloud-1"
|
||||
|
|
@ -47,7 +29,6 @@
|
|||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
no_log: true
|
||||
when: ((_ldap_current | default({})).get(item.key) | default(none) | string) != (item.value | string)
|
||||
|
||||
- name: Test LDAP configuration
|
||||
community.docker.docker_container_exec:
|
||||
|
|
|
|||
|
|
@ -49,42 +49,6 @@
|
|||
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:
|
||||
cmd: >-
|
||||
docker exec {{ item }} grep -q "strtolower((string)" /var/www/html/lib/private/Config/UserConfig.php
|
||||
loop: "{{ _nextcloud_php_containers.stdout_lines }}"
|
||||
register: _nextcloud_userconfig_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- 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(1) != 0
|
||||
|
||||
- name: Wait for Nextcloud to be ready
|
||||
ansible.builtin.shell:
|
||||
cmd: docker compose exec -T nextcloud php /var/www/html/occ status --output=json
|
||||
|
|
|
|||
|
|
@ -2,16 +2,7 @@
|
|||
---
|
||||
# tasks file for configuring notify_push in Nextcloud
|
||||
|
||||
- name: Read current notify_push base endpoint
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ config:app:get notify_push base_endpoint
|
||||
register: _notify_push_current
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Configure notify_push base endpoint
|
||||
community.docker.docker_container_exec:
|
||||
container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1"
|
||||
command: php /var/www/html/occ notify_push:setup https://{{ nextcloud_notify_push_domain | default(nextcloud_domains[0]) }}/push
|
||||
when: (_notify_push_current.stdout | default('') | trim) != ('https://' ~ (nextcloud_notify_push_domain | default(nextcloud_domains[0])) ~ '/push')
|
||||
command: php /var/www/html/occ notify_push:setup https://{{ nextcloud_notify_push_domain | default(nextcloud_domains[0]) }}/push
|
||||
|
|
@ -8,9 +8,7 @@
|
|||
chdir: "{{ nextcloud_docker_compose_dir }}"
|
||||
loop: "{{ nextcloud_apps_to_install }}"
|
||||
register: app_install_result
|
||||
changed_when:
|
||||
- "'already installed' not in app_install_result.stdout"
|
||||
- "'installed' in app_install_result.stdout"
|
||||
changed_when: "'installed' in app_install_result.stdout"
|
||||
failed_when:
|
||||
- app_install_result.rc != 0
|
||||
- "'already installed' not in app_install_result.stdout"
|
||||
|
|
@ -21,9 +19,7 @@
|
|||
chdir: "{{ nextcloud_docker_compose_dir }}"
|
||||
loop: "{{ nextcloud_apps_to_install }}"
|
||||
register: app_enable_result
|
||||
changed_when:
|
||||
- "'already enabled' not in app_enable_result.stdout"
|
||||
- "'enabled' in app_enable_result.stdout"
|
||||
changed_when: "'enabled' in app_enable_result.stdout"
|
||||
failed_when:
|
||||
- app_enable_result.rc != 0
|
||||
- "'already enabled' not in app_enable_result.stdout"
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@ service_name: traefik
|
|||
docker_compose_dir: "{{ docker_compose_base_dir }}/{{ service_name }}"
|
||||
docker_volume_dir: "{{ docker_volume_base_dir }}/{{ service_name }}"
|
||||
|
||||
# Optional /etc/hosts entries injected into the traefik container. Useful
|
||||
# when downstream middlewares (e.g. ForwardAuth to an authentik instance
|
||||
# running on a sibling LAN) need a public FQDN to resolve to an internal
|
||||
# IP because the DMZ doesn't hairpin the public address back inside.
|
||||
# Example: ["auth.example.com:172.16.19.101"]
|
||||
traefik_extra_hosts: []
|
||||
|
||||
# Deployment mode: 'dmz' or 'backend'
|
||||
# - dmz: Public-facing reverse proxy that routes to backend servers using file provider
|
||||
# - backend: Application server with docker provider for local container discovery
|
||||
|
|
|
|||
|
|
@ -33,12 +33,6 @@ services:
|
|||
{% endif %}
|
||||
networks:
|
||||
- {{ traefik_network }}
|
||||
{% if traefik_extra_hosts | default([]) | length > 0 %}
|
||||
extra_hosts:
|
||||
{% for h in traefik_extra_hosts %}
|
||||
- "{{ h }}"
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
networks:
|
||||
{{ traefik_network }}:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue