forked from Fediversity/Fediversity
318 lines
9.1 KiB
Markdown
318 lines
9.1 KiB
Markdown
---
|
|
gitea: none
|
|
include_toc: true
|
|
---
|
|
|
|
# Reverse proxy with nginx
|
|
|
|
Clients connecting from the Internet to our Matrix environment will usually
|
|
use SSL/TLS to encrypt whatever they want to send. This is one thing that
|
|
nginx does better than Synapse.
|
|
|
|
Furthermore, granting or denying access to specific endpoints is much easier
|
|
in nginx.
|
|
|
|
Synapse listens only on localhost, so nginx has to pass connections on from
|
|
the wild west that is the Internet to our server listening on the inside.
|
|
|
|
|
|
# Installing
|
|
|
|
Installing nginx and the [Let's Encrypt](https://letsencrypt.org/) plugin is
|
|
easy:
|
|
|
|
```
|
|
apt install nginx python3-certbot-nginx
|
|
```
|
|
|
|
Get your certificate for the base domain (which is probably not the machine on which
|
|
we're going to run Synapse):
|
|
|
|
```
|
|
certbot certonly --nginx --agree-tos -m system@example.com --non-interactive -d example.com
|
|
```
|
|
|
|
Get one for the machine on which we are going to run Synapse too:
|
|
|
|
```
|
|
certbot certonly --nginx --agree-tos -m system@example.com --non-interactive -d matrix.example.com
|
|
```
|
|
|
|
Substitute the correct e-mailaddress and FQDN, or course.
|
|
|
|
|
|
## Automatic renewal {#certrenew}
|
|
|
|
Certificates have a limited lifetime, and need to be updated every once in a
|
|
while. This should be done automatically by Certbot, see if `systemctl
|
|
list-timers` lists `certbot.timer`.
|
|
|
|
However, renewing the certificate means you'll have to restart the software
|
|
that's using it. We have 2 or 3 pieces of software that use certificates:
|
|
[coturn](../cotorun) and/or [LiveKit](../livekit), and [nginx](../nginx).
|
|
|
|
Coturn/LiveKit are special with regards to the certificate, see their
|
|
respective pages. For nginx it's pretty easy: tell Letsencrypt to restart it
|
|
after a renewal.
|
|
|
|
You do this by adding this line to the `[renewalparams]` in
|
|
`/etc/letsencrypt/renewal/<certificate name>.conf`:
|
|
|
|
```
|
|
renew_hook = systemctl try-reload-or-restart nginx
|
|
```
|
|
|
|
|
|
# Configuration of domain name {#configdomain}
|
|
|
|
Let's start with the configuration on the webserver that runs on the domain
|
|
name itself, in this case `example.com`.
|
|
|
|
Almost all traffic should be encrypted, so a redirect from http to https seems
|
|
like a good idea.
|
|
|
|
However, `.well-known/matrix/client` has to be available via http and https,
|
|
so that should *NOT* be redirected to https. Some clients don't understand the
|
|
redirect and will therefore not find the server if you redirect everything.
|
|
|
|
Under the `server_name` (the "domain name", the part after the username) you
|
|
will need a configuration like this:
|
|
|
|
```
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/ssl/dhparams.pem;
|
|
|
|
server_name example.com;
|
|
|
|
location /.well-known/matrix/client {
|
|
return 200 '{
|
|
"m.homeserver": {"base_url": "https://matrix.example.com"},
|
|
}';
|
|
default_type application/json;
|
|
}
|
|
|
|
location /.well-known/matrix/server {
|
|
return 200 '{"m.server": "matrix.example.com"}';
|
|
default_type application/json;
|
|
}
|
|
|
|
location / {
|
|
if ($scheme = http) {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
access_log /var/log/nginx/example_com-access.log;
|
|
error_log /var/log/nginx/example_com-error.log;
|
|
|
|
}
|
|
```
|
|
|
|
This defines a server that listens on both http and https. It hands out two
|
|
.well-known entries over both http and https, and every other request over
|
|
http is forwarded to https.
|
|
|
|
Be sure to substitute the correct values for `server_name`, `base_url` and the
|
|
certificate files (and [renew the certificate](#renewcert)).
|
|
|
|
See this [full configuration example](domain.conf) with some extra stuff.
|
|
|
|
|
|
# Configuration of the reverse proxy
|
|
|
|
For the actual proxy in front of Synapse, this is what you need: forward ports
|
|
443 and 8448 to Synapse, listening on localhost, and add a few headers so
|
|
Synapse know's who's on the other side of the line.
|
|
|
|
```
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
# For the federation port
|
|
listen 8448 ssl default_server;
|
|
listen [::]:8448 ssl default_server;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/ssl/dhparams.pem;
|
|
|
|
server_name matrix.example.com;
|
|
|
|
location ~ ^(/_matrix|/_synapse/client) {
|
|
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;
|
|
client_max_body_size 50M;
|
|
proxy_http_version 1.1;
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
Again, substitute the correct values. Don't forget to open the relevant ports
|
|
in the firewall. Ports 80 and 443 may already be open, 8448 is probably not.
|
|
|
|
This is a very, very basic configuration; just enough to give us a working
|
|
service. See this [complete example](revproxy.conf) which also includes
|
|
[Draupnir](../draupnir) and a protected admin endpoint.
|
|
|
|
|
|
# Synapse-admin {#synapse-admin}
|
|
|
|
If you also [install Synapse-Admin](../synapse-admin), you'll want to create
|
|
another vhost, something like this:
|
|
|
|
```
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/admin.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/ssl/dhparams.pem;
|
|
|
|
server_name admin.example.com;
|
|
|
|
root /var/www/synapse-admin;
|
|
|
|
access_log /var/log/nginx/admin-access.log;
|
|
error_log /var/log/nginx/admin-error.log;
|
|
}
|
|
```
|
|
|
|
You'll need an SSL certificate for this, of course. But you'll also need to
|
|
give it access to the `/_synapse/admin` endpoint in Synapse.
|
|
|
|
You don't want this endpoint to be available for just anybody on the Internet,
|
|
so restrict access to the IP-addresses from which you expect to use
|
|
Synapse-Admin.
|
|
|
|
In `/etc/nginx/sites-available/synapse` you want to add this bit:
|
|
|
|
```
|
|
location ~ ^/_synapse/admin {
|
|
allow 127.0.0.1;
|
|
allow ::1;
|
|
allow 111.222.111.222;
|
|
allow dead:beef::/64;
|
|
deny all;
|
|
|
|
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;
|
|
client_max_body_size 50M;
|
|
proxy_http_version 1.1;
|
|
}
|
|
```
|
|
|
|
This means access to `/_synapse/admin` is only allowed for the addresses
|
|
mentioned, but will be forwarded to Synapse in exactly the same way as
|
|
"normal" requests.
|
|
|
|
|
|
# LiveKit {#livekit}
|
|
|
|
If you run an SFU for Element Call, you need a virtual host for LiveKit. Make
|
|
sure you install, configure and run [Element Call LiveKit](../element-call#livekit).
|
|
Then create a virtual host much like this:
|
|
|
|
```
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/livekit.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/livekit.example.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/ssl/dhparams.pem;
|
|
|
|
server_name livekit.example.com;
|
|
|
|
# This is lk-jwt-service
|
|
location ~ ^(/sfu/get|/healthz) {
|
|
proxy_pass http://[::1]:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-Server $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;
|
|
}
|
|
|
|
location / {
|
|
proxy_pass http://[::1]:7880;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-Server $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;
|
|
}
|
|
|
|
access_log /var/log/nginx/livekit-access.log;
|
|
error_log /var/log/nginx/livekit-error.log;
|
|
}
|
|
```
|
|
|
|
|
|
# Element Call widget {#callwidget}
|
|
|
|
If you self-host the [Element Call widget](../element-call#widget), this
|
|
should be the configuration to publish that:
|
|
|
|
```
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/call.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/call.example.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/ssl/dhparams.pem;
|
|
|
|
server_name call.example.com;
|
|
|
|
root /var/www/element-call;
|
|
|
|
location /assets {
|
|
add_header Cache-Control "public, immutable, max-age=31536000";
|
|
}
|
|
|
|
location /apple-app-site-association {
|
|
default_type application/json;
|
|
}
|
|
|
|
location /^config.json$ {
|
|
alias public/config.json;
|
|
default_type application/json;
|
|
}
|
|
|
|
location / {
|
|
try_files $uri /$uri /index.html;
|
|
add_header Cache-Control "public, max-age=30, stale-while-revalidate=30";
|
|
}
|
|
|
|
access_log /var/log/nginx/call-access.log;
|
|
error_log /var/log/nginx/call-error.log;
|
|
}
|
|
```
|
|
|
|
|
|
# Firewall
|
|
|
|
For normal use, at least ports 80 and 443 must be openend, see [Firewall](../firewall).
|