OpenVPN / openvpn3-linux

OpenVPN 3 Linux client
GNU Affero General Public License v3.0
573 stars 152 forks source link

StatusManagerEvent signal is sent before backend client service is ready #238

Closed savely-krasovsky closed 5 months ago

savely-krasovsky commented 9 months ago

As far as I got it, from some point there was a change then StatusChange and AttentionRequired became "private" signals. There is LogForward method which allows to receive some signals with net.openvpn.v3.backends as sender. But I cannot receive any signals from net.openvpn.v3.sessions (while docs clearly says it should be proxified).

It lead me to the situation where I cannot track state of session initialization: I am importing configuration (Import), creating new session (NewTunnel), but if I will immediately call something like LogForward or Ready, I will get this error: Backend VPN process is not ready.

Without any signals I could not do it without manual sleeps in the code.

I use godbus, it's a bit more lower level than Python's dbus, but even if I didn't apply any Match filter and just print ALL signals my app is getting, I still unable to find any signals from sessions.

savely-krasovsky commented 9 months ago

I also relatively often get this error Object does not exist at path “/net/openvpn/v3/sessions” while trying to create session NewTunnel right after Import. I guess session daemon shuts down sometime and after importing configuration it needs time to spin up again. But I have no clue how to fix it without another sleep...

UPD. Oh I could even get Object does not exist at path “/net/openvpn/v3/configuration” while trying to Import configuration. Probably this is not ok and something crashing?

dsommers commented 9 months ago

I've not pushed out yet some new code which uses this feature from Python. It will be part of the project once the code is properly tested. But you can have a look at it here: https://gist.github.com/dsommers/d4447dba169b1278bcab4b7ebd235761

The approach to use is to listen to SessionManagerEvent. That is sent from the net.openvpn.v3.sessions service (openvpn3-service-sessionmgr).

To check if your setup is functional, try running this command as root: openvpn3-admin version --services. That should wake up all of the accessible services and retrieve the version of each service.

savely-krasovsky commented 9 months ago

I see the SessionManagerEvent signal, but it doesn't guarantee that session is up and ready for the calls. If I will try to immediately call LogForward after receiving this signal I am getting Backend VPN process is not ready. Sleep for 3000 ms helps, but it doesn't look good...

savely-krasovsky commented 9 months ago

To check if your setup is functional, try running this command as root: openvpn3-admin version --services. That should wake up all of the accessible services and retrieve the version of each service.

I am creating client app, so not sure how to ensure that D-Bus interfaces are running. After trying to call them I could get Object does not exist at path [service], but seems like they start to spin up after that, so next calls works, but it doesn't look good from frontend app developer point. Are there any methods to init services before calling them without root (as Flatpak app for example)?

dsommers commented 9 months ago

Depending on the system itself, I have seen that it in some cases the client process has not settled quickly enough, so a little delay is needed currently. I'm re-doing the D-Bus implementation (see the 171 issue ticket), which seems to be far more efficient in general. I'll have a look closer at when the SessionManagerEvent is sent, like after the client has properly registered. Until that point is reached, the session path needed for the LogForward may indeed not be ready just yet.

There are several things happening when NewTunnel is called:

When all these steps are done, that's when the session path will be fully functional. That SessionManagerEvent signal should probably be moved down to when the sessionmgr completes the RegistrationConfirmation call. That's when it is sure the configuration profile has been retrieved and parsed.

But it's awesome that you're looking into a client app! Keep us updated and we can collaborate further on these efforts.

savely-krasovsky commented 9 months ago

@dsommers thanks for clarifying. Yes, I understand that calling NewTunnel cause a lot of work under the hood, but currently SessionManagerEvent mostly useless, because it still requires a sleep. In my opinion this should be fixed at some point.

But I still don't understand what I can do with Object does not exist at path [service] errors. Just trying to call services until they will finally available doesn't seem good for me. Is it even known issues or there is something wrong with my system?

dsommers commented 9 months ago

I'll definitely try to improve the situation with v22_dev, once that is ready, yes.

Generally speaking, the challenge with D-Bus is that there are no strict delivery guarantees on signals. At least that's my experience so far. In many ways, it's not unreasonable to compare it with UDP traffic. Most of the time it works just fine, but it may be delayed.

