adrienverge / openfortivpn

Client for PPP+TLS VPN tunnel services
GNU General Public License v3.0
2.64k stars 317 forks source link

Revisit --persistent? #712

Open DimitriPapadopoulos opened 4 years ago

DimitriPapadopoulos commented 4 years ago

Right now --persistent re-runs the whole run_tunnel() function, including re-authenticating and getting a new cookie.

If a cookie is already available perhaps we should attempt to re-use it before re-authenticating. How many times should we try? Which error code tells us the cookie is invalid so that we can attempt to re-authenticate?

dlenski commented 3 years ago

If a cookie is already available perhaps we should attempt to re-use it before re-authenticating. How many times should we try? Which error code tells us the cookie is invalid so that we can attempt to re-authenticate?

We've been trying to figure this out in OpenConnect as well…

The only Fortinet VPN that I currently have access to only allows the SVPNCOOKIE to be used once to initiate a tunnel (GET /remote/sslvpn-tunnel, or the DTLS equivalent).

If the TLS or DTLS session gets disconnected, then when I create a new one, and try to reconnect the tunnel by redoing the same request with the same SVPNCOOKIE… the server simply redirects (302) to /remote/login, rather than beginning a PPP session. It gives no other indication that the cookie is invalid, not even something sane like a 4xx HTTP status. :-1:

I consider this a serious design flaw since it means it's impossible for the client to roam onto a different network connection, or otherwise be resilient to brief outages, unlike basically every other SSL VPN in existence.

You can test whether your Fortinet VPN has the same behavior (which ) by running sudo openconnect --prot=fortinet your.server.com --dump -vvv, and then causing openconnect to pause-and-reconnect with sudo killall -USR2 openconnect, and take a look at the log messages.

Would certainly be helpful to both projects to figure out if there are servers that can handle this in a saner way…

DimitriPapadopoulos commented 3 years ago

@dlenski Would love to help here, but I cannot connect to my FortiGate:

sudo /my/path/sbin/openconnect --protocol=fortinet --user=username --passwd-on-stdin my.server.com --dump -vvv

It just stands there and doesn't print or seem to be doing anything. I'm on Ubuntu 20.04. Am I doing anything stupid? Can I open a ticket on GitLab to discuss this issue, unless the error is obvious of course?

dlenski commented 3 years ago
sudo /my/path/sbin/openconnect --protocol=fortinet --user=username --passwd-on-stdin my.server.com --dump -vvv

Ah. That's because it's waiting for you to enter the password ("on stdin" :smile:) before it does anything else. In other words, run as echo password | openconnect --passwd-on-stdin… or else omit that option and let it do the login interactively.

Also, if you want to test the brand-spankin'-new DTLS support, you should build from https://gitlab.com/openconnect/openconnect/blob/ppp_dtls_wip.

However, the DTLS interacts with the cookie-reusability issue in a somewhat complicated way, so in order to figure out the cookie reusability, you should run with --no-dtls.

DimitriPapadopoulos commented 3 years ago

Of course 😀 Also, I'll try with --no-dtls as in my case I get:

No TLS PPP for DTLS state 1
DTLS initialised. DPD 10, Keepalive 0
Delaying tunnel with reason: PPP negotiation
No work to do; sleeping for 1000 ms...
Delaying tunnel with reason: awaiting PPP DTLS connection
No work to do; sleeping for 1000 ms...
Delaying tunnel with reason: awaiting PPP DTLS connection
No work to do; sleeping for 1000 ms...
Delaying tunnel with reason: awaiting PPP DTLS connection
No work to do; sleeping for 1000 ms...
Delaying tunnel with reason: awaiting PPP DTLS connection
No work to do; sleeping for 1000 ms...
Delaying tunnel with reason: awaiting PPP DTLS connection
Failed to connect DTLS tunnel; using HTTPS instead (state 4).
dlenski commented 3 years ago

Also, I'll try with --no-dtls as in my case I get:

That's interesting. It's saying that the DTLS connection initiation is working, but then the PPP-over-DTLS configuration is not completing. Does it then subsequently succeed via PPP-over-HTTPS? If so, please run with --dump -vvv and open a ticket for us at https://gitlab.com/openconnect/openconnect/issues… and include as much of the --dump log as possible, so we can see why the PPP-over-DTLS negotiation is failing.

But back to the topic of automatic reconnection / cookie reuse… PPP-over-HTTPS will make this part much clearer, anyway.

mrbaseman commented 3 years ago

That's interesting. It's saying that the DTLS connection initiation is working, but then the PPP-over-DTLS configuration is not completing.

This may be because DTLS, due to the fact that it uses the stateless UDP protocol, is blocked at some other firewall on the way between the client and the Fortigate (at least at our organization there is such policy implemented).

dlenski commented 3 years ago

@mrbaseman, yes, understood. The Fortinet server should not tell users that it supports DTLS, if such use is in fact blocked. However, in practice we've already found servers that do.

We've seen past examples with other VPN protocols as well… administrators routinely screw up UDP/DTLS/ESP configuration for their VPNs, and don't fix it because all the users just rely on the TLS fallback. :man_shrugging:

