From a5a3a1938d75d528fc1b314aeacd5cfc7f1f4171 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 10 Mar 2026 19:22:42 +0000 Subject: [PATCH 01/13] Add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} From 5481b7e31cce341ede4e634516cd8bcaff37bc69 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 7 Apr 2026 14:59:19 +0200 Subject: [PATCH 02/13] add initial mas setup --- .env.sample | 101 ++++-- abra.sh | 7 +- compose.mas.yml | 47 +++ compose.yml | 2 + homeserver.yaml.tmpl | 643 ++++++++++++++++++------------------ mas.config.yaml.tmpl | 41 +++ nginx.conf.tmpl | 33 +- well_known_client.conf.tmpl | 5 +- 8 files changed, 521 insertions(+), 358 deletions(-) create mode 100644 compose.mas.yml create mode 100644 mas.config.yaml.tmpl diff --git a/.env.sample b/.env.sample index b919531..d9d38c6 100644 --- a/.env.sample +++ b/.env.sample @@ -19,6 +19,71 @@ SECRET_FORM_SECRET_VERSION=v1 SECRET_MACAROON_VERSION=v1 SECRET_REGISTRATION_VERSION=v1 +## Authentication + +# All login / SSO / MAS-related toggles in one place. + +### Local password & registration (Synapse native) + +# With MAS_ENABLED=1 you must set PASSWORD_LOGIN_ENABLED=false — Synapse forbids legacy password DB alongside matrix_authentication_service. +PASSWORD_LOGIN_ENABLED=true +ENABLE_REGISTRATION=false + +# Token based registration. Enable ADMIN_INTERFACE (below) to use the admin interface to generate tokens. +#REGISTRATION_REQUIRES_TOKEN=true + +### OIDC via Keycloak-shaped API (e.g. Authentik) + +#COMPOSE_FILE="$COMPOSE_FILE:compose.keycloak.yml" +#KEYCLOAK_ENABLED=1 +#KEYCLOAK_ID=keycloak +#KEYCLOAK_NAME= +#KEYCLOAK_URL= +#KEYCLOAK_CLIENT_ID= +#KEYCLOAK_CLIENT_DOMAIN= +#KEYCLOAK_ALLOW_EXISTING_USERS=false +#SECRET_KEYCLOAK_CLIENT_SECRET_VERSION=v1 + +### Second OIDC provider (compose.keycloak2.yml) + +#COMPOSE_FILE="$COMPOSE_FILE:compose.keycloak2.yml" +#KEYCLOAK2_ENABLED=1 +#KEYCLOAK2_ID=keycloak2 +#KEYCLOAK2_NAME= +#KEYCLOAK2_URL= +#KEYCLOAK2_CLIENT_ID= +#KEYCLOAK2_CLIENT_DOMAIN= +#KEYCLOAK2_ALLOW_EXISTING_USERS=false +#SECRET_KEYCLOAK2_CLIENT_SECRET_VERSION=v1 + +### Third OIDC provider (compose.keycloak3.yml) + +#COMPOSE_FILE="$COMPOSE_FILE:compose.keycloak3.yml" +#KEYCLOAK3_ENABLED=1 +#KEYCLOAK3_ID=keycloak3 +#KEYCLOAK3_NAME= +#KEYCLOAK3_URL= +#KEYCLOAK3_CLIENT_ID= +#KEYCLOAK3_CLIENT_DOMAIN= +#KEYCLOAK3_ALLOW_EXISTING_USERS=false +#SECRET_KEYCLOAK3_CLIENT_SECRET_VERSION=v1 + +### Matrix Authentication Service (MAS) — Element X / OIDC-native auth + +#COMPOSE_FILE="$COMPOSE_FILE:compose.mas.yml" +#MAS_ENABLED=1 +#PASSWORD_LOGIN_ENABLED=false +#SECRET_MAS_ENCRYPTION_VERSION=v1 # length=64 # charset=hex +#SECRET_MAS_SYNAPSE_SHARED_VERSION=v1 # length=64 # charset=hex +# PEM private key: abra cannot generate this format — insert only (e.g. openssl genrsa 2048 | abra app secret insert …) +#SECRET_MAS_SIGNING_RSA_VERSION=v1 # generate=false + +### Shared secret auth (bridges / automation) + +#COMPOSE_FILE="$COMPOSE_FILE:compose.shared_secret_auth.yml" +#SHARED_SECRET_AUTH_ENABLED=1 +#SECRET_SHARED_SECRET_AUTH_VERSION=v1 # length=128 + ## Federation #DISABLE_FEDERATION=1 @@ -28,14 +93,6 @@ SERVE_SERVER_WELLKNOWN=false ALLOW_PUBLIC_ROOMS_FEDERATION=false -## Registration - -ENABLE_REGISTRATION=false -PASSWORD_LOGIN_ENABLED=true - -# Token based registration. Enable ADMIN_INTERFACE (below) to use the admin interface to generate tokens. -#REGISTRATION_REQUIRES_TOKEN=true - ## Room auto-join #AUTO_JOIN_ROOM_ENABLED=1 @@ -98,30 +155,8 @@ RETENTION_MAX_LIFETIME=4w #LOGIN_LIMIT_ACCOUNT_PER_SECOND=1 #LOGIN_LIMIT_ACCOUNT_BURST=10 -## Keycloak SSO - -#COMPOSE_FILE="$COMPOSE_FILE:compose.keycloak.yml" -#KEYCLOAK_ENABLED=1 -#KEYCLOAK_ID=keycloak -#KEYCLOAK_NAME= -#KEYCLOAK_URL= -#KEYCLOAK_CLIENT_ID= -#KEYCLOAK_CLIENT_DOMAIN= -#KEYCLOAK_ALLOW_EXISTING_USERS=false -#SECRET_KEYCLOAK_CLIENT_SECRET_VERSION=v1 - ## TURN -#COMPOSE_FILE="$COMPOSE_FILE:compose.keycloak3.yml" -#KEYCLOAK3_ENABLED=1 -#KEYCLOAK3_ID=keycloak3 -#KEYCLOAK3_NAME= -#KEYCLOAK3_URL= -#KEYCLOAK3_CLIENT_ID= -#KEYCLOAK3_CLIENT_DOMAIN= -#KEYCLOAK3_ALLOW_EXISTING_USERS=false -#SECRET_KEYCLOAK3_CLIENT_SECRET_VERSION=v1 - #COMPOSE_FILE="$COMPOSE_FILE:compose.turn.yml" #TURN_ENABLED=1 #TURN_URIS="[\"turns:coturn.foo.zone?transport=udp\", \"turns:coturn.foo.zone?transport=tcp\"]" @@ -189,12 +224,6 @@ RETENTION_MAX_LIFETIME=4w #SECRET_SIGNAL_HS_TOKEN_VERSION=v1 #SECRET_SIGNAL_PICKLE_KEY_VERSION=v1 -## Shared auth - -#COMPOSE_FILE="$COMPOSE_FILE:compose.shared_secret_auth.yml" -#SHARED_SECRET_AUTH_ENABLED=1 -#SECRET_SHARED_SECRET_AUTH_VERSION=v1 # length=128 - ## Web Client (Redirect) #WEB_CLIENT_LOCATION=https://element-web.example.com diff --git a/abra.sh b/abra.sh index 5009278..8d1a1e0 100644 --- a/abra.sh +++ b/abra.sh @@ -1,13 +1,14 @@ export DISCORD_BRIDGE_YAML_VERSION=v2 export ENTRYPOINT_CONF_VERSION=v3 -export HOMESERVER_YAML_VERSION=v35 +export HOMESERVER_YAML_VERSION=v36 export LOG_CONFIG_VERSION=v2 export SHARED_SECRET_AUTH_VERSION=v2 export SIGNAL_BRIDGE_YAML_VERSION=v6 export TELEGRAM_BRIDGE_YAML_VERSION=v6 -export NGINX_CONFIG_VERSION=v12 +export NGINX_CONFIG_VERSION=v13 export WK_SERVER_VERSION=v1 -export WK_CLIENT_VERSION=v1 +export WK_CLIENT_VERSION=v2 +export MAS_CONFIG_VERSION=v1 export PG_BACKUP_VERSION=v2 export ADMIN_CONFIG_VERSION=v1 diff --git a/compose.mas.yml b/compose.mas.yml new file mode 100644 index 0000000..629eef1 --- /dev/null +++ b/compose.mas.yml @@ -0,0 +1,47 @@ +--- +version: "3.8" + +# Matrix Authentication Service (MAS) — optional overlay for Element X / OIDC-native auth. + +services: + mas: + image: ghcr.io/element-hq/matrix-authentication-service:1.14.0 + command: ["server", "--config=/etc/mas/config.yaml"] + environment: + - DOMAIN + - SERVER_NAME + - STACK_NAME + networks: + - internal + configs: + - source: mas_config + target: /etc/mas/config.yaml + secrets: + - db_password + - mas_encryption + - mas_synapse_shared + - mas_signing_rsa + deploy: + restart_policy: + condition: on-failure + + app: + secrets: + - mas_synapse_shared + +configs: + mas_config: + name: ${STACK_NAME}_mas_config_${MAS_CONFIG_VERSION} + file: mas.config.yaml.tmpl + template_driver: golang + +secrets: + mas_encryption: + external: true + name: ${STACK_NAME}_mas_encryption_${SECRET_MAS_ENCRYPTION_VERSION} + mas_synapse_shared: + external: true + name: ${STACK_NAME}_mas_synapse_shared_${SECRET_MAS_SYNAPSE_SHARED_VERSION} + mas_signing_rsa: + external: true + name: ${STACK_NAME}_mas_signing_rsa_${SECRET_MAS_SIGNING_RSA_VERSION} diff --git a/compose.yml b/compose.yml index a9209f6..b027d2a 100644 --- a/compose.yml +++ b/compose.yml @@ -10,6 +10,7 @@ services: environment: - DOMAIN - STACK_NAME + - MAS_ENABLED - NGINX_ACCESS_LOG_LOCATION - NGINX_ERROR_LOG_LOCATION - MAX_UPLOAD_SIZE @@ -46,6 +47,7 @@ services: - macaroon - form_secret environment: + - MAS_ENABLED - ALLOWED_LIFETIME_MAX - ALLOW_PUBLIC_ROOMS_FEDERATION - AUTO_JOIN_ROOM diff --git a/homeserver.yaml.tmpl b/homeserver.yaml.tmpl index 0fb7002..4e4cbe8 100644 --- a/homeserver.yaml.tmpl +++ b/homeserver.yaml.tmpl @@ -1,317 +1,326 @@ -# All configuration options are documented on the following link: -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html - -{{ if eq (env "SHARED_SECRET_AUTH_ENABLED") "1" }} -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#modules-1 -modules: - - module: shared_secret_authenticator.SharedSecretAuthProvider - config: - shared_secret: {{ secret "shared_secret_auth" }} - m_login_password_support_enabled: true -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#server_name -server_name: {{ or (env "SERVER_NAME") (env "DOMAIN") }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#public_baseurl -public_baseurl: https://{{ env "DOMAIN" }}/ - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#require_auth_for_profile_requests -require_auth_for_profile_requests: {{ env "REQUIRE_AUTH_FOR_PROFILE_REQUESTS" }} - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_profile_requests_to_users_who_share_rooms -limit_profile_requests_to_users_who_share_rooms: {{ env "LIMIT_PROFILE_REQUESTS_TO_USERS_WHO_SHARE_ROOMS" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#serve_server_wellknown -serve_server_wellknown: {{ env "SERVE_SERVER_WELLKNOWN" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_without_auth -allow_public_rooms_without_auth: false - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_over_federation -allow_public_rooms_over_federation: {{ or (env "ALLOW_PUBLIC_ROOMS_FEDERATION") "true" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#listeners -listeners: - - port: 8008 - tls: false - type: http - x_forwarded: true - - {{ if eq (env "DISABLE_FEDERATION") "1" }} - resources: - {{ if eq (env "KEYCLOAK_ENABLED") "1" }} - - names: [client, openid] - compress: true - {{ else }} - - names: [client] - compress: true - {{ end }} - {{ else }} - resources: - {{ if eq (env "KEYCLOAK_ENABLED") "1" }} - - names: [client, openid, federation] - compress: true - {{ else }} - - names: [client, federation] - compress: true - {{ end }} - {{ end }} - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#delete_stale_devices_after -{{ if (env "DELETE_STALE_DEVICES_AFTER") }} -delete_stale_devices_after: {{ env "DELETE_STALE_DEVICES_AFTER" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#admin_contact -admin_contact: 'mailto:{{ env "ADMIN_EMAIL" }}' - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_remote_rooms -limit_remote_rooms: - enabled: true - complexity: 200.0 - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#max_avatar_size -max_avatar_size: 10M - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#forgotten_room_retention_period -forgotten_room_retention_period: 3d - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#request_token_inhibit_3pid_errors -request_token_inhibit_3pid_errors: true - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#redaction_retention_period -redaction_retention_period: {{ env "REDACTION_RETENTION_PERIOD" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_ips_max_age -user_ips_max_age: {{ env "USER_IPS_MAX_AGE" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#retention -retention: - enabled: true - default_policy: - min_lifetime: 1d - max_lifetime: {{ env "RETENTION_MAX_LIFETIME" }} - allowed_lifetime_min: 1d - allowed_lifetime_max: {{ env "ALLOWED_LIFETIME_MAX" }} - purge_jobs: - - longest_max_lifetime: 3d - interval: 12h - - shortest_max_lifetime: 3d - interval: 1d - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#federation_domain_whitelist -{{ if eq (env "DISABLE_FEDERATION") "1" }} -federation_domain_whitelist: [] -{{ else if eq (env "ENABLE_ALLOWLIST") "1" }} -federation_domain_whitelist: {{ env "FEDERATION_ALLOWLIST" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#database-1 -database: - name: psycopg2 - txn_limit: 10000 - args: - user: synapse - password: "{{ secret "db_password" }}" - database: synapse - host: "{{ env "STACK_NAME" }}_db" - port: 5432 - cp_min: 5 - cp_max: 10 - keepalives_idle: 10 - keepalives_interval: 10 - keepalives_count: 3 - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#log_config -log_config: "/data/log.config" - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#media_store_path -media_store_path: "/data/media_store" - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#max_upload_size -max_upload_size: 50M - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#turn -{{ if eq (env "TURN_ENABLED") "1" }} -turn_uris: {{ env "TURN_URIS" }} -turn_shared_secret: "{{ secret "turn_shared_secret" }}" -turn_user_lifetime: 1h -turn_allow_guests: {{ env "TURN_ALLOW_GUESTS" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_registration -enable_registration: {{ env "ENABLE_REGISTRATION" }} - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#registration_requires_token -registration_requires_token: {{ env "REGISTRATION_REQUIRES_TOKEN" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_3pid_lookup -enable_3pid_lookup: {{ env "ENABLE_3PID_LOOKUP" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_guest_access -allow_guest_access: false - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#registration_shared_secret -registration_shared_secret: {{ secret "registration" }} - -{{ if eq (env "AUTO_JOIN_ROOM_ENABLED") "1" }} -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#auto_join_rooms - -# AUTO_JOIN_ROOM only for backwards compatibility -{{ if (env "AUTO_JOIN_ROOM") }} -auto_join_rooms: - - "{{ env "AUTO_JOIN_ROOM" }}" -{{ else }} -auto_join_rooms: {{ env "AUTO_JOIN_ROOM_LIST" }} -{{ end }} - -{{ end }} - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#session_lifetime -{{ if (env "SESSION_LIFETIME") }} -session_lifetime: {{ env "SESSION_LIFETIME" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#report_stats -report_stats: false - -# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#track_puppeted_user_ips -track_puppeted_user_ips: {{ env "TRACK_PUPPETED_USER_IPS" }} - -{{ if eq (env "APP_SERVICES_ENABLED") "1" }} -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#app_service_config_files -app_service_config_files: {{ env "APP_SERVICE_CONFIGS" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#macaroon_secret_key -macaroon_secret_key: "{{ secret "macaroon" }}" - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#form_secret -form_secret: "{{ secret "form_secret" }}" - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#signing_key_path -signing_key_path: "/data/{{ env "DOMAIN" }}.signing.key" - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#old_signing_keys -{{ if (and (env "OLD_SIGNING_KEY_ID") (env "OLD_SIGNING_KEY") (env "OLD_SIGNING_KEY_EXPIRES")) }} -old_signing_keys: - "ed25519:{{ env "OLD_SIGNING_KEY_ID" }}": { key: "{{ env "OLD_SIGNING_KEY" }}", expired_ts: {{ env "OLD_SIGNING_KEY_EXPIRES" }} } -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#trusted_key_servers -{{ if eq (env "ENABLE_ALLOWLIST") "1" }} -trusted_key_servers: [] # NOTE(d1): defaults to requesting server directly, which matches FEDERATION_ALLOWLIST -{{ else }} -trusted_key_servers: - - server_name: "matrix.org" -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#oidc_providers -{{ if eq (env "KEYCLOAK_ENABLED") "1" }} -oidc_providers: - - idp_id: {{ env "KEYCLOAK_ID" }} - idp_name: {{ env "KEYCLOAK_NAME" }} - issuer: "{{ env "KEYCLOAK_URL" }}" - client_id: "{{ env "KEYCLOAK_CLIENT_ID" }}" - client_secret: "{{ secret "keycloak_client_secret" }}" - scopes: ["openid", "profile"] - allow_existing_users: {{ env "KEYCLOAK_ALLOW_EXISTING_USERS" }} - user_mapping_provider: - config: - localpart_template: "{{ "{{ user.preferred_username }}" }}" - display_name_template: "{{ "{{ user.name }}" }}" - - {{ if eq (env "KEYCLOAK2_ENABLED") "1" }} - - idp_id: {{ env "KEYCLOAK2_ID" }} - idp_name: {{ env "KEYCLOAK2_NAME" }} - issuer: "{{ env "KEYCLOAK2_URL" }}" - client_id: "{{ env "KEYCLOAK2_CLIENT_ID" }}" - client_secret: "{{ secret "keycloak2_client_secret" }}" - scopes: ["openid", "profile"] - allow_existing_users: {{ env "KEYCLOAK2_ALLOW_EXISTING_USERS" }} - user_mapping_provider: - config: - localpart_template: "{{ "{{ user.preferred_username }}" }}" - display_name_template: "{{ "{{ user.name }}" }}" - {{ end }} - - {{ if eq (env "KEYCLOAK3_ENABLED") "1" }} - - idp_id: {{ env "KEYCLOAK3_ID" }} - idp_name: {{ env "KEYCLOAK3_NAME" }} - issuer: "{{ env "KEYCLOAK3_URL" }}" - client_id: "{{ env "KEYCLOAK3_CLIENT_ID" }}" - client_secret: "{{ secret "keycloak3_client_secret" }}" - scopes: ["openid", "profile"] - allow_existing_users: {{ env "KEYCLOAK3_ALLOW_EXISTING_USERS" }} - user_mapping_provider: - config: - localpart_template: "{{ "{{ user.preferred_username }}" }}" - display_name_template: "{{ "{{ user.name }}" }}" - {{ end }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#sso -{{ if eq (env "KEYCLOAK_ENABLED") "1" }} -sso: - client_whitelist: - - https://{{ env "KEYCLOAK_CLIENT_DOMAIN" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#password_config -password_config: - enabled: {{ env "PASSWORD_LOGIN_ENABLED" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#email -{{ if eq (env "SMTP_ENABLED") "1" }} -email: - smtp_host: {{ env "SMTP_HOST" }} - smtp_port: {{ env "SMTP_PORT" }} - smtp_user: {{ env "SMTP_USER" }} - smtp_pass: "{{ secret "smtp_password" }}" - require_transport_security: true - notif_from: Your Friendly %(app)s homeserver <{{ env "SMTP_FROM" }}> - app_name: {{ env "SMTP_APP_NAME" }} - enable_notifs: true - client_base_url: https://{{ env "DOMAIN" }} -{{ end }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#encryption_enabled_by_default_for_room_type -encryption_enabled_by_default_for_room_type: {{ env "ENCRYPTED_BY_DEFAULT" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_directory -user_directory: - enabled: {{ env "USER_DIRECTORY_ENABLED" }} - search_all_users: {{ env "USER_DIRECTORY_SEARCH_ALL_USERS" }} - prefer_local_users: {{ env "USER_DIRECTORY_PREFER_LOCAL_USERS" }} - show_locked_users: {{ env "USER_DIRECTORY_SHOW_LOCKED_USERS" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#media_retention -media_retention: - local_media_lifetime: {{ env "MEDIA_RETENTION_LOCAL_LIFETIME" }} - remote_media_lifetime: {{ env "MEDIA_RETENTION_REMOTE_LIFETIME" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_metrics -enable_metrics: false - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#track_appservice_user_ips -track_appservice_user_ips: false - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#forget_rooms_on_leave -forget_rooms_on_leave: true - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#opentracing-1 -opentracing: - enabled: false - -# https://matrix-org.github.io/synapse/develop/usage/configuration/config_documentation.html#ratelimiting -rc_login: - address: - per_second: {{ env "LOGIN_LIMIT_IP_PER_SECOND" }} - burst_count: {{ env "LOGIN_LIMIT_IP_BURST" }} - account: - per_second: {{ env "LOGIN_LIMIT_ACCOUNT_PER_SECOND" }} - burst_count: {{ env "LOGIN_LIMIT_ACCOUNT_BURST" }} - -# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#web_client_location -web_client_location: {{ env "WEB_CLIENT_LOCATION" }} +# All configuration options are documented on the following link: +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html + +{{ if eq (env "SHARED_SECRET_AUTH_ENABLED") "1" }} +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#modules-1 +modules: + - module: shared_secret_authenticator.SharedSecretAuthProvider + config: + shared_secret: {{ secret "shared_secret_auth" }} + m_login_password_support_enabled: true +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#server_name +server_name: {{ or (env "SERVER_NAME") (env "DOMAIN") }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#public_baseurl +public_baseurl: https://{{ env "DOMAIN" }}/ + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#require_auth_for_profile_requests +require_auth_for_profile_requests: {{ env "REQUIRE_AUTH_FOR_PROFILE_REQUESTS" }} + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_profile_requests_to_users_who_share_rooms +limit_profile_requests_to_users_who_share_rooms: {{ env "LIMIT_PROFILE_REQUESTS_TO_USERS_WHO_SHARE_ROOMS" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#serve_server_wellknown +serve_server_wellknown: {{ env "SERVE_SERVER_WELLKNOWN" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_without_auth +allow_public_rooms_without_auth: false + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_over_federation +allow_public_rooms_over_federation: {{ or (env "ALLOW_PUBLIC_ROOMS_FEDERATION") "true" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#listeners +listeners: + - port: 8008 + tls: false + type: http + x_forwarded: true + + {{ if eq (env "DISABLE_FEDERATION") "1" }} + resources: + {{ if eq (env "KEYCLOAK_ENABLED") "1" }} + - names: [client, openid] + compress: true + {{ else }} + - names: [client] + compress: true + {{ end }} + {{ else }} + resources: + {{ if eq (env "KEYCLOAK_ENABLED") "1" }} + - names: [client, openid, federation] + compress: true + {{ else }} + - names: [client, federation] + compress: true + {{ end }} + {{ end }} + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#delete_stale_devices_after +{{ if (env "DELETE_STALE_DEVICES_AFTER") }} +delete_stale_devices_after: {{ env "DELETE_STALE_DEVICES_AFTER" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#admin_contact +admin_contact: 'mailto:{{ env "ADMIN_EMAIL" }}' + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_remote_rooms +limit_remote_rooms: + enabled: true + complexity: 200.0 + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#max_avatar_size +max_avatar_size: 10M + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#forgotten_room_retention_period +forgotten_room_retention_period: 3d + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#request_token_inhibit_3pid_errors +request_token_inhibit_3pid_errors: true + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#redaction_retention_period +redaction_retention_period: {{ env "REDACTION_RETENTION_PERIOD" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_ips_max_age +user_ips_max_age: {{ env "USER_IPS_MAX_AGE" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#retention +retention: + enabled: true + default_policy: + min_lifetime: 1d + max_lifetime: {{ env "RETENTION_MAX_LIFETIME" }} + allowed_lifetime_min: 1d + allowed_lifetime_max: {{ env "ALLOWED_LIFETIME_MAX" }} + purge_jobs: + - longest_max_lifetime: 3d + interval: 12h + - shortest_max_lifetime: 3d + interval: 1d + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#federation_domain_whitelist +{{ if eq (env "DISABLE_FEDERATION") "1" }} +federation_domain_whitelist: [] +{{ else if eq (env "ENABLE_ALLOWLIST") "1" }} +federation_domain_whitelist: {{ env "FEDERATION_ALLOWLIST" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#database-1 +database: + name: psycopg2 + txn_limit: 10000 + args: + user: synapse + password: "{{ secret "db_password" }}" + database: synapse + host: "{{ env "STACK_NAME" }}_db" + port: 5432 + cp_min: 5 + cp_max: 10 + keepalives_idle: 10 + keepalives_interval: 10 + keepalives_count: 3 + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#log_config +log_config: "/data/log.config" + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#media_store_path +media_store_path: "/data/media_store" + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#max_upload_size +max_upload_size: 50M + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#turn +{{ if eq (env "TURN_ENABLED") "1" }} +turn_uris: {{ env "TURN_URIS" }} +turn_shared_secret: "{{ secret "turn_shared_secret" }}" +turn_user_lifetime: 1h +turn_allow_guests: {{ env "TURN_ALLOW_GUESTS" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_registration +enable_registration: {{ env "ENABLE_REGISTRATION" }} + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#registration_requires_token +registration_requires_token: {{ env "REGISTRATION_REQUIRES_TOKEN" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_3pid_lookup +enable_3pid_lookup: {{ env "ENABLE_3PID_LOOKUP" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_guest_access +allow_guest_access: false + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#registration_shared_secret +registration_shared_secret: {{ secret "registration" }} + +{{ if eq (env "AUTO_JOIN_ROOM_ENABLED") "1" }} +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#auto_join_rooms + +# AUTO_JOIN_ROOM only for backwards compatibility +{{ if (env "AUTO_JOIN_ROOM") }} +auto_join_rooms: + - "{{ env "AUTO_JOIN_ROOM" }}" +{{ else }} +auto_join_rooms: {{ env "AUTO_JOIN_ROOM_LIST" }} +{{ end }} + +{{ end }} + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#session_lifetime +{{ if (env "SESSION_LIFETIME") }} +session_lifetime: {{ env "SESSION_LIFETIME" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#report_stats +report_stats: false + +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#track_puppeted_user_ips +track_puppeted_user_ips: {{ env "TRACK_PUPPETED_USER_IPS" }} + +{{ if eq (env "APP_SERVICES_ENABLED") "1" }} +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#app_service_config_files +app_service_config_files: {{ env "APP_SERVICE_CONFIGS" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#macaroon_secret_key +macaroon_secret_key: "{{ secret "macaroon" }}" + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#form_secret +form_secret: "{{ secret "form_secret" }}" + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#signing_key_path +signing_key_path: "/data/{{ env "DOMAIN" }}.signing.key" + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#old_signing_keys +{{ if (and (env "OLD_SIGNING_KEY_ID") (env "OLD_SIGNING_KEY") (env "OLD_SIGNING_KEY_EXPIRES")) }} +old_signing_keys: + "ed25519:{{ env "OLD_SIGNING_KEY_ID" }}": { key: "{{ env "OLD_SIGNING_KEY" }}", expired_ts: {{ env "OLD_SIGNING_KEY_EXPIRES" }} } +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#trusted_key_servers +{{ if eq (env "ENABLE_ALLOWLIST") "1" }} +trusted_key_servers: [] # NOTE(d1): defaults to requesting server directly, which matches FEDERATION_ALLOWLIST +{{ else }} +trusted_key_servers: + - server_name: "matrix.org" +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#oidc_providers +{{ if eq (env "KEYCLOAK_ENABLED") "1" }} +oidc_providers: + - idp_id: {{ env "KEYCLOAK_ID" }} + idp_name: {{ env "KEYCLOAK_NAME" }} + issuer: "{{ env "KEYCLOAK_URL" }}" + client_id: "{{ env "KEYCLOAK_CLIENT_ID" }}" + client_secret: "{{ secret "keycloak_client_secret" }}" + scopes: ["openid", "profile"] + allow_existing_users: {{ env "KEYCLOAK_ALLOW_EXISTING_USERS" }} + user_mapping_provider: + config: + localpart_template: "{{ "{{ user.preferred_username }}" }}" + display_name_template: "{{ "{{ user.name }}" }}" + + {{ if eq (env "KEYCLOAK2_ENABLED") "1" }} + - idp_id: {{ env "KEYCLOAK2_ID" }} + idp_name: {{ env "KEYCLOAK2_NAME" }} + issuer: "{{ env "KEYCLOAK2_URL" }}" + client_id: "{{ env "KEYCLOAK2_CLIENT_ID" }}" + client_secret: "{{ secret "keycloak2_client_secret" }}" + scopes: ["openid", "profile"] + allow_existing_users: {{ env "KEYCLOAK2_ALLOW_EXISTING_USERS" }} + user_mapping_provider: + config: + localpart_template: "{{ "{{ user.preferred_username }}" }}" + display_name_template: "{{ "{{ user.name }}" }}" + {{ end }} + + {{ if eq (env "KEYCLOAK3_ENABLED") "1" }} + - idp_id: {{ env "KEYCLOAK3_ID" }} + idp_name: {{ env "KEYCLOAK3_NAME" }} + issuer: "{{ env "KEYCLOAK3_URL" }}" + client_id: "{{ env "KEYCLOAK3_CLIENT_ID" }}" + client_secret: "{{ secret "keycloak3_client_secret" }}" + scopes: ["openid", "profile"] + allow_existing_users: {{ env "KEYCLOAK3_ALLOW_EXISTING_USERS" }} + user_mapping_provider: + config: + localpart_template: "{{ "{{ user.preferred_username }}" }}" + display_name_template: "{{ "{{ user.name }}" }}" + {{ end }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#sso +{{ if eq (env "KEYCLOAK_ENABLED") "1" }} +sso: + client_whitelist: + - https://{{ env "KEYCLOAK_CLIENT_DOMAIN" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#password_config +# With MAS (matrix_authentication_service), Synapse rejects password_config.enabled: true — set PASSWORD_LOGIN_ENABLED=false in app .env when MAS_ENABLED=1 (.env.sample). +password_config: + enabled: {{ env "PASSWORD_LOGIN_ENABLED" }} + +{{ if eq (env "MAS_ENABLED") "1" }} +# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#matrix_authentication_service +matrix_authentication_service: + enabled: true + endpoint: http://{{ env "STACK_NAME"}}_mas:8080/ + secret_path: /run/secrets/mas_synapse_shared +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#email +{{ if eq (env "SMTP_ENABLED") "1" }} +email: + smtp_host: {{ env "SMTP_HOST" }} + smtp_port: {{ env "SMTP_PORT" }} + smtp_user: {{ env "SMTP_USER" }} + smtp_pass: "{{ secret "smtp_password" }}" + require_transport_security: true + notif_from: Your Friendly %(app)s homeserver <{{ env "SMTP_FROM" }}> + app_name: {{ env "SMTP_APP_NAME" }} + enable_notifs: true + client_base_url: https://{{ env "DOMAIN" }} +{{ end }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#encryption_enabled_by_default_for_room_type +encryption_enabled_by_default_for_room_type: {{ env "ENCRYPTED_BY_DEFAULT" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_directory +user_directory: + enabled: {{ env "USER_DIRECTORY_ENABLED" }} + search_all_users: {{ env "USER_DIRECTORY_SEARCH_ALL_USERS" }} + prefer_local_users: {{ env "USER_DIRECTORY_PREFER_LOCAL_USERS" }} + show_locked_users: {{ env "USER_DIRECTORY_SHOW_LOCKED_USERS" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#media_retention +media_retention: + local_media_lifetime: {{ env "MEDIA_RETENTION_LOCAL_LIFETIME" }} + remote_media_lifetime: {{ env "MEDIA_RETENTION_REMOTE_LIFETIME" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_metrics +enable_metrics: false + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#track_appservice_user_ips +track_appservice_user_ips: false + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#forget_rooms_on_leave +forget_rooms_on_leave: true + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#opentracing-1 +opentracing: + enabled: false + +# https://matrix-org.github.io/synapse/develop/usage/configuration/config_documentation.html#ratelimiting +rc_login: + address: + per_second: {{ env "LOGIN_LIMIT_IP_PER_SECOND" }} + burst_count: {{ env "LOGIN_LIMIT_IP_BURST" }} + account: + per_second: {{ env "LOGIN_LIMIT_ACCOUNT_PER_SECOND" }} + burst_count: {{ env "LOGIN_LIMIT_ACCOUNT_BURST" }} + +# https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#web_client_location +web_client_location: {{ env "WEB_CLIENT_LOCATION" }} diff --git a/mas.config.yaml.tmpl b/mas.config.yaml.tmpl new file mode 100644 index 0000000..3d958d6 --- /dev/null +++ b/mas.config.yaml.tmpl @@ -0,0 +1,41 @@ +# Docs: https://element-hq.github.io/matrix-authentication-service/ + +http: + public_base: https://{{ env "DOMAIN" }}/ + trusted_proxies: + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + - 127.0.0.0/8 + - fd00::/8 + - ::1/128 + listeners: + - name: web + resources: + - name: discovery + - name: human + - name: oauth + - name: compat + - name: graphql + playground: false + - name: assets + binds: + - address: "[::]:8080" + +database: + uri: postgresql://synapse:{{ secret "db_password" }}@{{ env "STACK_NAME" }}_db:5432/mas?sslmode=disable + +matrix: + kind: synapse + homeserver: {{ or (env "SERVER_NAME") (env "DOMAIN") }} + endpoint: http://{{ env "STACK_NAME" }}_app:8008/ + secret_file: /run/secrets/mas_synapse_shared + +secrets: + # Plain hex in file (abra: length=64 charset=hex). See .env.sample modifiers. + encryption_file: /run/secrets/mas_encryption + keys: + - key_file: /run/secrets/mas_signing_rsa + +passwords: + enabled: true diff --git a/nginx.conf.tmpl b/nginx.conf.tmpl index 4169fc4..024e5fc 100644 --- a/nginx.conf.tmpl +++ b/nginx.conf.tmpl @@ -15,6 +15,14 @@ http { keepalive 16; } +{{ if eq (env "MAS_ENABLED") "1" }} + upstream mas_upstream { + zone mas_upstream 64k; + server {{ env "STACK_NAME"}}_mas:8080 resolve; + keepalive 8; + } +{{ end }} + server { listen 80; @@ -32,7 +40,30 @@ http { proxy_http_version 1.1; } - location ~* ^(\/_matrix|\/_synapse\/client) { +{{ if eq (env "MAS_ENABLED") "1" }} + # MAS on same Host as Synapse (public_base = https://$DOMAIN/): browser/OIDC paths live at repo root, not only under /_matrix/ + # Router reference: element-hq/matrix-authentication-service crates/router/src/endpoints.rs + # https://element-hq.github.io/matrix-authentication-service/setup/reverse-proxy.html + location ~ ^/(complete-compat-sso/|oauth2/|\.well-known/(openid-configuration|webfinger|change-password)|authorize|login|logout|register(/|$)|account/|upstream/|consent/|link(\?|/|$)|device/|recover(/|$)|assets/|graphql(/|$)|api/) { + proxy_pass http://mas_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + client_max_body_size 50M; + } + # Matrix CS API compat (login / logout / refresh and subpaths, e.g. …/login/sso/redirect) — before generic /_matrix + location ~ ^/_matrix/client/[^/]+/(login|logout|refresh)(/.*)?$ { + proxy_pass http://mas_upstream; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + client_max_body_size 50M; + } +{{ end }} + + location ~* ^(\/_matrix|\/_synapse\/client|\/_synapse\/mas) { proxy_pass http://matrix_upstream; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto https; diff --git a/well_known_client.conf.tmpl b/well_known_client.conf.tmpl index 8cacb96..a629317 100644 --- a/well_known_client.conf.tmpl +++ b/well_known_client.conf.tmpl @@ -1,5 +1,8 @@ { "m.homeserver": { "base_url": "https://{{ env "DOMAIN" }}" - } + }{{ if eq (env "MAS_ENABLED") "1" }}, + "org.matrix.msc2965.authentication": { + "issuer": "https://{{ env "DOMAIN" }}/" + }{{ end }} } From cf30cebf8edbc3b72c2ea7efdc08f8caf4d38f2f Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 7 Apr 2026 16:20:34 +0200 Subject: [PATCH 03/13] add function to init mas database --- abra.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/abra.sh b/abra.sh index 8d1a1e0..28ee3eb 100644 --- a/abra.sh +++ b/abra.sh @@ -12,6 +12,13 @@ export MAS_CONFIG_VERSION=v1 export PG_BACKUP_VERSION=v2 export ADMIN_CONFIG_VERSION=v1 +ensure_mas_database () { + if ! psql -U synapse -d postgres -v ON_ERROR_STOP=1 -Atqc "SELECT 1 FROM pg_database WHERE datname = 'mas'" | grep -qx 1 + then + psql -U synapse -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE mas OWNER synapse" + fi +} + set_admin () { admin=akadmin if [ -n "$1" ] From dd92cd4bd7a2dbde8c78ee2f7f966497e2d79ffc Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 8 Apr 2026 13:02:22 +0200 Subject: [PATCH 04/13] add config for upstream oidc provider for mas --- .env.sample | 12 ++++++++++++ compose.mas-upstream.yml | 21 +++++++++++++++++++++ mas.config.yaml.tmpl | 30 ++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 compose.mas-upstream.yml diff --git a/.env.sample b/.env.sample index d9d38c6..d4aca8d 100644 --- a/.env.sample +++ b/.env.sample @@ -78,6 +78,18 @@ ENABLE_REGISTRATION=false # PEM private key: abra cannot generate this format — insert only (e.g. openssl genrsa 2048 | abra app secret insert …) #SECRET_MAS_SIGNING_RSA_VERSION=v1 # generate=false +#### MAS upstream OIDC provider (e.g. Authentik) +# See mas-authentik-and-roadmap.md for migration procedure. +# Create a new OAuth2 app in your IdP with redirect URI: https:///upstream/callback/ +#COMPOSE_FILE="$COMPOSE_FILE:compose.mas-upstream.yml" +#MAS_UPSTREAM_PROVIDER_ID= # ULID, e.g. 01JSHPZHAXC50QBKH67MH33TNF — generate at https://www.ulidtools.com +#MAS_UPSTREAM_ISSUER= # e.g. https://auth.example.com/application/o/matrix-mas/ +#MAS_UPSTREAM_CLIENT_ID= +#MAS_UPSTREAM_HUMAN_NAME=Authentik +# For migration from previous direct Keycloud-style config: set to oidc- so syn2mas maps users correctly. +#MAS_UPSTREAM_SYNAPSE_IDP_ID= +#SECRET_MAS_UPSTREAM_CLIENT_SECRET_VERSION=v1 + ### Shared secret auth (bridges / automation) #COMPOSE_FILE="$COMPOSE_FILE:compose.shared_secret_auth.yml" diff --git a/compose.mas-upstream.yml b/compose.mas-upstream.yml new file mode 100644 index 0000000..317761c --- /dev/null +++ b/compose.mas-upstream.yml @@ -0,0 +1,21 @@ +--- +version: "3.8" + +# Upstream OIDC provider for MAS (e.g. Authentik, Keycloak). +# Requires compose.mas.yml. Adds the client secret and env vars needed by mas.config.yaml.tmpl. + +services: + mas: + environment: + - MAS_UPSTREAM_PROVIDER_ID + - MAS_UPSTREAM_ISSUER + - MAS_UPSTREAM_CLIENT_ID + - MAS_UPSTREAM_HUMAN_NAME + - MAS_UPSTREAM_SYNAPSE_IDP_ID + secrets: + - mas_upstream_client_secret + +secrets: + mas_upstream_client_secret: + external: true + name: ${STACK_NAME}_mas_upstream_client_secret_${SECRET_MAS_UPSTREAM_CLIENT_SECRET_VERSION} diff --git a/mas.config.yaml.tmpl b/mas.config.yaml.tmpl index 3d958d6..59a74e0 100644 --- a/mas.config.yaml.tmpl +++ b/mas.config.yaml.tmpl @@ -39,3 +39,33 @@ secrets: passwords: enabled: true + schemes: + - version: 1 + algorithm: bcrypt + unicode_normalization: true + - version: 2 + algorithm: argon2id + +{{ if env "MAS_UPSTREAM_PROVIDER_ID" }} +# https://element-hq.github.io/matrix-authentication-service/setup/sso.html +upstream_oauth2: + providers: + - id: {{ env "MAS_UPSTREAM_PROVIDER_ID" }} + {{ if env "MAS_UPSTREAM_SYNAPSE_IDP_ID" }}synapse_idp_id: {{ env "MAS_UPSTREAM_SYNAPSE_IDP_ID" }}{{ end }} + human_name: {{ or (env "MAS_UPSTREAM_HUMAN_NAME") "SSO" }} + issuer: {{ env "MAS_UPSTREAM_ISSUER" }} + client_id: {{ env "MAS_UPSTREAM_CLIENT_ID" }} + client_secret_file: /run/secrets/mas_upstream_client_secret + token_endpoint_auth_method: client_secret_basic + scope: "openid profile email" + claims_imports: + localpart: + action: require + template: "{{ "{{ user.preferred_username }}" }}" + displayname: + action: suggest + template: "{{ "{{ user.name }}" }}" + email: + action: suggest + template: "{{ "{{ user.email }}" }}" +{{ end }} From 6f47fca73beb8c534b812b73edf89eece3e05e41 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 8 Apr 2026 13:18:28 +0200 Subject: [PATCH 05/13] add script to perform mas migration compatibility check --- abra.sh | 39 ++++++++++++++++++++++++++ homeserver.yaml.tmpl | 66 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/abra.sh b/abra.sh index 28ee3eb..ff5b189 100644 --- a/abra.sh +++ b/abra.sh @@ -19,6 +19,45 @@ ensure_mas_database () { fi } +# Local helper: fetch homeserver.yaml from app, push to mas, then syn2mas check + dry-run. +prepare_mas_migration () { + local hs_local syn_cfg + + syn_cfg=/tmp/homeserver.yaml + + cleanup_prepare_mas_migration() { + rm -f "homeserver.yaml" + } + trap cleanup_prepare_mas_migration EXIT + + echo "Fetching /data/homeserver.yaml from app to homeserver.yaml (abra app run … cat)..." + if ! abra app run -t "$DOMAIN" app cat /data/homeserver.yaml > "homeserver.yaml" + then + return 1 + fi + if [ ! -s "homeserver.yaml" ]; then + echo "Error: fetched homeserver.yaml is empty." >&2 + return 1 + fi + + echo "Copying into mas:/tmp" + abra app cp -C "$DOMAIN" "homeserver.yaml" "mas:/tmp" || return 1 + + echo "Running mas-cli syn2mas check..." + abra app run -t "$DOMAIN" mas -- mas-cli syn2mas check \ + --config /etc/mas/config.yaml \ + --synapse-config "$syn_cfg" || return 1 + + echo "Running mas-cli syn2mas migrate --dry-run..." + abra app run -t "$DOMAIN" mas -- mas-cli syn2mas migrate \ + --config /etc/mas/config.yaml \ + --synapse-config "$syn_cfg" \ + --dry-run || return 1 + + # trap - EXIT + # cleanup_prepare_mas_migration +} + set_admin () { admin=akadmin if [ -n "$1" ] diff --git a/homeserver.yaml.tmpl b/homeserver.yaml.tmpl index 4e4cbe8..691f97d 100644 --- a/homeserver.yaml.tmpl +++ b/homeserver.yaml.tmpl @@ -17,19 +17,27 @@ server_name: {{ or (env "SERVER_NAME") (env "DOMAIN") }} public_baseurl: https://{{ env "DOMAIN" }}/ # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#require_auth_for_profile_requests +{{ if (env "REQUIRE_AUTH_FOR_PROFILE_REQUESTS") }} require_auth_for_profile_requests: {{ env "REQUIRE_AUTH_FOR_PROFILE_REQUESTS" }} +{{ end }} # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_profile_requests_to_users_who_share_rooms +{{ if (env "LIMIT_PROFILE_REQUESTS_TO_USERS_WHO_SHARE_ROOMS") }} limit_profile_requests_to_users_who_share_rooms: {{ env "LIMIT_PROFILE_REQUESTS_TO_USERS_WHO_SHARE_ROOMS" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#serve_server_wellknown +{{ if (env "SERVE_SERVER_WELLKNOWN") }} serve_server_wellknown: {{ env "SERVE_SERVER_WELLKNOWN" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_without_auth allow_public_rooms_without_auth: false # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_public_rooms_over_federation -allow_public_rooms_over_federation: {{ or (env "ALLOW_PUBLIC_ROOMS_FEDERATION") "true" }} +{{ if (env "ALLOW_PUBLIC_ROOMS_FEDERATION") }} +allow_public_rooms_over_federation: {{ env "ALLOW_PUBLIC_ROOMS_FEDERATION" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#listeners listeners: @@ -64,7 +72,9 @@ delete_stale_devices_after: {{ env "DELETE_STALE_DEVICES_AFTER" }} {{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#admin_contact +{{ if (env "ADMIN_EMAIL") }} admin_contact: 'mailto:{{ env "ADMIN_EMAIL" }}' +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#limit_remote_rooms limit_remote_rooms: @@ -81,19 +91,27 @@ forgotten_room_retention_period: 3d request_token_inhibit_3pid_errors: true # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#redaction_retention_period +{{ if (env "REDACTION_RETENTION_PERIOD") }} redaction_retention_period: {{ env "REDACTION_RETENTION_PERIOD" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_ips_max_age +{{ if (env "USER_IPS_MAX_AGE") }} user_ips_max_age: {{ env "USER_IPS_MAX_AGE" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#retention retention: enabled: true default_policy: min_lifetime: 1d + {{ if (env "RETENTION_MAX_LIFETIME") }} max_lifetime: {{ env "RETENTION_MAX_LIFETIME" }} + {{ end }} allowed_lifetime_min: 1d + {{ if (env "ALLOWED_LIFETIME_MAX") }} allowed_lifetime_max: {{ env "ALLOWED_LIFETIME_MAX" }} + {{ end }} purge_jobs: - longest_max_lifetime: 3d interval: 12h @@ -134,20 +152,30 @@ max_upload_size: 50M # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#turn {{ if eq (env "TURN_ENABLED") "1" }} +{{ if (env "TURN_URIS") }} turn_uris: {{ env "TURN_URIS" }} +{{ end }} turn_shared_secret: "{{ secret "turn_shared_secret" }}" turn_user_lifetime: 1h +{{ if (env "TURN_ALLOW_GUESTS") }} turn_allow_guests: {{ env "TURN_ALLOW_GUESTS" }} {{ end }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_registration +{{ if (env "ENABLE_REGISTRATION") }} enable_registration: {{ env "ENABLE_REGISTRATION" }} +{{ end }} # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#registration_requires_token +{{ if (env "REGISTRATION_REQUIRES_TOKEN") }} registration_requires_token: {{ env "REGISTRATION_REQUIRES_TOKEN" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_3pid_lookup +{{ if (env "ENABLE_3PID_LOOKUP") }} enable_3pid_lookup: {{ env "ENABLE_3PID_LOOKUP" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#allow_guest_access allow_guest_access: false @@ -177,7 +205,9 @@ session_lifetime: {{ env "SESSION_LIFETIME" }} report_stats: false # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#track_puppeted_user_ips +{{ if (env "TRACK_PUPPETED_USER_IPS") }} track_puppeted_user_ips: {{ env "TRACK_PUPPETED_USER_IPS" }} +{{ end }} {{ if eq (env "APP_SERVICES_ENABLED") "1" }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#app_service_config_files @@ -216,7 +246,9 @@ oidc_providers: client_id: "{{ env "KEYCLOAK_CLIENT_ID" }}" client_secret: "{{ secret "keycloak_client_secret" }}" scopes: ["openid", "profile"] + {{ if (env "KEYCLOAK_ALLOW_EXISTING_USERS") }} allow_existing_users: {{ env "KEYCLOAK_ALLOW_EXISTING_USERS" }} + {{ end }} user_mapping_provider: config: localpart_template: "{{ "{{ user.preferred_username }}" }}" @@ -229,7 +261,9 @@ oidc_providers: client_id: "{{ env "KEYCLOAK2_CLIENT_ID" }}" client_secret: "{{ secret "keycloak2_client_secret" }}" scopes: ["openid", "profile"] + {{ if (env "KEYCLOAK2_ALLOW_EXISTING_USERS") }} allow_existing_users: {{ env "KEYCLOAK2_ALLOW_EXISTING_USERS" }} + {{ end }} user_mapping_provider: config: localpart_template: "{{ "{{ user.preferred_username }}" }}" @@ -243,7 +277,9 @@ oidc_providers: client_id: "{{ env "KEYCLOAK3_CLIENT_ID" }}" client_secret: "{{ secret "keycloak3_client_secret" }}" scopes: ["openid", "profile"] + {{ if (env "KEYCLOAK3_ALLOW_EXISTING_USERS") }} allow_existing_users: {{ env "KEYCLOAK3_ALLOW_EXISTING_USERS" }} + {{ end }} user_mapping_provider: config: localpart_template: "{{ "{{ user.preferred_username }}" }}" @@ -260,8 +296,10 @@ sso: # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#password_config # With MAS (matrix_authentication_service), Synapse rejects password_config.enabled: true — set PASSWORD_LOGIN_ENABLED=false in app .env when MAS_ENABLED=1 (.env.sample). +{{ if (env "PASSWORD_LOGIN_ENABLED") }} password_config: enabled: {{ env "PASSWORD_LOGIN_ENABLED" }} +{{ end }} {{ if eq (env "MAS_ENABLED") "1" }} # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#matrix_authentication_service @@ -286,19 +324,37 @@ email: {{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#encryption_enabled_by_default_for_room_type +{{ if (env "ENCRYPTED_BY_DEFAULT") }} encryption_enabled_by_default_for_room_type: {{ env "ENCRYPTED_BY_DEFAULT" }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#user_directory +{{ if or (env "USER_DIRECTORY_ENABLED") (env "USER_DIRECTORY_SEARCH_ALL_USERS") (env "USER_DIRECTORY_PREFER_LOCAL_USERS") (env "USER_DIRECTORY_SHOW_LOCKED_USERS") }} user_directory: + {{ if (env "USER_DIRECTORY_ENABLED") }} enabled: {{ env "USER_DIRECTORY_ENABLED" }} + {{ end }} + {{ if (env "USER_DIRECTORY_SEARCH_ALL_USERS") }} search_all_users: {{ env "USER_DIRECTORY_SEARCH_ALL_USERS" }} + {{ end }} + {{ if (env "USER_DIRECTORY_PREFER_LOCAL_USERS") }} prefer_local_users: {{ env "USER_DIRECTORY_PREFER_LOCAL_USERS" }} + {{ end }} + {{ if (env "USER_DIRECTORY_SHOW_LOCKED_USERS") }} show_locked_users: {{ env "USER_DIRECTORY_SHOW_LOCKED_USERS" }} + {{ end }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#media_retention +{{ if or (env "MEDIA_RETENTION_LOCAL_LIFETIME") (env "MEDIA_RETENTION_REMOTE_LIFETIME") }} media_retention: + {{ if (env "MEDIA_RETENTION_LOCAL_LIFETIME") }} local_media_lifetime: {{ env "MEDIA_RETENTION_LOCAL_LIFETIME" }} + {{ end }} + {{ if (env "MEDIA_RETENTION_REMOTE_LIFETIME") }} remote_media_lifetime: {{ env "MEDIA_RETENTION_REMOTE_LIFETIME" }} + {{ end }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#enable_metrics enable_metrics: false @@ -314,13 +370,21 @@ opentracing: enabled: false # https://matrix-org.github.io/synapse/develop/usage/configuration/config_documentation.html#ratelimiting +{{ if or (and (env "LOGIN_LIMIT_IP_PER_SECOND") (env "LOGIN_LIMIT_IP_BURST")) (and (env "LOGIN_LIMIT_ACCOUNT_PER_SECOND") (env "LOGIN_LIMIT_ACCOUNT_BURST")) }} rc_login: +{{ if and (env "LOGIN_LIMIT_IP_PER_SECOND") (env "LOGIN_LIMIT_IP_BURST") }} address: per_second: {{ env "LOGIN_LIMIT_IP_PER_SECOND" }} burst_count: {{ env "LOGIN_LIMIT_IP_BURST" }} +{{ end }} +{{ if and (env "LOGIN_LIMIT_ACCOUNT_PER_SECOND") (env "LOGIN_LIMIT_ACCOUNT_BURST") }} account: per_second: {{ env "LOGIN_LIMIT_ACCOUNT_PER_SECOND" }} burst_count: {{ env "LOGIN_LIMIT_ACCOUNT_BURST" }} +{{ end }} +{{ end }} # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#web_client_location +{{ if (env "WEB_CLIENT_LOCATION") }} web_client_location: {{ env "WEB_CLIENT_LOCATION" }} +{{ end }} From 60bd8b1b49e5a08bc584933521fc2774ad140568 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 8 Apr 2026 14:06:10 +0200 Subject: [PATCH 06/13] provide readme for mas setup and migration --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index a76dc78..86e04a0 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,50 @@ See [`#27`](https://git.coopcloud.tech/coop-cloud/matrix-synapse/pulls/27) for m You'll need to deploy something like [this](https://git.autonomic.zone/ruangrupa/well-known-uris). This could be implemented in this recipe but we haven't merged it in yet. Change sets are welcome. +### Matrix Authentication Service (MAS) + +[MAS](https://element-hq.github.io/matrix-authentication-service/) is Element’s OAuth/OIDC-native auth service for Matrix: it handles login, tokens, and upstream IdPs while Synapse delegates authentication via `matrix_authentication_service`. + +**Enable the stack:** + +- In `.env`, uncomment `compose.mas.yml` (and `compose.mas-upstream.yml` plus upstream envs if you use an external IdP), and uncomment the `SECRET_MAS_*` version lines. +- `abra app secret generate YOURAPPDOMAIN` +- **Manually insert** the PEM RSA key for `SECRET_MAS_SIGNING_RSA_VERSION` (`generate=false` in `.env.sample`) — abra cannot generate that format; see the comment there (e.g. `openssl genrsa 2048` piped to `abra app secret insert`). +- `abra app cmd YOURAPPDOMAIN db ensure_mas_database` (once, creates the `mas` database in Postgres) +- `abra app deploy YOURAPPDOMAIN` + +**If you plan to migrate an existing homeserver with `syn2mas`:** deploy and configure MAS as above, but **leave `MAS_ENABLED=1` commented** until migration and cutover are done, so Synapse keeps using your current login path until you intentionally switch. You cannot use Synapse legacy OIDC/Keycloak SSO alongside MAS; plan IdP apps and envs accordingly. + +
+Migrating an existing server (syn2mas) + +Requires PostgreSQL on Synapse and a dedicated MAS database. Backup Postgres (and configs) before you start. Official background: [MAS migration guide](https://element-hq.github.io/matrix-authentication-service/setup/migration.html). + +1. **Prepare (Synapse still running):** With MAS in `COMPOSE_FILE` but **`MAS_ENABLED` still off**, deploy, then run checks from your machine: + ```bash + abra app cmd YOURAPPDOMAIN prepare_mas_migration + ``` + This fetches rendered `homeserver.yaml` into the MAS container, runs `syn2mas check`, then `migrate --dry-run` (the dry run rolls back MAS data at the end). The file stays in the MAS container until next restart, so you can repeat this step to provide the file for the actual migration. + +2. **Optional snapshot:** save a copy of the rendered config while `app` is up, e.g. `abra app run -t YOURAPPDOMAIN app cat /data/homeserver.yaml > homeserver.snapshot.yaml`. + +3. **Downtime — stop Synapse:** run on the **host** with Docker/Swarm access (not inside a container), e.g.: + ```bash + docker service scale _app=0 + ``` + Use the real service name from `docker service ls` (suffix `_app`). + +4. **Migration:** with MAS still running and Synapse at zero replicas, + ```bash + abra app run YOURAPPDOMAIN mas -- mas-cli syn2mas migrate \ + --config /etc/mas/config.yaml \ + --synapse-config /tmp/homeserver.yaml + ``` + +5. **Cutover:** in `.env`, set `MAS_ENABLED=1`, `PASSWORD_LOGIN_ENABLED=false`, remove legacy Keycloak/SSO envs, then `abra app deploy YOURAPPDOMAIN` (Synapse comes back with MAS delegation). `syn2mas` does not write to the Synapse database; if you abort before serving traffic through MAS, you can often drop and recreate the MAS DB and revert env. + +
+ ## Bridges For all Bridges: - Setting it up is a bit of a chicken/egg & chasing cats moment. From 7492e8bd4ef14c244b6f372cbb852a9c1ac03a51 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 8 Apr 2026 14:53:39 +0200 Subject: [PATCH 07/13] add mas healthcheck --- .env.sample | 1 - compose.mas.yml | 17 +++++++++++++++++ mas.config.yaml.tmpl | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index d4aca8d..921bc3f 100644 --- a/.env.sample +++ b/.env.sample @@ -79,7 +79,6 @@ ENABLE_REGISTRATION=false #SECRET_MAS_SIGNING_RSA_VERSION=v1 # generate=false #### MAS upstream OIDC provider (e.g. Authentik) -# See mas-authentik-and-roadmap.md for migration procedure. # Create a new OAuth2 app in your IdP with redirect URI: https:///upstream/callback/ #COMPOSE_FILE="$COMPOSE_FILE:compose.mas-upstream.yml" #MAS_UPSTREAM_PROVIDER_ID= # ULID, e.g. 01JSHPZHAXC50QBKH67MH33TNF — generate at https://www.ulidtools.com diff --git a/compose.mas.yml b/compose.mas.yml index 629eef1..64f7670 100644 --- a/compose.mas.yml +++ b/compose.mas.yml @@ -21,6 +21,23 @@ services: - mas_encryption - mas_synapse_shared - mas_signing_rsa + # Official image is distroless (no curl/wget); upstream suggests `mas-cli config check` for probes. + # See https://github.com/element-hq/matrix-authentication-service/issues/3741 — validates config, not HTTP. + # GET /health is still served (resource `health` in mas.config.yaml.tmpl) for probes from other images. + healthcheck: + test: + [ + "CMD", + "/usr/local/bin/mas-cli", + "--config", + "/etc/mas/config.yaml", + "config", + "check", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s deploy: restart_policy: condition: on-failure diff --git a/mas.config.yaml.tmpl b/mas.config.yaml.tmpl index 59a74e0..7a344b1 100644 --- a/mas.config.yaml.tmpl +++ b/mas.config.yaml.tmpl @@ -19,6 +19,8 @@ http: - name: graphql playground: false - name: assets + # https://element-hq.github.io/matrix-authentication-service/reference/configuration.html#httplisteners + - name: health binds: - address: "[::]:8080" From 625b0381f8f115dffd6fa7e8dac1d5d2da953ae5 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 Apr 2026 11:48:25 +0200 Subject: [PATCH 08/13] readd cleanup trap to mas migration script --- abra.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/abra.sh b/abra.sh index ff5b189..5eeb0e2 100644 --- a/abra.sh +++ b/abra.sh @@ -54,8 +54,8 @@ prepare_mas_migration () { --synapse-config "$syn_cfg" \ --dry-run || return 1 - # trap - EXIT - # cleanup_prepare_mas_migration + trap - EXIT + cleanup_prepare_mas_migration } set_admin () { From c71dc162cbf741ddd614726ac10aea73b38d3acb Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 Apr 2026 11:48:25 +0200 Subject: [PATCH 09/13] readd cleanup trap to mas migration script --- abra.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abra.sh b/abra.sh index 5eeb0e2..fa799ff 100644 --- a/abra.sh +++ b/abra.sh @@ -41,7 +41,7 @@ prepare_mas_migration () { fi echo "Copying into mas:/tmp" - abra app cp -C "$DOMAIN" "homeserver.yaml" "mas:/tmp" || return 1 + abra app cp "$DOMAIN" "homeserver.yaml" "mas:/tmp" || return 1 echo "Running mas-cli syn2mas check..." abra app run -t "$DOMAIN" mas -- mas-cli syn2mas check \ From 3f488167bc7af636f7cf18a69c8c607e0e2447bc Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 11 May 2026 13:18:01 +0200 Subject: [PATCH 10/13] chore: publish 7.1.0+v1.149.1 release --- compose.yml | 2 +- release/7.1.0+v1.149.1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 release/7.1.0+v1.149.1 diff --git a/compose.yml b/compose.yml index b027d2a..c4e6caa 100644 --- a/compose.yml +++ b/compose.yml @@ -108,7 +108,7 @@ services: restart_policy: condition: on-failure labels: - - "coop-cloud.${STACK_NAME}.version=7.0.2+v1.149.1" + - "coop-cloud.${STACK_NAME}.version=7.1.0+v1.149.1" - "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT}" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8008/health"] diff --git a/release/7.1.0+v1.149.1 b/release/7.1.0+v1.149.1 new file mode 100644 index 0000000..84cbdf6 --- /dev/null +++ b/release/7.1.0+v1.149.1 @@ -0,0 +1 @@ +added matrix-authentication-service as opt-in to the recipe, see readme for details \ No newline at end of file From d82d539424b434c05069d91bcb140dca73da4a52 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 11 May 2026 13:51:33 +0200 Subject: [PATCH 11/13] docs: update mad readme --- .env.sample | 2 +- README.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 921bc3f..e6bea9f 100644 --- a/.env.sample +++ b/.env.sample @@ -71,7 +71,7 @@ ENABLE_REGISTRATION=false ### Matrix Authentication Service (MAS) — Element X / OIDC-native auth #COMPOSE_FILE="$COMPOSE_FILE:compose.mas.yml" -#MAS_ENABLED=1 +#MAS_ENABLED=1 # !!! Leave commented if you plan to migrate an existing homeserver #PASSWORD_LOGIN_ENABLED=false #SECRET_MAS_ENCRYPTION_VERSION=v1 # length=64 # charset=hex #SECRET_MAS_SYNAPSE_SHARED_VERSION=v1 # length=64 # charset=hex diff --git a/README.md b/README.md index 86e04a0..4ac257d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ You'll need to deploy something like [this](https://git.autonomic.zone/ruangrupa [MAS](https://element-hq.github.io/matrix-authentication-service/) is Element’s OAuth/OIDC-native auth service for Matrix: it handles login, tokens, and upstream IdPs while Synapse delegates authentication via `matrix_authentication_service`. +> [!IMPORTANT] +> **If you plan to migrate an existing homeserver with `syn2mas`:** deploy and configure MAS as below, but **leave `MAS_ENABLED=1` commented** until migration and cutover are done, so Synapse keeps using your current login path until you intentionally switch. You cannot use Synapse legacy OIDC/Keycloak SSO alongside MAS; plan IdP apps and envs accordingly. + **Enable the stack:** - In `.env`, uncomment `compose.mas.yml` (and `compose.mas-upstream.yml` plus upstream envs if you use an external IdP), and uncomment the `SECRET_MAS_*` version lines. @@ -57,8 +60,6 @@ You'll need to deploy something like [this](https://git.autonomic.zone/ruangrupa - `abra app cmd YOURAPPDOMAIN db ensure_mas_database` (once, creates the `mas` database in Postgres) - `abra app deploy YOURAPPDOMAIN` -**If you plan to migrate an existing homeserver with `syn2mas`:** deploy and configure MAS as above, but **leave `MAS_ENABLED=1` commented** until migration and cutover are done, so Synapse keeps using your current login path until you intentionally switch. You cannot use Synapse legacy OIDC/Keycloak SSO alongside MAS; plan IdP apps and envs accordingly. -
Migrating an existing server (syn2mas) From cff6cfb001b4706eb280db700c7aeb31908e8c53 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 12 May 2026 21:46:13 +0200 Subject: [PATCH 12/13] fix minor bugs with mas integration --- .env.sample | 8 ++++---- README.md | 2 +- abra.sh | 28 ++++++++++++++++++++++++++-- compose.mas-upstream.yml | 6 +++--- mas.config.yaml.tmpl | 2 +- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.env.sample b/.env.sample index e6bea9f..9c0c782 100644 --- a/.env.sample +++ b/.env.sample @@ -73,9 +73,9 @@ ENABLE_REGISTRATION=false #COMPOSE_FILE="$COMPOSE_FILE:compose.mas.yml" #MAS_ENABLED=1 # !!! Leave commented if you plan to migrate an existing homeserver #PASSWORD_LOGIN_ENABLED=false -#SECRET_MAS_ENCRYPTION_VERSION=v1 # length=64 # charset=hex -#SECRET_MAS_SYNAPSE_SHARED_VERSION=v1 # length=64 # charset=hex -# PEM private key: abra cannot generate this format — insert only (e.g. openssl genrsa 2048 | abra app secret insert …) +#SECRET_MAS_ENCRYPTION_VERSION=v1 # length=64 charset=hex +#SECRET_MAS_SYNAPSE_SHARED_VERSION=v1 # length=64 charset=hex +# PEM private key: abra cannot generate this format — use `abra app cmd -l YOURAPPDOMAIN generate_mas_signing_rsa` #SECRET_MAS_SIGNING_RSA_VERSION=v1 # generate=false #### MAS upstream OIDC provider (e.g. Authentik) @@ -87,7 +87,7 @@ ENABLE_REGISTRATION=false #MAS_UPSTREAM_HUMAN_NAME=Authentik # For migration from previous direct Keycloud-style config: set to oidc- so syn2mas maps users correctly. #MAS_UPSTREAM_SYNAPSE_IDP_ID= -#SECRET_MAS_UPSTREAM_CLIENT_SECRET_VERSION=v1 +#SECRET_MAS_UPSTREAM_CLIENT_VERSION=v1 ### Shared secret auth (bridges / automation) diff --git a/README.md b/README.md index 4ac257d..ed026b4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ You'll need to deploy something like [this](https://git.autonomic.zone/ruangrupa - In `.env`, uncomment `compose.mas.yml` (and `compose.mas-upstream.yml` plus upstream envs if you use an external IdP), and uncomment the `SECRET_MAS_*` version lines. - `abra app secret generate YOURAPPDOMAIN` -- **Manually insert** the PEM RSA key for `SECRET_MAS_SIGNING_RSA_VERSION` (`generate=false` in `.env.sample`) — abra cannot generate that format; see the comment there (e.g. `openssl genrsa 2048` piped to `abra app secret insert`). +- `abra app cmd -l YOURAPPDOMAIN generate_mas_signing_rsa` — generates and inserts the PEM RSA key for `SECRET_MAS_SIGNING_RSA_VERSION`. Requires `openssl` on the local machine. - `abra app cmd YOURAPPDOMAIN db ensure_mas_database` (once, creates the `mas` database in Postgres) - `abra app deploy YOURAPPDOMAIN` diff --git a/abra.sh b/abra.sh index fa799ff..3d9e95a 100644 --- a/abra.sh +++ b/abra.sh @@ -8,7 +8,7 @@ export TELEGRAM_BRIDGE_YAML_VERSION=v6 export NGINX_CONFIG_VERSION=v13 export WK_SERVER_VERSION=v1 export WK_CLIENT_VERSION=v2 -export MAS_CONFIG_VERSION=v1 +export MAS_CONFIG_VERSION=v2 export PG_BACKUP_VERSION=v2 export ADMIN_CONFIG_VERSION=v1 @@ -19,9 +19,33 @@ ensure_mas_database () { fi } +# Generate a PEM RSA private key and insert it as the MAS signing secret. +# `abra app secret generate` can only produce random hex/charset strings, so this +# secret is marked `generate=false` in .env.sample and handled here instead. +generate_mas_signing_rsa() { + if ! command -v openssl &> /dev/null; then + echo "openssl is required on your local machine to generate the MAS signing key." + echo "It could not be found in your PATH, please install openssl to proceed." + exit 1 + fi + + KEY=$(openssl genrsa 2048 2>/dev/null) + if [ -z "$KEY" ]; then + echo "Failed to generate RSA private key with openssl." + exit 1 + fi + + if printf '%s\n' "$KEY" | abra app secret insert -C "$APP_NAME" mas_signing_rsa v1; then + echo "MAS signing RSA key generated and inserted as v1." + else + echo "Failed to insert MAS signing RSA key." + exit 1 + fi +} + # Local helper: fetch homeserver.yaml from app, push to mas, then syn2mas check + dry-run. prepare_mas_migration () { - local hs_local syn_cfg + local syn_cfg syn_cfg=/tmp/homeserver.yaml diff --git a/compose.mas-upstream.yml b/compose.mas-upstream.yml index 317761c..fbb86db 100644 --- a/compose.mas-upstream.yml +++ b/compose.mas-upstream.yml @@ -13,9 +13,9 @@ services: - MAS_UPSTREAM_HUMAN_NAME - MAS_UPSTREAM_SYNAPSE_IDP_ID secrets: - - mas_upstream_client_secret + - mas_upstream_client secrets: - mas_upstream_client_secret: + mas_upstream_client: external: true - name: ${STACK_NAME}_mas_upstream_client_secret_${SECRET_MAS_UPSTREAM_CLIENT_SECRET_VERSION} + name: ${STACK_NAME}_mas_upstream_client_${SECRET_MAS_UPSTREAM_CLIENT_VERSION} diff --git a/mas.config.yaml.tmpl b/mas.config.yaml.tmpl index 7a344b1..7766856 100644 --- a/mas.config.yaml.tmpl +++ b/mas.config.yaml.tmpl @@ -57,7 +57,7 @@ upstream_oauth2: human_name: {{ or (env "MAS_UPSTREAM_HUMAN_NAME") "SSO" }} issuer: {{ env "MAS_UPSTREAM_ISSUER" }} client_id: {{ env "MAS_UPSTREAM_CLIENT_ID" }} - client_secret_file: /run/secrets/mas_upstream_client_secret + client_secret_file: /run/secrets/mas_upstream_client token_endpoint_auth_method: client_secret_basic scope: "openid profile email" claims_imports: From d75ca4f11f23265f3550191c91193978dc69fdb2 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 12 May 2026 22:28:22 +0200 Subject: [PATCH 13/13] chore: publish 7.1.1+v1.149.1 release Co-authored-by: Cursor --- .env.sample | 2 +- README.md | 6 ++---- abra.sh | 22 ++++++++++++++++++++++ compose.yml | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.env.sample b/.env.sample index 9c0c782..4bb0c9e 100644 --- a/.env.sample +++ b/.env.sample @@ -71,7 +71,7 @@ ENABLE_REGISTRATION=false ### Matrix Authentication Service (MAS) — Element X / OIDC-native auth #COMPOSE_FILE="$COMPOSE_FILE:compose.mas.yml" -#MAS_ENABLED=1 # !!! Leave commented if you plan to migrate an existing homeserver +#MAS_ENABLED=1 # Leave commented if you plan to migrate an existing homeserver #PASSWORD_LOGIN_ENABLED=false #SECRET_MAS_ENCRYPTION_VERSION=v1 # length=64 charset=hex #SECRET_MAS_SYNAPSE_SHARED_VERSION=v1 # length=64 charset=hex diff --git a/README.md b/README.md index ed026b4..bb7e781 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,9 @@ Requires PostgreSQL on Synapse and a dedicated MAS database. Backup Postgres (an ``` Use the real service name from `docker service ls` (suffix `_app`). -4. **Migration:** with MAS still running and Synapse at zero replicas, +4. **Migration:** with MAS still running and Synapse at zero replicas, run `run_mas_migration` from your machine. The homeserver snapshot at `/tmp/homeserver.yaml` in `mas` must still be present from step 1. ```bash - abra app run YOURAPPDOMAIN mas -- mas-cli syn2mas migrate \ - --config /etc/mas/config.yaml \ - --synapse-config /tmp/homeserver.yaml + abra app cmd YOURAPPDOMAIN run_mas_migration ``` 5. **Cutover:** in `.env`, set `MAS_ENABLED=1`, `PASSWORD_LOGIN_ENABLED=false`, remove legacy Keycloak/SSO envs, then `abra app deploy YOURAPPDOMAIN` (Synapse comes back with MAS delegation). `syn2mas` does not write to the Synapse database; if you abort before serving traffic through MAS, you can often drop and recreate the MAS DB and revert env. diff --git a/abra.sh b/abra.sh index 3d9e95a..263e352 100644 --- a/abra.sh +++ b/abra.sh @@ -80,6 +80,28 @@ prepare_mas_migration () { trap - EXIT cleanup_prepare_mas_migration + + echo "" + echo "=== Next migration step: stop Synapse (downtime) ===" + echo "Run on a host whose Docker CLI targets this Swarm (same machine you use for 'abra app deploy')." + if [ -n "${STACK_NAME:-}" ]; then + echo " docker service scale ${STACK_NAME}_app=0" + else + echo "STACK_NAME is not set here; resolve the Synapse service name with 'docker service ls' on that host, then:" + echo "docker service scale _app=0" + fi +} + +# Run syn2mas migrate for real (writes MAS data). Run from your operator machine as MAS image is distroless. +# Requires /tmp/homeserver.yaml in the mas container (e.g. from prepare_mas_migration) and +# Synapse scaled down before migrate. +run_mas_migration () { + local syn_cfg=/tmp/homeserver.yaml + + echo "Running mas-cli syn2mas migrate in mas via abra app run..." + abra app run -t "$DOMAIN" mas -- mas-cli syn2mas migrate \ + --config /etc/mas/config.yaml \ + --synapse-config "$syn_cfg" } set_admin () { diff --git a/compose.yml b/compose.yml index c4e6caa..c3303e2 100644 --- a/compose.yml +++ b/compose.yml @@ -108,7 +108,7 @@ services: restart_policy: condition: on-failure labels: - - "coop-cloud.${STACK_NAME}.version=7.1.0+v1.149.1" + - "coop-cloud.${STACK_NAME}.version=7.1.1+v1.149.1" - "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT}" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8008/health"]