Skip to content

Instantly share code, notes, and snippets.

@alkavan
Last active May 1, 2025 08:06
Show Gist options
  • Save alkavan/326189af0b6c2019794341b576281a8b to your computer and use it in GitHub Desktop.
Save alkavan/326189af0b6c2019794341b576281a8b to your computer and use it in GitHub Desktop.
Synapse + TURN server installation on Fedora Linux 41

Fedora 41 | Synapse+TURN Server Installation

This tutorial shows how to install your own synapse chat server along with coturn server for making video/voice calls on Fedora Linux 41.

Initial System Setup

Update system.

sudo dnf update -y

Set your timezone.

sudo timedatectl set-timezone UTC && date

Install nano text editor, a nice monitor, and terminal multiplexer.

sudo dnf install -y nano htop tmux

Reboot system

sudo reboot

Synapse Chat Server Installation

Install synapse server and nginx to act as proxy:

sudo dnf install -y matrix-synapse nginx python3-lxml

Homeserver configuration

sudo synapse_homeserver -c /etc/synapse/homeserver.yaml \
--generate-config \
--server-name chat.mydomain.com \
--data-directory=/var/db/synapse \
--report-stats=no \
--enable-registration

These settings will enable registrations and will not report metrics.

The /etc/synapse/homeserver.yaml config file should look something like this (add what is missing):

# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "chat.mydomain.com"
pid_file: /var/run/synapse/homeserver.pid
listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    bind_addresses: ['::1', '127.0.0.1']
    resources:
      - names: [client, federation]
        compress: false
database:
  name: sqlite3
  args:
    database: /var/db/synapse/homeserver.db
log_config: "/etc/synapse/chat.mydomain.com.log.config"
media_store_path: /var/db/synapse/media_store
registration_shared_secret: "secret_key"
report_stats: false
macaroon_secret_key: "secret_key"
form_secret: "secret_key"
signing_key_path: "/etc/synapse/chat.mydomain.com.signing.key"
trusted_key_servers:
  - server_name: "matrix.org"

enable_registration: true
enable_registration_without_verification: true

# URL preview settings
url_preview_enabled: true
url_preview_ip_range_blacklist:
  - '127.0.0.0/8'          # Localhost
  - '10.0.0.0/8'           # Private network
  - '172.16.0.0/12'        # Private network
  - '192.168.0.0/16'       # Private network
  - '100.64.0.0/10'        # Carrier-grade NAT
  - '169.254.0.0/16'       # Link-local
  - '::1/128'              # IPv6 localhost
  - 'fc00::/7'             # IPv6 private network
url_preview_url_blacklist: []
max_spider_size: 10M       # Maximum size of pages to fetch (default: 10MB)

# vim:ft=yaml

Ofcourse the enable_registration and enable_registration_without_verification fields are to your own discretion

Notice that secret_key are different and they should be generated by the config generation script -- don't copy/paste these content directly!

For different database settings consult official docs, we are using SQLite3 because it's easy for the sake of this tutorial. However, server with lots of users probably should use different option. Having said that, SQLite works greate for the use-case of a few users.

Edit the log configuration that was just generated and set handlers.file.filename to /var/log/synapse/homeserver.log:

sudo nano /etc/synapse/chat.mydomain.com.log.config

Reset permissions for /etc/synpase directory contents assuming config was created by the root user:

