Show HN: Encrypted VPN in 2k lines of Go
github.comRelated: Wireguard is a new VPN for linux in 4k lines of C https://www.wireguard.com/
the model of wireguard has been proven correct by formal methods. Builds on modern crypto, and they kept code short for auditing purposes.
There's also a work-in-progress Go implementation of Wireguard, found over here: https://git.zx2c4.com/wireguard-go/about/
From the linked page:
> There is no group of users that should be using the code in this repository here under any circumstances at the moment, not even beta testers or dare devils.
Despite the warning I attempted to use it, but without any real docs I didn't get very far.
The Go implementation is not done yet! But it will be soon. In the meantime, feel free to use the Linux kernel implementation, which works quite well and has extensive documentation and man pages:
- https://www.wireguard.com/quickstart/
- https://www.wireguard.com/install/
- https://git.zx2c4.com/WireGuard/about/src/tools/wg.8
- https://git.zx2c4.com/WireGuard/about/src/tools/wg-quick.8
I’ve played with the Linux implementation, I love it!
But I use a Mac so I can’t join the VPN I made :/
If you join the mailing list [1], there'll be an announcement when it's ready, which hopefully won't be before long. Other perks include free stickers too :D [2].
[1] Send a blank email to wireguard-join at lists dot zx2c4 dot com.
[2] https://lists.zx2c4.com/pipermail/wireguard/2017-May/001338....
My concern is that 'built on modern crypto' and 'reviewed by cryptographers' amounts to 'rolled our own crypto'. IMHO history has shown us time and time again that this is a bad idea - we should use the protocols and ciphers that have stood the test of time. Building subnet using TLS was an architectural choice to avoid playing the role of cryptographer and inevitably getting it wrong.
What a bunch of senseless FUD.
WireGuard is based on the Noise Protocol Framework [1], designed by the same fella as the Signal Protocol, and already used in production by millions of devices all around the world inside of WhatsApp. Not only that, but we have a formal verification [2] that the crypto is correct in the symbolic model. The WireGuard paper itself [3] was presented to the academic community at NDSS [4]. It's most certainly not the hastily-made nonsense you imply it is with the phrase "rolled our own crypto".
Meanwhile your project, "subnet", tunnels TCP over TCP, which is well known for having pathologically bad performance characteristics [5]. It also has no binding between certificates and the IP addresses that a certificate is allowed to be inside the tunnel, and, unless I've misread, it allows different peers to hijack each others' IP addresses simply by asking [6].
There's a lot of work that goes into doing tunneling well. I encourage your effort to make a fun toy project; it's a great learning opportunity. But please don't spread FUD about other projects without first understanding them.
[2] https://www.wireguard.com/formal-verification/
[3] https://www.wireguard.com/papers/wireguard.pdf
[4] https://www.ndss-symposium.org/ndss2017/
[5] https://www.google.com/search?q=tcp+over+tcp
[6] https://github.com/twitchyliquid64/subnet/blob/50fc8fe2b6ccf...
> What a bunch of senseless FUD.
inb4 strawman arguments and other funzies.
Perhaps you should argue with me on my actual assertion, which is that 'we should use crypto that has stood the test and scrutiny of time'. Do not interpret my criticism as a personal attack.
> already used in production by millions of devices all around the world inside of WhatsApp.
Cool. This is good because that means we have a lot of eyes looking at it.
> formal verification that the crypto is correct in the symbolic model.
This is great and we need more of this kind of work. I don't think its good enough however; most practical attacks rely on side channels, implementation mistakes (formal verification helps in catching these but don't get you all the way), and other obscurities. I stand by my assertion that we should use crypto that has stood the test of time.
> The WireGuard paper itself [3] was presented to the academic community at NDSS
Congrats I guess? relevance?
> not the hastily-made nonsense you imply it is with the phrase "rolled our own crypto"
I made no such implication. 'Roll your own crypto' is a common phrase (at least in my circles) meaning to do it yourself instead of depend on someone else's implementation. Again, I don't see what this has to do with my assertion to use old and battle tested crypto.
> "subnet", tunnels TCP over TCP, which is well known for having pathologically bad performance characteristics
Correct, have a gold star. What does this have to do with crypto?
As you could tell if you scrolled down and read the other discussions around TCP-in-TCP, I am fully aware of the implication of my design decision, and stand by it is the right balance of simplicity, security, and speed (well, thats relative, but for what I was intending it for).
> It also has no binding between certificates and the IP addresses that a certificate is allowed to be inside the tunnel, and, unless I've misread, it allows different peers to hijack each others' IP addresses simply by asking
Correct, I am making the assumption that if posess a private key and cert minted by the server you are trusted. This could be fixed at the cost of additional complexity, loosing the ability for a client to change his address, and less alcohol-time on the developers part. That said, if someone asked me to network together hostile entities, I would have given him some coolaid instead of Go code. But I'm digressing, lets get back on topic.
> But please don't spread FUD about other projects without first understanding them.
I have done no such thing. I stand by my assertion that we should use Crypto that has stood the test of time. Am I not allowed to raise such assertions and discuss them on merit?
Bonus points for being classy despite being attacked.
> What a bunch of senseless FUD
Is not a very courteous way to begin a rebuttal. Definitely makes it feel like an ad hominem.
It builds on Noise, which is very highly regarded and reasonably well researched in crypto circles. Ironically (vis-a-vis your comment), it was designed largely to avoid the mistakes inherent in developers "rolling [their] own" crypto on TLS with poorly chosen stacks, ciphers, parameters, downgrade criteria, etc.
Guess you could say noise is pretty quiet xD
+1 for trying to eliminate complexity from developer error. This was one of the worst cows in the herd for OpenSSL.
That said, I think a bit of good design on the APIs part can go a long way. For Instance, I think Go's crypto/tls aint bad: Its pretty difficult to 'accidentally' configure it in a shocking configuration (suites have to be overridden, turning off verification requires you to set a field called InsecureNoVerify etc).
The project structure is fighting againts norms. The author should not have src checked in. They should have their package as the root so it is "go get-able" and does not require the user to alter their GOPATH. To ensure that the proper dependency versions are present, they should vendor the dependencies. I would have opened an issue on GitHub for them, but I am not signed in currently. Cheers on releasing a neat tool.
EDIT: got off my mobile and to a laptop and submitted https://github.com/twitchyliquid64/subnet/issues/3.
Thanks! This is definitely something I need to get around to once I read up a bit more on vendoring.
Check out the `dep` tool: https://github.com/golang/dep
It is the future of official vendoring tools.
You could also look at `govendor`: https://github.com/kardianos/govendor
I've used govendor in some projects and found it agreeable. Best of luck :-)
Checkout https://github.com/cloudflare/hellogopher
to get you started with a nice workflow. Uses gvt for vendoring. Tests over multiple packages are globbed correctly
Good job! I'm looking into trying Go soon, is there something you'd recommend looking at for learning the Go ecosystem norms?
The first is doing the tour at golang.org, then reading Effective Go at https://golang.org/doc/effective_go.html. There are lots of good reads at the golang blog, such as https://blog.golang.org/organizing-go-code. I really enjoy Go, and hope you do too!
Is it just me or Golang started gaining momentum recently? More and more related articles/OSS projects/HN links show up every day.
When I first saw Go I was put off by its syntax, but after giving it a chance I very quickly fell in love with it.
I've used C#, Python, Ruby, Java, C, Obj-C, JS and Go for mid-large scale projects. Go is the only language I've ever truly loved; sometimes I even feel like I'm writing poetry (as ridiculous as that may sound).
The community is really incredible^, I've yet to encounter anything that didn't have great documentation or solved by packages. My favorite SO answer of any language is probably this one by icza: https://stackoverflow.com/questions/22892120/how-to-generate... . I also feel that it's very easy to read and understand other peoples code compared to other languages (imo, the only aspect of Go that I think can be difficult for newcomers to grasp is pointers). All in all, I feel very confident that I'll still be writing Go code after ten years.
^ (Although I hate when certain "elitists" downvote questions because they feel they've been sufficiently answered elsewhere.. if a SO answer says how to display time to YYYY-MM-DD:HH-MM then don't complain if someone ask how to display time as YYYY-MM-DD.. I have a strong dislike towards jquery (or rather JS), but I think the great thing about jquery is that every imaginable edge-case seem to be documented)
Yeah, I also love Golang, though I am a full-job front-end developer. Sometimes it can make me shout "WHAT THE FUCK"[1] to the IDE, but it's the most enjoyable language I worked with during last 20 years.
[1] http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in...
Genuine question as I've only dabbled in Go, but I've heard complaints from other dabblers that Go seems to encourage large files rather than breaking things up in a more modular fashion. Is that a reality? Or has the complainer just been unlucky in the code they've viewed?
I have been using Go for about 4 years at work and I would say they have been unlucky with the code they have looked at. You have the freedom to organize your code as you wish, you can easily go from one extreme to the other, one function per file to 1000 in one file. The only thing that isn't encouraged is to have very small packages, for example, if your project ends up split into 10 packages, but each of them is just one file (or one code file plus a test file), then most Go developers would suggest to group more files into a single package.
To add some meat onto that: a package should be usable (and reusable) on its own. If you have a package that is a single function or struct, it is unlikely (but still possible!) to be useful on its own and likely requires the context of your other packages to be useful.
I've only dabbled myself, but I can see how that would start; I come from a Java background which forces you to have one module / class per file, but Go doesn't. It's very easy to start hacking up a thing, and it's easier in development to just keep it in one file. Of course, the nature of code is that it grows, so spend enough time there and it'll start growing. If you're familiar with the code (as in, you wrote it yourself), single files shouldn't be a problem.
It's not uncommon in other projects either, NodeJS for example has a relatively small number of large files (https://github.com/nodejs/node/blob/master/src/node.cc).
Go definitely prefers modular fashion. But definitely no gratuitous files/packages structure like Java where a dozen packages each with 2-3 files having nothing more than 50 lines of code out of which 30 are comments and imports of another dozen packages.
Unlike Java, Go does not have limitation of having each public class in its own file. So public symbols of any type can be put together in same file if it makes sense. Go prefers arranging code what makes sense for application functionality instead of auto-generated scaffolding like 'handlers', 'utils', 'assets', 'entities' etc like I do in my Java code.
Most of Go code I was working on or contributed to usually has quite a split out structure and more complex structs are defined in multiple files with related methods grouped together. I have seen couple of Go packages which consist of one huge file but I think it's quite rare. Most people will structure their code into multiple files to keep it more readable/maintainable and file sizes relatively small.
I don't see any reason why Go would encourage large files. A Go package consists of every Go source file in a directory. You can use as many or as few files as you want per package.
We don't really have the "one class one file" beat into us like in java or c#. That means you gotta think a little to see how a package naturally splits up. Some people are better at that then others.
Yeah, I found it a strange assertion to make, so was wondering if it held any water. Sometimes different languages have different ways of working, which seem odd to novices entering the space from a different background, so I didn't want to assume that they were wrong.
Looks like it may be a case of coming across some less well written projects and making assumptions about the entire language, which is nice as Go seems fun :)
I think Golang is very suitable for network programming tasks. It gives you wonderful libraries to work with sockets, strict rules to structure your code, automatic memory management, goroutines for concurrent programming etc.
Most network related small apps were written in C, now you can write them in Go. With the added benefit of simple libraries, memory management and goroutines. You don't have to mess arround with platform specific sockets, threading libraries etc. Plus every piece of code you open is written in understandable Go. With C, there is years of baggage, multiple standards, pre-compiled headers, undefined behaviour etc.
And lastly, new is always more exciting than old :)
No it's not you [0]. Rust, Go, R, and Python have the highest year-to-year growth.
[0] https://stackoverflow.blog/2017/09/06/incredible-growth-pyth...
Python is interesting in that list, given how it's at least 4 times as old as all of the other ones in that list. What's behind that? I don't recall any major developments after Python 3, and most of the news behind that one was about its backwards incompatibility (and currently how 2.x isn't about to go anytime soon)
Explosion of interest in machine learning and data science and decent Python-based tools for those fields.
They followed up on that question with another blog post: https://stackoverflow.blog/2017/09/14/python-growing-quickly...
Python's huge popularity in scientific computing fields along with it being widely taught in schools. Notice also that R was on the list, which is obviously due to huge uptick in data science. R has been around for awhile as well.
Nice, I'll try it out once I get home.
A couple of question:
- What is the throughput once you fixed the issues ?
- If you were to implement a client for mobile (iOS, Android), how would you go about it ? (Just theoretically, I understand it's a personal project)
I'm using openvpn on a cloud server and one of the big advantages is the availability of mobile client apps.
Throughput: It depends on the link, but I'm getting 16mbps peak where my connection to my ISP gets me 20mbps peak.
Mobile: You have to use the APIs that are available on the platform to hook into the network layer. After that its pretty straightforward though - you open some TLS connections, do verification, and encapsulate network traffic in some simple structs.
sshuttle is another great tool with a similar use case. One advantage is that it doesn't require any server setup at all, as long as you have ssh access.
A neat thing about sshuttle is that it restreams TCP connections inside of the SSH tunnel so that you don't incur the classic TCP-over-TCP problem [1].
[1] http://sshuttle.readthedocs.io/en/stable/how-it-works.html
Sshuttle is very cool. But it only does TCP and UDP. (no ICMP)
In other comments the author seems to already be aware, but for anyone wondering why TCP over TCP is less than ideal, this is a good read: http://sites.inka.de/bigred/devel/tcp-tcp.html
Am I reading this correctly in that this uses TLS - and ends up tunneling TCP and UDP over TCP?
Correct. While simple, this does have the performance impact you're alluding to. On my 20mbps (down) connection, I peak out at 16mbps on subnet.
The 'double congestion-control' effect can be alleviated by opening 10 or so TLS connections and pumping the packets down those, to spread the effects of TCP congestion control.
Does this work on Windows? I see the TODO item "Get working on OSX", but if this project could bring x-compatibility on the big 3 platforms (thanks Go!), that could really set it apart.
I'm afraid not :/ Windows is a whole new kettle of fish to get working - you need a device driver to emulate TUN/TAP.
That said, I'm using a library called Water for the low-level networking, and that library just added support for Windows. Implementing full windows support only requires you to implement a few network methods (such as helpers_linux.go) - PRs welcome :)
Upon reading the title I wondered "would anyone ever create a VPN client that doesn't use encryption?" and thought, with the "2k lines" as additional evidence, that it was the source code which was somehow encrypted/obfuscated to e.g. prevent censorship...
I was also expecting to see a single file at that line count, but then again I'm not really familiar with Go. Is this style of "many tiny files in multiple nested directories" common/expected for Go? I know it's rather common in many other languages, but also not what I expect when I see "Simple" or explicit mention of a low line count.
>would anyone ever create a VPN client that doesn't use encryption?"
They do, but generally call it tunneling. L2TP and GRE links that don't use IPSEC are fairly common on private WANs.
I suspect the author was trying to point out that the encryption was included in the 2k lines, versus say, calling out to ssh.
I wouldn't use that repo as a representative of what "go code looks like". Most go repos are pretty similar in "structure", but I really don't know how to navigate this. The 'src' directory looks like the author is really fighting the gopath concept, but also trying to vendor dependencies. I agree, if my hook was "in xx lines of code" I would try to avoid boilerplate and keep it all together as much as possible.
I guess the main question is why should I use/trust this above OpenVPN/Tinc or WireGuard etc?
I'm glad you're asking these kinds of questions, they need to come up more often especially in a software-supply-chain context.
I am a strong advocate of the saying 'trust but verify'. I believe you should closely audit whatever OSS software you are looking at using in light of your threat model.
To get round to your question: Why should I use subnet over OpenVPN/Tinc etc? That decision is entirely your prerogative. Subnet is small (quick(er) to audit), easy to understand, and has the bare minimum functionality needed to implement a VPN with full mutual authentication. OpenVPN and others have far more features and are almost certainty xx% faster. Where you want to draw the line is up to you.
It appears all the ISPs I use have figured out how to kill OpenVPN after a few Mbytes have passed, resulting in annoying VPN service interruptions and restarts (I need to figure out how to restart it automatically) - and the browser reacts to the interrupted transfer by restarting it on the now VPNless network connection. Obscurity might be a defense against this (but wouldn't be if one were targeted instead of getting caught in a driftnet).
That's not been my experience with OpenVPN. It's setup to reconnect to the VPN and only route traffic via the VPN (separate box, actually a repurposed old laptop). Take a look at ping and ping-restart options
Thanks for the suggestions, but decreasing the ping interval only made the VPN fail faster, and there was no difference in behavior between ping-restart and ping-exit :-(.
Because you can easily read and understand the code.
For what it’s worth, this only really applies to a comparison against OpenVPN or IPSec. WireGuard has similar code complexity and size, and is approximately the same when compared against the incumbent VPN options.
I don't think the comparison to Wireguard is apples-to-apples. Wireguard implements it's own semi-custom cryptosystem (Noise) whereas subnet rides on top of Go standard TLS implementation. So depending on how you view things, Wireguard is either much simpler (if you compare to the whole TLS stack) or much more complex (if you assume Go TLS to be reliable/trustworthy)
Maybe I misunderstood your comment, but WireGuard is less than 4k LoC, which is a few orders of magnitude smaller than OpenVPN or IPsec.
Yep you misunderstood, I was saying WireGuard has similar code complexity to the Go VPN implementation linked here :)
Because your government banned those.
language used in the project is Erlang, not Go.
What do you mean? I don't think I saw a single Erlang file in that repository.
beg a pardon, was looking at the other submission.
I think you commented on the wrong post :)