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://<server_name>/.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 <server_name>: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 `<well-known>/matrixserver`,
because the file path joined the directory and `server` without a
separator.

The file now lands at `<well-known>/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) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-24 11:44:14 +02:00
1 parent 7a82a92e84
commit 045ef095f2
6 files changed
+30 -22

No files matched your search

+1 -1
View File
@@ -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 `<server_name>: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 `<server_name>: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.
@@ -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:
@@ -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:
@@ -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)
+1 -1
View File
@@ -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"
@@ -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;
}