We continue to make rapid progress on Fortinet/F5 PPP-over-DTLS in OpenConnect, so if anyone else wants to test it out, please do.

dwmw2 commented 3 years ago

I've been playing with the reconnect support. The server has a tunnel-connect-without-reauth option which is visible to the user: <auth-ses check-src-ip='1' tun-connect-without-reauth='1' tun-user-ses-timeout='240' />... but it only helps a tiny bit.

You still can't just reestablish the DTLS session; you have to reconnect on the TCP socket and refetch the /remote/fortisslvpn_xml before you can pass any traffic again. What's worse is that as far as I can tell you can only reconnect once (that is, you can only connect twice) before it starts returning "error" as the assigned address (e.g. <assigned-addr ipv6='error' />) and failing to connect.

I don't know if this is just because I'm doing something wrong, or if it's a fundamental limitation of the server. Does the official client manage to do any better?

DimitriPapadopoulos commented 3 years ago

I have not used FortiClient in a roaming context. What should I test? For example switch from Wi-Fi to Ethernet while VPN SSL is running?

dwmw2 commented 3 years ago

Yeah, exactly that. Although that might fall foul of checks that you're coming from the same IP address with your reconnection, if such checks are enabled.

dlenski commented 3 years ago

I've been playing with the reconnect support. The server has a tunnel-connect-without-reauth option which is visible to the user: <auth-ses check-src-ip='1' tun-connect-without-reauth='1' tun-user-ses-timeout='240' />... but it only helps a tiny bit.

You still can't just reestablish the DTLS session; you have to reconnect on the TCP socket and refetch the /remote/fortisslvpn_xml before you can pass any traffic again.

Ah, now that sounds just like GlobalProtect :man_shrugging:

What's worse is that as far as I can tell you can only reconnect once (that is, you can only connect twice) before it starts returning "error" as the assigned address (e.g. <assigned-addr ipv6='error' />) and failing to connect.

Quite possibly the 1 literally just means "you can only reconnect once"? :joy:

I don't know if this is just because I'm doing something wrong, or if it's a fundamental limitation of the server. Does the official client manage to do any better?

Is it possible that we must PPP-Terminate at the PPP layer (or alternatively must not PPP-Terminate :man_shrugging:) in order to be able to reconnect?

dlenski commented 3 years ago

I've been playing with the reconnect support. The server has a tunnel-connect-without-reauth option which is visible to the user: <auth-ses check-src-ip='1' tun-connect-without-reauth='1' tun-user-ses-timeout='240' />... but it only helps a tiny bit.

You still can't just reestablish the DTLS session; you have to reconnect on the TCP socket and refetch the /remote/fortisslvpn_xml before you can pass any traffic again.

Ah, now that sounds just like GlobalProtect :man_shrugging:

What's worse is that as far as I can tell you can only reconnect once (that is, you can only connect twice) before it starts returning "error" as the assigned address (e.g. <assigned-addr ipv6='error' />) and failing to connect.

Quite possibly the 1 literally just means "you can only reconnect once"? :joy:

I don't know if this is just because I'm doing something wrong, or if it's a fundamental limitation of the server. Does the official client manage to do any better?

Is it possible that we must PPP-Terminate at the PPP layer (or alternatively must not PPP-Terminate :man_shrugging:) in order to be able to reconnect?

dwmw2 commented 3 years ago

Having to PPP terminate would be insane. That would make it usable only for the artificial test cases like where we just send SIGUSR2 to OpenConnect to trigger a reconnect, and not actually useful for the real world cases where we've changed physical networks and can't send anything over the old transport any more... THAT'S WHY WE ARE TRYING TO ESTABLISH A NEW ONE! :)

dwmw2 commented 3 years ago

That said, I wouldn't put it past them to have done it that way. It wouldn't even be the most insane thing I've seen.

DimitriPapadopoulos commented 3 years ago

Nothing good happens when switching from Wi-Fi to Ethernet while FortiCllent is connected. The connection is just lost. I'm behind my provider's router, so the visible IP address shouldn't change. But then I don't have access to a FortiGate that supports DTLS. Note that FortiClient for Linux can be download - the one you're after is under FortiClient VPN - Remote Access.

dwmw2 commented 3 years ago

I have set up a test Fortigate VM which definitely has the tunnel-connect-without-reauth option set, and which does DTLS over both IPv6 and Legacy IP. You're welcome to an account on that if you want.

dwmw2 commented 3 years ago

Oh, I'm being stupid. I do have endless reconnect working. Authenticate once to get the SVPNCOOKIE, then I can reuse that cookie over and over again in subsequent invocations as long as I quit with Ctrl-\ and don't let it sign off.

The 'error' thing is exactly what I get if I do the first authentication over Legacy IP and then connect the second time over IPv6, or vice versa. I wonder if routing changes (due to actually getting on the VPN) were causing my second reconnect to happen over Legacy IP instead of IPv6 in my testing? I think we'll need to fix the IP address (and hence the protocol) that we (re)connect to?