Add whoami-based sync worker routing for user-level sticky sessions

This adds a new routing mechanism for sync workers that resolves access tokens
to usernames via Synapse's whoami endpoint, enabling true user-level sticky
routing regardless of which device or token is used.

Previously, sticky routing relied on parsing the username from native Synapse
tokens (`syt_<base64 username>_...`), which only works with native Synapse auth
and provides device-level stickiness at best. This new approach works with any
auth system (native Synapse, MAS, etc.) because Synapse handles token validation
internally.

Implementation uses nginx's auth_request module with an njs script because:
- The whoami lookup requires an async HTTP subrequest (ngx.fetch)
- js_set handlers must return synchronously and don't support async operations
- auth_request allows the async lookup to complete, then captures the result
  via response headers into nginx variables

The njs script:
- Extracts access tokens from Authorization header or query parameter
- Calls Synapse's whoami endpoint to resolve token -> username
- Caches results in a shared memory zone to minimize latency
- Returns the username via a `X-User-Identifier` header

The username is then used by nginx's upstream hash directive for consistent
worker selection. This leverages nginx's built-in health checking and failover.
This commit is contained in:
Slavi Pantaleev
2026-02-04 03:14:47 +02:00
parent 81f815d19b
commit 5cc69ca7eb
6 changed files with 368 additions and 13 deletions

View File

@@ -28,6 +28,7 @@ matrix_synapse_reverse_proxy_companion_version: 1.29.4-alpine
matrix_synapse_reverse_proxy_companion_base_path: "{{ matrix_synapse_base_path }}/reverse-proxy-companion"
matrix_synapse_reverse_proxy_companion_confd_path: "{{ matrix_synapse_reverse_proxy_companion_base_path }}/conf.d"
matrix_synapse_reverse_proxy_companion_njs_path: "{{ matrix_synapse_reverse_proxy_companion_base_path }}/njs"
# List of systemd services that matrix-synapse-reverse-proxy-companion.service depends on
matrix_synapse_reverse_proxy_companion_systemd_required_services_list: "{{ matrix_synapse_reverse_proxy_companion_systemd_required_services_list_default + matrix_synapse_reverse_proxy_companion_systemd_required_services_list_auto + matrix_synapse_reverse_proxy_companion_systemd_required_services_list_custom }}"
@@ -290,3 +291,77 @@ matrix_synapse_reverse_proxy_companion_synapse_cache_proxy_cache_valid_time: "24
# As such, it trusts the protocol scheme forwarded by the upstream proxy.
matrix_synapse_reverse_proxy_companion_trust_forwarded_proto: true
matrix_synapse_reverse_proxy_companion_x_forwarded_proto_value: "{{ '$http_x_forwarded_proto' if matrix_synapse_reverse_proxy_companion_trust_forwarded_proto else '$scheme' }}"
########################################################################################
# #
# njs module #
# #
########################################################################################
# Controls whether the njs module is loaded.
matrix_synapse_reverse_proxy_companion_njs_enabled: "{{ matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_enabled }}"
########################################################################################
# #
# /njs module #
# #
########################################################################################
########################################################################################
# #
# Whoami-based sync worker routing #
# #
########################################################################################
# Controls whether the whoami-based sync worker router is enabled.
# When enabled, the reverse proxy will call Synapse's /_matrix/client/v3/account/whoami endpoint
# to resolve access tokens to usernames, allowing consistent routing of requests from the same user
# to the same sync worker regardless of which device or token they use.
#
# This works with any authentication system (native Synapse auth, MAS, etc.) because Synapse
# handles the token validation internally.
#
# Without this, sticky routing falls back to parsing the username from the access token (only works
# with native Synapse tokens of the form syt_<base64 username>_...), which only provides
# device-level stickiness (same token -> same worker) rather than user-level stickiness.
#
# Enabled by default when there are sync workers, because sync workers benefit from user-level
# stickiness due to their per-user in-memory caches.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_enabled: "{{ matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'sync_worker') | list | length > 0 }}"
# The whoami endpoint path (Matrix spec endpoint).
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_endpoint: /_matrix/client/v3/account/whoami
# The full URL to the whoami endpoint.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_url: "http://{{ matrix_synapse_reverse_proxy_companion_client_api_addr }}{{ matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_endpoint }}"
# Cache duration (in seconds) for whoami lookup results.
# Token -> username mappings are cached to avoid repeated whoami calls.
# A longer TTL reduces load on Synapse but means username changes take longer to take effect.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_cache_ttl_seconds: 3600
# Size of the shared memory zone for caching whoami results (in megabytes).
# Each cached entry is approximately 100-200 bytes.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_cache_size_mb: 1
# Controls whether verbose logging is enabled for the whoami sync worker router.
# When enabled, logs cache hits/misses and routing decisions.
# Useful for debugging, but should be disabled in production.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_logging_enabled: false
# The length of the access token to show in logs when logging is enabled.
# Keeping this short is a good idea from a security perspective.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_logging_token_length: 12
# Controls whether debug response headers are added to sync requests.
# When enabled, adds X-Sync-Worker-Router-User-Identifier and X-Sync-Worker-Router-Upstream headers.
# Useful for debugging routing behavior, but should be disabled in production.
matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_debug_headers_enabled: false
########################################################################################
# #
# /Whoami-based sync worker routing #
# #
########################################################################################