Run Both A Syncthing Discovery And Relay Server Over Port 443

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

[2] https://docs.syncthing.net/users/stdiscosrv.html

[3] https://docs.syncthing.net/users/strelaysrv.html#

Leave a comment

Your email address will not be published. Required fields are marked *