Advertising a specific address to global discovery

Hello, I created a topic about this before but maybe wasn’t entirely clear on my issue: Custom device address is not propagated to Discovery servers.

I am running a Syncthing instance on my Raspberry Pi from my home connection. My home connection has a dynamic IP, so I use a dynamic DNS service (no-ip.com) to have a domain I can use to connect to it even if my home IP changes during a (forced) reconnect. My Raspberry Pi routes all outgoing traffic through a VPN and routes incoming traffic for Syncthing, nginx etc. over my regular connection, because my VPN does not support port forwarding – this part of my network configuration 100% works, the problem isn’t here.

The problem is that because the Raspberry Pi routes all outgoing traffic over the VPN, the servers Syncthing contacts to figure out my public IP address will return the VPN’s IP address instead (which does not support port forwarding, so clients will fail to connect to it).

Clients that have my Raspberry Pi’s device ID and do not explicitly specify “tcp://my-dynamic.domain” instead of “dynamic” in its “Addresses” section will fail to connect, because it will just attempt to connect to the VPN’s server IP address.

I figured that I’d just force Syncthing to advertise a specific global address instead of the automatically determined one. To that end I went to the advanced options, edited the Raspberry Pi’s device section (its own device section on the Raspberry Pi instance) and changed its “Addresses” field to just “tcp://my-dynamic.domain”. I was hoping that this will then be advertised to the global discovery server, today I found out however that this fails when a friend tried to connect to it and instead only got the VPN’s IP address (I checked in their device section).

Can I somehow do this?

Syncthing should announce its listening address to the discovery servers so I would expect that to work. When the listening address is unspecified (0.0.0.0) or private (192.168/24 etc) it will get replaced with the actual source address seen by the discovery server though.

So are you saying that specifying addresses that contains my dynamic DNS server’s domain in “Sync Protocol Listen Addresses” for the Raspberry Pi would fix the issue? Currently it is set to “default”. So something like: “tcp://my-dynamic.domain:22000” for example? Because it obviously translates the “default” (which according to docs is basically just “0.0.0.0” and relays) to my VPN’s IP address.

Yes. Though the announcement will use the resolved address and not the name. And the address will need to be a valid listening address. Maybe I misread your message but that’s where I thought you had entered it.

No, I had entered it here:

, in the “Advanced” section. I don’t think “tcp://my-dynamic.domain:22000” is a valid listening address, right? At least I’m not sure how my system would know if it can bind to that. I can try entering that and see.

Ah, yeah, entering something there for the local device has no effect.

If the name resolves to an address on this device i expect it to be a valid listen address. If it doesn’t, like if it’s a port forward somewhere, it won’t be.

2020-05-09 18:29:37 Listen (BEP/tcp): listen tcp MY-REAL-IP-ADDRESS:22000: bind: cannot assign requested address
2020-05-09 18:29:37 Entering the backoff state.
2020-05-09 18:29:37 c.S.listenerSupervisor: Failed service 'tcp://my-dynamic.domain:22000' (2.999817 failures of 2.000000), restarting: false, error: "{tcp://my-dynamic-domain:22000 tcp://my-dynamic-domain:22000} returned unexpectedly", stacktrace: [unknown stack trace]

It manages to resolve my real IP address, but can’t bind to the public IPv4 address.

Yeah then you can’t do that. You can’t make Syncthing announce an address that it doesn’t have and doesn’t use to reach the discovery server.

As far as I can tell the problem is this:

2020-05-09 18:29:47 quic://0.0.0.0:22000 resolved external address quic://185.206.225.38:22000 (via stun.syncthing.net:3478)

Which is my VPN’s IP address and not the public address I want to actually advertise. Is there no way to override which IP address is advertised somehow?

No

Is this something you’d consider in a feature request on GitHub, or no?

I think no, large footgun potential and fairly small utility. But fee free to drum up more support or use cases for it and someone else may think it’s worth it.

Hm, I think I have a better chance of making this work if I just force traffic to some destination IPs (stun.syncthing.net) through an IP routing table then, so that my clearnet IP contacts the servers responsible for resolving my external IP address instead. Thank you for your quick support.

Just tested this, that did work:

2020-05-09 18:46:31 quic://0.0.0.0:22000 resolved external address quic://MY-REAL-IP-ADDRESS:22000 (via stun.syncthing.net:3478)

Luckily I made a tool for exactly this quite a while ago:

$ sudo vpn-whitelist stun.syncthing.net --ufw
stun.syncthing.net:
ip route add 139.59.84.212 via 192.168.178.1 dev eth0 table main
ufw allow out on eth0 to 139.59.84.212 comment 'Whitelist stun.syncthing.net (2020-05-09 18:45:48)'
ip route add 198.211.120.59 via 192.168.178.1 dev eth0 table main
ufw allow out on eth0 to 198.211.120.59 comment 'Whitelist stun.syncthing.net (2020-05-09 18:45:49)'

If anyone else runs into this, you can find it here: github.com/cryzed/vpn-whitelist.

You need to do that for discovery servers, not just stun. As stun is only used for quic and working out the external address. You probably want TCP which leaves it to the discovery servers to look up the address.

Also, discovery servers will not be able to lookup the port (as we don’t reuse the same outgoing port for discovery and sync protocol), so it will use your <xternal address>:<default listen port which is 22000>. If your router doesn’t route port 22000 to you directly, it won’t work.

You need to do that for discovery servers, not just stun. As stun is only used for quic and working out the external address.

But aren’t the STUN servers used to resolve my external IP address, which is then advertised to the global discovery servers? I’m just asking because what I just did definitely works, I just confirmed this with my friend. The only issue was that my Syncthing instance advertised the wrong external IP address (the VPN IP address instead of my clearnet IP address). As long as the correct external IP address is determined, everything else works.

My router does forward the default ports, so that’s not an issue.

If it works, it most likely only works for quic and not tcp.

Yes, it does advertise that external address, but only for quic/UDP connections, as that is what stun is for (UDP nat punchthrough).

For TCP we trust the discovery server to identify where the requests came from and use that as the external address.

Am I correct in my assumption then, that the default global discovery servers are “discovery(-1).syncthing.net” and future ones will follow a similar pattern? I couldn’t find any concrete domains in the documentation. They currently seem to point to the same IP as “stun.syncthing.net”?

https://github.com/syncthing/syncthing/blob/master/lib/config/config.go#L63 has the list, yet it might change

Thank you, that’s great.