-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
[WIP] Implement pluggable transport interface #340
base: master
Are you sure you want to change the base?
Conversation
Hi certaintls, Yes I love the idea of using hysteria as a pluggable transport for other apps. If all we need is an adapter for code in Also, what's a good way to test it in real applications? |
I will write an e2e test in this PR. But, for testing with real applications, I don't know an easy way yet, I will get back to you. |
Hi certaintls, I think there are some problems on how this transport is currently implemented. QUIC is a "multiplexed" protocol, which means there are actually 2 layers of "Accept" - one for the connection, one for the streams inside the connection. The connection itself cannot be directly used for data transmission. Hysteria creates a "control stream" for negotiation & authentication etc. after the QUIC connection is established. Then each new "connection" from this client is just a new stream inside the QUIC connection. Basically, as a server, you need listen for both new connections, and new streams coming from every existing connections (semantically this is the real "Accept"). I'm not sure how to adapt this to Pluggable Transports API 🤔 |
The reason your current code doesn't work is that in Whenever you call |
Thanks for digging into this. Your explanation makes sense. I pushed a new commit fbfe9ab In this commit, I close the control stream and listen/accept new ones. However, in the test, below is never returned from a new stream
I am keeping digging. |
The test is able to pass in my debugging session, this makes me think the problem is with deadlock between client-server wait. |
Here is a little bit more debugging info. If I step through debugging mode, the test passes, and below is the debugging info
But, if I just run the test, I got
|
The new code still has problems. You are only accepting the first stream of each connection, so it won't work when the same client dials again. And needless to say, you also shouldn't accept streams of only the first connection, otherwise it wouldn't be able to handle multiple clients. One way I can think of to make this work, is to have an internal goroutine for accepting connections. Then for each accepted connection, start a goroutine for handling the control stream & accepting streams. Put those streams into a channel or something for |
Yes, I am aware of the limitation. Currently, I am hoping to get a proof of concept working first. On the other hand, I have a scheduled meeting tonight to discuss the multiplexing handling with other PT implementers.
I was also thinking something similar, thanks for the suggestion! |
I re-wrote the server side code using this suggestion. The latest change should complete the feature. However, there are still things to do:
|
The code look pretty good to me now 👍 Maybe you should close |
I have located the exact line that is causing the flickering in test: https://github.com/HyNetwork/hysteria/blob/master/pkg/core/client.go#L80 If I place a single break point on this line I will look into this more. |
I have some to debug the test a little bit more. The exact breakpoint to make the test succeed isn't L80, but L85 https://github.com/HyNetwork/hysteria/blob/master/pkg/core/client.go#L85 . In other words, if I place a single breakpoint on L85 Will look into this more. |
I put breakpoints inside |
Have you tried debugging on the server side instead? Looks like the server was blocked on |
Yes, I started the debugging from the server side.
Right, I will come back to this again once I got more time. |
Just to provide an update: I definitely have not forgot this PR. I switched gear a little bit to improve PT testing (manual and automated) in general in OperatorFoundation/shapeshifter-dispatcher#42 and OperatorFoundation/shapeshifter-dispatcher#43 I also have a security audit lined up for this implementation as well as the major code of this project that needed for the PT implementation in the week of Sept.26th |
A security audit was conducted by Cure53.de for the PT related work. I copy and paste the relevant parts from the report below: Identified VulnerabilitiesThe following section lists all vulnerabilities and implementation issues identified throughout the testing period. Please note that findings are listed in chronological order rather than by their degree of severity and impact. The aforementioned severity rank is vulnerability is given a unique identifier (e.g., RPT-03-001) to facilitate any future follow-up correspondence. RPT-03-003 WP1: Lack of socket dial timeouts (Low)During a source code review of the hysteria-master repository, the observation was made that the DialTCP and ListenUDP functions utilize the Golang functions net.DialTCP and net.DialUDP for the purpose of establishing new connections. A sufficiently well-positioned attacker could leverage this behavior and halt the thread executing the DialTCP and ListenUDP functions, thereby preventing thread execution. Affected file: Affected code:
To mitigate this issue, Cure53 advises utilizing net.DialTimeout rather than RPT-03-004 WP1: UDP session hijacking via race condition (Medium)Whilst deep-dive reviewing the hysteria-master repository, the discovery was made that the Hysteria client application integrates a feature to initiate a SOCKS5 proxy. Depending on the configuration, this proxy enforces an authentication step comprising a username and password before serving any requests. Upon successful authentication, the SOCKS5 proxy implementation receives the connection request from the client. Valid connection requests constitute either TCP or UDP. For UDP connection requests, the SOCKS5 proxy consequently opens a UDP listener for incoming client connections, and Assume an attacker is able to reach the Hysteria application running as a client. The attacker bides their time until a legitimate client successfully authenticates to the Hysteria application. Subsequently, the attacker attempts to connect to the listener Affected file: Affected code:
To mitigate this issue, Cure53 advises sending a token to the legitimate client upon successful authentication. In this way, following the connection to the open listener via UDP, the client must provide this token in order to complete the connection |
Wow, I never expected this project to get a security audit from Cure53. Thanks for your effort - I will definitely take a look and fix the issues. |
RPT-03-003 is fixed in fc28c01. However, I don't think fixing RPT-03-004 is possible, at least not in the way they suggested. We are implementing a standard SOCKS5 server, and unless I missed something, the SOCKS5 specification never explained how a UDP relay could determine and limit the client address. This token authentication certainly isn't part of the spec - so there's no way to maintain compatibility with standard SOCKS5 clients if we implement it. |
@tobyxdd I noticed in this commit e3c3088#diff-643b6b726bd0f79dadeca7c4173d92d4534ecce85edc6a53760cbfb93c7d0e56 , you removed Sorry for having disappeared for quite long. |
Hi @certaintls , the purpose of this commit was to separate QUIC's PacketConn initialization process from the server/client transports. These "transports" now only handle outbound connections (how the server & client connect to remote servers).
Can you elaborate? Which ListenUDP and how is it coupled with SOCKS5Client? 🤔 |
Any estimated time to merge and release this ? |
This PR implements Pluggable Transport Specification v3.0 - Go Transport API so other applications can use
hysteria
as a pluggable transport.Status update (Mar.12.2023):
Other notes:
https://github.com/HyNetwork/hysteria/wiki/Protocolhttps://hysteria.network/docs/Threat Model
Background
Exposing the Great Firewall's Dynamic Blocking of Fully Encrypted Traffic
Adversary capabilities
As the research above shows, we assume a censor can probabilistically drop connection packets. For simplicity, an arbitrary connection which is fighting the censor can result in 10% packet loss, 30% packet loss and 60% packet loss. The more percentage packet loss is, the more head-of-line blocking will be, and the worse the performance it becomes. Practically speaking, with 30% packet loss, applications like live streaming, real-time broadcasting, online meeting, and live gaming are much disrupted. With 60% packet loss, even reading censored news become extremely slow and require users manual retries over and over.
The adversary's goals
Goal of Hysteria PT
Taking advantage of QUIC protocol, hysteria PT should be able:
Non-goals of Hysteria PT
The proxy application shipped in Hysteria repository does not actually use Hysteria PT interface, therefore, its usage and DevOps are not the concerns to Hysteria PT.