-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Provide ShadowsocksReader/Writer as a library #100
Conversation
This makes logf reusable outside main
This better matches the original code
Hi @fortuna Thanks for the PR and sorry for the delay. I have some free time to investigate recently. Since it's quite a big change set, I'll have to spend some time to study it first. Questions will be communicated here in the coming days. |
Thanks. Let me know if you have any questions. I can also walk you through the changes if it helps. |
@fortuna The changes are way too aggressive for little benefit. Specifically, the entire PR seems to depend on the new type closeWriter interface { CloseWrite() error }
type closeReader interface { CloseRead() error }
func copyHalfClose(dst, src net.Conn) (int64, error) {
n, err := io.Copy(dst, src)
if w, ok := dst.(closeWriter); ok { w.CloseWrite() }
if r, ok := src.(closeReader); ok { r.CloseRead() }
return n, err
} There are other minor issues which I won't go after here. I believe the PR can be significantly simplified without the unnecessary Additionally, I'd like to know what is missing for go-ss2 to be used as a library? I thought it's currently already usable as a library, and I failed to see what this PR improves upon that. Please explain a bit more about what you need. Thanks a lot! |
@riobard, I'm sorry the intent of my changes and our needs were not clear. I'll try to break them down and clarify. DuplexConnThe goal of DuplexConn is to add API safety. The suggestion you gave for There's an implicit assumption throughout the code that the connections can have the Reader and Writer closed separately. By defining I could change the code to use Notice that while it may feel like a large change, besides the addition of Does that clarify what the stream.goI'd say the biggest change was in Does that make sense? Needed APIsThe functionality I need can be found at https://github.com/Jigsaw-Code/outline-ss-server/blob/78993a32d867366f3fbf7bfa502cc0dba34bcf95/server.go#L35 This is already available in libraries ✔️ :
These are not available in libraries, or need change:
Does this address your concerns? Do you have suggestions on how to achieve that in way that you are comfortable with? |
@fortuna As library authors we should require as little as possible from library users. My main objection against What we should worry about is what to do if the As for the needed API, my take is that you think the building blocks currently exposed in |
I agree with the point of not asking too much from the user. To be clear, we are not asking more from the user: you can pass a But if you are not comfortable with that, I'll look into reverting it back to On the needed API, yes, I'd like to have a higher level and easier to use API. You provide a "AEAD Reader/Writer", but I need a "Shadowsocks Reader/Writer", which is higher level. It should take care of the IV, counter, and the details of the SS crypto protocol. Currently that logic is in Does it make sense? Are you ok with that part of the change? I think this is actually the most important part of the change. |
Yes, please revert back to use
Indeed it does make sense to have a higher-level API that combines the two layers so library users can invoke directly without worrying too much about the internals here. I'll look thru the Outline use case and the example you sent to see what to do. Now that we agree to drop One question: does Outline intend to provide backward compatibility with the deprecated |
I believe I addressed most of your concern, but there are still some open questions. DuplexConnI reverted back to use net.Conn. The change touches a lot less files now. APII'm not sure I understand what you'd like to do here. Do you have any objection to the ShadowsocksReader and ShadowsocksWriter? They encapsulate and expose the Shadowsocks encryption and do what we need. Stream ciphers
We will not support the old non-AEAD ciphers. I see no valid reason to use them in new servers. |
FYI, I realized I don't need to keep the new relay code in go-shadowsocks2, so I moved to outline-ss-server and reverted that part of the change as you wanted. |
Sorry for the delay. Got busy last week. Now with the |
I've updated the PR description to clarify: I'm providing the Currently that logic is split across This change moves both the crypto and initialization logic to ShadowsocksReader and ShadowsocksWriter, making them standalone classes that can be easily reused for our purposes. As a bonus, I added a number of unit tests to make sure things continue to work. Does that make sense? This change is a lot smaller now. |
@riobard Does the explanation above make sense to you? I need the IV logic in the reader/writer. |
@fortuna I see how you want to reorganize the code. What I don't understand is why not directly use |
That's right, In the multi-user implementation I need the Reader separate from the Writer so I can wrap it in different ways before creating a connection later. I can't take a connection as input directly because I need to be able to replay the bytes when trying each cipher. I wrap the original net.TCPConn with a Once I find the cipher, I have to stop buffering for replay, so I create a new See the |
@riobard Any thoughts? Are you ok with proceeding with refactoring the Reader/Writer so they independently contain the full logic I need for multi user and testing? If not, I guess I can proceed with my own fork of the code, but it would be a pity to not make the contributions upstreamed. |
@fortuna I now understand your concern. I tend to agree with the change, but I need to consider several factors together, namely
I'm on the move this week and I'd like to spend some time on the implications. Please let me know if you have further feedback regarding issues raised here. Thanks! :) |
Here are my thoughts on the points you raised: API stability.It seems the non-backward compatible change would be the removal of Of course, that's not foolproof, but may give you some piece of mind. Their search is pretty good. You can see, for instance, all the references for My guess is that those methods don't actually address existing developer needs, making them not useful. Maybe because they are not easy to use, since you need to call them when you have the salt, which requires some sort of connection interception. The new writer and reader I'm introducing are a lot easier to use, since it does the interception and initialization for you. Multi-user supportI think the multi-user discussion should happen separately. While I think it would be great if a new version of the protocol supports it, our users need the existing protocol to support multi-users right now, so we'll proceed with our multi-user solution regardless of the new protocol. We will certainly consider the new protocol when it's agreed upon. UDP ModeThis is our multi-user code for UDP: https://github.com/Jigsaw-Code/outline-ss-server/blob/d29f83117efeaa294b02bd8fc68ee0ed7db46774/udp.go#L37 I simply call Shadowstream - Shadowaead symmetryThis change provides standalone reader and writer for shadowaead, but not shadowstream, so you're right it introduces an asymmetry. I figured that implementing them for shadowstream would be of no value, since I wouldn't use it. Final thoughtsMy initial intent with the go-shadowsocks2 changes was to have it contain as many of the functionality of outline-ss-server as possible, so we would benefit the community and maintain only a little bit of code in our fork. I realize I'm proposing significant changes, and I understand if you're not comfortable with that. Given the situation, I believe it will be best if I also move the ShadowsocksReader/Writer to our code, so we won't need to change go-shadowsocks2 at all. We'll still depend on the cipher primitives, so that's code we won't need to maintain. |
FYI, I moved the change to outline-ss-server: No need for this change anymore. |
@fortuna Understood. We'll consider an API redesign in the next major release to address the issues :) |
This change does some refactoring that allows the use of go-shadowsocks2 as a library and customize its behavior.
Background
In Outline (https://getoutline.org) we need to customize the server to allow for multiple users, single port and to collect metrics. However, none of the existing servers provide that in a way we can use. And we will certainly need other changes in the future.
go-shadowsocks2 seemed to be the best implementation to base our server, since it's modern and the most modular. We will be migrating from shadowsocks-libev which we currently use, but is harder to turn into a library.
Even though we need to have our own server binary, we would like to diverge as little as possible from go-shadowsocks2. That's why I'm introducing this PR that will make it possible for us to keep most functionality in go-ss2.
We want to contribute back to the community, so it's our intention to upstream as many fixes and features as possible back to go-ss2. To us, the more of our features we have in go-ss2, the better, but we respect if you decide to not accept some of the features.
This Change
Going back to the PR, the change I'm making is providing the
ShadowsocksReader
andShadowsocksWriter
classes that encapsulate the Shadowsocks crypto logic.Currently that logic is split across
shadowaead.streamConn
,shadowaead.Reader
andshadowaead.Writer
. While encrypting/decrypting is in the Reader and Writer, the salt initialization is in the streamConn, which prevents reuse.This change moves the initialization logic to ShadowsocksReader and ShadowsocksWriter, making them standalone classes that can be easily reused.
As a bonus, I added a number of unit tests to make sure things continue to work.
Use
You may be interested in seeing how a server using go-shadowsocks2 as a library would look like. Here is a fully functioning server that I was able to deploy using the Outline Manager:
https://github.com/Jigsaw-Code/outline-ss-server.
It shows how we can add multi-user, single port and metrics tracking with with prometheus.io without having to modify go-shadowsocks2.
I'd be happy to upstream those features as a follow up if you'd like.
Please let me know what your thoughts on this are on our approach and how you would like to advance with this PR.