OK, good morning, everyone.
My name is Shilun Weimberg, and today I'm going to talk about proxy guard,
why guard behind the reverse proxy.
So, very interesting title, so let's get started.
So, a bit about me.
I work at Shiont for almost three years now,
and Shiont is an organization with actually one main goal,
is fostering collaboration between all the different research networks around the world.
We have a focus on Europe, so we have a research network in Europe,
and we provide services to national research organizations and universities.
For example, Edgero, you might all know it, it's here too,
if you can log in if you're still a student or you've worked for research organization.
But mainly this talk is about edgvpn,
which is also a service that Shiont provides,
and it's co-developed by other research networks.
So, I mainly work at edgvpn at Shiont,
and I put on the Linux Penguin in there because I am mostly using Linux,
even though I'm presenting this on a MacBook, it's quite funny,
but in my day-to-day job, I write software for Linux,
so this talk will be quite Linux focused.
So a bit about edgvpn, edgvpn as an instance is a VPN for research organizations,
so it can be used by a university,
as an alternative to commercial software,
so this Cisco or the 14th.
And it's free and open source, so you can host it yourself,
you don't have to be in university, you can run it at home,
and it's pretty much a layer on top of existing VPN protocols,
so I run on the slide, open VPN and wireguards,
so wireguards only since the latest version,
but that version is already currently almost 3 years already.
So it started as open VPN,
an open VPN has UDP and TCP out of the box,
which is pretty nice because UDP is nice and fast,
but on some networks UDP can be blocked,
and then you have TCP as a fallback.
And we made wireguards integrated into edgvpn,
but then it only has UDP because the wireguards project
only supports UDP out of the box.
So yeah, why is this a problem?
I already explained a bit, UDP can be blocked,
there's still quite a lot of networks that block UDP traffic,
and even if they don't block it,
they might be anti-U issues,
either due to configuration errors in the network,
or they intentionally try to block two large packets.
And this, for TCP, this all works pretty well,
but for UDP in practice, you see quite a lot of problems.
Think about hotels or public cafes.
There's quite a lot of problems with UDP traffic.
You can get around in a bit by setting your wireguards
to 443 or 53 for DNS.
And 443 because of quick, so if you've seen in the last presentation,
if quick gets more and more popular,
then maybe 443 starts working quite well,
but currently there's still quite a lot of networks
where UDP is playing on broken.
So we need to have some kind of fallback in wireguards,
and how do we want to do that?
Well, wireguard is a really slim and mean protocol.
It's 4,000 lines of code according to the white paper.
Maybe nowadays, it's a bit more or less,
depending on how you count.
But if you compare that for example to OpenVPN,
the open source component of OpenVPN is 70,000 lines of code,
but that's excluding the cryptographic libraries.
So if you count OpenSSL into that, you get a lot more.
So OpenVPN also has more features due to that,
so it has TCP support, and yeah, for wireguard, that's missing.
And they actually documented on the website,
and they say transforming wireguards UDP packets into TCP,
is the job of an upper layer of application.
So this is the aim of the stock is creating this tool,
and I would explain a bit why I did not just use another tool
that was already made.
So finding a tool, well, there are actually quite a lot of projects
that tunnel UDP over TCP,
and they just do this by taking the UDP packet,
and then converting it to a TCP socket,
with like a custom header in front.
But some of them only had a client implementation,
and some only a server implementation.
I wanted both, still that were some projects that still had this.
They were either written in a different programming language,
so for HTTP and itself, we prefer to go,
because we were writing our clients now and go.
And if we had an implementation in go,
we could use that as a library,
and also on the server side, we already have some go component,
so that it would fit nicely in our packaging as well.
And what we also would like to have
is run wireguard behind the reverse proxy.
Well, this might sound a bit complicated or a bit why,
but that's due to poor sharing.
So in HTTP and you have a reverse proxy for the API,
and for the web interface,
and this can then run on port 443 and 80 on your reverse proxy.
And it might be nice to have an HTTP and server
that only has these ports open.
So you can have everything in your reverse proxy,
and it would be like one configuration with everything in it,
and this would be really nice.
If you could solve all of these use cases into one tool,
and if you run it behind the reverse proxy,
you also get TLS for free.
So it's a bit more obfuscation.
So if there's networks that do some more packet inspection,
to check if it's actually TLS traffic,
and not just normal TCP,
you might even have more luck with it too.
So that's how proxy got started.
So the first version in go immediately,
I had some go experience,
so it was not that tricky.
And even the first implementation was really easy,
because it was just setting up a UDP socket,
and that's in go.
You just have the standard library,
you have a function to do that.
And then you just simply,
well, you receive packets over the UDP socket,
and you send it over a TCP socket,
and you do have a client part and a server part.
So yeah, you do need to have two components.
And the first part was without a reverse proxy,
so it was just a simple socket.
It did nothing with reverse proxies.
And then there wasn't a question I had,
and how to deal with cut off packets.
So first, I pretty much just took the packet itself
and dumped it over a new socket,
but with TCP, you can have fragmentation,
and then the packets get split up.
And the other hand on the server side,
you have to reassemble it into one whole packet again,
because you need to forward the whole wide
packet to the other hand,
and not just the tiny bit.
So I looked a bit at other tools,
and that was this tool by Movat,
and they also implemented UDP over TCP,
and they had the idea of using just a simple length header
in front of the packet.
So each overhead for each packet would be two bytes,
because that's the total maximum length of a packet in UDP.
So what we pretty much do is in this tool,
in the first version we made,
is we have these four components,
and we got a wire guard server on client and server,
and we have a proxy of client and proxy of server.
So this wire guard client and wire guard server
is just the totally unmodified wire guard project,
deployed on the Linux machine,
and the proxy guard then is the tool that I made,
which is just a simple go binary,
and you would then have to set up wire guard
that it would connect to proxy guard client, local host.
So if you would look at the wire guard show in your terminal,
it would show that the endpoint is just your local host,
proxy guard.
And then you have to make sure that proxy guard on the server
is sensitive wire guard on the server,
and you get traffic pack.
And it's just the whole data stream of UDP packets,
so there on the arrow on the left it gets UDP packets,
and it converts it to TCP packets,
by figuring out the length of the packets,
and then adding that as a header in form of it.
And then on the other end, on the proxy guard server,
we can look at this length and then get the packet out of that,
because that's many different packets next to each other.
So pretty much the server is the reverse of the client,
so the client converts UDP to TCP,
and then back again.
So yeah, you get a TCP sequence of the length and the packet,
and you get that send back to the client,
and send back to the wire guard client again.
So it's not actually that tricky or innovative.
So checking the requirements,
we have a really simple implementation,
so I really like this, because well,
I didn't have to use any order 2,
I could easily maintain this myself.
I may already have a really simple client and server.
I think it was like 300 lines also,
it was not that much.
And we have a go implementation,
meaning I could use it in our HTTP client,
without going through hoops of using other languages,
for infrontient interface.
So what we do not have yet,
though, is running it behind the reverse proxy,
to do port sharing, or have any TLS yet.
So let's take a look how we can do that.
So reverse proxy is like a web focus tool.
So you need some kind of web protocol to proxy your traffic.
So what did you be used?
Well, my immediate thought was web sockets,
and actually I did implement this in a branch and get.
But I soon figured out that web sockets,
yes, I used a library for it, and everything just worked.
But it was quite complex, and debugging it,
and figuring out how it exactly works,
and for me was quite a mystery,
and another expert on web sockets.
And I figured out why would we even need web sockets.
We can just use the same exact methodology
that web sockets used, which I will explain on the next slide.
But do not use this complex packet format for our use case,
because it did not make much sense.
I guess web sockets is great, but it just does way more than we need.
So yeah, fine, let's do our own protocol.
So let's reuse this handshake of web sockets,
and keep the packet format the same as the our initial version.
So we have the sequence of packets with a length and value.
I guess you might say there's now 15 competing standards,
as the XKCD says, but I would argue that they're still 14.
We just have this one more standard that's very specific to our use case,
which I still kind of like.
It just makes it much more simpler for us to do our own thing.
But also the thing about the handshake of web sockets
is that there's a lot of different TCP release
that actually do the same approach, and they also have their own protocol-ish.
So I think it's pretty fine for our use case.
So how would that work?
In web sockets, you do a connection upgrade.
You might have seen it on the previous presentation too.
So you would send a get request with a connection upgrade,
and then the upgrade header is pretty much the name of your protocol.
I call it UOTLV-slash-1.
It's UDP over TCP-length value version one.
So if we ever want to increment the version,
we can do that and then support it on both the client and server side.
And what's cool about this is that if you have your own name for your protocol,
you can set it up with, for example, Apache to only tunnel that specific protocol
to proxy guard and ignore all the other bogus values.
So in essence, it sends this handshake,
so this gets requests to the reverse proxy by the proxy.
And then the reverse proxy sends this to the proxy.
And we can then use this HTTP connection that has been set up
to use the underlying TCP connection.
And we use this TCP connection just in this exact same way
that we have shown in the first implementation of proxy guard,
where we tunnel the UDP packets.
So it's just that the new version to a sport of reverse proxy use case
has just won HTTP requests and response again for switching protocols.
Which made it actually pretty easy.
And the programming language had a nice interface for this.
So we just had to make a tiny HTTP server and a tiny HTTP client,
but it's not a lot of code.
It was not that tricky.
It was just that I didn't know about connection upgrades before.
I thought Websock and like specific to them.
So it was kind of nice seeing that we could use the same approach.
So I prepared a little demo.
I do will not do it live because well VPNs and network.
I do not trust it with that live.
But yeah, so I have a small white guard conflict here.
It's just an example of how a white guard conflict you do like.
With proxy guard integration.
So important to know is that in proxy guard you need to set the white guard endpoint to local host
because that's your local host proxy guard.
And then proxy guard takes care of actually sending it over the internet to the proxy guard server.
This VPN profile is a full tunnel one.
So which is important to demo because that's actually the most tricky to implement.
Because you then could have a routing loop.
Because white guard has a kill switch.
So if you enable white guard, it would only sort of.
It would not tunnel.
Yeah, so with white guard, it's in the sense that if you enable white guard,
it would only make packets go over the internet that are already encrypted by white guard.
And in this case, well, we want to send it to proxy guard.
And proxy guard still has to send it over the internet.
And we do not want that white guard blocks proxy guard.
So we need some kind of way of saying, hey, proxy guard.
Make sure that you can still send packets.
And we do this with a mark, FW mark of 51820.
And this value, we also configure in proxy guard saying,
the packets that coming out of proxy guard should not be blocked by white guard.
They have like the same mark of the packets.
Yeah, as white guard itself.
And then you need to set a listen port for white guard.
And they have the endpoints for proxy guard itself.
So we have this white guard config.
We have proxy guard on the client.
So we set that up with the same FW mark.
So that's a Linux specific feature.
We have proxy guard on the server.
We start that.
So, and here I will show the reverse proxy config.
So that's the same proxy pass match.
So everything to this proxy guard.
First I'm example.org.
This is the whole HTTP path.
Tunnel that to proxy that to the proxy guard local host.
That I just started.
So that's how it works in Apache.
And you could also set this up with NGNX or Cadi or anything that would support proxy passes.
And then I just say do white guard quick up to bring white guard up.
And you can see here in WG show.
It has connected to proxy guard local host.
And I can ping the internet.
So it doesn't work that much different from white guard.
It's only that you have two more components to worry about.
And also yeah, routing can get quite tricky.
The problem with this approach that I showed on the demo is that it's quite manual.
Like you have to set up a lot of different components.
And we wanted to have good client implementation.
So it could just work with one press on the bottom.
So for edge VPN it's just an all protocol.
So here in the edge VPN client you see white guard proxy guard.
But we also made a fork of a Windows developer that he made a fork of white guard on Windows.
Red would also support proxy guard.
Red would say the proxy endpoint which is the one you've configured in your reverse proxy.
And you could just put up a tunnel.
And this would be a new key in the white guard config.
So some challenges.
I explained that the initial implementation was actually easy.
But it's just everything apart from that is just quite tricky.
The whole client implementation you have to support every OS.
You have to support mobile devices.
That can get quite tricky.
And also I'm only like a Linux dev myself.
So I do not have experience on all the platforms.
So I'll testing that was quite tricky.
I could do some more performance testing and improvements.
I didn't even speak about performance in this presentation yet.
No doubt it could be better but it's a really simple tool.
So maybe not that much more room for improvement.
There's yeah roaming and routing issues.
So if your phone goes to sleep.
We have an issue on Android where the proxy guard connection currently doesn't recover.
That's something I have to investigate.
And macOS iOS that's platform that I want to support.
But I didn't really actively test on it yet.
I'm also working on a Linux demon.
That automatically sets this up.
So it would wait for a wide-rout interface to be brought up.
And it would dynamically get all the wide-rout parameters and set up proxy guard.
And it would automatically restart proxy guard when you're flowing.
We cover it from sleep.
And when you're roaming that works.
So you can immediately use proxy guard again.
Yeah, so that was it.
In the nutshell, the home pages and code work.
So it's on our HTTP and handle.
But you can totally use it separate from HTTP and it's just a simple go binary.
And if you want to learn more about wide-rout, we're not an official wide-rout project.
But they have a stand here at first time.
I learned about yesterday.
I'm going to do more presentation.
If you want to get funding for open source.
So HTTP has received funding before from NLNet.
They're also at first time, and they have also cool stickers.
So make sure to go there.
And if you want to learn more about Giant or talk to me in the afternoon here,
we also have a stand.
So yeah.
Thank you for listening.
Thank you.
Yeah.
Hang on.
By putting the packet length in your own packet,
I was thinking that means it's not going to work with
with jumbo packets.
But I don't think there's a use case.
Use that with jumbo packets, right?
No.
Yeah.
So it's fine.
Okay.
Thanks for your question.
One more.
As soon as you convert into HTTP to TCP,
so when you establish TCP session,
did you have a problem with time-weight sockets?
Because as I guess you have many, many UDP clients,
which connect and sometimes there are the same clients,
which connect quickly, but you draw up the same.
Sorry, come here.
I mean, sometimes there are clients,
which reconnects very quickly,
but they're really finished in session,
but they still use themselves.
All right.
Do you mean that the client is going?
Sometimes.
You can't know the time-weight time-out in line,
please.
All right.
Yeah.
It's a TCP is also tricky,
but we also implemented time-outs.
So if a client is gone,
it would cut off that client after a while.
So yeah, that is implemented.
Yeah.
If that was your question,
but...
I mean, it's just how you keep in time-weight.
Yeah.
Out of that it takes time, you mean.
Yeah.
To set up.
Yeah.
Cool.
Thank you very much.
Thank you.