Introduction
From Wikipedia: “Syncthing is a free, open-source peer-to-peer file synchronization application available for Windows, macOS, Linux, Android, Solaris, Darwin, and BSD. It can sync files between devices on a local network, or between remote devices over the Internet. Data security and data safety are built into the design of the software.” [1] If you want additional privacy you can run your own discovery and relay servers on the same host following the official documentation, but you will be forced to run your relay over port 22067. This can be a problem because some networks treat traffic on “unusual” ports like this as suspicious and may even block it entirely. A better solution would be to run both on port 443 so everything looks like normal TLS traffic, that is the purpose of this post.
Prerequisites
I am assuming a basic familiarity with the linux command line, nginx, and syncthing. I am also assuming that you have a domain name registered, DNS set up, and a VPS or similar system to run this on, a valid TLS certificate, and all needed packages are installed. In addition, run the following:
$ nginx -V
and look for the option –with-stream_ssl_preread_module. If this option is not present, this will not work.
Configuration
Begin by configuring the discovery server according to the official syncthing documentation for nginx under the reverse proxy set up section [2]. In addition, create the file /etc/systemd/system/syncthing-discovery.service as follows:
[Unit]
Description=Syncthing Discovery Server
After=network.target
Documentation=man:stdiscosrv(1)
[Service]
User=syncthing
Group=syncthing
WorkingDirectory=/var/lib/syncthing/discosrv
ExecStart=/usr/bin/stdiscosrv -http -listen=127.0.0.1:8443
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=stdiscosrv.service
This is a copy of the default stdiscosrv service file with tweaks made to suit our needs (see reference [2] for detailed explanations of the various options). Start and enable the service. Add a CNAME record to your DNS zone named discovery.<yourdomain> to point to this server (of course you can name it whatever you want, but I use discovery for clarity). Now, create the file /etc/systemd/system/syncthing-relay.service with the following contents:
[Unit]
Description=Syncthing Relay Server
After=network.target
Documentation=man:strelaysrv(1)
[Service]
User=syncthing
Group=syncthing
WorkingDirectory=/var/lib/syncthing/relaysrv
ExecStart=/usr/bin/strelaysrv -ext-address=<your ip address>:443 -listen=127.0.0.1:22067 -protocol=tcp4
Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=strelaysrv.service
Once again we have copied the default strealysrv.service file and modified it for our purposes. To see a complete description of available options (and to see how to make this a public relay and contribute to the project) see reference [3]. Start and enable this service as well. Finally, bring it all together by adding a stream block that reads the SNI field in the client hello message and passes the traffic to the appropriate server. The relay’s traffic will not have an SNI field, which is the key to this whole approach. An example nginx configuration which accomplishes all of this is shown below:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
multi_accept on;
worker_connections 1024;
use epoll;
}
stream {
ssl_preread on;
access_log off;
map $ssl_preread_server_name $name {
discovery.<your domain> discovery;
<additional vhost>.<your domain> <additional vhost>;
"" relay;
}
upstream discovery {
server 127.0.0.1:443;
}
upstream <additional vhost> {
server 127.0.1.1:443;
}
upstream relay {
server 127.0.0.1:22067;
}
server {
listen <your ip address>:443;
proxy_pass $name;
}
}
http {
add_header Strict-Transport-Security "max-age=31536000";
access_log off;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_certificate <your certificate location>;
ssl_certificate_key <your private key location>;
ssl_verify_client optional_no_ca;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!EXP:!MD5:!SHA1;
ssl_ecdh_curve X25519:secp384r1:prime256v1;
ssl_stapling on;
ssl_stapling_verify on;
server {
listen 127.0.0.1:443 ssl http2;
server_name discovery.<yourdomain>;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
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 $http_x_forwarded_proto;
proxy_set_header X-SSL-Cert $ssl_client_cert;
location / {
proxy_pass http://127.0.0.1:8443;
}
}
server {
listen 127.0.1.1:443 ssl http2;
server_name <additional vhost>.<yourdomain>;
<additional vhost configuration here>
}
}
The beauty of this approach is that it allows syncthing discovery and relay functionality to be easily integrated with an existing nginx setup, with all TLS traffic going over port 443 and without doing any port redirection as discussed in [3].
References
[1] https://wikipedia.org/wiki/Syncthing