From 34fbb07af8de311a4d316325bcf574341f6be5dd Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Tue, 30 Sep 2025 22:17:49 +0200 Subject: Optimize the dendrite pipeline --- whiterabbit.sh | 208 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 179 insertions(+), 29 deletions(-) diff --git a/whiterabbit.sh b/whiterabbit.sh index e6e3b58..f8eb350 100755 --- a/whiterabbit.sh +++ b/whiterabbit.sh @@ -3,6 +3,13 @@ set -eu BASE_DIR="/opt/matrix" +# Detect docker compose binary +DOCKER_COMPOSE=$(command -v docker-compose || command -v "docker" compose || true) +if [ -z "$DOCKER_COMPOSE" ]; then + echo "[ERROR] docker-compose or docker compose not found" >&2 + exit 1 +fi + usage() { cat <<'EOF' whiterabbit: auto-configure Matrix homeservers (Dendrite or Synapse) @@ -20,11 +27,19 @@ Notes: • All Matrix servers share the same Coturn (TURN domain/secret). • TLS certificates are issued automatically using Let's Encrypt. • Requirements: docker, docker-compose, nginx, certbot, coturn. - EOF exit 1 } +require_nonempty() { + VAR_NAME="$1" + VAR_VALUE="$2" + if [ -z "$VAR_VALUE" ]; then + echo "[ERROR] $VAR_NAME cannot be empty" >&2 + exit 1 + fi +} + list_servers() { echo "=== Installed servers ===" ls -1 "$BASE_DIR" 2>/dev/null || echo "No servers installed" @@ -34,12 +49,19 @@ install_turn() { echo "=== Installing global Coturn ===" printf "TURN domain (eg. turn.example.com): " read -r TURN_DOMAIN + require_nonempty TURN_DOMAIN "$TURN_DOMAIN" + printf "TURN IP (eg. 127.0.0.1): " read -r TURN_SERVER_IP + require_nonempty TURN_SERVER_IP "$TURN_SERVER_IP" + printf "Listening device (eg. eth0): " read -r TURN_LISTENING_DEVICE + require_nonempty TURN_LISTENING_DEVICE "$TURN_LISTENING_DEVICE" + printf "TURN shared secret: " read -r TURN_SECRET + require_nonempty TURN_SECRET "$TURN_SECRET" mkdir -p /etc/turn cat < /etc/turn/turnserver.conf @@ -61,30 +83,53 @@ EOF echo "$TURN_DOMAIN" > /etc/turn/domain echo "$TURN_SECRET" > /etc/turn/secret - systemctl enable --now coturn || true + if command -v systemctl >/dev/null; then + systemctl enable --now coturn || true + else + service coturn start || true + fi + echo "[OK] Global Coturn configured ($TURN_DOMAIN)" } common_prompts() { printf "Matrix domain (eg. matrix.example.com): " read -r MATRIX_DOMAIN + require_nonempty MATRIX_DOMAIN "$MATRIX_DOMAIN" + printf "Let's Encrypt certificate email: " read -r EMAIL + require_nonempty EMAIL "$EMAIL" + printf "Postgres secret: " read -r DB_PASS + require_nonempty DB_PASS "$DB_PASS" + printf "registration_shared_secret: " read -r REG_SECRET + require_nonempty REG_SECRET "$REG_SECRET" + printf "MATRIX server IP (eg. 127.0.0.1): " read -r MATRIX_SERVER_IP + require_nonempty MATRIX_SERVER_IP "$MATRIX_SERVER_IP" TURN_DOMAIN=$(cat /etc/turn/domain) TURN_SECRET=$(cat /etc/turn/secret) + + # Generate unique port numbers from hash of domain + HASH=$(printf "%s" "$MATRIX_DOMAIN" | cksum | cut -d ' ' -f1) + PORT_BASE=$(( (HASH % 1000) + 8000 )) + PORT_HTTP=$PORT_BASE + PORT_HTTPS=$((PORT_BASE+1)) } configure_nginx() { DOMAIN="$1" SERVICE_PORT="$2" + mkdir -p /etc/nginx/sites-available + mkdir -p /etc/nginx/sites-enabled + cat < "/etc/nginx/sites-available/$DOMAIN" server { listen 80; @@ -112,13 +157,25 @@ server { EOF ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" - nginx -t && systemctl reload nginx - - certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" + nginx -t || { echo "[ERROR] Invalid nginx config"; exit 1; } + if command -v systemctl >/dev/null; then + systemctl reload nginx + else + nginx -s reload + fi + + if command -v certbot >/dev/null; then + certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" || { + echo "[WARN] Certbot failed for $DOMAIN" + } + else + echo "[WARN] Certbot not installed, skipping TLS setup" + fi echo "[OK] Nginx and SSL certificate configured for $DOMAIN" } + install_dendrite() { echo "=== Installing Matrix Dendrite ===" common_prompts @@ -136,63 +193,147 @@ POSTGRES_DB=dendrite MONOLITH_HOSTNAME=monolith MONOLITH_IMAGE=matrixdotorg/dendrite-monolith:latest -MONOLITH_PORT_HTTP=8008 -MONOLITH_PORT_HTTPS=8448 +MONOLITH_PORT_HTTP=$PORT_HTTP +MONOLITH_PORT_HTTPS=$PORT_HTTPS EOF - cat <<'EOF' > compose.yml +cat < compose.yml services: postgres: - image: postgres:${POSTGRES_VERSION:-15-alpine} + hostname: \${POSTGRES_HOSTNAME:-postgres} + image: postgres:\${POSTGRES_VERSION:-15-alpine} restart: always volumes: - ./dendrite_postgres_data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_DB: ${POSTGRES_DB} - networks: [ internal ] + POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-$DB_PASS} + POSTGRES_USER: \${POSTGRES_USER:-dendrite} + POSTGRES_DB: \${POSTGRES_DB:-dendrite} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-dendrite}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - internal + monolith: - image: ${MONOLITH_IMAGE} + hostname: \${MONOLITH_HOSTNAME:-monolith} + image: \${MONOLITH_IMAGE:-matrixdotorg/dendrite-monolith:latest} ports: - - ${MONOLITH_PORT_HTTP}:8008 - - ${MONOLITH_PORT_HTTPS}:8448 + - \${MONOLITH_PORT_HTTP:-8008}:8008 + - \${MONOLITH_PORT_HTTPS:-8448}:8448 volumes: - ./config:/etc/dendrite - ./dendrite_media:/var/dendrite/media - depends_on: [ postgres ] - networks: [ internal ] + - ./dendrite_jetstream:/var/dendrite/jetstream + - ./dendrite_search_index:/var/dendrite/searchindex + - ./:/mnt + depends_on: + postgres: + condition: service_healthy + networks: + - internal restart: unless-stopped + networks: internal: attachable: true +volumes: + dendrite_postgres_data: + dendrite_media: + dendrite_jetstream: + dendrite_search_index: EOF - cat < config/dendrite.yaml + # --- Plik konfiguracji Dendrite --- + +cat < config/dendrite.yaml version: 2 global: server_name: $MATRIX_DOMAIN private_key: /mnt/matrix_key.pem + tls_cert: /mnt/server.crt + tls_key: /mnt/server.key + old_private_keys: [] + key_validity_period: 168h0m0s database: connection_string: postgresql://dendrite:$DB_PASS@postgres/dendrite?sslmode=disable + max_open_conns: 90 + max_idle_conns: 5 + conn_max_lifetime: -1 well_known_server_name: "$MATRIX_DOMAIN:443" well_known_client_name: "https://$MATRIX_DOMAIN" - + cache: + max_size_estimated: 1gb + max_age: 1h + trusted_third_party_id_servers: + - matrix.org + - vector.im + disable_federation: false + report_stats: + enabled: false client_api: registration_disabled: true registration_shared_secret: "$REG_SECRET" - turn: turn_user_lifetime: "5m" turn_uris: - turn:$TURN_DOMAIN?transport=udp - turn:$TURN_DOMAIN?transport=tcp turn_shared_secret: "$TURN_SECRET" +federation_api: + send_max_retries: 16 + disable_tls_validation: false +media_api: + base_path: ./media_store + max_file_size_bytes: 10485760 +sync_api: + search: + enabled: false + index_path: "./searchindex" +user_api: + auto_join_rooms: + - "#main:$MATRIX_DOMAIN" +logging: + - type: std + level: info + - type: file + level: info + params: + path: ./logs +jetstream: + addresses: [] + disable_tls_validation: false + storage_path: ./ + topic_prefix: Dendrite +metrics: + enabled: false + basic_auth: + username: metrics + password: metrics +dns_cache: + enabled: false + cache_size: 256 + cache_lifetime: "5m" EOF - docker compose up -d + # Generate necessary project directory + $DOCKER_COMPOSE up -d - configure_nginx "$MATRIX_DOMAIN" "8008" + $DOCKER_COMPOSE down + + # --- Generowanie kluczy Dendrite + TLS jeśli nie istnieją --- + echo "[INFO] Generating Dendrite keys..." + +docker run --rm --entrypoint="" -v $(pwd):/mnt matrixdotorg/dendrite-monolith:latest /usr/bin/generate-keys -private-key /mnt/matrix_key.pem -tls-cert /mnt/server.crt -tls-key /mnt/server.key + echo "[OK] Keys generated in $PWD" + + # --- Start kontenerów --- + $DOCKER_COMPOSE up -d + + # --- Konfiguracja Nginx + SSL/TLS --- + configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP" echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running." } @@ -225,13 +366,13 @@ services: SYNAPSE_REPORT_STATS: "yes" POSTGRES_PASSWORD: $DB_PASS ports: - - 8008:8008 - - 8448:8448 + - $PORT_HTTP:8008 + - $PORT_HTTPS:8448 volumes: - ./data:/data EOF - docker compose up -d + $DOCKER_COMPOSE up -d cat < data/homeserver-extra.yaml turn_uris: [ "turn:$TURN_DOMAIN?transport=udp", "turn:$TURN_DOMAIN?transport=tcp" ] @@ -239,7 +380,7 @@ turn_shared_secret: "$TURN_SECRET" turn_user_lifetime: "5m" EOF - configure_nginx "$MATRIX_DOMAIN" "8008" + configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP" echo "[OK] Synapse server ($MATRIX_DOMAIN) is running." } @@ -247,10 +388,19 @@ EOF remove_server() { DOMAIN="$1" [ -z "$DOMAIN" ] && usage + SERVER_DIR="$BASE_DIR/$DOMAIN" - rm -rf "$SERVER_DIR" + case "$DOMAIN" in + ""|"/"|".") echo "[ERROR] Refusing to delete dangerous path ($DOMAIN)"; exit 1 ;; + esac + + if [ -d "$SERVER_DIR" ]; then + (cd "$SERVER_DIR" && $DOCKER_COMPOSE down -v) || true + rm -rf "$SERVER_DIR" + fi + rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" - nginx -t && systemctl reload nginx + nginx -t && (systemctl reload nginx 2>/dev/null || nginx -s reload) echo "[OK] Server $DOMAIN removed." } -- cgit v1.2.3