This is similar to Tor's OutboundBindAddress config option: if you're running headless snowflake on a server with multiple IP addresses, you want a way to control which IP address your outgoing connections will come from.
Tricks can be done with routing and iptables and etc, but those are OS-specific, require messing with stuff at the root level, etc. Maybe it is a two-line patch to let snowflake call bind() on the outbound connection socket?
Designs
Child items 0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
I was poking around the pion/webrtc documentation for any exciting new features and found an option that forces the public IP address of host candidates. In theory, this should allow an operator who knows their public IP to ensure that clients connect to the public IP of their choosing.
This is a bit fraught, because it bypasses external validation, so an operator should be sure that the configured IP address is correct and that it is not rotating. There are also a few gotchas mentioned in the documentation that may require some investigating (such as not being able to set STUN URLs).
I do this on the router - mine calls it "DHCP reservation" - and clients all connect to the same IP address which I’ve assigned to the machine(s) running snowflake. Perhaps that’s risky? I’m uncertain about that.
This request was never to bypass the need for stun, since the external IP may change at the whim of your ISP (I can tell you bedtime stories about that), only to bind to a specific local (RFC1918) IP behind several NAT gateways on a multi-homed system. It's just to facilitate multiple instances of 'proxy' on the same system since they will now both use the default routing (metric). So far I'm now running two separate snowflake proxies on two separate raspberrry PIs and that works fine, this was just a nice to have, but we didn't seem to get past the actual understanding of the request with detours into off topic statements and also no clear statement as far as I can tell to why it was closed or denied or what is a duplicate issue or workaround.
Providing a public IP address (in some cases) eliminates the need to talk to a STUN server. Probably could be useful on the client side as well if it's behind a 1:1 DNAT, since some censors are targeting STUN connections.
There are still some things I'm uncertain about:
when ICETransportPolicy is set to allow srflx, it seems to be superior to host in all cases. Is there a reason to not hardcoded ICECandidateType to srflx in our case?
what’s the point of still allowing STUN server URLs when ICECandidateTypeHost is chosen? I thought host candidate (by default) has higher priority over srflx candidate and the whole point is to eliminate the need for STUN servers?
Posted in pion's slack channel. Hopefully I can get some insights.
I was talking about the second parameter of SetNAT1To1IPs
ICECandidateTypeSrflx seems to provide several advantages over ICECandidateTypeHost:
Doesn't break mDNS (we might not use mdns, but it's good to have)
No STUN needed, one fewer point of failure
The resulting srflx candidate will have lower priority than host candidates, but it shouldn't be a problem since AFAIK host candidates are only used when the WebRTC peers are on the same network, which is unlikely in snowflake's case.
So, we talking in a context where the proxy user (hoster) knows the public IP address of their server and provides it to the Snowflake proxy?
I think I understand now.
In this case, AFAIU it's quite the opposite.
host candidates are only used when the WebRTC peers are on the same network
No, a host candidate of a Snowflake proxy with a public IP can be communicating with a srflx candidate of the client.
So it looks to me like calling SetNAT1To1IPs with ICECandidateTypeHost is the only option to achieve it, but either way I don't know if simply overriding the addresses of all candidates is the right thing to do.
I think you are right. Initially I thought normally (without calling SetNAT1To1IPs) host candidates only contains local IPs, even if the host has a public IP. But pretty sure I was wrong. So ICECandidateTypeHost does seem to be the sensible option in most cases.
I still don't understand what's the point of hainvg STUN servers with ICECandidateTypeHost. The only reason I can think of is to create fallback srflx candidates in case the provided public IP doesn't work, but as a user I'd rather getting an error instead.
(I'm trying to get my hand on a server with multiple public IP so I can better test things.)
Just noticed that we have a keep-local-addresses option, introduced in 0fae4ee8 (for client) and 1867f895 (for proxy)
When enabled, this option will filter out all the host candidates. This would nullify any changes made with SetNAT1To1IPs(<ips>, ICECandidateTypeHost) if we ended up going that route.
I'm not sure why we have such an option, when it is no longer part of the WebRTC standard. I get that LAN address would most likely be useless in snowflake's case, but STUN should be able to figure it out.
It only filters host candidates with local (reserved) addresses, like 192.168. ..., 10. .... I thought the original issue is talking about multiple public addresses, for the most part?
why we have such an option
For privacy, to not give the peers and the broker (and the domain front) info about your network.
But I believe it's what the mDNS candidates are supposed to address.
Cecylia has given more context. This change was probably done prior to mDNS candidiates. Maybe we should revist it, making mDNS be a config option instead. (Also related: #40123 (closed))
Filtering candidates (like we filter host candidates with local addresses) and only signaling the ones with the desired interface could be an option. But I don't think it's gonna work for srflx candidates when behind a NAT, since it's not simple to know which interface sent the STUN request. Also it won't change the fact that the other interfaces would still be active (the proxy is listening on them), although the peer wouldn't know about it.
With the --outbound-address config you can set ONE prefered outbound address to use. Currently there is no way to set multiple such addresses.
This config just gives whatever address you provide the highest priority. If connection cannot be established, it will try other addresses just like before.
Thank you. Then I have to write a new feature request.
My wish is to give proxy one fix IPv4 and one fix IPv6.
Proxy does not have to use other IPs on this system.
That sounds like a reasonable request to me, although there'd still be no guarantee the IPs would be used, in case of typo, unreachable, etc. I've added a log message warning the IP provided is not chosen for connection with client, but unfortunately there seem to be a bug with the library we use and it's not working as intended on the first connection: !136 (comment 2884027)
Yes with --outbound-address it may work, sure we only need one PER PROCESS, but then we can start different processes ON THE SAME MACHINE using different values for this parameter. I will try it out later.