VPN tunnel for apps
-
@mehdi You only have to change one file (docker.js) so I’ll send that to you today!
But the app has no other issues. It is on the Cloudron network. It has everything network-wise that the OpenVPN container has which means there are no issues anywhere. The only issue is that Nginx doesn’t take that into account because it inherits all network ports and modes from the OVPN Client it’s connected to.
I actually have an idea though to prove that everything works except the web interface using the app terminal.
I will also be sending you my OVPN client in chat so you can see how it works.
-
@mehdi Oh, and just to make it clear, the app is running fine even in the terminal. If I install the LAMP app for instance and connect it to the OpenVPN Client. I can go to LAMP's terminal and type
curl 127.0.0.1:80
and it will give me the LAMP Welcome page, but from the IP address of the OpenVPN Client. It'll just never get past the "Starting..." stage without a 200 response and Nginx is routing it to the wrong location. Maybe I could try hardcoding it to the right port after it installs it's config.Like, this works. But I don't know how to get NGINX to route it to the port I need it to route to. Docker is supposed to
bind
theapp.httpPort
(say 34567) to theexposedPort
(say 80). But that Docker binding command has to be removed to connect the containers so NGINX has to bind them manually somehow and I don't know how to work with Nginx. -
I'm near positive the way to route NGINX properly is to to add another reverse proxy which leads from the Docker randomized
app.http
port to theexposedPort
(AKAapp.manifest.httpPort
). I'm not sure how though. But I can clearly access the exposedPort's data from either terminal.In the terminal of either of the two network-bonded apps (the LAMP or the VPN Client) - I can curl (localhost) the exposed ports of either app (the VPN Client or the LAMP) and get back the correct HTML pages.
It's just that the reverse proxy is forwarding it to a port that Docker wasn't allowed to bind. So with no binding, that means no NGINX web page.
-
For people just joining and for me to read this in the future:
Cloudron has a user-defined network
cloudron
that it uses for all of it's apps and services. I'm connecting my OpenVPN Client app directly to another app (LAMP for testing) usingNetworkMode
which is the official way to do this. When doing so, both apps share the exact same network space (including both being connected to thecloudron
network) and can, in fact, even talk to each other directly (I need to set the OpenVPN Client's exposed port to something ridiculous like in the 50000s so it doesn't conflict with any regular app's commonly exposed ports - but in the end the OpenVPN Client will expose all of it's internal network ports-P
for compatibility with all other apps connecting to it - but for now, this will do since I'm just testing).Now, the NGINX Reverse Proxy resides on the main
box
level of Cloudron and routes to the randomized Docker binding port of the app which forwards it to it's "real" port (I honestly don't know where the IP translation takes place...I can't find it).So, solution, place a second NGINX proxy at the randomized Docker
app.http
port and have that nginx server be inside of the internal network of the LAMP testing app so it can then internally forward the web request to it'sexposedPort
which is, also, port 80 (for LAMP anyway). -
@mehdi said in VPN tunnel for apps:
There is no good reason to make all the traffic, even the local one, be through OpenVPN.
All local traffic is local, it doesn't go through the VPN. All of the connected-to-the-vpn-client apps run indefinitely. Eventually after Cloudron says "Starting..." it switches the message to "Not Responding" but the app will continue indefinitely because there's nothing actually wrong with it. It's connecting to all the local services it needs, it's just, this feature was designed without consideration of Cloudron's network. That's why from within the app's terminal, I can access everything locally and everything externally (the open web) from Cloudron and the web sees my VPN Client's IP Address instead of Cloudrons. This is a perfectly running app. Except...I can't expose it's internal IP:ExposedPort to NGINX to have it actually pass the health check (meaning the web app isn't being routed to the right place and I think I need a second NGINX reverse proxy to get me routed to the app's exposed port). It gets as far as
app.httpPort
and then because there is no binding for theapp.mainfest.httpPort
(AKA:ExposedPort
), it just gets stuck on the web side of things.I can
curl
all I want into it the LAMP welcome. But can't access it from outside the container itself. -
Quick example of all local processes running for the second container:
Oct 02 12:05:28 2020-10-02 16:05:28,718 INFO success: redis entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Oct 02 12:05:28 2020-10-02 16:05:28,719 INFO success: redis-service entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Oct 02 12:05:40 [GET] /healthcheck
It just gets stuck at the health check because NGINX is giving it a 502 Bad Gateway since port 80 is not bound to port
app.httpPort
. That's what I still need to figure out and I think what's best is @robi's double nginx server suggestion. I just don't know how to set up NGINX inside of the LAMP container frombox
code though I'm sure it's possible. -
I still think the solution lies in this function:
https://git.cloudron.io/cloudron/box/-/blob/master/src/reverseproxy.js#L463
But that function is setting NGINX config listening on the server's public IP port. I need NGINX inside of the container to forward to the
app.manifest.httpPort
(in LAMP's case it's port 80).Or maybe it doesn't need to be inside of the container...since if I SSH into the VPS and do
curl ip-address-of-lamp:80
then it returns the HTML just fine. -
SUCCESS!!!
I'm so close. I can manually edit the NGINX configuration of the LAMP-test app and get it's web app working using it's local IP (not 127.0.0.1:
app.httpPort
, but 172.18.0.3:80 - the port 80 is what LAMP uses). Both web pages come up for their each individual domain names. WOOOO!It still doesn't make it past the "starting..." step though. That might be because I'm editing the NGINX file and then restarting the app to make it's web interface work. If the NGINX file was properly created from the beginning the check might pass.
-
@Lonk said in VPN tunnel for apps:
@mehdi said in VPN tunnel for apps:
There is no good reason to make all the traffic, even the local one, be through OpenVPN.
All local traffic is local, it doesn't go through the VPN.
Sorry, I meant: There is no good reason to make all the traffic, even the local one, be through the OpenVPN client container.
-
@mehdi said in VPN tunnel for apps:
What you should try to achieve is making only the app's outgoing traffic go through OpenVPN, and the connexion between it and Nginx and stuff stay exactly the same as it currently is
That is currently the case as far as I can tell. All incoming traffic bypasses the VPN entirely. Outgoing traffic uses the VPN's connection. But the NGINX needs an incredibly minor change. There's no way around that.
-
But the bigger deal is that it works now!
Okay, I know it says not responding but it actually is fully working (web page and everything). The "not responding" status is something I think will fix itself when I get the Nginx IP
proxy_pass
working. Because it's sort of a race condition as to when I start the app and when I manually rewrite the Nginx IP. And I'll never go fast enough to beat the "Not responding message". -
I just need a function like
getAppByManifestId(manifestID)
- and this will be a fully functioning proof of concept. And then another function forgetLocalIPAddressByApp(app)
.Oh, and I need to change some slight NGINX config, make it more dynamic (Cloudron hardcodes it's
proxy_pass
to 127.0.0.1 rn and it needs to be dynamic). -
You can't getAppByManifestId since there can be many apps for a given manifest id. I think you have to store whatever you want in the database in the apps table. Then when you get the app, you can get that information (like say the network id). Sorry, if my answer is totally off since I didn't read the full thread completely.
-
@girish I had actually nailed that issue out actually as a matter of fact. The only thing I have left is
getLocalIPAddressByApp(app)
- but maybe theapp
object already stores its internal IP (I didn’t think it did, but it stores a lot)? -
@girish As for the first function, it was mainly due to me needing to find a way to identify the VPN client app by the only unique identifier I knew. I’ve hardcoded it for my use case rn...but that won’t work for the production version now that I think about it - so I’ll still need to get app data using the manifest.id (com.joelstickney.openvpn-client-cloudron). So when the app that needs to connect to it starts, it attaches to the container ID of the Open VPN client on
containerCreate
.Maybe right now I’ll store the things I need in ENV variables and find a way to use the “apps” object to parse through all of them till I find the VPN client.