From 045ef095f2e98b3591c3bcce76ce900c3ef40c62 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Sat, 24 Oct 2026 11:44:14 +0200 Subject: [PATCH] fix(web-app-matrix): serve the federation delegation on the server name The LiveKit JWT service checks each OpenID token over federation against the user's homeserver. Server discovery found no delegation: https:///.well-known/matrix/server hit the redirect vhost of web-opt-rdr-domains, which answered 301 to the dashboard and then 404, so the lookup fell back to :8448. In compose OpenResty runs in the host network and answers there; in swarm it publishes 80 and 443 only, the dial was refused and Element Call's token exchange returned 401 M_UNAUTHORIZED (main run 40017166046, Nextcloud#12). The role also wrote the delegation to `/matrixserver`, because the file path joined the directory and `server` without a separator. The file now lands at `/matrix/server`, and the redirect vhost serves `/.well-known/matrix/` from the shared well-known directory, so discovery delegates to the Synapse domain on 443 in both modes. Both flavors share tasks/utils/well_known.yml, which also includes web-opt-rdr-domains for the server name when no play-wide redirect claims it, so the delegation has a vhost there too. Co-Authored-By: Claude Opus 5.5 (1M context) --- roles/web-app-matrix/README.md | 2 +- .../tasks/flavor/ansible/05_proxy_wiring.yml | 12 ++--------- .../tasks/flavor/compose/04_webserver.yml | 12 ++--------- .../web-app-matrix/tasks/utils/well_known.yml | 20 +++++++++++++++++++ roles/web-app-matrix/vars/main.yml | 2 +- .../templates/redirect-domain.conf.j2 | 4 ++++ 6 files changed, 30 insertions(+), 22 deletions(-) create mode 100644 roles/web-app-matrix/tasks/utils/well_known.yml diff --git a/roles/web-app-matrix/README.md b/roles/web-app-matrix/README.md index 5cd3320e22..0f562d7ce5 100644 --- a/roles/web-app-matrix/README.md +++ b/roles/web-app-matrix/README.md @@ -152,7 +152,7 @@ MDAD knobs are rendered from `templates/flavor/ansible/vars.yml.j2`. Central-ser A `mount --make-rshared /` runs once in the deploy container before the runner starts, because MDAD's systemd units bind-mount `/matrix/synapse/storage` with `bind-propagation=slave`, which requires shared/slave propagation on the source. -**Federation.** Synapse serves the `client` and `federation` resources on one listener. OpenResty serves the Synapse domain on 443 and, as `default_server`, on `services.synapse.ports.public.federation`, and the role writes `/.well-known/matrix/server` as the compose flavor does. A server-name lookup that falls back to `:8448` therefore lands on Synapse as well. +**Federation.** Synapse serves the `client` and `federation` resources on one listener. OpenResty serves the Synapse domain on 443 and, as `default_server`, on `services.synapse.ports.public.federation`, and the role writes `/.well-known/matrix/server`, which the redirect vhost of the server name (`web-opt-rdr-domains`) serves, so server discovery delegates to the Synapse domain on 443. The `:8448` fallback only answers where OpenResty runs in the host network (compose); in swarm OpenResty publishes 80 and 443 only. **Registry proxy.** When the stack's docker daemon pulls through a registry proxy (the local registry cache), the runner's nested daemon uses the same proxy via a `docker.service` drop-in and, when the outer daemon trusts the cache's CA, fetches that CA from the proxy before it starts. diff --git a/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml b/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml index 937813ce29..7b1ce8ee18 100644 --- a/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml +++ b/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml @@ -153,16 +153,8 @@ proxy_internal_port: "{{ MATRIX_SYNAPSE_CONTAINER_PORT }}" notify: restart-openresty -- name: "📁 front-proxy: create {{ MATRIX_WELL_KNOWN_DIRECTORY }}" - ansible.builtin.file: - path: "{{ MATRIX_WELL_KNOWN_DIRECTORY }}" - state: directory - mode: "0755" - -- name: "📁 front-proxy: create {{ MATRIX_WELL_KNOWN_FILE }}" - stack_host_template: - src: "well-known.j2" - dest: "{{ MATRIX_WELL_KNOWN_FILE }}" +- name: "🔀 front-proxy: federation delegation" + ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/well_known.yml" - name: "🔀 front-proxy: Jitsi web" ansible.builtin.include_role: diff --git a/roles/web-app-matrix/tasks/flavor/compose/04_webserver.yml b/roles/web-app-matrix/tasks/flavor/compose/04_webserver.yml index d63e255708..c387f0e519 100644 --- a/roles/web-app-matrix/tasks/flavor/compose/04_webserver.yml +++ b/roles/web-app-matrix/tasks/flavor/compose/04_webserver.yml @@ -1,15 +1,7 @@ --- # include-gated: when: DEPLOYMENT_MODE == "compose" -- name: 📁 create {{ MATRIX_WELL_KNOWN_DIRECTORY }} - ansible.builtin.file: - path: "{{ MATRIX_WELL_KNOWN_DIRECTORY }}" - state: directory - mode: "0755" - -- name: 📁 create {{ MATRIX_WELL_KNOWN_FILE }} - stack_host_template: - src: "well-known.j2" - dest: "{{ MATRIX_WELL_KNOWN_FILE }}" +- name: "🔀 federation delegation" + ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/well_known.yml" - name: "📂 include role sys-stk-front-proxy for {{ MATRIX_ELEMENT_DOMAIN }}" ansible.builtin.include_role: diff --git a/roles/web-app-matrix/tasks/utils/well_known.yml b/roles/web-app-matrix/tasks/utils/well_known.yml new file mode 100644 index 0000000000..eee6555705 --- /dev/null +++ b/roles/web-app-matrix/tasks/utils/well_known.yml @@ -0,0 +1,20 @@ +--- +- name: "📁 create {{ MATRIX_WELL_KNOWN_DIRECTORY }}" + ansible.builtin.file: + path: "{{ MATRIX_WELL_KNOWN_DIRECTORY }}" + state: directory + mode: "0755" + +- name: "📁 create {{ MATRIX_WELL_KNOWN_FILE }}" + stack_host_template: + src: "well-known.j2" + dest: "{{ MATRIX_WELL_KNOWN_FILE }}" + +- name: "🔀 serve the federation delegation on {{ MATRIX_SERVER_NAME }}" + ansible.builtin.include_role: + name: web-opt-rdr-domains + vars: + redirect_domain_mappings: + - source: "{{ MATRIX_SERVER_NAME }}" + target: "{{ MATRIX_ELEMENT_DOMAIN }}" + when: MATRIX_SERVER_NAME not in (CURRENT_PLAY_REDIRECT_DOMAINS | map(attribute='source') | list) diff --git a/roles/web-app-matrix/vars/main.yml b/roles/web-app-matrix/vars/main.yml index 040161198f..663064061c 100644 --- a/roles/web-app-matrix/vars/main.yml +++ b/roles/web-app-matrix/vars/main.yml @@ -11,7 +11,7 @@ MATRIX_SERVER_NAME: "{{ MATRIX_ONION_NODE if (MATRIX_ONION_NODE and lookup('doma MATRIX_ADMINISTRATOR_USERNAME: "{{ lookup('users', 'administrator').username }}" MATRIX_ADMINISTRATOR_PASSWORD: "{{ lookup('users', 'administrator').password }}" MATRIX_WELL_KNOWN_DIRECTORY: "{{ [ lookup('nginx','directories.data.well_known'), 'matrix' ] | path_join }}" -MATRIX_WELL_KNOWN_FILE: "{{ MATRIX_WELL_KNOWN_DIRECTORY }}server" +MATRIX_WELL_KNOWN_FILE: "{{ [ MATRIX_WELL_KNOWN_DIRECTORY, 'server' ] | path_join }}" MATRIX_REGISTRATION_FILE_FOLDER: "/registrations/" MATRIX_BRIDGE_SOURCE_DIR: "{{ [ lookup('container', application_id, 'directories.config'), 'mautrix' ] | path_join }}" MATRIX_BRIDGE_CONFIG_TARGET: "/config/config.yaml" diff --git a/roles/web-opt-rdr-domains/templates/redirect-domain.conf.j2 b/roles/web-opt-rdr-domains/templates/redirect-domain.conf.j2 index 509eb90c54..a618287e80 100644 --- a/roles/web-opt-rdr-domains/templates/redirect-domain.conf.j2 +++ b/roles/web-opt-rdr-domains/templates/redirect-domain.conf.j2 @@ -7,6 +7,10 @@ server { {% if web_opt_rdr_domains_logout_enabled | bool %} {% include 'roles/web-svc-logout/templates/logout-proxy.conf.j2' %} {% endif %} + location ^~ /.well-known/matrix/ { + alias {{ [ lookup('nginx', 'directories.data.well_known'), 'matrix' ] | path_join }}/; + default_type application/json; + } location / { return 301 {{ lookup('tls', application_id, 'protocols.web') }}://{{ target }}$request_uri; }