From c35fd32d1a3c9bdf91e2091b05741978606a3900 Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Thu, 25 Sep 2025 23:21:00 +0200 Subject: Add makefile and enable support for building on arch based distributions Organize script into modular functions Add manpages Signed-off-by: Filip Wandzio --- Makefile | 15 +++ PKGBUILD | 15 +++ README | 1 + readme.txt | 1 - whiterabbit.1 | 51 ++++++++ whiterabbit.sh | 367 ++++++++++++++++++++++++++++++++------------------------- 6 files changed, 287 insertions(+), 163 deletions(-) create mode 100644 Makefile create mode 100644 PKGBUILD create mode 100644 README delete mode 100644 readme.txt create mode 100644 whiterabbit.1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fe1c42b --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +MANDIR ?= $(PREFIX)/share/man/man1 + +install: + @echo "Installing whiterabbit..." + install -Dm755 whiterabbit.sh $(BINDIR)/whiterabbit + install -Dm644 whiterabbit.1 $(MANDIR)/whiterabbit.1 + @echo "Installed to $(BINDIR)/whiterabbit and man page to $(MANDIR)/whiterabbit.1" + +uninstall: + @echo "Removing whiterabbit..." + rm -f $(BINDIR)/whiterabbit + rm -f $(MANDIR)/whiterabbit.1 + @echo "Removed whiterabbit and its man page" diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..c1cd771 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,15 @@ +pkgname=whiterabbit +pkgver=1.0.0 +pkgrel=1 +pkgdesc="Auto-configure Matrix homeservers (Dendrite/Synapse) with Coturn and Nginx/Let's Encrypt" +arch=('x86_64') +url="https://example.com/whiterabbit" +license=('MIT') +depends=('docker' 'docker-compose' 'nginx' 'certbot' 'coturn' 'bash') +source=("whiterabbit.sh") +sha256sums=('SKIP') + +package() { + install -Dm755 "$srcdir/whiterabbit.sh" "$pkgdir/usr/bin/whiterabbit" +} + diff --git a/README b/README new file mode 100644 index 0000000..2ecdd73 --- /dev/null +++ b/README @@ -0,0 +1 @@ +Simple shell script allowing sysadmins to easily spawn mutiple matrix dendrite monolith instances. diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 2ecdd73..0000000 --- a/readme.txt +++ /dev/null @@ -1 +0,0 @@ -Simple shell script allowing sysadmins to easily spawn mutiple matrix dendrite monolith instances. diff --git a/whiterabbit.1 b/whiterabbit.1 new file mode 100644 index 0000000..e02030f --- /dev/null +++ b/whiterabbit.1 @@ -0,0 +1,51 @@ +.\" Manpage for whiterabbit +.TH WHITERABBIT 1 "2025-09-25" "1.0.0" "whiterabbit manual" +.SH NAME +whiterabbit \- auto-configure Matrix homeservers (Dendrite or Synapse) +.SH SYNOPSIS +.B whiterabbit +[\fIturn\fR|\fIdendrite\fR|\fIsynapse\fR|\fIlist\fR|\fIremove \fR] +.SH DESCRIPTION +whiterabbit sets up Matrix homeservers with a global Coturn server and Nginx/Let's Encrypt support. + +Main commands: +.TP +.B turn +Install or configure the global Coturn server. +.TP +.B dendrite +Add a new Matrix Dendrite server. +.TP +.B synapse +Add a new Matrix Synapse server. +.TP +.B list +List all configured Matrix servers. +.TP +.B remove +Remove a Matrix server by domain. + +.SH EXAMPLES +.TP +whiterabbit turn +Set up the global Coturn server. +.TP +whiterabbit dendrite +Add a Dendrite server with TLS and Nginx. +.TP +whiterabbit synapse +Add a Synapse server with TLS and Nginx. +.TP +whiterabbit list +Show all servers. +.TP +whiterabbit remove matrix.example.com +Remove a server. + +.SH NOTES +All Matrix servers share the same Coturn instance. +TLS certificates are issued automatically via Let's Encrypt. +Requires docker, docker-compose, nginx, certbot, and coturn. + +.SH AUTHOR +Filip Wandzio diff --git a/whiterabbit.sh b/whiterabbit.sh index e68351f..e6e3b58 100755 --- a/whiterabbit.sh +++ b/whiterabbit.sh @@ -1,104 +1,185 @@ -#!/usr/bin/sh -set -euo pipefail - -echo "=== Matrix Dendrite + Coturn auto-setup (Docker + standalone Coturn) ===" - -# ---- User Input ---- -read -rp "Matrix domain (eg. matrix.example.com): " MATRIX_DOMAIN -read -rp "TURN domain(eg. turn.example.com): " TURN_DOMAIN -read -rp "Let's Encrypt certificate email: " EMAIL -read -rp "Postgres secret: " DB_PASS -read -rp "registration_shared_secret: " REG_SECRET -read -rp "TURN shared secret: " TURN_SECRET -read -rp "TURN IP (eg. 127.0.0.1): " TURN_SERVER_IP -read -rp "MATRIX server IP (eg. 127.0.0.1): " MATRIX_SERVER_IP -read -rp "Listening device (eg. eth0): " TURN_LISTENING_DEVICE - -INSTALL_DIR="/opt/matrix/$MATRIX_DOMAIN" -mkdir -p "$INSTALL_DIR/config" -cd "$INSTALL_DIR" - -cat < .env -# PostgreSQL +#!/bin/sh +set -eu + +BASE_DIR="/opt/matrix" + +usage() { + cat <<'EOF' +whiterabbit: auto-configure Matrix homeservers (Dendrite or Synapse) +with a global Coturn instance and Nginx + Let's Encrypt proxy. + +Main actions: + turn Install or configure the global Coturn server + dendrite Add a new Matrix server (Dendrite) + synapse Add a new Matrix server (Synapse) + list Show all configured Matrix servers + remove Remove a Matrix server by domain + +Notes: + • Coturn should be installed only once using 'turn'. + • 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 +} + +list_servers() { + echo "=== Installed servers ===" + ls -1 "$BASE_DIR" 2>/dev/null || echo "No servers installed" +} + +install_turn() { + echo "=== Installing global Coturn ===" + printf "TURN domain (eg. turn.example.com): " + read -r TURN_DOMAIN + printf "TURN IP (eg. 127.0.0.1): " + read -r TURN_SERVER_IP + printf "Listening device (eg. eth0): " + read -r TURN_LISTENING_DEVICE + printf "TURN shared secret: " + read -r TURN_SECRET + + mkdir -p /etc/turn + cat < /etc/turn/turnserver.conf +listening-device=$TURN_LISTENING_DEVICE +listening-port=3478 +tls-listening-port=5349 +listening-ip=$TURN_SERVER_IP +min-port=49152 +max-port=65535 +use-auth-secret +static-auth-secret=$TURN_SECRET +realm=$TURN_DOMAIN +syslog +no-rfc5780 +no-stun-backward-compatibility +response-origin-only-with-rfc5780 +EOF + + echo "$TURN_DOMAIN" > /etc/turn/domain + echo "$TURN_SECRET" > /etc/turn/secret + + systemctl enable --now coturn || true + echo "[OK] Global Coturn configured ($TURN_DOMAIN)" +} + +common_prompts() { + printf "Matrix domain (eg. matrix.example.com): " + read -r MATRIX_DOMAIN + printf "Let's Encrypt certificate email: " + read -r EMAIL + printf "Postgres secret: " + read -r DB_PASS + printf "registration_shared_secret: " + read -r REG_SECRET + printf "MATRIX server IP (eg. 127.0.0.1): " + read -r MATRIX_SERVER_IP + + TURN_DOMAIN=$(cat /etc/turn/domain) + TURN_SECRET=$(cat /etc/turn/secret) +} + +configure_nginx() { + DOMAIN="$1" + SERVICE_PORT="$2" + + cat < "/etc/nginx/sites-available/$DOMAIN" +server { + listen 80; + server_name $DOMAIN; + + location /.well-known/matrix/server { + default_type application/json; + return 200 '{ "m.server": "$DOMAIN:443" }'; + } + + location /.well-known/matrix/client { + default_type application/json; + return 200 '{ + "m.homeserver": { "base_url": "https://$DOMAIN" }, + "m.identity_server": { "base_url": "https://vector.im" } + }'; + } + + location / { + proxy_pass http://127.0.0.1:$SERVICE_PORT; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-For \$remote_addr; + } +} +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" + + echo "[OK] Nginx and SSL certificate configured for $DOMAIN" +} + +install_dendrite() { + echo "=== Installing Matrix Dendrite ===" + common_prompts + + INSTALL_DIR="$BASE_DIR/$MATRIX_DOMAIN" + mkdir -p "$INSTALL_DIR/config" + cd "$INSTALL_DIR" || exit 1 + + cat < .env POSTGRES_HOSTNAME=postgres POSTGRES_VERSION=15-alpine POSTGRES_USER=dendrite POSTGRES_PASSWORD=$DB_PASS POSTGRES_DB=dendrite -# Monolith MONOLITH_HOSTNAME=monolith MONOLITH_IMAGE=matrixdotorg/dendrite-monolith:latest MONOLITH_PORT_HTTP=8008 MONOLITH_PORT_HTTPS=8448 EOF -cat <<'EOF' > compose.yml + cat <<'EOF' > compose.yml services: postgres: - 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:-itsasecret} - 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 + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_DB: ${POSTGRES_DB} + networks: [ internal ] monolith: - hostname: ${MONOLITH_HOSTNAME:-monolith} - image: ${MONOLITH_IMAGE:-matrixdotorg/dendrite-monolith:latest} + image: ${MONOLITH_IMAGE} ports: - - ${MONOLITH_PORT_HTTP:-8008}:8008 - - ${MONOLITH_PORT_HTTPS:-8448}:8448 + - ${MONOLITH_PORT_HTTP}:8008 + - ${MONOLITH_PORT_HTTPS}:8448 volumes: - ./config:/etc/dendrite - ./dendrite_media:/var/dendrite/media - - ./dendrite_jetstream:/var/dendrite/jetstream - - ./dendrite_search_index:/var/dendrite/searchindex - - ./:/mnt - depends_on: - postgres: - condition: service_healthy - networks: - - internal + depends_on: [ postgres ] + 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 + cat < config/dendrite.yaml version: 2 global: server_name: $MATRIX_DOMAIN private_key: /mnt/matrix_key.pem - 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" client_api: registration_disabled: true - guests_disabled: true registration_shared_secret: "$REG_SECRET" turn: @@ -107,118 +188,80 @@ client_api: - 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 - dynamic_thumbnails: false - thumbnail_sizes: - - width: 96 - height: 96 - method: crop - - width: 640 - height: 480 - method: scale - -user_api: - bcrypt_cost: 10 - auto_join_rooms: - - "#main:$MATRIX_DOMAIN" - -logging: - - type: std - level: info EOF -cat < /etc/turnserver.conf -listening-device=$TURN_LISTENING_DEVICE -listening-port=3478 -tls-listening-port=5349 -listening-ip=$TURN_SERVER_IP -min-port=49152 -max-port=65535 -use-auth-secret -static-auth-secret=$TURN_SECRET -syslog -no-rfc5780 -no-stun-backward-compatibility -response-origin-only-with-rfc5780 -EOF + docker compose up -d -echo "[INFO] Generating server 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 + configure_nginx "$MATRIX_DOMAIN" "8008" -echo "[INFO] Booting up containers(HTTP-only test)..." -docker compose up -d + echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running." +} -echo "[INFO] Waiting for dendrite monolith to answer..." -for i in {1..30}; do - if curl -fs "http://$MATRIX_SERVER_IP:8008/_matrix/client/versions" >/dev/null 2>&1; then - echo "[OK] Monolith działa na http://$MATRIX_SERVER_IP:8008" - break - fi - echo " ...retry $i" - sleep 5 -done +install_synapse() { + echo "=== Installing Matrix Synapse ===" + common_prompts -echo "[INFO] Setting up NGINX RevPrx files" -cat < /etc/nginx/sites-available/$MATRIX_DOMAIN -server { - listen 80; - listen [::]:80; - server_name $MATRIX_DOMAIN; - location /.well-known/matrix/client { - return 301 https://$host$request_uri; - } - location /.well-known/matrix/server { - return 301 https://$host$request_uri; - } - location / { - return 301 https://$host$request_uri; - } -} + INSTALL_DIR="$BASE_DIR/$MATRIX_DOMAIN" + mkdir -p "$INSTALL_DIR" + cd "$INSTALL_DIR" || exit 1 -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - server_name $MATRIX_DOMAIN; + cat < docker-compose.yml +version: '3.4' +services: + db: + image: postgres:15-alpine + environment: + POSTGRES_USER: synapse + POSTGRES_PASSWORD: $DB_PASS + POSTGRES_DB: synapse + volumes: + - ./db:/var/lib/postgresql/data - ssl_certificate /etc/letsencrypt/live/$MATRIX_DOMAIN/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/$MATRIX_DOMAIN/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + synapse: + image: matrixdotorg/synapse:latest + depends_on: [ db ] + environment: + SYNAPSE_SERVER_NAME: $MATRIX_DOMAIN + SYNAPSE_REPORT_STATS: "yes" + POSTGRES_PASSWORD: $DB_PASS + ports: + - 8008:8008 + - 8448:8448 + volumes: + - ./data:/data +EOF - location / { - proxy_pass http://127.0.0.1:8008; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } + docker compose up -d - location /.well-known/matrix/client { - default_type application/json; - add_header Access-Control-Allow-Origin *; - return 200 '{"m.homeserver": {"base_url": "https://$MATRIX_DOMAIN"}}'; - } + cat < data/homeserver-extra.yaml +turn_uris: [ "turn:$TURN_DOMAIN?transport=udp", "turn:$TURN_DOMAIN?transport=tcp" ] +turn_shared_secret: "$TURN_SECRET" +turn_user_lifetime: "5m" +EOF - location /.well-known/matrix/server { - default_type application/json; - add_header Access-Control-Allow-Origin *; - return 200 '{"m.server": "$MATRIX_DOMAIN:443"}'; - } + configure_nginx "$MATRIX_DOMAIN" "8008" + + echo "[OK] Synapse server ($MATRIX_DOMAIN) is running." } -EOF -ln -s /etc/nginx/sites-available/$MATRIX_DOMAIN -echo "============================================" -echo " Matrix server setup finished!" -echo " HomeServer: https://$MATRIX_DOMAIN:8448" -echo " TURN: $TURN_DOMAIN (3478/5349)" +remove_server() { + DOMAIN="$1" + [ -z "$DOMAIN" ] && usage + SERVER_DIR="$BASE_DIR/$DOMAIN" + rm -rf "$SERVER_DIR" + rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" + nginx -t && systemctl reload nginx + echo "[OK] Server $DOMAIN removed." +} + +# --- CLI router --- +CMD="${1:-}" + +case "$CMD" in + turn) install_turn ;; + dendrite) install_dendrite ;; + synapse) install_synapse ;; + list) list_servers ;; + remove) remove_server "${2:-}" ;; + *) usage ;; +esac -- cgit v1.2.3