In this case, since the NewTunnel results in two binaries being started (first via D-Bus (openvpn3-service-backendstart, second via a fork() + execve() call - openvpn3-service-client), this does introduces some random latencies - which can be quite annoying. So when the SesionManagerEvent is sent almost immediately after the StartClient call, I believe the situation will be improved by putting it later in the chain.

savely-krasovsky commented 8 months ago

@dsommers is there any workaround to pre-activate all necessary D-Bus services? Currently client user should click "Connect" button like 2 or 3 times to finally connect. Because of Object does not exist at path “/net/openvpn/v3/... errors. We are getting this issue every time after cold PC boot. I guess sure we can make some python script and put it in in ~/.config/autostart or even create systemd one-shot user service. But it's kinda too much? Maybe there are better workarounds for now? Just running openvpn3-admin version --services before connecting should be ok?

dsommers commented 8 months ago

What is this "Connect" button? Do you have some code providing such a button? Also, have a look at the OpenVPN 3 Indicator project; we've recently embraced that project as an alternative GUI for OpenVPN 3 Linux.

That said, the common behaviour across D-Bus libraries is to give that "Object does not exist" behaviour if the D-Bus service is not running and not starting quick enough. That even goes for the gdbus command line against various services as well. The service needs to start incredibly quickly to respond in time to avoid that error. On a slower system this happens far more often than on systems being more responsive.

The approach I've taken in GDBus++ and the refactoring of openvpn3-linux, is to do a for-loop check doing a StartServiceByName and then GetNameOwner. That usually works after the first round, some times on slow responses it needs 2 rounds. I've found a sleep of 0.4 seconds is usually enough between each round if it fails. You can see the C++ code here: https://codeberg.org/OpenVPN/gdbuspp/src/commit/584d477fb3fbb9b3905cf20463fb10c56edbbfb7/gdbuspp/proxy/utils.cpp#L170 I do consider this more like a hack. A more appropriate approach would be to listen for a D-Bus signal from the D-Bus daemon when a specific service name is registered and hold back until that happens. But glib2 is generally not happy about acting on signals while processing in a method call; even though the GDBus++ might be more easily capable of doing that.

savely-krasovsky commented 8 months ago

@dsommers thanks for the reply! Very useful. Well, by "connect" button I mean starting an OpenVPN3 session. Then I call consequently Import, Connect, etc I got those errors. Thanks for the detailed answer, now I know where to dig... Currently as a workaround I am just calling openvpn3-admin version --services, mitigates the problem completely, but feels wrong.

savely-krasovsky commented 8 months ago

Well, it worked! Much better solution, thank you again.

dsommers commented 8 months ago

Just a note ... you seem to import configurations each time. Consider using persistent configuration imports.

$ openvpn3 config-import --persistent --name CONFIG_NAME --config CONFIG_FILE

With this approach, the configuration profile will always be present on the system for the user which did the import. To grant more users access, have a look at the openvpn3 config-acl command. Granting root access will make it possible to use the openvpn3-session@CONFIG_NAME.service systemd unit file to start a VPN session during boot.

And once imported, all you need to do: openvpn3 session-start --config CONFIG_NAME ... even after a reboot; no import needed.

savely-krasovsky commented 8 months ago

Yes, I know about persistent configurations, but in our case we dynamically fetch and assign configurations for each user based on conditions (e.g. where OpenVPN over UDP could be blocked). Today it could ok, tomorrow already not.

dsommers commented 7 months ago

This should be fixed with the latest devsnapshot. The issuing of the "session created" signal is now postponed until after the session object is created and the backend VPN client process has completed the registration with the session manager.

https://codeberg.org/OpenVPN/openvpn3-linux/src/commit/87872f6c5d6f5913e5ccf955f424919b426512f2/src/sessionmgr/tunnel-queue.cpp#L200

dsommers commented 5 months ago

Please re-test this with the latest v22_dev release. I believe this issue is resolved, but a confirmation would be great!

savely-krasovsky commented 5 months ago

I can carefully say that now SessionManagerEvent works as intended.

dsommers commented 5 months ago

Okay, closing this issue based on this feedback. If there are more related issues, please open a new ticket on Codeberg ... https://codeberg.org/OpenVPN/openvpn3-linux/issues