Skip to content

Instantly share code, notes, and snippets.

@officialdarnyc
Forked from alansikora/rcmf-setup.sh
Last active July 30, 2024 15:23
Show Gist options
  • Save officialdarnyc/be431264bb6c2a0fa9eb8a23bbffd1b2 to your computer and use it in GitHub Desktop.
Save officialdarnyc/be431264bb6c2a0fa9eb8a23bbffd1b2 to your computer and use it in GitHub Desktop.
Rocket.Chat Matrix Federation
# !/bin/bash
# Rocket.Chat Matrix Federation Setup
# Author: Alan Sikora <alan.sikora@rocket.chat>
# https://github.com/alansikora
PWD=$(pwd)
[[ $1 = "-dev" ]] && DEV=true || DEV=false
# Set image versions
ROCKETCHAT_IMAGE_TAG="5.4.0"
SYNAPSE_IMAGE_TAG="v1.71.0"
TRAEFIK_IMAGE_TAG="v2.9.4"
REDIS_IMAGE_TAG="6.2.7"
NGINX_IMAGE_TAG="1.23.2"
ELEMENT_IMAGE_TAG="v1.11.14"
# Set config
RC_BRIDGE_PORT=3300
updateEnvFile () {
echo "" > .env
cat << EOF > .env
# Rocket.Chat CLI
RC_IMAGE=$RC_IMAGE
ROCKETCHAT_IMAGE_TAG=$ROCKETCHAT_IMAGE_TAG
SYNAPSE_IMAGE_TAG=$SYNAPSE_IMAGE_TAG
TRAEFIK_IMAGE_TAG=$TRAEFIK_IMAGE_TAG
REDIS_IMAGE_TAG=$REDIS_IMAGE_TAG
NGINX_IMAGE_TAG=$NGINX_IMAGE_TAG
ELEMENT_IMAGE_TAG=$ELEMENT_IMAGE_TAG
# Setup
SERVER_HOSTNAME=$SERVER_HOSTNAME
RC_BRIDGE_PORT=$RC_BRIDGE_PORT
# Let's Encrypt
LETSENCRYPT_EMAIL=$LETSENCRYPT_EMAIL
# Matrix
AS_UNIQUE_ID=$AS_UNIQUE_ID
AS_HS_TOKEN=$AS_HS_TOKEN
AS_AS_TOKEN=$AS_AS_TOKEN
EOF
}
alpineImageOrNot () {
read -p "Do you with to use the alpine version (y/n) [n]: "
if [ "$REPLY" = "y" ]; then
RC_IMAGE="$RC_IMAGE-alpine"
fi;
}
imageLatestStable () {
RC_IMAGE="rocket.chat:$ROCKETCHAT_IMAGE_TAG"
if [ $DEV = true ]; then
alpineImageOrNot
fi
}
echo ""
echo "Rocket.Chat Matrix Federation Setup"
if [ $DEV = true ]; then
echo ">>>>>>>> DEV MODE ENABLED <<<<<<<<<"
fi
echo ""
# Check if docker is installed
DOCKER_INFO=$(docker info &> /dev/null)
RESULT=$?
if [ $RESULT -ne 0 ]; then
if [ $RESULT -eq 127 ]; then
echo "Please, install the latest docker version then run this script again:"
echo "https://docs.docker.com/engine/install/"
echo ""
exit 1
else
echo "Looks like you might have docker installed, but the wrong permissions, try this:"
echo ""
echo "> sudo groupadd docker"
echo "> sudo usermod -aG docker $USER"
echo "> newgrp docker"
echo ""
echo "And run this command again."
echo ""
exit 1
fi
fi
# Import env vars
if [ -f "$PWD/.env" ]; then
printf "Loading env vars...\n"
export $(grep -v '^#' .env | xargs)
echo ""
fi
# Generate application service data
if [ -z "$AS_UNIQUE_ID" ]; then
CURRENT=$(date +%s)
AS_UNIQUE_ID=${AS_UNIQUE_ID:=$(echo "unique_$CURRENT" | sha1sum | head -c 40)}
AS_HS_TOKEN=${AS_HS_TOKEN:=$(echo "hs_$CURRENT" | sha256sum | head -c 64)}
AS_AS_TOKEN=${AS_AS_TOKEN:=$(echo "as_$CURRENT" | sha256sum | head -c 64)}
fi
if [ -z "$SERVER_HOSTNAME" ]; then
read -p "Type your server's hostname (without https or trailing slashes): "
SERVER_HOSTNAME="$REPLY"
updateEnvFile
printf "\nPlease, create the following records pointing to your server's IP address:\n"
printf " - $SERVER_HOSTNAME\n"
printf " - synapse.$SERVER_HOSTNAME\n"
printf " - element.$SERVER_HOSTNAME\n"
printf " - traefik.$SERVER_HOSTNAME\n"
printf "\n"
fi
if [ -z "$LETSENCRYPT_EMAIL" ]; then
read -p "Type your email address, will be used to issue certificates: "
LETSENCRYPT_EMAIL="$REPLY"
updateEnvFile
printf "\n"
fi
if [ $DEV = true ]; then
printf "Which Rocket.Chat image you want to use? (current: ";
if [ -z "${RC_IMAGE}" ]; then
printf "none"
else
printf "$RC_IMAGE"
fi
printf ")\n"
RC_IMAGE_DEFAULT_OPTION=1
if ! [ -z "${RC_IMAGE}" ]; then
printf "0) keep current\n"
RC_IMAGE_DEFAULT_OPTION=0
fi;
printf "1) latest stable\n"
printf "2) latest dev\n"
printf "3) specific version\n"
printf "4) specific PR\n"
read -p "Pick an option [$RC_IMAGE_DEFAULT_OPTION]: "
case $REPLY in
1)
imageLatestStable
;;
2)
RC_IMAGE="rocket.chat:develop"
ROCKETCHAT_IMAGE_TAG="develop"
alpineImageOrNot
;;
3)
read -p "Type the version: "
RC_IMAGE="rocket.chat:$REPLY"
ROCKETCHAT_IMAGE_TAG="$REPLY"
;;
4)
read -p "Type the PR number: "
RC_IMAGE="ghcr.io/rocketchat/rocket.chat:pr-$REPLY"
ROCKETCHAT_IMAGE_TAG="pr-$REPLY"
;;
*)
if [ "$RC_IMAGE_DEFAULT_OPTION" = 1 ]; then
imageLatestStable
fi
esac
echo ""
else
imageLatestStable
fi
updateEnvFile
# Setup synapse
printf "Setting up Synapse...\n"
mkdir -p data/matrix/synapse
if [ ! -f "$PWD/data/matrix/synapse/homeserver.yaml" ]; then
docker run -it --rm \
-v $(pwd)/data/matrix/synapse:/data \
-e SYNAPSE_SERVER_NAME="$SERVER_HOSTNAME" \
-e SYNAPSE_REPORT_STATS=yes \
-e UID=1000 \
-e GID=1000 \
matrixdotorg/synapse:$SYNAPSE_IMAGE_TAG generate
fi
if ! grep -q "enable_registration: true" data/matrix/synapse/homeserver.yaml; then
sed -i '$ d' data/matrix/synapse/homeserver.yaml
tee -a data/matrix/synapse/homeserver.yaml > /dev/null <<EOF
app_service_config_files:
- /data/registration.yaml
retention:
enabled: true
enable_registration: true
enable_registration_without_verification: true
suppress_key_server_warning: true
federation_ip_range_blacklist:
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
- '100.64.0.0/10'
- '169.254.0.0/16'
- '::1/128'
- 'fe80::/64'
- 'fc00::/7'
database:
name: psycopg2
args:
user: synapse
password: itsasecret
database: synapse
host: postgres
cp_min: 5
cp_max: 10
redis:
enabled: true
host: redis
port: 6379
allow_public_rooms_without_auth: true
allow_public_rooms_over_federation: true
# vim:ft=yaml
EOF
fi
tee data/matrix/synapse/registration.yaml > /dev/null <<EOF
id: rocketchat_$AS_UNIQUE_ID
hs_token: $AS_HS_TOKEN
as_token: $AS_AS_TOKEN
url: http://172.17.0.1:$RC_BRIDGE_PORT
sender_localpart: rocket.cat
de.sorunome.msc2409.push_ephemeral: true
namespaces:
users:
- exclusive: false
regex: .*
rooms:
- exclusive: false
regex: .*
aliases:
- exclusive: false
regex: .*
rocketchat:
homeserver_url: http://synapse:8008
homeserver_domain: $SERVER_HOSTNAME
EOF
# Setup Nginx
printf "\nSetting up Nginx...\n"
mkdir -p data/matrix/nginx
> data/matrix/nginx/matrix.conf
tee -a data/matrix/nginx/matrix.conf > /dev/null <<EOF
server {
listen 80;
listen $RC_BRIDGE_PORT;
server_name $SERVER_HOSTNAME;
location /.well-known/matrix/server {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.server": "synapse.$SERVER_HOSTNAME:443"}';
}
location /.well-known/matrix/client {
access_log off;
add_header Access-Control-Allow-Origin *;
default_type application/json;
return 200 '{"m.homeserver": {"base_url": "https://synapse.$SERVER_HOSTNAME"}}';
}
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
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_pass http://rocketchat:3000;
proxy_read_timeout 90;
}
}
EOF
# Setup Traefik
printf "\nSetting up Traefik...\n"
mkdir -p data/traefik/config
# - Setup ACME
if [ ! -f "$PWD/data/traefik/acme.json" ]; then
echo "{}" > data/traefik/acme.json
chmod 0600 data/traefik/acme.json
fi
# - Setup config: middlewares
tee data/traefik/config/middlewares.yml > /dev/null <<'EOF'
http:
middlewares:
httpsredirect:
redirectScheme:
scheme: https
permanent: true
EOF
# - Setup config: routers
tee data/traefik/config/routers.yml > /dev/null <<'EOF'
http:
routers:
redirecttohttps:
entryPoints:
- "web"
middlewares:
- "httpsredirect"
rule: "HostRegexp(`{host:.+}`)"
service: "noop@internal"
EOF
# - Setup traefik.yml
tee data/traefik/traefik.yml > /dev/null <<EOF
entryPoints:
web:
address: ":80"
web-secure:
address: ":443"
api:
dashboard: true
insecure: true
providers:
file:
directory: "/config"
watch: true
docker:
endpoint: "unix:///var/run/docker.sock"
network: "federation"
watch: true
exposedByDefault: false
certificatesResolvers:
letsencrypt:
acme:
caServer: https://acme-v02.api.letsencrypt.org/directory
email: "$LETSENCRYPT_EMAIL"
storage: "/acme.json"
httpChallenge:
entryPoint: "web"
EOF
# Setup Element
printf "\nSetting up Element...\n"
mkdir -p data/element
tee data/element/config.json > /dev/null <<EOF
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://synapse.$SERVER_HOSTNAME",
"server_name": "$SERVER_HOSTNAME"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"hosting_signup_link": "https://element.io/matrix-services?utm_source=element-web&utm_medium=web",
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"uisi_autorageshake_app": "element-auto-uisi",
"showLabsSettings": true,
"piwik": {
"url": "https://piwik.riot.im/",
"siteId": 1,
"policyUrl": "https://element.io/cookie-policy"
},
"roomDirectory": {
"servers": [
"matrix.org",
"gitter.im",
"libera.chat"
]
},
"enable_presence_by_hs_url": {
"https://matrix.org": false,
"https://matrix-client.matrix.org": false
},
"terms_and_conditions_links": [
{
"url": "https://element.io/privacy",
"text": "Privacy Policy"
},
{
"url": "https://element.io/cookie-policy",
"text": "Cookie Policy"
}
],
"hostSignup": {
"brand": "Element Home",
"cookiePolicyUrl": "https://element.io/cookie-policy",
"domains": [
"matrix.org"
],
"privacyPolicyUrl": "https://element.io/privacy",
"termsOfServiceUrl": "https://element.io/terms-of-service",
"url": "https://ems.element.io/element-home/in-app-loader"
},
"sentry": {
"dsn": "https://029a0eb289f942508ae0fb17935bd8c5@sentry.matrix.org/6",
"environment": "develop"
},
"posthog": {
"projectApiKey": "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
"apiHost": "https://posthog.element.io"
},
"features": {
"feature_spotlight": true
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
}
EOF
# Setup PostgresSQL
printf "\nSetting up PostgreSQL...\n"
mkdir -p data/postgres/data
# Setup Docker Compose
printf "\nSetting up Docker Compose...\n"
tee docker-compose.yml > /dev/null <<'EOF'
services:
traefik:
image: "traefik:${TRAEFIK_IMAGE_TAG}"
restart: "unless-stopped"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./data/traefik/traefik.yml:/etc/traefik/traefik.yml:ro"
- "./data/traefik/config:/config:ro"
- "./data/traefik/acme.json:/acme.json"
labels:
- "traefik.enable=true"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
- "traefik.http.routers.traefik.rule=Host(`traefik.$SERVER_HOSTNAME`)"
- "traefik.http.routers.traefik.entrypoints=web-secure"
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
networks:
- internal
postgres:
image: "postgres:14"
restart: "unless-stopped"
environment:
POSTGRES_PASSWORD: itsasecret
POSTGRES_USER: synapse
POSTGRES_DB: synapse
POSTGRES_INITDB_ARGS: "--encoding='UTF8' --lc-collate='C' --lc-ctype='C'"
volumes:
- "./data/postgres/data:/var/lib/postgresql/data"
networks:
- internal
redis:
image: "redis:${REDIS_IMAGE_TAG}"
restart: "unless-stopped"
networks:
- internal
synapse:
image: "matrixdotorg/synapse:${SYNAPSE_IMAGE_TAG}"
restart: "unless-stopped"
environment:
SYNAPSE_CONFIG_DIR: "/data"
SYNAPSE_CONFIG_PATH: "/data/homeserver.yaml"
UID: "1000"
GID: "1000"
TZ: "America/New_York"
volumes:
- "./data/matrix/synapse:/data"
ports:
- 8008:8008
- 8448:8448
labels:
- "traefik.enable=true"
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
- "traefik.http.routers.synapse.rule=Host(`synapse.$SERVER_HOSTNAME`)"
- "traefik.http.routers.synapse.entrypoints=web-secure"
- "traefik.http.routers.synapse.tls=true"
- "traefik.http.routers.synapse.tls.certresolver=letsencrypt"
networks:
- internal
nginx:
image: "nginx:${NGINX_IMAGE_TAG}"
restart: "unless-stopped"
volumes:
- "./data/matrix/nginx/matrix.conf:/etc/nginx/conf.d/matrix.conf"
labels:
- "traefik.enable=true"
- "traefik.http.services.nginx.loadbalancer.server.port=80"
- "traefik.http.routers.nginx.rule=Host(`$SERVER_HOSTNAME`)"
- "traefik.http.routers.nginx.entrypoints=web-secure"
- "traefik.http.routers.nginx.tls=true"
- "traefik.http.routers.nginx.tls.certresolver=letsencrypt"
networks:
- internal
element:
image: vectorim/element-web:${ELEMENT_IMAGE_TAG}
restart: unless-stopped
volumes:
- ./data/element/config.json:/app/config.json
labels:
- "traefik.enable=true"
- "traefik.http.services.element.loadbalancer.server.port=80"
- "traefik.http.routers.element.rule=Host(`element.$SERVER_HOSTNAME`)"
- "traefik.http.routers.element.entrypoints=web-secure"
- "traefik.http.routers.element.tls=true"
- "traefik.http.routers.element.tls.certresolver=letsencrypt"
networks:
- internal
rocketchat:
image: "${RC_IMAGE}"
command: >
bash -c
"for i in `seq 1 30`; do
node main.js &&
s=$$? && break || s=$$?;
echo \"Tried $$i times. Waiting 5 secs...\";
sleep 5;
done; (exit $$s)"
restart: unless-stopped
volumes:
- ./uploads:/app/uploads
- ./data/matrix/synapse/registration.yaml:/app/matrix-federation-config/registration.yaml
environment:
- PORT=3000
- ROOT_URL=https://localhost:3000
- MONGO_URL=mongodb://mongodb:27017/rocketchat?replicaSet=rs0&directConnection=true
- MONGO_OPLOG_URL=mongodb://mongodb:27017/local?replicaSet=rs0&directConnection=true
- NODE_ENV=production
# - REG_TOKEN=${REG_TOKEN}
depends_on:
- mongodb
ports:
- 3000:3000
- 3300:3300
networks:
- internal
# labels:
# - "traefik.enable=true"
# - "traefik.http.services.rc.loadbalancer.server.port=3000"
# - "traefik.http.routers.rc.rule=Host(`$SERVER_HOSTNAME`)"
# - "traefik.http.routers.rc.entrypoints=web-secure"
# - "traefik.http.routers.rc.tls=true"
# - "traefik.http.routers.rc.tls.certresolver=letsencrypt"
# - "traefik.http.services.bridge.loadbalancer.server.port=$RC_BRIDGE_PORT"
# - "traefik.http.routers.bridge.rule=Host(`$SERVER_HOSTNAME`)"
# - "traefik.http.routers.bridge.entrypoints=web-secure"
# - "traefik.http.routers.bridge.tls=true"
# - "traefik.http.routers.bridge.tls.certresolver=letsencrypt"
mongodb:
image: mongo:5.0
restart: unless-stopped
volumes:
- ./data/mongo/db:/data/db
command: mongod --oplogSize 128 --replSet rs0
labels:
- "traefik.enable=false"
networks:
- internal
# this container's job is just run the command to initialize the replica set.
# it will run the command and remove himself (it will not stay running)
mongo-init-replica:
image: mongo:5.0
command: >
bash -c
"for i in `seq 1 30`; do
mongo mongodb/rocketchat --eval \"
rs.initiate({
_id: 'rs0',
members: [ { _id: 0, host: 'localhost:27017' } ]})\" &&
s=$$? && break || s=$$?;
echo \"Tried $$i times. Waiting 5 secs...\";
sleep 5;
done; (exit $$s)"
depends_on:
- mongodb
networks:
- internal
networks:
internal:
attachable: true
EOF
# Rocket.Chat Matrix Federation Tester
# Author: Alan Sikora <alan.sikora@rocket.chat>
# https://github.com/alansikora
echo ""
echo "Rocket.Chat Matrix Federation Tester"
echo ""
checkURL () {
echo ""
printf "Checking $URL_TO_TEST..."
RESPONSE=$(curl -s -L $URL_TO_TEST)
if [[ "$RESPONSE" == *"$EXPECTED_URL_SUBSTRING"* ]]; then
echo "OK!"
else
echo "$URL_TO_TEST is not as expected"
exit 1
fi
}
# Import env vars
if [ -f "$PWD/.env" ]; then
printf "Loading env vars...\n"
export $(grep -v '^#' .env | xargs)
fi
if [ -z "${SERVER_HOSTNAME}" ]; then
echo "You must run the setup script first and have a .env file on your directory"
exit 1
fi
# Check if synapse URL is correct
URL_TO_TEST="https://synapse.$SERVER_HOSTNAME"
EXPECTED_URL_SUBSTRING="<h1>It works! Synapse is running</h1>"
checkURL
# Check if client's well known is correct
URL_TO_TEST="https://$SERVER_HOSTNAME/.well-known/matrix/client"
EXPECTED_URL_SUBSTRING="\"base_url\": \"https://synapse.$SERVER_HOSTNAME\""
checkURL
# Check if servers's well known is correct
URL_TO_TEST="https://$SERVER_HOSTNAME/.well-known/matrix/server"
EXPECTED_URL_SUBSTRING="\"synapse.$SERVER_HOSTNAME:443\""
checkURL
# Check if Element's URL is correct
URL_TO_TEST="https://element.$SERVER_HOSTNAME/"
EXPECTED_URL_SUBSTRING="<noscript>Sorry, Element requires JavaScript to be enabled.</noscript>"
checkURL
# Check if Rocket.Chat's URL is correct
URL_TO_TEST="https://$SERVER_HOSTNAME/"
EXPECTED_URL_SUBSTRING="<title>Rocket.Chat</title>"
checkURL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment