Setup of a niltalk chatserver behind an Apache reverse https proxy
Since our generic proxy approach does not work out of the box for the niltalk chat server, we will see in this post how to put everything together for this case.
We assume thet we will run the talk server at the address https://talk.example.com
.
Installing niltalk
Let’s login to out target machine and be sure, that we have no open port 9000 to the outer world.
If we do not want to build the server by ourselves, we get the binary:
user@talk.example.com:/home/user$ wget https://github.com/knadh/niltalk/releases/download/v0.1.1/niltalk_0.1.1_linux_amd64.tar.gz
After unpacking, we create a target directory
user@talk.example.com:/home/user$ sudo mkdir /opt/niltalk
and copy the binary there
user@talk.example.com:/home/user$ sudo cp niltalk /opt/niltalk
For the static web templates, we need to clone the niltalk git-respository
user@talk.example.com:/home/user$ git clone https://github.com/knadh/niltalk.git
and to copy the static
directory also to the target folder:
user@talk.example.com:/home/user$ sudo cp niltalk/static /opt/niltalk
To create a configuration, we have to run the server with the command
user@talk.example.com:/opt/niltalk$ sudo niltalk --new-config
This creates a file config.toml
that we can amend later on as needed. For now, it makes sense to set the storage to memory
:
# Storage kind, one of redis|memory|fs.
storage = "memory"
# Redis cache server.
# Rooms are cached until they expires. Messages are not cached.
#[store]
#address = "redis:6379" # Eg: 127.0.0.1:6379
#password = ""
#db = 0
#active_conns = 100
#idle_conns = 20
#timeout = "3s"
#prefix_room = "NIL:ROOM:%s"
#prefix_session = "NIL:SESS:ROOM:%s"
# InMemory store config.
[store]
# no options available.
# FileSystem store config.
# [store]
# path = "db.json"
At this point we have the chat server installed. Running it by typing sudo /opt/nilserver/nilserver
should result in an open port 9000.
Creating the service
Since we want to start the talk server by default, we create a service file /lib/systemd/system/niltalk.service
with the following contents:
[Unit]
Description=niltalk server
After=multi-user.target
[Service]
WorkingDirectory=/opt/niltalk
ExecStart=/opt/niltalk/niltalk --static-dir=/opt/niltalk/static
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
We enable the service by linking it to the right place:
user@talk.example.com:/etc/systemd/system/multi-user.target.wants$ sudo ln -s /lib/systemd/system/niltalk.service .
Now we should be able to start and stop the service using the sudo service niltalk start
and sudo service niltalk stop
commands, respectively.
Apache reverse proxy
The standard approach does not work here. The talk server is using web sockets, which are not proxied/rewritten by default. So we have to amend our configuration to do that.
But first, we can go with the “standard” approach to get the certificate. The Apache talk.example.com.conf
configuration reads as
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName talk.example.com
ServerAlias talk.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:9000/
ProxyPassReverse / http://127.0.0.1:9000/
</VirtualHost>
So we get our certificate via sudo certbot --apache -d talk.example.com
.
But now we have to add some configuration on the newly created talk.example.com-le-ssl.conf
:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerName talk.example.com
ServerAlias talk.example.com
ProxyPreserveHost On
ProxyRequests Off
RewriteEngine On
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://127.0.0.1:9000%{REQUEST_URI} [P,QSA,L]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule .* http://127.0.0.1:9000%{REQUEST_URI} [P,QSA,L]
RequestHeader set X-Forwarded-Proto "https"
<Location />
Require all granted
ProxyPassReverse http://127.0.0.1:9000/
ProxyPassReverseCookieDomain 127.0.0.1 talk.example.com
</Location>
SSLCertificateFile /etc/letsencrypt/live/talk.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/talk.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
So basically, we are enabling the websocket use for our proxy.
In order to avoid client reconnection every 5 minutes, we use a (maybe dirty, I’m open for better solutions) workaround and set the connection timeout in /etc/apache2/apache2.conf
to 12 hours:
Timeout 43200
In order to get this all working, we have to enable the Apache headers
module and to restart the webserver:
$ sudo a2enmod headers
$ sudo service apache2 restart
Finally, we have to set the right IP and URL in our niltalk config file /opt/niltalk/config.toml
:
[app]
# Address to listen, use "tor" to run an hidden service.
# address = "0.0.0.0:9000"
address = "127.0.0.1:9000"
# No trailing slashes.
root_url = "https://talk.example.com"
After restarting the chat server by
$ sudo service niltalk stop
$ sudo service niltalk start
we are done.
That’s it! Now we have a niltalk chatserver behind an Apache reverse https proxy.
Main source for the Apache config part: mattermost.