aboutsummaryrefslogtreecommitdiffstats
path: root/whiterabbit.sh
diff options
context:
space:
mode:
Diffstat (limited to 'whiterabbit.sh')
-rwxr-xr-xwhiterabbit.sh208
1 files 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
3 3
4BASE_DIR="/opt/matrix" 4BASE_DIR="/opt/matrix"
5 5
6# Detect docker compose binary
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
10 exit 1
11fi
12
6usage() { 13usage() {
7 cat <<'EOF' 14 cat <<'EOF'
8whiterabbit: auto-configure Matrix homeservers (Dendrite or Synapse) 15whiterabbit: auto-configure Matrix homeservers (Dendrite or Synapse)
@@ -20,11 +27,19 @@ Notes:
20 • All Matrix servers share the same Coturn (TURN domain/secret). 27 • All Matrix servers share the same Coturn (TURN domain/secret).
21 • TLS certificates are issued automatically using Let's Encrypt. 28 • TLS certificates are issued automatically using Let's Encrypt.
22 • Requirements: docker, docker-compose, nginx, certbot, coturn. 29 • Requirements: docker, docker-compose, nginx, certbot, coturn.
23
24EOF 30EOF
25 exit 1 31 exit 1
26} 32}
27 33
34require_nonempty() {
35 VAR_NAME="$1"
36 VAR_VALUE="$2"
37 if [ -z "$VAR_VALUE" ]; then
38 echo "[ERROR] $VAR_NAME cannot be empty" >&2
39 exit 1
40 fi
41}
42
28list_servers() { 43list_servers() {
29 echo "=== Installed servers ===" 44 echo "=== Installed servers ==="
30 ls -1 "$BASE_DIR" 2>/dev/null || echo "No servers installed" 45 ls -1 "$BASE_DIR" 2>/dev/null || echo "No servers installed"
@@ -34,12 +49,19 @@ install_turn() {
34 echo "=== Installing global Coturn ===" 49 echo "=== Installing global Coturn ==="
35 printf "TURN domain (eg. turn.example.com): " 50 printf "TURN domain (eg. turn.example.com): "
36 read -r TURN_DOMAIN 51 read -r TURN_DOMAIN
52 require_nonempty TURN_DOMAIN "$TURN_DOMAIN"
53
37 printf "TURN IP (eg. 127.0.0.1): " 54 printf "TURN IP (eg. 127.0.0.1): "
38 read -r TURN_SERVER_IP 55 read -r TURN_SERVER_IP
56 require_nonempty TURN_SERVER_IP "$TURN_SERVER_IP"
57
39 printf "Listening device (eg. eth0): " 58 printf "Listening device (eg. eth0): "
40 read -r TURN_LISTENING_DEVICE 59 read -r TURN_LISTENING_DEVICE
60 require_nonempty TURN_LISTENING_DEVICE "$TURN_LISTENING_DEVICE"
61
41 printf "TURN shared secret: " 62 printf "TURN shared secret: "
42 read -r TURN_SECRET 63 read -r TURN_SECRET
64 require_nonempty TURN_SECRET "$TURN_SECRET"
43 65
44 mkdir -p /etc/turn 66 mkdir -p /etc/turn
45 cat <<EOF > /etc/turn/turnserver.conf 67 cat <<EOF > /etc/turn/turnserver.conf
@@ -61,30 +83,53 @@ EOF
61 echo "$TURN_DOMAIN" > /etc/turn/domain 83 echo "$TURN_DOMAIN" > /etc/turn/domain
62 echo "$TURN_SECRET" > /etc/turn/secret 84 echo "$TURN_SECRET" > /etc/turn/secret
63 85
64 systemctl enable --now coturn || true 86 if command -v systemctl >/dev/null; then
87 systemctl enable --now coturn || true
88 else
89 service coturn start || true
90 fi
91
65 echo "[OK] Global Coturn configured ($TURN_DOMAIN)" 92 echo "[OK] Global Coturn configured ($TURN_DOMAIN)"
66} 93}
67 94
68common_prompts() { 95common_prompts() {
69 printf "Matrix domain (eg. matrix.example.com): " 96 printf "Matrix domain (eg. matrix.example.com): "
70 read -r MATRIX_DOMAIN 97 read -r MATRIX_DOMAIN
98 require_nonempty MATRIX_DOMAIN "$MATRIX_DOMAIN"
99
71 printf "Let's Encrypt certificate email: " 100 printf "Let's Encrypt certificate email: "
72 read -r EMAIL 101 read -r EMAIL
102 require_nonempty EMAIL "$EMAIL"
103
73 printf "Postgres secret: " 104 printf "Postgres secret: "
74 read -r DB_PASS 105 read -r DB_PASS
106 require_nonempty DB_PASS "$DB_PASS"
107
75 printf "registration_shared_secret: " 108 printf "registration_shared_secret: "
76 read -r REG_SECRET 109 read -r REG_SECRET
110 require_nonempty REG_SECRET "$REG_SECRET"
111
77 printf "MATRIX server IP (eg. 127.0.0.1): " 112 printf "MATRIX server IP (eg. 127.0.0.1): "
78 read -r MATRIX_SERVER_IP 113 read -r MATRIX_SERVER_IP
114 require_nonempty MATRIX_SERVER_IP "$MATRIX_SERVER_IP"
79 115
80 TURN_DOMAIN=$(cat /etc/turn/domain) 116 TURN_DOMAIN=$(cat /etc/turn/domain)
81 TURN_SECRET=$(cat /etc/turn/secret) 117 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)
121 PORT_BASE=$(( (HASH % 1000) + 8000 ))
122 PORT_HTTP=$PORT_BASE
123 PORT_HTTPS=$((PORT_BASE+1))
82} 124}
83 125
84configure_nginx() { 126configure_nginx() {
85 DOMAIN="$1" 127 DOMAIN="$1"
86 SERVICE_PORT="$2" 128 SERVICE_PORT="$2"
87 129
130 mkdir -p /etc/nginx/sites-available
131 mkdir -p /etc/nginx/sites-enabled
132
88 cat <<EOF > "/etc/nginx/sites-available/$DOMAIN" 133 cat <<EOF > "/etc/nginx/sites-available/$DOMAIN"
89server { 134server {
90 listen 80; 135 listen 80;
@@ -112,13 +157,25 @@ server {
112EOF 157EOF
113 158
114 ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" 159 ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
115 nginx -t && systemctl reload nginx 160 nginx -t || { echo "[ERROR] Invalid nginx config"; exit 1; }
116 161 if command -v systemctl >/dev/null; then
117 certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" 162 systemctl reload nginx
163 else
164 nginx -s reload
165 fi
166
167 if command -v certbot >/dev/null; then
168 certbot --nginx --non-interactive --agree-tos -m "$EMAIL" -d "$DOMAIN" || {
169 echo "[WARN] Certbot failed for $DOMAIN"
170 }
171 else
172 echo "[WARN] Certbot not installed, skipping TLS setup"
173 fi
118 174
119 echo "[OK] Nginx and SSL certificate configured for $DOMAIN" 175 echo "[OK] Nginx and SSL certificate configured for $DOMAIN"
120} 176}
121 177
178
122install_dendrite() { 179install_dendrite() {
123 echo "=== Installing Matrix Dendrite ===" 180 echo "=== Installing Matrix Dendrite ==="
124 common_prompts 181 common_prompts
@@ -136,63 +193,147 @@ POSTGRES_DB=dendrite
136 193
137MONOLITH_HOSTNAME=monolith 194MONOLITH_HOSTNAME=monolith
138MONOLITH_IMAGE=matrixdotorg/dendrite-monolith:latest 195MONOLITH_IMAGE=matrixdotorg/dendrite-monolith:latest
139MONOLITH_PORT_HTTP=8008 196MONOLITH_PORT_HTTP=$PORT_HTTP
140MONOLITH_PORT_HTTPS=8448 197MONOLITH_PORT_HTTPS=$PORT_HTTPS
141EOF 198EOF
142 199
143 cat <<'EOF' > compose.yml 200cat <<EOF > compose.yml
144services: 201services:
145 postgres: 202 postgres:
146 image: postgres:${POSTGRES_VERSION:-15-alpine} 203 hostname: \${POSTGRES_HOSTNAME:-postgres}
204 image: postgres:\${POSTGRES_VERSION:-15-alpine}
147 restart: always 205 restart: always
148 volumes: 206 volumes:
149 - ./dendrite_postgres_data:/var/lib/postgresql/data 207 - ./dendrite_postgres_data:/var/lib/postgresql/data
150 environment: 208 environment:
151 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 209 POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-$DB_PASS}
152 POSTGRES_USER: ${POSTGRES_USER} 210 POSTGRES_USER: \${POSTGRES_USER:-dendrite}
153 POSTGRES_DB: ${POSTGRES_DB} 211 POSTGRES_DB: \${POSTGRES_DB:-dendrite}
154 networks: [ internal ] 212 healthcheck:
213 test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-dendrite}"]
214 interval: 5s
215 timeout: 5s
216 retries: 5
217 networks:
218 - internal
219
155 monolith: 220 monolith:
156 image: ${MONOLITH_IMAGE} 221 hostname: \${MONOLITH_HOSTNAME:-monolith}
222 image: \${MONOLITH_IMAGE:-matrixdotorg/dendrite-monolith:latest}
157 ports: 223 ports:
158 - ${MONOLITH_PORT_HTTP}:8008 224 - \${MONOLITH_PORT_HTTP:-8008}:8008
159 - ${MONOLITH_PORT_HTTPS}:8448 225 - \${MONOLITH_PORT_HTTPS:-8448}:8448
160 volumes: 226 volumes:
161 - ./config:/etc/dendrite 227 - ./config:/etc/dendrite
162 - ./dendrite_media:/var/dendrite/media 228 - ./dendrite_media:/var/dendrite/media
163 depends_on: [ postgres ] 229 - ./dendrite_jetstream:/var/dendrite/jetstream
164 networks: [ internal ] 230 - ./dendrite_search_index:/var/dendrite/searchindex
231 - ./:/mnt
232 depends_on:
233 postgres:
234 condition: service_healthy
235 networks:
236 - internal
165 restart: unless-stopped 237 restart: unless-stopped
238
166networks: 239networks:
167 internal: 240 internal:
168 attachable: true 241 attachable: true
242volumes:
243 dendrite_postgres_data:
244 dendrite_media:
245 dendrite_jetstream:
246 dendrite_search_index:
169EOF 247EOF
170 248
171 cat <<EOF > config/dendrite.yaml 249 # --- Plik konfiguracji Dendrite ---
250
251cat <<EOF > config/dendrite.yaml
172version: 2 252version: 2
173global: 253global:
174 server_name: $MATRIX_DOMAIN 254 server_name: $MATRIX_DOMAIN
175 private_key: /mnt/matrix_key.pem 255 private_key: /mnt/matrix_key.pem
256 tls_cert: /mnt/server.crt
257 tls_key: /mnt/server.key
258 old_private_keys: []
259 key_validity_period: 168h0m0s
176 database: 260 database:
177 connection_string: postgresql://dendrite:$DB_PASS@postgres/dendrite?sslmode=disable 261 connection_string: postgresql://dendrite:$DB_PASS@postgres/dendrite?sslmode=disable
262 max_open_conns: 90
263 max_idle_conns: 5
264 conn_max_lifetime: -1
178 well_known_server_name: "$MATRIX_DOMAIN:443" 265 well_known_server_name: "$MATRIX_DOMAIN:443"
179 well_known_client_name: "https://$MATRIX_DOMAIN" 266 well_known_client_name: "https://$MATRIX_DOMAIN"
180 267 cache:
268 max_size_estimated: 1gb
269 max_age: 1h
270 trusted_third_party_id_servers:
271 - matrix.org
272 - vector.im
273 disable_federation: false
274 report_stats:
275 enabled: false
181client_api: 276client_api:
182 registration_disabled: true 277 registration_disabled: true
183 registration_shared_secret: "$REG_SECRET" 278 registration_shared_secret: "$REG_SECRET"
184
185 turn: 279 turn:
186 turn_user_lifetime: "5m" 280 turn_user_lifetime: "5m"
187 turn_uris: 281 turn_uris:
188 - turn:$TURN_DOMAIN?transport=udp 282 - turn:$TURN_DOMAIN?transport=udp
189 - turn:$TURN_DOMAIN?transport=tcp 283 - turn:$TURN_DOMAIN?transport=tcp
190 turn_shared_secret: "$TURN_SECRET" 284 turn_shared_secret: "$TURN_SECRET"
285federation_api:
286 send_max_retries: 16
287 disable_tls_validation: false
288media_api:
289 base_path: ./media_store
290 max_file_size_bytes: 10485760
291sync_api:
292 search:
293 enabled: false
294 index_path: "./searchindex"
295user_api:
296 auto_join_rooms:
297 - "#main:$MATRIX_DOMAIN"
298logging:
299 - type: std
300 level: info
301 - type: file
302 level: info
303 params:
304 path: ./logs
305jetstream:
306 addresses: []
307 disable_tls_validation: false
308 storage_path: ./
309 topic_prefix: Dendrite
310metrics:
311 enabled: false
312 basic_auth:
313 username: metrics
314 password: metrics
315dns_cache:
316 enabled: false
317 cache_size: 256
318 cache_lifetime: "5m"
191EOF 319EOF
192 320
193 docker compose up -d 321 # Generate necessary project directory
322 $DOCKER_COMPOSE up -d
194 323
195 configure_nginx "$MATRIX_DOMAIN" "8008" 324 $DOCKER_COMPOSE down
325
326 # --- Generowanie kluczy Dendrite + TLS jeśli nie istnieją ---
327 echo "[INFO] Generating Dendrite keys..."
328
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
330 echo "[OK] Keys generated in $PWD"
331
332 # --- Start kontenerów ---
333 $DOCKER_COMPOSE up -d
334
335 # --- Konfiguracja Nginx + SSL/TLS ---
336 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP"
196 337
197 echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running." 338 echo "[OK] Dendrite server ($MATRIX_DOMAIN) is running."
198} 339}
@@ -225,13 +366,13 @@ services:
225 SYNAPSE_REPORT_STATS: "yes" 366 SYNAPSE_REPORT_STATS: "yes"
226 POSTGRES_PASSWORD: $DB_PASS 367 POSTGRES_PASSWORD: $DB_PASS
227 ports: 368 ports:
228 - 8008:8008 369 - $PORT_HTTP:8008
229 - 8448:8448 370 - $PORT_HTTPS:8448
230 volumes: 371 volumes:
231 - ./data:/data 372 - ./data:/data
232EOF 373EOF
233 374
234 docker compose up -d 375 $DOCKER_COMPOSE up -d
235 376
236 cat <<EOF > data/homeserver-extra.yaml 377 cat <<EOF > data/homeserver-extra.yaml
237turn_uris: [ "turn:$TURN_DOMAIN?transport=udp", "turn:$TURN_DOMAIN?transport=tcp" ] 378turn_uris: [ "turn:$TURN_DOMAIN?transport=udp", "turn:$TURN_DOMAIN?transport=tcp" ]
@@ -239,7 +380,7 @@ turn_shared_secret: "$TURN_SECRET"
239turn_user_lifetime: "5m" 380turn_user_lifetime: "5m"
240EOF 381EOF
241 382
242 configure_nginx "$MATRIX_DOMAIN" "8008" 383 configure_nginx "$MATRIX_DOMAIN" "$PORT_HTTP"
243 384
244 echo "[OK] Synapse server ($MATRIX_DOMAIN) is running." 385 echo "[OK] Synapse server ($MATRIX_DOMAIN) is running."
245} 386}
@@ -247,10 +388,19 @@ EOF
247remove_server() { 388remove_server() {
248 DOMAIN="$1" 389 DOMAIN="$1"
249 [ -z "$DOMAIN" ] && usage 390 [ -z "$DOMAIN" ] && usage
391
250 SERVER_DIR="$BASE_DIR/$DOMAIN" 392 SERVER_DIR="$BASE_DIR/$DOMAIN"
251 rm -rf "$SERVER_DIR" 393 case "$DOMAIN" in
394 ""|"/"|".") echo "[ERROR] Refusing to delete dangerous path ($DOMAIN)"; exit 1 ;;
395 esac
396
397 if [ -d "$SERVER_DIR" ]; then
398 (cd "$SERVER_DIR" && $DOCKER_COMPOSE down -v) || true
399 rm -rf "$SERVER_DIR"
400 fi
401
252 rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN" 402 rm -f "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
253 nginx -t && systemctl reload nginx 403 nginx -t && (systemctl reload nginx 2>/dev/null || nginx -s reload)
254 echo "[OK] Server $DOMAIN removed." 404 echo "[OK] Server $DOMAIN removed."
255} 405}
256 406