chown synapse:synapse /etc/synapse/*

Create directory to store data and media and give synapse user group write access:

sudo mkdir -p /var/db/synapse/media_store
sudo chown -R root:synapse /var/db/synapse/
sudo chmod -R g+w /var/db/synapse/

Create directory to store log files and give synapse user group write access:

sudo mkdir /var/log/synapse
sudo chown root:synapse /var/log/synapse
sudo chmod g+w /var/log/synapse

Start synapse server and check status:

sudo systemctl enable --now synapse
sudo systemctl status synapse

Check the logs that everything is okay:

cat /var/log/synapse/homeserver.log

Check the server datafiles are stored correctly:

ls -al /var/db/synapse/

Setup nginx backend for the chat server

Create server configuration file:

sudo nano /etc/nginx/conf.d/mydomain.conf

Paste the following configuration inside:

server {
    listen 80;
    listen [::]:80;
    
    server_name chat.mydomain.com;
    root /var/www/synapse;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Start nginx server and check status:

sudo systemctl enable --now nginx
sudo systemctl status nginx

Create a simple index.html page and web directory:

Create directory for web content root:

sudo mkdir -p /var/www/synapse
sudo cat << EOF > /var/www/synapse/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>welcome</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>hey!</body>
</html>
EOF

Set the correct content type for SELinux:

sudo chcon -R -t httpd_sys_content_t /var/www

SSL/TLS Server Certificates

We are using nginx as proxy backend for the synapse server. This allows us to expose the server in a more secure manner and easily manage TLS traffic. This is the reason synapse is configured for HTTP and not HTTPS, nginx provides the security layer.

If you don't have one already -- generate verified SSL certificates for the server:

sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d chat.mydomain.com

Activate certificate renewal timer:

sudo systemctl start certbot-renew.timer
sudo systemctl status certbot-renew.timer

Update nginx server configuration

If you used Let's Encrypt it changed your HTTP server to support TLS, but you can discard it's changes and update to the following config using your domain ofc.

sudo nano /etc/nginx/conf.d/mydomain.conf

Put the following content inside and update for you domain:

# HTTPS server block
server {
    server_name chat.mydomain.com;

    listen 443 ssl;
    listen [::]:443 ssl;

    # For the federation port
    listen 8448 ssl default_server;
    listen [::]:8448 ssl default_server;

    # Enable HTTP2 transport
    http2 on;

    # SSL configuration (adjust paths to your certificates)
    ssl_certificate /etc/letsencrypt/live/chat.mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.mydomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Matrix routing
    location ~ ^(/_matrix|/_synapse/client) {
        # note: do not add a path (even a single /) after the port in `proxy_pass`,
        # otherwise nginx will canonicalise the URI and cause signature verification
        # errors.
        proxy_pass http://localhost: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 50M;

        # Synapse responses may be chunked, which is an HTTP/1.1 feature.
        proxy_http_version 1.1;
    }

    # Serve static files or a webroot for the root path "/"
    root /var/www/synapse;
    index index.html index.htm;

    # Serve the webroot for all other requests
    location / {
        try_files $uri $uri/ /index.html;
    }
}

# HTTP server block (redirect)
server {
    server_name chat.mydomain.com;

    listen 80;
    listen [::]:80;

#    location / {
#        try_files $uri $uri/ /index.html;
#    }

    # Redirect all HTTP traffic to HTTPS
    return 301 https://$host$request_uri;
}

sudo systemctl restart nginx and test the configuration.

TURN Server Installation

This server is 100% optional, only if you want to make super private audio/video calls

A TURN (Traversal Using Relays around NAT) server facilitates communication between devices in WebRTC applications when direct peer-to-peer connections are blocked by NATs or firewalls. It acts as a relay, forwarding data between peers that cannot establish a direct connection due to restrictive network configurations. When a client behind a NAT initiates a connection, the TURN server provides a public IP address and port for relaying traffic, ensuring connectivity even in challenging network environments. This comes at the cost of increased latency and bandwidth usage compared to direct connections, but it ensures reliable communication when other methods, like STUN, fail.

coturn Installation Instructions

Install EPEL package repository and coturn server:

sudo dnf install -y coturn sqlite

Install secret key generation utility:

sudo dnf install -y pwgen

Edit /etc/turnserver.conf to set up coturn:

sudo nano /etc/coturn/turnserver.conf

Update the following configuration fields (uncomment or add):

listening-port=3478
tls-listening-port=5349

# lsiten to all addresses
listening-ip=0.0.0.0
listening-ip=::

# configure external/internal VPS IP
external-ip=134.122.60.115/10.133.0.2

min-port=49152
max-port=65535

use-auth-secret
# shared secret key generated with 'pwgen -s 64 1'
static-auth-secret=DvZPQRfbidYcFGlRdZDM5a3ilkoUmmDIT9GHUiphQ5YOcI60IMkj9xX7JVLkn9RT

# the default settings is for SQLite database.
# other database configuration types in the configuration file.
userdb=/var/db/turndb

realm=chat.mydomain.com

user-quota=4
total-quota=100

no-tcp-relay

# if you have SSL certificate use it here.
# you can create one with Lets Encrypt.
cert=/etc/pki/coturn/public/turn_server_cert.pem
pkey=/etc/pki/coturn/private/turn_server_pkey.pem

log-file=/var/log/coturn/turnserver.log
simple-log

There are some more useful setting in the file, read the descriptions.

Create coturn database file and give permissions:

sudo sqlite3 /var/db/turndb < /usr/share/coturn/schema.sql
sudo chown root:coturn /var/db/turndb
sudo chmod g+w /var/db/turndb

Start server and check status:

sudo systemctl start coturn
sudo systemctl enable coturn
sudo systemctl status coturn

You can also check the server logs:

cat /var/log/coturn/turnserver.log

Edit synapse configuration file:

sudo nano /etc/synapse/homeserver.yaml

Add the following lines:

turn_uris:
  - "turn:chat.mydomain.com?transport=udp"
  - "turn:chat.mydomain.com?transport=tcp"
turn_shared_secret: "your_secret_key_here"
turn_user_lifetime: 86400000
turn_allow_guests: true

remember to use the same secret key as use for the static-auth-secret field in /etc/coturn/turnserver.conf

Restart synapse server:

systemctl restart synapse

Server security

Install and start firewalld service:

sudo dnf install -y firewalld
systemctl start firewalld.service

Allow HTTP/HTTPS traffic:

sudo firewall-cmd --zone=public --permanent --add-service=http
sudo firewall-cmd --zone=public --permanent --add-service=https

Add rules for coturn server:

sudo firewall-cmd --zone=public --permanent --add-port=3478/tcp
sudo firewall-cmd --zone=public --permanent --add-port=3478/udp
sudo firewall-cmd --zone=public --permanent --add-port=5349/tcp
sudo firewall-cmd --zone=public --permanent --add-port=5349/udp
sudo firewall-cmd --zone=public --permanent --add-port=49152-65535/udp

Reload the firewall:

sudo firewall-cmd --reload

Confirm and inspect firewall rules:

sudo firewall-cmd --list-all

A typical setup should like like this:

public (default, active)
  target: default
  ingress-priority: 0
  egress-priority: 0
  icmp-block-inversion: no
  interfaces: eth0 eth1
  sources:
  services: dhcpv6-client http https mdns ssh
  ports: 3478/tcp 3478/udp 5349/tcp 5349/udp 49152-65535/udp
  protocols:
  forward: yes
  masquerade: yes
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

That is it! Note that the following configuration should allow you making private and secure 1-on-1 video and audio calls on your synapse server. It might even enable some limited conference calls, however to have the full conference call functionality a more advanced setup is required, and more services. I might add that in future updates.

Remember! Synapse will use the local network for making calls if such option is detected, so to test peer-to-peer call properly client should be on different networks, for example, mobile network not WiFi!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment