Timac / VPNStatus

VPNStatus, a replacement for macOS builtin VPN Status
https://blog.timac.org/2018/0719-vpnstatus/
MIT License
221 stars 31 forks source link

Alternative to polling #32

Closed halo closed 5 months ago

halo commented 6 months ago

Hi! Thank you for building this tool and posting a blog entry about it.

I'm trying to reduce the time it takes for the VPN to automatically connect, because I don't find polling ideal (it leaks the IP too often). The best solution for this to be the "connect on demand" switch that IKEv2 VPNs support natively on macOS.

Do you happen to know if the internal Network Extension libraries you're using could help in setting that switch programmatically? Maybe by passing some reference of the VPN service to the System Configuration Framework? I know it's unlikely, but I couldn't resist asking, maybe you have an idea (I fought with System Configuration before, it's not fun).

Or maybe one could listen to changes in network conditions instead of polling with a timer?

I'm even considering using NETunnelProviderManager to (re)create my IKEv2 configurations from within an app, just to be able to set the "connect on demand" flag somehow. But I haven't tried that yet, maybe that could work.

PS: I didn't know it would be so hard to convert the NEConfiguration calls to Swift. So I'd rather not try. Or could a swift package for ANPNEServicesManager contain Objective-C bridges? 😆

PPS: I found SwiftUI to be extremely satisfying for menu bar items and avoiding IBOutlets. What is your macOS target? Do you intend to be much backwards compatible?

Thank you for your time and insight.

Timac commented 6 months ago

Hey @halo,

Thanks for the great suggestions and kind words.

I'm trying to reduce the time it takes for the VPN to automatically connect, because I don't find polling ideal (it leaks the IP too often). The best solution for this to be the "connect on demand" switch that IKEv2 VPNs support natively on macOS.

The Connect on demand feature is indeed natively supported for IKEv2 VPNs on macOS and works great. I would recommend to use it if we want to prevent IP leaking. Are you trying to get this feature for other types of VPNs?

Or maybe one could listen to changes in network conditions instead of polling with a timer?

It is possible to listen to changes in network conditions. However, since it wouldn't be handled by the system, this wouldn't help to prevent IP leaking. Note that you can reduce the delay using AlwaysConnectedRetryDelay.

I'm even considering using NETunnelProviderManager to (re)create my IKEv2 configurations from within an app, just to be able to set the "connect on demand" flag somehow. But I haven't tried that yet, maybe that could work.

It sounds like you just want to enable the Connect on demand flag in your IKEv2 profile. What are you currently using to create them? Why can you just enable Connect on demand?

PS: I didn't know it would be so hard to convert the NEConfiguration calls to Swift. So I'd rather not try. Or could a swift package for ANPNEServicesManager contain Objective-C bridges? 😆

When working with reverse-engineered code and private APIs, I find it easier to use Objective-C than Swift. So for this project, I don't plan to use Swift.

PPS: I found SwiftUI to be extremely satisfying for menu bar items and avoiding IBOutlets. What is your macOS target? Do you intend to be much backwards compatible?

The SwiftUI MenuBarExtra has some limitations compared to using AppKit. For such a project with limited interface and containing some reversed-engineered code, the use of Objective-C and AppKit is more appropriate than Swift and SwiftUI. Don't get me wrong, I really like to use Swift and SwiftUI in other projects, but I see no advantage here.

halo commented 5 months ago

Ah, excellent questions!

I think it would be best, to first clarify my most fundamental scenarios.

1️⃣ Quickly configure an IKEv2 service

.mobileconfig is nice, but it takes an awful lot of time to open the profile in System Settings and confirm it with administrator credentials, and then I still have to enter the EAP authentication username and password manually:

eap

So, I envision a small app like Wireguard that can create multiple IKEv2 services ready-to-go. From what I understand I would need to use NETunnelProviderProtocol and not Personal VPN Entitlement.

But this is a nice-to-have, since I could live with the one-time setup of creating the VPN services.

2️⃣ Deactivate VPN for a short period of time

To deactivate "Connect on Demand" for an IKEv2 service, I have to do 6 clicks and enter admin credentials. That's much more than the one click in your VPNStatus app (which I only can use when "Connect on Demand" is deactivated, though).

Solution: Either I manage to set that switch programmatically (SystemConfiguration, ANPNEServicesManager, etc.) or I use my VPN-Creator-App from above to modify the services I previously created (NEVPNManager).

3️⃣ Switch from one VPN to another.

When "VPN Alpha" has "Connect on Demand" (or in VPNStatus "Always auto connect"), I cannot just click on "Connect Beta" (even though the caption suggests that it should work). Both, in the native VPN menu and in VPNStatus, all that does is briefly disconnect Alpha and then immediately connect Alpha again.


Are you trying to get this feature for other types of VPNs

I'm fine with only supporting IKEv2.

Note that you can reduce the delay using AlwaysConnectedRetryDelay.

Well, a delay of a second or two would be acceptable (though frustrating, given native support for "Connect on Demand" for taking care of both connecting and reconnecting). But how would you estimate the battery consumption penalty on having the timer trigger every second? I can imagine, that after 3 hours or so, it would be noticeable. But maybe I'm totally mistaken.

To alleviate this battery/performance problem, I thought of subscribing to network condition changes. That's a pretty good time to trigger the check (such as when I plug in an ethernet adapter or the Wi-Fi connects to a hotspot).

I find it easier to use Objective-C than Swift

Interesting, and totally legit for undocumented paths 😄


Overall, I'm rather frustrated with the user experience of VPNs. I guess that's why you created your app in the first place :)

Again, thank you for your time and brain-power :)

Timac commented 5 months ago

Thanks a lot for the clarification! I have a couple of comments and more questions.

So, I envision a small app like Wireguard that can create multiple IKEv2 services ready-to-go.

How often and why do you create configurations? I have set up a couple of VPNs, but most of them are always disconnected. I never need to add new mobile configurations. That's the point I don't understand.

To deactivate "Connect on Demand" for an IKEv2 service, I have to do 6 clicks and enter admin credentials.

Why 6 clicks? In my case on macOS Sonoma, I only have 4 clicks and don't need to enter the admin credentials:

  1. Click on System Settings in the Dock
  2. Click on the VPN menu
  3. Click on (i) for the selected VPN
  4. Click on Connect on demand

If you open often the VPN settings, you could even write a small Apple Script app to reduce the number of clicks. For example:

tell application "System Settings"
    activate
    delay 1
    reveal pane id "com.apple.NetworkExtensionSettingsUI.NESettingsUIExtension*vpn"
end tell

When "VPN Alpha" has "Connect on Demand" (or in VPNStatus "Always auto connect"), I cannot just click on "Connect Beta" (even though the caption suggests that it should work). Both, in the native VPN menu and in VPNStatus, all that does is briefly disconnect Alpha and then immediately connect Alpha again.

Sadly if you have to switch frequently between multiple VPNs, you can't use Connect on demand or Always auto connect. Unless you have some rules. In my case, I have some VPNs with Connect on demand and rules based on the SSID. I use Always auto connect when I am under a known SSID (the other VPNs with Connect on demand are not connected) and want to connect to one specific VPN for a long time.

But how would you estimate the battery consumption penalty on having the timer trigger every second?

Having a timer triggering every second shouldn't cause any battery issues nowadays. The only problem with VPNtatus is that querying the VPN status used to take some time. Is it possible that this limitation doesn't exist anymore (APIs improvements, switch to Apple Silicon, macOS updates, …).

To alleviate this battery/performance problem, I thought of subscribing to network condition changes.

I don't think you would encounter any battery/performance problem. But listening to network condition changes would definitively be a smarter approach.

halo commented 5 months ago

Thank you for your reply.

How often and why do you create configurations?

I create connections to customer sites and anonymization services. Multiple sites, multiple anonymization countries, on multiple laptops, for multiple laptop-users.

I'm way below the threshold for MDM and slightly above the threshold of repetitive frustration :)

I only have 4 clicks and don't need to enter the admin credentials

Credentials are being asked if you're a non admin-user (I guess it depends on which user installed the profile).

The novice pathway is likely the following (it could be that that's the only way that would always work reliably if I were to explain it over the phone):

  1. Click on 🍏
  2. Click on System Settings
  3. Click on VPN
  4. Click on ℹ︎ for the selected VPN
  5. [enter username, enter password, try to not mistype, then press OK]
  6. Click on Connect on demand
  7. Click on OK
  8. Click on x to close the System Settings window

And that's just to toggle. You may have to do it again to reactivate.

In my case, I have some VPNs with Connect on demand and rules based on the SSID

Just to be sure: You're referring to VPNStatus, right? Are there rules for SSID in the operating system?

Why can you just enable Connect on demand?

Apart from the time that it takes, this is also incredibly hard to teach novice users. They will not remember the steps or not do them because it's too tedious (even I don't like to do this even 2 times a day)

But I'm incredibly thankful for our brainstorming here. Because I now have a path forward, I think. I'll skip native "Connect on demand" and fork VPNStatus and will only leave these menu items:

Clicking on "Connect Alpha" will set "Automatically connect" for that service, unset it for the other services, disconnect Beta and connect Alpha. So it's truly one-click and novice users understand it perfectly well. That you only can deactivate it for some time (rather than just disconnect and then forget to reconnect), resembles "Connect on Demand". If you select "Quit" all VPNs are disconnected.

I know, this would be highly customized (and I might have to think over the menu items again and again). The native VPN menu is just so very inflexible (I guess that's partly why you wrote VPNStatus).