aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Wandzio <contact@philw.dev>2025-10-01 22:21:25 +0200
committerFilip Wandzio <contact@philw.dev>2025-10-01 22:21:25 +0200
commit37e65300245be45d4000797b3ada53c68022fc26 (patch)
treeae304da2869d2069b3359def36e36d29877fc7c8
parent34fbb07af8de311a4d316325bcf574341f6be5dd (diff)
downloadwhiterabbit-37e65300245be45d4000797b3ada53c68022fc26.tar.gz
whiterabbit-37e65300245be45d4000797b3ada53c68022fc26.zip
Implement use flags for subcommants
Optimize synapse pipeline
-rw-r--r--Makefile14
-rw-r--r--wh.1 (renamed from whiterabbit.1)30
-rwxr-xr-xwh.sh (renamed from whiterabbit.sh)259
3 files changed, 201 insertions, 102 deletions
diff --git a/Makefile b/Makefile
index fe1c42b..5a8c27d 100644
--- a/Makefile
+++ b/Makefile
@@ -4,12 +4,12 @@ MANDIR ?= $(PREFIX)/share/man/man1
4 4
5install: 5install:
6 @echo "Installing whiterabbit..." 6 @echo "Installing whiterabbit..."
7 install -Dm755 whiterabbit.sh $(BINDIR)/whiterabbit 7 install -Dm755 wh.sh $(BINDIR)/wh
8 install -Dm644 whiterabbit.1 $(MANDIR)/whiterabbit.1 8 install -Dm644 wh.1 $(MANDIR)/wh.1
9 @echo "Installed to $(BINDIR)/whiterabbit and man page to $(MANDIR)/whiterabbit.1" 9 @echo "Installed to $(BINDIR)/whand man page to $(MANDIR)/wh.1"
10 10
11uninstall: 11uninstall:
12 @echo "Removing whiterabbit..." 12 @echo "Removing wh..."
13 rm -f $(BINDIR)/whiterabbit 13 rm -f $(BINDIR)/wh
14 rm -f $(MANDIR)/whiterabbit.1 14 rm -f $(MANDIR)/wh.1
15 @echo "Removed whiterabbit and its man page" 15 @echo "Removed wh and its man page"
diff --git a/whiterabbit.1 b/wh.1
index e02030f..8a6230f 100644
--- a/whiterabbit.1
+++ b/wh.1
@@ -4,25 +4,26 @@
4whiterabbit \- auto-configure Matrix homeservers (Dendrite or Synapse) 4whiterabbit \- auto-configure Matrix homeservers (Dendrite or Synapse)
5.SH SYNOPSIS 5.SH SYNOPSIS
6.B whiterabbit 6.B whiterabbit
7[\fIturn\fR|\fIdendrite\fR|\fIsynapse\fR|\fIlist\fR|\fIremove <domain>\fR] 7[\fIturn\fR|\fIdendrite\fR|\fIsynapse\fR|\fIlist\fR|\fIremove <domain>\fR|
8\fI-t\fR|\fI-d\fR|\fI-s\fR|\fI-l\fR|\fI-r <domain>\fR]
8.SH DESCRIPTION 9.SH DESCRIPTION
9whiterabbit sets up Matrix homeservers with a global Coturn server and Nginx/Let's Encrypt support. 10whiterabbit sets up Matrix homeservers with a global Coturn server and Nginx/Let's Encrypt support.
10 11
11Main commands: 12Main commands and their short flag equivalents:
12.TP 13.TP
13.B turn 14.B turn, -t
14Install or configure the global Coturn server. 15Install or configure the global Coturn server.
15.TP 16.TP
16.B dendrite 17.B dendrite, -d
17Add a new Matrix Dendrite server. 18Add a new Matrix Dendrite server.
18.TP 19.TP
19.B synapse 20.B synapse, -s
20Add a new Matrix Synapse server. 21Add a new Matrix Synapse server.
21.TP 22.TP
22.B list 23.B list, -l
23List all configured Matrix servers. 24List all configured Matrix servers.
24.TP 25.TP
25.B remove <domain> 26.B remove <domain>, -r <domain>
26Remove a Matrix server by domain. 27Remove a Matrix server by domain.
27 28
28.SH EXAMPLES 29.SH EXAMPLES
@@ -30,17 +31,32 @@ Remove a Matrix server by domain.
30whiterabbit turn 31whiterabbit turn
31Set up the global Coturn server. 32Set up the global Coturn server.
32.TP 33.TP
34whiterabbit -t
35Same as above using flag.
36.TP
33whiterabbit dendrite 37whiterabbit dendrite
34Add a Dendrite server with TLS and Nginx. 38Add a Dendrite server with TLS and Nginx.
35.TP 39.TP
40whiterabbit -d
41Same as above using flag.
42.TP
36whiterabbit synapse 43whiterabbit synapse
37Add a Synapse server with TLS and Nginx. 44Add a Synapse server with TLS and Nginx.
38.TP 45.TP
46whiterabbit -s
47Same as above using flag.
48.TP
39whiterabbit list 49whiterabbit list
40Show all servers. 50Show all servers.
41.TP 51.TP
52whiterabbit -l
53Same as above using flag.
54.TP
42whiterabbit remove matrix.example.com 55whiterabbit remove matrix.example.com
43Remove a server. 56Remove a server.
57.TP
58whiterabbit -r matrix.example.com
59Same as above using flag.
44 60
45.SH NOTES 61.SH NOTES
46All Matrix servers share the same Coturn instance. 62All Matrix servers share the same Coturn instance.
diff --git a/whiterabbit.sh b/wh.sh
index f8eb350..db1c12d 100755
--- a/whiterabbit.sh
+++ b/wh.sh
@@ -3,12 +3,10 @@ set -eu
3 3
4BASE_DIR="/opt/matrix" 4BASE_DIR="/opt/matrix"
5 5
6# Detect docker compose binary 6DOCKER_COMPOSE=$(command -v docker-compose || command -v docker compose) || {
7DOCKER_COMPOSE=$(command -v docker-compose || command -v "docker" compose || true)
8if [ -z "$DOCKER_COMPOSE" ]; then
9 echo "[ERROR] docker-compose or docker compose not found" >&2 7 echo "[ERROR] docker-compose or docker compose not found" >&2
10 exit 1 8 exit 1
11fi 9}
12 10
13usage() { 11usage() {
14 cat <<'EOF' 12 cat <<'EOF'
@@ -21,12 +19,6 @@ Main actions:
21 synapse Add a new Matrix server (Synapse) 19 synapse Add a new Matrix server (Synapse)
22 list Show all configured Matrix servers 20 list Show all configured Matrix servers
23 remove Remove a Matrix server by domain 21 remove Remove a Matrix server by domain
24
25Notes:
26 • Coturn should be installed only once using 'turn'.
27 • All Matrix servers share the same Coturn (TURN domain/secret).
28 • TLS certificates are issued automatically using Let's Encrypt.
29 • Requirements: docker, docker-compose, nginx, certbot, coturn.
30EOF 22EOF
31 exit 1 23 exit 1
32} 24}
@@ -34,15 +26,26 @@ EOF
34require_nonempty() { 26require_nonempty() {
35 VAR_NAME="$1" 27 VAR_NAME="$1"
36 VAR_VALUE="$2" 28 VAR_VALUE="$2"
37 if [ -z "$VAR_VALUE" ]; then 29 [ -n "$VAR_VALUE" ] || {
38 echo "[ERROR] $VAR_NAME cannot be empty" >&2 30 echo "[ERROR] $VAR_NAME cannot be empty" >&2
39 exit 1 31 exit 1
40 fi 32 }
41} 33}
42 34
43list_servers() { 35list_servers() {
44 echo "=== Installed servers ===" 36 echo "=== Installed servers ==="
45 ls -1 "$BASE_DIR" 2>/dev/null || echo "No servers installed" 37 for dir in "$BASE_DIR"/*; do
38 [ -d "$dir" ] || continue
39 name=$(basename "$dir")
40
41 status=$(docker ps --filter "name=^${name}$" --format "{{.Status}}")
42 ports=$(docker ps --filter "name=^${name}$" --format "{{.Ports}}" | sed 's/, /\n /g')
43
44 [ -n "$status" ] && {
45 echo "- $name [RUNNING] on:"
46 echo " $ports"
47 } || echo "- $name [STOPPED]"
48 done
46} 49}
47 50
48install_turn() { 51install_turn() {
@@ -83,11 +86,10 @@ EOF
83 echo "$TURN_DOMAIN" > /etc/turn/domain 86 echo "$TURN_DOMAIN" > /etc/turn/domain
84 echo "$TURN_SECRET" > /etc/turn/secret 87 echo "$TURN_SECRET" > /etc/turn/secret
85 88
86 if command -v systemctl >/dev/null; then 89 case $(command -v systemctl >/dev/null && echo y || echo n) in
87 systemctl enable --now coturn || true 90 y) systemctl enable --now coturn || true ;;
88 else 91 n) service coturn start || true ;;
89 service coturn start || true 92 esac
90 fi
91 93
92 echo "[OK] Global Coturn configured ($TURN_DOMAIN)" 94 echo "[OK] Global Coturn configured ($TURN_DOMAIN)"
93} 95}
@@ -100,23 +102,13 @@ common_prompts() {
100 printf "Let's Encrypt certificate email: " 102 printf "Let's Encrypt certificate email: "
101 read -r EMAIL 103 read -r EMAIL
102 require_nonempty EMAIL "$EMAIL" 104 require_nonempty EMAIL "$EMAIL"
103 105 DB_PASS=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)
104 printf "Postgres secret: " 106 REG_SECRET=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 64)
105 read -r DB_PASS
106 require_nonempty DB_PASS "$DB_PASS"
107
108 printf "registration_shared_secret: "
109 read -r REG_SECRET
110 require_nonempty REG_SECRET "$REG_SECRET"
111
112 printf "MATRIX server IP (eg. 127.0.0.1): " 107 printf "MATRIX server IP (eg. 127.0.0.1): "
113 read -r MATRIX_SERVER_IP 108 read -r MATRIX_SERVER_IP
114 require_nonempty MATRIX_SERVER_IP "$MATRIX_SERVER_IP" 109 require_nonempty MATRIX_SERVER_IP "$MATRIX_SERVER_IP"
115
116 TURN_DOMAIN=$(cat /etc/turn/domain) 110 TURN_DOMAIN=$(cat /etc/turn/domain)
117 TURN_SECRET=$(cat /etc/turn/secret) 111 TURN_SECRET=$(cat /etc/turn/secret)
118
119 # Generate unique port numbers from hash of domain
120 HASH=$(printf "%s" "$MATRIX_DOMAIN" | cksum | cut -d ' ' -f1) 112 HASH=$(printf "%s" "$MATRIX_DOMAIN" | cksum | cut -d ' ' -f1)
121 PORT_BASE=$(( (HASH % 1000) + 8000 )) 113 PORT_BASE=$(( (HASH % 1000) + 8000 ))
122 PORT_HTTP=$PORT_BASE 114 PORT_HTTP=$PORT_BASE
@@ -158,21 +150,19 @@ EOF
158 150
159 ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" 151 ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
160 nginx -t || { echo "[ERROR] Invalid nginx config"; exit 1; } 152 nginx -t || { echo "[ERROR] Invalid nginx config"; exit 1; }
161 if command -v systemctl >/dev/null; then 153
162 systemctl reload nginx 154
163 else 155 case $(command -v systemctl >/dev/null && echo y || echo n) in
164 nginx -s reload 156 y) systemctl reload nginx ;;
165 fi 157 n) nginx -s reload ;;
166 158 esac
167 if command -v certbot >/dev/null; then 159
168 certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" || { 160 case $(command -v certbot >/dev/null && echo y || echo n) in
169 echo "[WARN] Certbot failed for $DOMAIN" 161 y) certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" || {
170 } 162 echo "[WARN] Certbot failed for $DOMAIN"
171 else 163 } ;;
172 echo "[WARN] Certbot not installed, skipping TLS setup" 164 n) echo "[WARN] Certbot not installed, skipping TLS setup" ;;
173 fi 165 esac
174
175 echo "[OK] Nginx and SSL certificate configured for $DOMAIN"
176} 166}
177 167
178 168
@@ -201,6 +191,7 @@ cat <<EOF > compose.yml
201services: 191services:
202 postgres: 192 postgres:
203 hostname: \${POSTGRES_HOSTNAME:-postgres} 193 hostname: \${POSTGRES_HOSTNAME:-postgres}
194 container_name: ${MATRIX_DOMAIN}-db
204 image: postgres:\${POSTGRES_VERSION:-15-alpine} 195 image: postgres:\${POSTGRES_VERSION:-15-alpine}
205 restart: always 196 restart: always
206 volumes: 197 volumes:
@@ -219,6 +210,7 @@ services:
219 210
220 monolith: 211 monolith:
221 hostname: \${MONOLITH_HOSTNAME:-monolith} 212 hostname: \${MONOLITH_HOSTNAME:-monolith}
213 container_name: ${MATRIX_DOMAIN}
222 image: \${MONOLITH_IMAGE:-matrixdotorg/dendrite-monolith:latest} 214 image: \${MONOLITH_IMAGE:-matrixdotorg/dendrite-monolith:latest}
223 ports: 215 ports:
224 - \${MONOLITH_PORT_HTTP:-8008}:8008 216 - \${MONOLITH_PORT_HTTP:-8008}:8008
@@ -246,8 +238,6 @@ volumes:
246 dendrite_search_index: 238 dendrite_search_index:
247EOF 239EOF
248 240
249 # --- Plik konfiguracji Dendrite ---
250
251cat <<EOF > config/dendrite.yaml 241cat <<EOF > config/dendrite.yaml
252version: 2 242version: 2
253global: 243global:
@@ -318,23 +308,13 @@ dns_cache:
318 cache_lifetime: "5m" 308 cache_lifetime: "5m"
319EOF 309EOF
320 310
321 # Generate necessary project directory 311 echo "[INFO] Generating Dendrite keys..."
322 $DOCKER_COMPOSE up -d
323
324 $DOCKER_COMPOSE down
325
326 # --- Generowanie kluczy Dendrite + TLS jeśli nie istnieją ---
327 echo "[INFO] Generating Dendrite keys..."
328 312
329docker 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 313 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
330 echo "[OK] Keys generated in $PWD" 314 echo "[OK] Keys generated in $PWD"
331 315
332 # --- Start kontenerów ---
333 $DOCKER_COMPOSE up -d 316 $DOCKER_COMPOSE up -d
334
335 # --- Konfiguracja Nginx + SSL/TLS ---
336 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP" 317 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP"
337
338 echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running." 318 echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running."
339} 319}
340 320
@@ -345,73 +325,176 @@ install_synapse() {
345 INSTALL_DIR="$BASE_DIR/$MATRIX_DOMAIN" 325 INSTALL_DIR="$BASE_DIR/$MATRIX_DOMAIN"
346 mkdir -p "$INSTALL_DIR" 326 mkdir -p "$INSTALL_DIR"
347 cd "$INSTALL_DIR" || exit 1 327 cd "$INSTALL_DIR" || exit 1
328 mkdir -p data db
329 chown -R 991:991 data/
330 chown -R 999:999 db/
331 DB_USER="synapse"
332 DB_NAME="synapse"
333 DB_PASS=$(openssl rand -hex 16)
334 REG_SECRET=$(openssl rand -hex 32)
335
336 : "${PORT_HTTP:=8008}"
337 : "${PORT_HTTPS:=8448}"
338
339 cat <<EOF > .env
340# Matrix Synapse environment
341MATRIX_DOMAIN=$MATRIX_DOMAIN
342PORT_HTTP=$PORT_HTTP
343PORT_HTTPS=$PORT_HTTPS
344
345DB_USER=$DB_USER
346DB_NAME=$DB_NAME
347DB_PASS=$DB_PASS
348
349REG_SECRET=$REG_SECRET
350TURN_DOMAIN=$TURN_DOMAIN
351TURN_SECRET=$TURN_SECRET
352EOF
348 353
349 cat <<EOF > docker-compose.yml 354
350version: '3.4' 355cat <<EOF > compose.yml
351services: 356services:
352 db: 357 db:
353 image: postgres:15-alpine 358 image: postgres:15-alpine
359 restart: always
354 environment: 360 environment:
355 POSTGRES_USER: synapse 361 POSTGRES_USER: ${DB_USER}
356 POSTGRES_PASSWORD: $DB_PASS 362 POSTGRES_PASSWORD: ${DB_PASS}
357 POSTGRES_DB: synapse 363 POSTGRES_DB: ${DB_NAME}
364 LANG: C
365 LC_ALL: C
366 command: postgres -c lc_collate=C -c lc_ctype=C
358 volumes: 367 volumes:
359 - ./db:/var/lib/postgresql/data 368 - ./db:/var/lib/postgresql/data
369 networks:
370 - synapse-net
360 371
361 synapse: 372 synapse:
362 image: matrixdotorg/synapse:latest 373 image: matrixdotorg/synapse:latest
363 depends_on: [ db ] 374 restart: always
375 depends_on:
376 - db
364 environment: 377 environment:
365 SYNAPSE_SERVER_NAME: $MATRIX_DOMAIN 378 SYNAPSE_SERVER_NAME: ${MATRIX_DOMAIN}
366 SYNAPSE_REPORT_STATS: "yes" 379 SYNAPSE_REPORT_STATS: "yes"
367 POSTGRES_PASSWORD: $DB_PASS
368 ports: 380 ports:
369 - $PORT_HTTP:8008 381 - "${PORT_HTTP}:8008"
370 - $PORT_HTTPS:8448 382 - "${PORT_HTTPS}:8448"
371 volumes: 383 volumes:
372 - ./data:/data 384 - ./data:/data
373EOF 385 networks:
386 - synapse-net
374 387
375 $DOCKER_COMPOSE up -d 388networks:
389 synapse-net:
390 driver: bridge
391EOF
376 392
377 cat <<EOF > data/homeserver-extra.yaml 393$DOCKER_COMPOSE --env-file .env up -d
378turn_uris: [ "turn:$TURN_DOMAIN?transport=udp", "turn:$TURN_DOMAIN?transport=tcp" ] 394docker run -it --rm -v ./data:/data -e SYNAPSE_SERVER_NAME=$MATRIX_DOMAIN -e SYNAPSE_REPORT_STATS=no matrixdotorg/synapse:latest generate
395
396echo "[INFO] Generating homeserver.yaml for Postgres + TURN..."
397cat <<EOF > data/homeserver.yaml
398server_name: $MATRIX_DOMAIN
399pid_file: /data/homeserver.pid
400
401listeners:
402 - port: $PORT_HTTP
403 tls: false
404 type: http
405 x_forwarded: true
406 resources:
407 - names: [client, federation]
408 compress: false
409
410database:
411 name: psycopg2
412 args:
413 user: $DB_USER
414 password: $DB_PASS
415 host: db
416 database: $DB_NAME
417 cp_min: 5
418 cp_max: 10
419
420enable_registration: false
421registration_shared_secret: "$REG_SECRET"
422
423turn_uris:
424 - turn:$TURN_DOMAIN?transport=udp
425 - turn:$TURN_DOMAIN?transport=tcp
379turn_shared_secret: "$TURN_SECRET" 426turn_shared_secret: "$TURN_SECRET"
380turn_user_lifetime: "5m" 427turn_user_lifetime: "5m"
428
429log_config: "/data/$MATRIX_DOMAIN.log.config"
430media_store_path: /data/media_store
431report_stats: false
432
433macaroon_secret_key: $REG_SECRET
434form_secret: $REG_SECRET
435signing_key_path: "/data/$MATRIX_DOMAIN.signing.key"
436
437trusted_key_servers:
438 - server_name: "matrix.org"
439suppress_key_server_warning: true
381EOF 440EOF
382 441
383 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP" 442 echo "[INFO] Restarting Synapse with full config..."
443 $DOCKER_COMPOSE --env-file .env restart synapse
444 $DOCKER_COMPOSE --env-file .env restart synapse
384 445
385 echo "[OK] Synapse server ($MATRIX_DOMAIN) is running." 446 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP"
447 echo "[OK] Synapse server ($MATRIX_DOMAIN) is running with Postgres."
386} 448}
387 449
450
388remove_server() { 451remove_server() {
389 DOMAIN="$1" 452 [ "$#" -eq 0 ] && echo "Usage: remove_server <domain1> [domain2 ...]" && return 1
390 [ -z "$DOMAIN" ] && usage
391 453
392 SERVER_DIR="$BASE_DIR/$DOMAIN" 454 for DOMAIN in "$@"; do
393 case "$DOMAIN" in 455 SERVER_DIR="$BASE_DIR/$DOMAIN"
394 ""|"/"|".") echo "[ERROR] Refusing to delete dangerous path ($DOMAIN)"; exit 1 ;; 456
395 esac 457 case "$DOMAIN" in
458 ""|"/"|".")
459 echo "[ERROR] Refusing to delete dangerous path ($DOMAIN)"
460 continue
461 ;;
462 esac
463
464 [ -d "$SERVER_DIR" ] && \
465 echo "[INFO] Stopping and removing server: $DOMAIN" && \
466 (cd "$SERVER_DIR" && $DOCKER_COMPOSE down -v) || \
467 echo "[WARN] Failed to stop container for $DOMAIN"
396 468
397 if [ -d "$SERVER_DIR" ]; then
398 (cd "$SERVER_DIR" && $DOCKER_COMPOSE down -v) || true
399 rm -rf "$SERVER_DIR" 469 rm -rf "$SERVER_DIR"
400 fi 470 rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
471 done
401 472
402 rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
403 nginx -t && (systemctl reload nginx 2>/dev/null || nginx -s reload) 473 nginx -t && (systemctl reload nginx 2>/dev/null || nginx -s reload)
404 echo "[OK] Server $DOMAIN removed." 474 echo "[OK] Done removing servers: $*"
405} 475}
406 476
407# --- CLI router ---
408CMD="${1:-}" 477CMD="${1:-}"
409 478
410case "$CMD" in 479case "$CMD" in
480 -t) install_turn ;;
481 -d) install_dendrite ;;
482 -s) install_synapse ;;
483 -l) list_servers ;;
484 -r)
485 [ -z "$2" ] && { echo "Usage: $0 -r <domain1> [domain2 ...]"; exit 1; }
486 shift
487 remove_server "$@"
488 ;;
411 turn) install_turn ;; 489 turn) install_turn ;;
412 dendrite) install_dendrite ;; 490 dendrite) install_dendrite ;;
413 synapse) install_synapse ;; 491 synapse) install_synapse ;;
414 list) list_servers ;; 492 list) list_servers ;;
415 remove) remove_server "${2:-}" ;; 493 remove)
494 [ -z "$2" ] && { echo "Usage: $0 remove <domain1> [domain2 ...]"; exit 1; }
495 shift
496 remove_server "$@"
497 ;;
416 *) usage ;; 498 *) usage ;;
417esac 499esac
500