Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refused connections incorrectly get accepted #899

Open
michamos opened this issue Sep 25, 2023 · 5 comments
Open

refused connections incorrectly get accepted #899

michamos opened this issue Sep 25, 2023 · 5 comments

Comments

@michamos
Copy link

I noticed the following unexpected behavior: when the TCP connection gets refused by the server, sshuttle doesn't refuse the connection but accepts it and returns an empty response instead.

This is not just an academic difference: when trying to reach an (allegedly misconfigured) server that advertizes both IPv4 and IPv6 addresses through A and AAAA DNS records respectively but refuses IPv6 connections, happy eyeballs will allow the IPv4 connexion to be established if the IPv6 one gets refused, but this fails with the current sshuttle behavior of accepting actually refused connexions.

Could this be fixed, or is it a shortcoming of the sshuttle approach?

Example:

Currently, cds.cern.ch has a CNAME DNS record pointing to cds-lb.cern.ch with the following A & AAAA addresses:

cds-lb.cern.ch.		40	IN	AAAA	2001:1458:d00:67::100:281
cds-lb.cern.ch.		40	IN	AAAA	2001:1458:d00:64::100:1de
cds-lb.cern.ch.		40	IN	A	188.185.12.33
cds-lb.cern.ch.		40	IN	A	188.185.86.126
cds-lb.cern.ch.		40	IN	A	188.185.27.152

However, it refuses IPv6 HTTP connections.

  • Without sshuttle, I can connect despite this as the client (here curl, but browsers behave similarly) falls back to using IPv4:
curl -v http://cds.cern.ch -I
*   Trying [2001:1458:d00:67::100:281]:80...
* connect to 2001:1458:d00:67::100:281 port 80 failed: Connection refused
*   Trying [2001:1458:d00:64::100:1de]:80...
*   Trying 188.185.86.126:80...
* connect to 2001:1458:d00:64::100:1de port 80 failed: Connection refused
* Connected to cds.cern.ch (188.185.86.126) port 80 (#0)
> HEAD / HTTP/1.1
> Host: cds.cern.ch
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
[...]
  • When tunneling all mentioned IP addresses, there is no fallback as the first tried IPv6 connection gets accepted and I can't access the website:
curl -v http://cds.cern.ch -I
*   Trying [2001:1458:d00:64::100:1de]:80...
* Connected to cds.cern.ch (2001:1458:d00:64::100:1de) port 80 (#0)
> HEAD / HTTP/1.1
> Host: cds.cern.ch
> User-Agent: curl/7.88.1
> Accept: */*
> 
* Empty reply from server
* Closing connection 0
curl: (52) Empty reply from server
@brianmay
Copy link
Member

disclaimer: I am going purely from memory here.

I think because sshuttle is implemented at the application level, there is not much else it can do here. We can't initiate the outgoing connection until after we accept the incoming connection, and by this stage it is too late to properly reject the incoming connection if the outgoing connection fails.

i.e. we don't get notified of the incoming connection unless we accept it also.

@edmcman
Copy link

edmcman commented Feb 7, 2024

I wonder if we could use NFQUEUE for this?

@edmcman
Copy link

edmcman commented Feb 7, 2024

https://github.com/oremanj/python-netfilterqueue

Maybe using set mark to identify packets that need to be rejected by a later iptables rule, since it doesn't seem like nfqueue can reject directly, only drop.

@edmcman
Copy link

edmcman commented Mar 2, 2024

I just made a quick prototype to see if this worked outside of sshuttle: https://gist.github.com/edmcman/16a47334f569380d00fd6fd80b597e93

ed@penguin:~/netfilterqueue$ nc -v google.com 80
nc: connect to google.com (142.251.40.206) port 80 (tcp) failed: Connection refused
nc: connect to google.com (2607:f8b0:4006:806::200e) port 80 (tcp) failed: Network is unreachable
ed@penguin:~/netfilterqueue$ nc -v google.com 80
nc: connect to google.com (142.251.40.206) port 80 (tcp) failed: Connection refused
nc: connect to google.com (2607:f8b0:4006:806::200e) port 80 (tcp) failed: Network is unreachable
ed@penguin:~/netfilterqueue$ nc -v google.com 80
nc: connect to google.com (142.251.40.206) port 80 (tcp) failed: Connection refused
nc: connect to google.com (2607:f8b0:4006:806::200e) port 80 (tcp) failed: Network is unreachable
ed@penguin:~/netfilterqueue$ nc -v google.com 80
Connection to google.com (142.251.40.206) 80 port [tcp/http] succeeded!

So it seems like it could work.

@edmcman
Copy link

edmcman commented Mar 2, 2024

I've been playing with this here. It's at the point where it configures some iptables chains and intercepts the packets using netfilterqueue.

But, there's an architectural problem. SocksWrapper wants a socket, whereas netfilterqueue uses a callback function when a packet is queued. Maybe I could open a local socket and forward data from the netfilterqueue? But I don't see any easy way to send data in the reverse direction.

Alternatively, I wonder if I could modify the TPROXY technique to use netfilterqueue only to reject invalid connections, and if the connection goes through, actually do the proxying with TPROXY. So it would look something like this:

  1. Get callback from netfilterqueue
  2. Mux attempt connect
  3. If connect fails, mark packet to be rejected
  4. If packet not rejected fall through to TPROXY
  5. Setup SocksWrapper as usual using the TPROXY socket

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants