Comments (21)
Great! Been busy the last weeks but I will merge this later today. Thanks again! :)
from pairdrop.
Just a final comment to say I nuked my custom docker image and re-pulled this morning from 1.7.3
All worked smoothly, zero problems.
from pairdrop.
Glad to hear this is mostly working for you!
- I'm using a Cloudflare (Argo) tunnel to handle the HTTPS proxying (routing to
localhost:8081
)
As everything else is working fine, probably here is the issue. For auto discovery, PairDrop groups all devices with the same ip address together. Therefor, the header cf-connecting-ip
or x-forward-for
must be set properly along every step of the routing process so the clients real ip gets passed along. Then, PairDrop groups all clients together that are behind the same NAT:
https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#http-server
https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs
Is there any additional http server between cloudflare and the docker? The docs have examples for apache2 and nginx. Not sure how to do it with cloudflare-one but if you could present your current config I could have a look and add a working version to the docs later.
whatโs weird though is, when there is a problem with the cloudflare config normally all devices are mutually visible no matter what their original ip is. In your case every device seems to present a different ip.
It could also be, that devices connect to your server with their ipv6 ip address which is different for every device. You would then have to find a way to prevent users from connecting via ipv6 for auto discovery to work
from pairdrop.
Thank you kindly for the reply!
I definitely do have IPv6 on both the docker host as well as the connecting devices, so maybe that's it! I'm at work now but will give it a shot later and see what I can find out.
Nothing special on the CF tunnel side, I don't see anywhere that I could enable or disable those forwardfor headers so that's a bit of a black box. If that turns out to be the problem I'll have to switch to nginx + LetsEncrypt etc
from pairdrop.
Looking forward to your findings! If it does not work I should probably add a flag to log the incoming ip addresses for debugging. That way you could easily find out whether PairDrop sees the correct ip address or any of the proxy ip addresses is used instead
from pairdrop.
I did some testing. The docker container doesn't have ss
or tcpdump
installed so I had to use netstat
in a tight loop (watch -n0.5 netstat -an
) but I was able to detect that, as you suspected, the proxied connections coming in via the Cloudflare tunnel all appeared to share the same RFC1918 IP, not their true source IP. Example:
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 :::3000 :::* LISTEN
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47884 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:52150 ESTABLISHED
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47906 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47990 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47900 TIME_WAIT
[...]
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47952 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47868 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47918 TIME_WAIT
tcp 0 0 ::ffff:192.19.16.3:3000 ::ffff:192.19.16.1:47944 TIME_WAIT
Not sure that there's any way I can "fix" this from the CF side, I poked around in their dashboard for a while but didn't find much (except for this "No Happy Eyeballs" switch which I tried togglingโmade no difference)
I found this hacky method for disabling IPv6 on a per-container basis (adding --sysctl net.ipv6.conf.all.disable_ipv6=1
to the docker run
which seems to work by the way) but that didn't solve this either.
Here's the output of ip addr
from inside the container (no more IPv6):
root@6f94135e58b5:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
176: eth0@if177: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:c0:13:10:03 brd ff:ff:ff:ff:ff:ff
inet 192.19.16.3/24 brd 192.19.16.255 scope global eth0
valid_lft forever preferred_lft forever
What DID work was removing the localhost-only bind (change -p 127.0.0.1:8081:3000
to just -p 8081:3000
) and then connecting over HTTP directly to the private IP of my docker host: http://192.168.138.48:8081
So it seems the end result is that, for now, this isn't compatible with Cloudflare tunnels and I will need to switch to a slightly more complicated setup e.g. Nginx+LE.
from pairdrop.
Thanks for testing so thoroughly!
the proxied connections coming in via the Cloudflare tunnel all appeared to share the same RFC1918 IP
It is expected, that the connecting IP is not the real IP of the client but that of the proxy. The same happens when you use traefik or another reverse proxy in between that relays the requests.
Not sure that there's any way I can "fix" this from the CF side
To get around this there is a x-forwarded-for
header that contains all ip addresses of the request including the real ip of the client. Additionally there is the cf-connecting-ip
header that is sometimes set by cloudflare.
Currently, this is the implementation - the ip addresses in the headers are used whenever they are present:
if (request.headers['cf-connecting-ip']) {
this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0];
} else if (request.headers['x-forwarded-for']) {
this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
} else {
this.ip = request.connection.remoteAddress;
}
I will create a debugging flag to log the headers and the remoteAddress of the connecting peers to make it easier for you to debug. I would put it on a separate branch first. Could you then clone the repo, checkout the branch and build the docker image yourself for testing purposes?
What DID work was removing the localhost-only bind (change
-p 127.0.0.1:8081:3000
to just-p 8081:3000
) and then connecting over HTTP directly to the private IP of my docker host: http://192.168.138.48:8081
Does this mean that the problem indeed lies with cloudflare-one?
from pairdrop.
Sure I am happy to test a separate branch. I will do a bit more testing today, I am trying to set up a new docker host and a fresh nginx setup.
I should have put this detail in: when I got it to work at the end of the above post, I was connecting directly to the docker host's IP over a site to site VPN, so there was no HTTP proxy involved, and PairDrop would have seen the actual real IPs of the clients.
from pairdrop.
Top! I just pushed the debugging bit to add_ip_debugging_flag
.
You need to
- checkout the branch
- To build execute:
docker build --pull . -f Dockerfile -t pairdrop
- To run execute:
docker run --rm --name=pairdrop -e DEBUG_MODE=true -p 127.0.0.1:8081:3000 -it pairdrop npm run start
Then it will log the following on every peer connect:
----DEBUGGING-PEER-IP-START----
remoteAddress: ::ffff:172.17.0.1
x-forwarded-for: 127.0.0.1
cf-connecting-ip: undefined
PairDrop uses: 127.0.0.1
IP is private: false
if IP is private, '127.0.0.1' is used instead
----DEBUGGING-PEER-IP-END----
Would be nice, if you could test this before setting up your new nginx setup to make it work with cloudflare-one :)
I was connecting directly to the docker host's IP over a site to site VPN, so there was no HTTP proxy involved
Thanks for the clarification!
from pairdrop.
๐ Nice! Yes sure I will test now. The new build btw is a separate VM entirely, I won't touch the original setup, so we can definitely do any testing you want on there.
from pairdrop.
Any news? :)
from pairdrop.
Sorry. I got totally derailed by something else. And had to rebuild my VMware infra this past week, set up new Docker hosts, ROUTE-48 shutdown caused some issues, has to move some things to HE.net, etc... ๐ตโ๐ซ
I'm planning to revisit this weekend, will post soon!
from pairdrop.
No worries! I'm just trying to keep the issue board somewhat tidy. Post whenever you're ready! Thanks for the quick reply and good luck with your rebuild! :)
from pairdrop.
@luckman212 Have you tried again?
The debugging flag is now also available on the master branch:
docker run --rm --name=pairdrop -e DEBUG_MODE=true -p 127.0.0.1:8081:3000 -it pairdrop npm run start
See https://github.com/schlagmichdoch/PairDrop/blob/master/docs/host-your-own.md#debug-mode for more info
from pairdrop.
oops - posted this earlier on #71 by mistake!
Thanks for your patience. Been a really busy time and I didn't have time to look at this (until now). Spent the last 2 days working on it, debugging etc and am here to report my findings, as well as a Pull Request.
Findings
- The original problem appears to mostly be caused by Cloudflare Argo tunnel/proxy combined with IPv6.
- If the Docker host has IPv6 enabled, it's fairly difficult to disable IPv6 for a single container. There are ways, such as the
--sysctl net.ipv6.conf.all.disable_ipv6=1
or creating a separate Docker network that has IPv6 disabled (docker network create --ipv6=false...
). I opted for the latter as it seemed like a better choice. - Cloudflare absolutely does not allow disabling the creation of IPv6 CNAME records on Argo tunnels (anymore) unless you're on an Enterprise plan. They are trying to push IPv6 forward (and I do appreciate that) but in this case it would have been great if I could have selectively disabled the AAAA record from being returned for my PairDrop instance.
All that added up to me not able to "see" local devices even when they were on the same LAN, due to fully working IPv6 across the board from client <-> cloudflare <-> tunnel <-> Docker host <-> PairDrop container
. I did come up with a working solution though.
PR
I made a patch that adds a IPV6_LOCALIZE
flag. This flag accepts a parameter between 1 and 8 to truncate the client IPv6 (proxied or otherwise) to a specific number of segments. In most cases, for a standard /64
subnet, the correct parameter would be IPV6_LOCALIZE=4
but it will accept other sizes to be more flexible. This makes it possible for devices on the same /64 to automatically communicate (tested with a sample size of n=1).
I also encountered another bug/problem while testing which doesn't seem related to any of this: sending a TEXT message to another peer using the Submit button seems broken in recent builds (tested with master
branch as well as the live PairDrop.net instance). The message is never sent, instead a js error is logged to the console:
If you instead send using CTRL+ENTER it works. I made a couple of small edits to the scripts and HTML which fixes that as well, plus a bug about disabling/enabling the submit button due to innerText
not always equalling \n
.
After building a new image from that branch, and running with e.g.:
docker run --rm \
--name=pairdrop-test \
--hostname=pairdrop \
--network=no_v6 \
-e TZ=America/New_York \
-e RATE_LIMIT=true \
-e WS_FALLBACK=true \
-e IPV6_LOCALIZE=4 \
-e DEBUG_MODE=true \
-v pairdrop-test:/home/node/app \
-it pairdrop-test npm run start
I now have a fully working instance of PairDrop, with working HTTPS behind the Cloudflare proxy, and device discovery working! ๐
Hope these are ok, awaiting your feedback.
from pairdrop.
I now have a fully working instance of PairDrop, with working HTTPS behind the Cloudflare proxy, and device discovery working! ๐
Awesome, good work!
PR looks good so far, but I need to do some testing myself and look into localizing IPv6 addresses. Before we merge this, we must add this env var to the docs as well: here and here
2. If the Docker host has IPv6 enabled, it's fairly difficult to disable IPv6 for a single container. There are ways, such as the
--sysctl net.ipv6.conf.all.disable_ipv6=1
or creating a separate Docker network that has IPv6 disabled (docker network create --ipv6=false...
). I opted for the latter as it seemed like a better choice.
Not sure if I understand that correctly: Is disabling IPv6 on the docker network level sufficient to solve this issue or not?
I also encountered another bug/problem while testing which doesn't seem related to any of this: sending a TEXT message to another peer using the Submit button seems broken in recent builds (tested with
master
branch as well as the live PairDrop.net instance).
bug about disabling/enabling the submit button due to
innerText
not always equalling\n
.
Thanks for reporting these issues! As they are independent from the rest and urgent, I committed fixes for them adding you as co-author: 6e4bda0 & 8a17b82
Would be great if you could fixup your PR only keeping your changes to index.js.
from pairdrop.
Updated PR is coming shortly.
To answer the other question about "is disabling IPv6 on the docker network level sufficient to solve this issue or not?" - the answer is NO. In my case even when IPv6 was fully disabled at the network level (Docker host and/or container) the proxy headers x-forwarded-for
and cf-connecting-ip
still contain full IPv6 addresses of the client and thus trigger the discovery issue anyway.
from pairdrop.
@schlagmichdoch I am new to the process of reverting individual files from a PR, so I hope I did that correctly! Hope the index.js
is OK now. I also updated the docs.
Made a bit of a mess by accidentally hitting the Sync Fork button in GitHub ๐ก but I believe I was able to revert that cleanly and squash everything down to a single commit.
LMK.
from pairdrop.
the answer is NO. In my case even when IPv6 was fully disabled at the network level (Docker host and/or container) the proxy headers
x-forwarded-for
andcf-connecting-ip
still contain full IPv6 addresses of the client and thus trigger the discovery issue anyway.
Thanks for clarifying!
I am new to the process of reverting individual files from a PR, so I hope I did that correctly! Hope the
index.js
is OK now.
Everything looks good! Thanks for rebasing and tidying up so quickly.
I did some digging and if I understand it correctly, global IPv6 addresses are always comprised of 48bits routing identifier + 16bits subnet identifier:
http://www.tcpipguide.com/free/t_IPv6GlobalUnicastAddressFormat-2.htm
https://www.ibm.com/docs/en/ts3500-tape-library?topic=formats-subnet-masks-ipv4-prefixes-ipv6
All my devices on the same network (that are not behind some kind of vpn) confirm this behaviour.
So even if you define your local network e.g. as /96 you could still identify devices from this (sub)network by using the first 64 bits. In that regard your approach of using the first 4 hexdects of the IPv6 address would be analoguous to using the complete IPv4 address regardless of the used subnet mask (e.g. 255.255.0.0). In that regard I'm thinking whether it would maybe even make sense to use IPV6_LOCALIZE=4
as default and document the possibility of omiting this behaviour by using IPV6_LOCALIZE=false
for users that want to use PairDrop in the edge case of a complete intranet with subnets that do not use 64 bits prรคfixes.
What do you think?
Apart from that I think this is ready to be merged. Thanks a lot for contributing!
from pairdrop.
Thanks for looking it over. After 10 years or so of monkeying around with IPv6, about the only thing I can say with certainty is: I've seen some crazy stuff. Some ISPs do weird or broken things, so I wouldn't be surprised if there are edge cases we haven't thought of where peers that aren't on the same logical network end up sharing part of a /64 somehow and surprisingly becoming visible to each other.
So, not sure about making IPV6_LOCALIZE=4
the default right out of the gate. It's working well for me so far but maybe tag a release and allow a bit more testing in the wild first?
I pushed 1 more change to explicitly handle IPV6_LOCALIZE=false
so that it's declarative.
from pairdrop.
Hey @schlagmichdoch ๐
Just wanted to drop in another data point: I've been using this patched version for a week or so now and it's working great for me among a handful of devices, both on- and off-net. Macs using Safari, iPhones, Windows laptops using Edge browser, IPv4 & v6. All working well (for me at least).
Let me know what you think of the PR in its current form.
from pairdrop.
Related Issues (20)
- [Enhancement] Add Kannada language HOT 3
- [Enhancement] Implement the websocket fallback as a real fallback
- [Bug] link with parameters not working (parameters after = cut off) HOT 4
- [Enhancement] Make AddOn Snapdrop for Firefox compatible with PairDrop HOT 4
- [Bug] Text for "Download Again" button is cut when downloading vertical video (CSS bug)
- Favicon changed? now showing up white on white HOT 4
- [Query] How to enable send with PairDrop option? HOT 8
- [Bug] PairDrop Siri Shortcut has broken UI HOT 7
- [Enhancement] Chrome extension for PairDrop HOT 4
- [Bug] Error `Invalid Group Specifier Name` on Safari HOT 6
- [Bug] Grey line on the right HOT 5
- [Bug] Same file, same devices, same network but different transfer speed from mobile to desktop HOT 6
- PairDrop on Firefox for iOS hangs on Loading [Bug] HOT 4
- [Bug] Pairdrop user gets offline while still choosing files to send HOT 4
- [Enhancement] Add automatic release notes
- [Enhancement] Warn user when TURN fallback HOT 1
- [Bug] Copying message via button fails using HONOR browser HOT 6
- [Question] Is there any reasoning behind using `"display": "minimal-ui"` in the manifest? HOT 6
- Remove extra back quote (`) from the command in documentation HOT 1
- [Bug] URL not clickable HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pairdrop.