#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/functions.sh" resolve_paths require_env require_command "yq" require_command "openssl" require_docker # ============================================================================ # PHASE 1: Infrastructure Setup # ============================================================================ resolve_volume_path mkdir -p "${VOLUME_PATH}/synapse" "${VOLUME_PATH}/mas" "${VOLUME_PATH}/postgres" "${VOLUME_PATH}/proxy" if [[ "${WORKER_COUNT}" -gt 0 ]]; then mkdir -p "${VOLUME_PATH}/webapp" fi log_info "Initializing PostgreSQL..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --wait matrix-db # Wait for database readiness for ATTEMPT in $(seq 1 60); do if docker compose -f "$COMPOSE_FILE" exec -T matrix-db psql -U "$PG_USER" -c "SELECT 1;" > /dev/null 2>&1; then break fi [[ $ATTEMPT -eq 30 ]] && { log_error "Database timeout"; exit 1; } sleep 2 done # Database initialization INIT_SQL=$(cat < /dev/null # ============================================================================ # PHASE 2: Configuration Generation # ============================================================================ log_info "Generating Synapse configuration..." docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/synapse:/data" \ -e SYNAPSE_SERVER_NAME="$HOMESERVER_FQDN" \ -e SYNAPSE_REPORT_STATS=no \ ghcr.io/element-hq/synapse:latest generate || { log_error "Synapse config generation failed"; exit 1; } SYNAPSE_CFG="${VOLUME_PATH}/synapse/homeserver.yaml" fix_ownership "${VOLUME_PATH}/synapse" yq -y -i '.database.name = "psycopg2"' "$SYNAPSE_CFG" yq -y -i ".database.args.user = \"${PG_USER_MATRIX}\"" "$SYNAPSE_CFG" yq -y -i ".database.args.password = \"${PG_PASSWORD_MATRIX}\"" "$SYNAPSE_CFG" yq -y -i ".database.args.database = \"${PG_DB_MATRIX}\"" "$SYNAPSE_CFG" yq -y -i ".database.args.host = \"matrix-db\"" "$SYNAPSE_CFG" yq -y -i ".database.args.port = 5432" "$SYNAPSE_CFG" yq -y -i ".database.args.sslmode = \"prefer\"" "$SYNAPSE_CFG" yq -y -i ".public_baseurl = \"https://${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG" yq -y -i ".server_name = \"${HOMESERVER_FQDN}\"" "$SYNAPSE_CFG" log_info "Generating MAS configuration..." docker run -it --rm --network matrix_network \ -v "${VOLUME_PATH}/mas:/data" \ ghcr.io/element-hq/matrix-authentication-service:latest config generate \ | grep -v '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' > "${VOLUME_PATH}/mas/config.yaml" || { log_error "Mas config generation failed"; exit 1; } MAS_CFG="${VOLUME_PATH}/mas/config.yaml" if ! yq '.clients' "$MAS_CFG" > /dev/null 2>&1; then yq -y -i '.clients = []' "$MAS_CFG" fi yq -y -i ".public_base_url = \"https://${MAS_FQDN}\"" "$MAS_CFG" yq -y -i ".http.listen = \"0.0.0.0:8000\"" "$MAS_CFG" yq -y -i ".database.uri = \"postgres://${PG_USER_MAS}:${PG_PASSWORD_MAS}@matrix-db:5432/auth?sslmode=prefer\"" "$MAS_CFG" fix_ownership "${VOLUME_PATH}/mas" # ============================================================================ # PHASE 3: OIDC Linking # ============================================================================ log_info "Linking Synapse and MAS via OIDC..." CLIENT_ID="synapse-client" CLIENT_SECRET=$(openssl rand -hex 32) ADMIN_SECRET=$(openssl rand -hex 32) MATRIX_SECRET=$(openssl rand -hex 32) MATRIX_ENDPOINT="http://synapse:8008" EXISTING_CLIENT=$(yq '.clients[] | select(.client_id == "'"${CLIENT_ID}"'")' "$MAS_CFG" 2>/dev/null || echo "") if [[ -z "$EXISTING_CLIENT" ]]; then CLIENT_OBJ="{ \"client_id\": \"${CLIENT_ID}\", \"client_auth_method\": \"client_secret_basic\", \"client_secret\": \"${CLIENT_SECRET}\", \"redirect_uris\": [\"https://${HOMESERVER_FQDN}/_synapse/client/oidc/callback\"], \"grant_types\": [\"authorization_code\", \"refresh_token\"], \"response_types\": [\"code\"], \"scopes\": [\"openid\", \"profile\", \"email\"], \"application_type\": \"web\" }" yq -y -i ".clients += [$CLIENT_OBJ]" "$MAS_CFG" || { log_error "Failed to register Synapse client in MAS"; exit 1; } log_info " -> Registered Synapse as OIDC client in MAS" else log_info " -> Synapse client already registered in MAS, skipping" fi # Ensure the matrix section exists if ! yq '.matrix' "$MAS_CFG" > /dev/null 2>&1; then yq -y -i '.matrix = {}' "$MAS_CFG" fi yq -y -i ".matrix.homeserver = \"${HOMESERVER_FQDN}\"" "$MAS_CFG" yq -y -i ".matrix.secret = \"${MATRIX_SECRET}\"" "$MAS_CFG" yq -y -i ".matrix.endpoint = \"${MATRIX_ENDPOINT}\"" "$MAS_CFG" ISSUER_URL="https://${MAS_FQDN}" fix_ownership "$SYNAPSE_CFG" if ! yq '.oidc_providers' "$SYNAPSE_CFG" 2>/dev/null | grep -q "idp_id"; then yq -y -i ".oidc_providers += [{ \"idp_id\": \"mas\", \"idp_name\": \"Matrix Authentication Service\", \"idp_brand\": \"mas\", \"issuer\": \"${ISSUER_URL}\", \"client_id\": \"${CLIENT_ID}\", \"client_secret\": \"${CLIENT_SECRET}\", \"shared_secret\": \"${MATRIX_SECRET}\", \"scopes\": [\"openid\", \"profile\"], \"skip_verification\": false }]" "$SYNAPSE_CFG" || { log_error "Failed to inject OIDC config"; exit 1; } fi if [[ "${INCLUDE_LIVEKIT:-FALSE}" == "TRUE" ]]; then JWT_URL="https://${JWT_FQDN:-jwt.${DOMAIN}}" yq -y -i ".livekit.url = \"$JWT_URL\"" "$SYNAPSE_CFG" fi log_info "Config generation complete"