355 lines
11 KiB
Markdown
355 lines
11 KiB
Markdown
---
|
|
layout: post
|
|
author: Sam Hadow
|
|
tags: messaging podman sysadmin
|
|
---
|
|
|
|
In this blog post I'll describe how to self host a [matrix.org](https://matrix.org/) instance using podman. The homeserver used will be synapse.
|
|
|
|
# What are matrix.org and synapse?
|
|
|
|
First a short introduction:
|
|
matrix.org is an open source, secure and decentralized communication protocol. Secure because it defines how end to end encryption should be implemented in the clients.
|
|
With matrix.org you have [homeservers](https://matrix.org/ecosystem/servers/) and [clients](https://matrix.org/ecosystem/clients/). Homeservers are what the clients connect to and can federate with each other *(meaning someone with an account on homeserver A can talk to someone with an account on homeserver B)*.
|
|
[Bridges](https://matrix.org/ecosystem/bridges/) to other messaging services can also be hosted alongside a homeserver. Hosting your own homeserver allows you to host the bridges you want.
|
|
In this blog post I explain how to self host a specific homeserver, which is maintained by the matrix.org foundation: synapse.
|
|
|
|
# self hosting synapse
|
|
|
|
## initial setup
|
|
|
|
### creating secret and directories
|
|
|
|
Create the database secret not to expose it in your configuration files:
|
|
|
|
```bash
|
|
echo -n "<postgres-pass>" > /tmp/secret
|
|
podman secret create synapse_postgres_pass /tmp/secret
|
|
```
|
|
|
|
Adapt the path to your own path for the directories:
|
|
|
|
```bash
|
|
mkdir -p /home/data/podman/synapse/{db,config,media,logs}
|
|
```
|
|
|
|
### generating the configuration file
|
|
|
|
*(adapt your-domain to your own, for example: example.org)*
|
|
|
|
```bash
|
|
podman pod create --name synapse -p 8008:8008 -m=2048m
|
|
|
|
podman run -it --rm \
|
|
-v /home/data/podman/synapse/config:/data:Z \
|
|
-e SYNAPSE_SERVER_NAME=<your-domain> \
|
|
-e SYNAPSE_REPORT_STATS=no \
|
|
docker.io/matrixdotorg/synapse:latest generate
|
|
```
|
|
|
|
### modifying the configuration file
|
|
|
|
The previous command will generate a homeserver.yaml file. You'll have to modify this file before using synapse. You'll have to modify at least the following parts:
|
|
|
|
+ modify the database part *(replace the password)*
|
|
```yaml
|
|
database:
|
|
name: psycopg2
|
|
txn_limit: 10000
|
|
args:
|
|
user: synapse
|
|
password: <POSTGRES_PASSWORD>
|
|
dbname: synapse
|
|
host: 127.0.0.1
|
|
port: 5432
|
|
cp_min: 5
|
|
cp_max: 10
|
|
```
|
|
+ set `enable_registration` to false unless you want users to register freely on your instance.
|
|
+ set a shared secret file with `registration_shared_secret_path` to have access to an [API](https://element-hq.github.io/synapse/latest/admin_api/register_api.html) to create users. Be sure to use a secure secret as anyone having this secret can register on your instance as an admin. Also keep in mind the path is relative to the container, not the host.
|
|
|
|
|
|
### reverse proxy
|
|
|
|
In this guide the port used is 8008, we'll use nginx to server the synapse homeserver on the port 443.
|
|
*(adapt your-domain to your own)*
|
|
This part is important for users to have the name user@your-domain while hosting synapse on a subdomain. And it's also important for clients and other homeserver to recognize your server.
|
|
|
|
in sites-available:
|
|
|
|
```nginx
|
|
# synapse.conf
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
server_name synapse.<your-domain>;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/<your-domain>/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/<your-domain>/privkey.pem;
|
|
|
|
location /.well-known/matrix/server {
|
|
return 200 '{"m.server": "synapse.<your-domain>:443"}';
|
|
add_header Content-Type application/json;
|
|
}
|
|
location /.well-known/matrix/client {
|
|
return 200 '{"m.homeserver": {"base_url": "https://synapse.<your-domain>"},"m.identity_server": {"base_url": "https://vector.im"}}';
|
|
add_header Content-Type application/json;
|
|
add_header "Access-Control-Allow-Origin" *;
|
|
}
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8008;
|
|
proxy_set_header X-Forwarded-For $remote_addr;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Host $host;
|
|
|
|
# Nginx by default only allows file uploads up to 1M in size
|
|
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
|
|
client_max_body_size 256M;
|
|
|
|
# Synapse responses may be chunked, which is an HTTP/1.1 feature.
|
|
proxy_http_version 1.1;
|
|
}
|
|
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
}
|
|
```
|
|
|
|
In your http.conf add this snippet in the https server block:
|
|
|
|
```nginx
|
|
location /.well-known/matrix/server {
|
|
return 200 '{"m.server": "synapse.<your-domain>:443"}';
|
|
add_header Content-Type application/json;
|
|
add_header "Access-Control-Allow-Origin" *;
|
|
}
|
|
|
|
location /.well-known/matrix/client {
|
|
return 200 '{"m.homeserver": {"base_url": "https://synapse.<your-domain>"},"m.identity_server": {"base_url": "https://vector.im"}}';
|
|
add_header Content-Type application/json;
|
|
add_header "Access-Control-Allow-Origin" *;
|
|
}
|
|
```
|
|
|
|
## running the pod
|
|
|
|
### with podman command line
|
|
|
|
Again adapt the path:
|
|
*The database version can be different but it needs to be pinned to a specific version to avoid issues with updates. You'll have to manually update this container.*
|
|
|
|
```bash
|
|
podman run -d --pod=synapse \
|
|
--secret synapse_postgres_pass,type=env,target=POSTGRES_PASSWORD \
|
|
-e POSTGRES_DB="synapse" \
|
|
-e POSTGRES_USER="synapse" \
|
|
-e POSTGRES_INITDB_ARGS="--encoding=UTF-8 --lc-collate=C --lc-ctype=C" \
|
|
-v /home/data/podman/synapse/db:/var/lib/postgresql/data:Z \
|
|
--name=synapse-db \
|
|
docker.io/library/postgres:16
|
|
|
|
podman run -d --pod=synapse \
|
|
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
|
|
-v /home/data/podman/synapse/config:/data:Z \
|
|
-v /home/data/podman/synapse/media:/data/media:z \
|
|
-v /home/data/podman/synapse/logs:/data/logs:Z \
|
|
--name=synapse-app \
|
|
--label io.containers.autoupdate=registry docker.io/matrixdotorg/synapse:latest
|
|
```
|
|
|
|
Then you can generate the systemd services:
|
|
|
|
```bash
|
|
cd ~/.config/systemd/user/
|
|
podman generate systemd --restart-policy=on-failure --files --new --name synapse
|
|
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now pod-synapse.service
|
|
```
|
|
|
|
### with systemd services directly
|
|
|
|
Create these files in `~/.config/systemd/user/`.
|
|
|
|
```ini
|
|
# pod-synapse.service
|
|
|
|
[Unit]
|
|
Description=Podman pod-synapse.service
|
|
Documentation=man:podman-generate-systemd(1)
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
RequiresMountsFor=/run/user/1000/containers
|
|
Wants=container-synapse-app.service container-synapse-db.service
|
|
Before=container-synapse-app.service container-synapse-db.service
|
|
|
|
[Service]
|
|
Environment=PODMAN_SYSTEMD_UNIT=%n
|
|
Restart=on-failure
|
|
TimeoutStopSec=70
|
|
ExecStartPre=/usr/bin/podman pod create \
|
|
--infra-conmon-pidfile %t/pod-synapse.pid \
|
|
--pod-id-file %t/pod-synapse.pod-id \
|
|
--exit-policy=stop \
|
|
--name synapse \
|
|
-p 8008:8008 \
|
|
-m=2048m \
|
|
--replace
|
|
ExecStart=/usr/bin/podman pod start \
|
|
--pod-id-file %t/pod-synapse.pod-id
|
|
ExecStop=/usr/bin/podman pod stop \
|
|
--ignore \
|
|
--pod-id-file %t/pod-synapse.pod-id \
|
|
-t 10
|
|
ExecStopPost=/usr/bin/podman pod rm \
|
|
--ignore \
|
|
-f \
|
|
--pod-id-file %t/pod-synapse.pod-id
|
|
PIDFile=%t/pod-synapse.pid
|
|
Type=forking
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
```ini
|
|
# container-synapse-db.service
|
|
[Unit]
|
|
Description=Podman container-synapse-db.service
|
|
Documentation=man:podman-generate-systemd(1)
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
RequiresMountsFor=%t/containers
|
|
BindsTo=pod-synapse.service
|
|
After=pod-synapse.service
|
|
|
|
[Service]
|
|
Environment=PODMAN_SYSTEMD_UNIT=%n
|
|
Restart=on-failure
|
|
TimeoutStopSec=70
|
|
ExecStart=/usr/bin/podman run \
|
|
--cidfile=%t/%n.ctr-id \
|
|
--cgroups=no-conmon \
|
|
--rm \
|
|
--pod-id-file %t/pod-synapse.pod-id \
|
|
--sdnotify=conmon \
|
|
--replace \
|
|
-d \
|
|
--secret synapse_postgres_pass,type=env,target=POSTGRES_PASSWORD \
|
|
-e POSTGRES_DB=synapse \
|
|
-e POSTGRES_USER=synapse \
|
|
-e "POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C" \
|
|
-v /home/data/podman/synapse/db:/var/lib/postgresql/data:Z \
|
|
--name=synapse-db \
|
|
docker.io/library/postgres:16
|
|
ExecStop=/usr/bin/podman stop \
|
|
--ignore -t 10 \
|
|
--cidfile=%t/%n.ctr-id
|
|
ExecStopPost=/usr/bin/podman rm \
|
|
-f \
|
|
--ignore -t 10 \
|
|
--cidfile=%t/%n.ctr-id
|
|
Type=notify
|
|
NotifyAccess=all
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
```ini
|
|
# container-synapse-app.service
|
|
|
|
[Unit]
|
|
Description=Podman container-synapse-app.service
|
|
Documentation=man:podman-generate-systemd(1)
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
RequiresMountsFor=%t/containers
|
|
BindsTo=pod-synapse.service
|
|
After=pod-synapse.service
|
|
|
|
[Service]
|
|
Environment=PODMAN_SYSTEMD_UNIT=%n
|
|
Restart=on-failure
|
|
TimeoutStopSec=70
|
|
ExecStart=/usr/bin/podman run \
|
|
--cidfile=%t/%n.ctr-id \
|
|
--cgroups=no-conmon \
|
|
--rm \
|
|
--pod-id-file %t/pod-synapse.pod-id \
|
|
--sdnotify=conmon \
|
|
--replace \
|
|
-d \
|
|
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
|
|
-v /home/data/podman/synapse/config:/data:Z \
|
|
-v /home/data/podman/synapse/media:/data/media:z \
|
|
-v /home/data/podman/synapse/logs:/data/logs:Z \
|
|
--name=synapse-app \
|
|
--label io.containers.autoupdate=registry docker.io/matrixdotorg/synapse:latest
|
|
ExecStop=/usr/bin/podman stop \
|
|
--ignore -t 10 \
|
|
--cidfile=%t/%n.ctr-id
|
|
ExecStopPost=/usr/bin/podman rm \
|
|
-f \
|
|
--ignore -t 10 \
|
|
--cidfile=%t/%n.ctr-id
|
|
Type=notify
|
|
NotifyAccess=all
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
## managing the homeserver
|
|
|
|
### registering users
|
|
|
|
With access to the server, creating the user interactively:
|
|
|
|
```bash
|
|
podman exec -it synapse-app /bin/sh
|
|
register_new_matrix_user --user <username> --config /data/homeserver.yaml
|
|
```
|
|
|
|
Or with the registration API, you can use [this script](https://git.hadow.fr/sam.hadow/Useful-scripts/src/branch/main/create_matrix_account.sh).
|
|
|
|
## what about bridges?
|
|
|
|
Bridges allow you to receive message from other services in matrix. In this section I'll give an example on how to self host a bridge alongside your matrix instance with the discord bridge.
|
|
|
|
### discord bridge example
|
|
|
|
Creating the initial bridge files:
|
|
|
|
```bash
|
|
mkdir -p /home/data/podman/synapse/mautrix-discord
|
|
podman run --rm -v /home/data/podman/synapse/mautrix-discord:/data:z dock.mau.dev/mautrix/discord:latest
|
|
```
|
|
|
|
Create the database: *(adapt the password)*
|
|
|
|
```bash
|
|
podman exec -it synapse-db /bin/bash
|
|
psql -U synapse
|
|
CREATE USER discordmautrix WITH PASSWORD 'password';
|
|
CREATE DATABASE discordmautrix WITH OWNER = 'discordmautrix' ;
|
|
```
|
|
|
|
Then delete synapse container and recreate it with the bridge registration file: *(don't forget to regenerate, or adapt, the systemd service)*
|
|
|
|
```bash
|
|
podman run -d --pod=synapse \
|
|
-e SYNAPSE_CONFIG_PATH=/data/homeserver.yaml \
|
|
-v /home/data/podman/synapse/config:/data:Z \
|
|
-v /home/data/podman/synapse/media:/data/media:z \
|
|
-v /home/data/podman/synapse/logs:/data/logs:Z \
|
|
-v /home/data/podman/synapse/mautrix-discord-registration.yaml:/data/mautrix-discord-registration.yaml:Z \
|
|
--name=synapse-app \
|
|
--label io.containers.autoupdate=registry docker.io/matrixdotorg/synapse:latest
|
|
```
|
|
|
|
#### using the bridge
|
|
|
|
You'll then have access to a user "discord bridge bot" on your instance where you can log in into your discord account and then bridge your messages.
|