Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows support #41

Open
bersbersbers opened this issue Mar 27, 2020 · 18 comments · May be fixed by #102
Open

Windows support #41

bersbersbers opened this issue Mar 27, 2020 · 18 comments · May be fixed by #102

Comments

@bersbersbers
Copy link

This package looks great - I would like to use it on Windows 10. However, ...

C:\Users\bers>vpn-slice
WARNING: Couldn't configure platform provider: 'OSError' object is not callable
Traceback (most recent call last):
  File "c:\users\bers\appdata\local\programs\python\python38\lib\site-packages\vpn_slice\util.py", line 17, in __getattr__
    return self[k]
KeyError: 'process'

Is Windows support planned?

@dlenski
Copy link
Owner

dlenski commented Mar 27, 2020

However, ...

Error message should be clearer here. Just fixed, thanks.

Is Windows support planned?

I don't use Windows for software development myself, and very little interest in starting 😅

That said, contributions would be most welcome and I'd be glad to give advice on how to do it:

@bersbersbers
Copy link
Author

Thanks for the feedback! The diff of #11 looks worse than it is, as it does both refactor and add macOS support at the same time. I'll see if I find the time to do anything with this, although I doubt it in the short term. Whoever wants to take this, it's all yours :)

@bersbersbers
Copy link
Author

bersbersbers commented Mar 29, 2020

So I did a little digging. It seems that this can be ported without too much effort, especially if one is OK with relying on WSL for ip and dig. However, two bigger road blocks that I have identified:

  1. Unable to pass script taking parameters on Windows, https://gitlab.com/openconnect/openconnect/-/issues/114 (with a potential workaround)
  2. os.fork() not being available on Windows, https://docs.python.org/3/library/os.html#os.fork
    # we continue running in a new child process, so the VPN can actually
    # start in the background, because we need to actually send traffic to it
    if args.fork and os.fork():
    raise SystemExit

@dlenski
Copy link
Owner

dlenski commented Mar 29, 2020

  1. Unable to pass script taking parameters on Windows, https://gitlab.com/openconnect/openconnect/-/issues/114 (with a potential workaround)

It's super ugly, but one workaround for now would be for you to pass a --script argument of vpn-slice.js" param1 param2 param3, which will "trick" OpenConnect into executing:

cscript.exe "vpn-slice.js" param1 param2 param3""

… which should do the right thing. One of the issues here is that Windows hasn't had a historically consistent and standardized way to split/parse/quote command-line arguments, which makes it a bit of a trial-and-error mess to pass the above into OpenConnect. 🤷‍♂️

  1. os.fork() not being available on Windows, https://docs.python.org/3/library/os.html#os.fork

Yes, this is important. I've thought about adding a reason=post-connect script invocation to OpenConnect itself, so that vpn-slice doesn't have to fork.

This would also help with timing issues on POSIX, where sometimes creating the tunnel takes too long and vpn-slice tries to use it too soon.

@bersbersbers
Copy link
Author

Good points!

I do have to be honest, though: I was aiming at using vpn-slice to get split tunneling up and running, since I had no idea how to interpret routes, metrics and interfaces. In researching a bunch of stuff trying to understand the Windows vpnc scripts, I have, as a side-product, solved my original problems without having to use vpn-slice at all. This means I would not even benefit myself from implementing a Windows provider for vpn-slice, and it would cost me considerable time as well (also considering that I am currently not even able to build openconnect for Windows myself.) Further assuming that issues such as the lack of fork() will not be the last OS-specific issue I would be facing, I am inclined to summarize my efforts so far so other people can pick it up, but not go ahead with the actual implementation and, most importantly, thorough testing.

@dlenski
Copy link
Owner

dlenski commented Mar 31, 2020

That seems reasonable. If you can summarize how you've set up split-tunneling on Windows, it may indeed help others to implement this more cleanly later.

@bersbersbers
Copy link
Author

bersbersbers commented Apr 12, 2020

Alright, this is 1/2, summarizing what I tried in vpn-slice before finding a solution without. I did settle on using WSL to be able to reuse DigProvider.

__main__.py: Add

    elif platform.startswith("win32"):
        from .crossos import DigProvider
        from .win import (
            Win32ProcessProvider,
            WinRouteProvider,
            WinHostsFileProvider,
        )
        DigProvider.dig = "wsl.exe dig"

        return dict(
            process=Win32ProcessProvider,
            route=WinRouteProvider,
            dns=DigProvider,
            hosts=WinHostsFileProvider,
        )

So I moved the DigProvider from posix.py into a new crossos.py; it should run the same if one simple overwrites DigProvider.dig. (Obviously, this adds from .crossos import DigProvider wherever else required.)

I did the same with the HostsFileProvider, calling it CrossOsHostsFileProvider and adding an abstract method lock_hosts_file (to offer fcntl.flock on POSIX, which is not available on Windows).

Also, I noticed that the PosixProcessProvider is completely OS-independent (os.kill, os.getpid), so I moved that one, too, making it a CrossOsProcessProvider.

The main work is in win.py. The Win32ProcessProvider seems to work fine based on PowerShell, as does the WinHostsFileProvider (which is basically the CrossOsHostsFileProvider with no locking). The WinRouteProvider is incomplete - I struggled until I figured out that BSD route is not what I get in wsl.exe route, so I wanted to switch a part to ip route, but ran out of time. It's a non-working hybrid between the RouteProvider and the Iproute2Provider now. Btw, it seems that routes cannot be added from within wsl.exe (or maybe wsl.exe needs to be run elevated), so maybe route.exe is also needed.

Attached is a patch, maybe someone wants to continue what I started.
0001-First-steps-towards-windows-support.patch.txt

@bersbersbers
Copy link
Author

bersbersbers commented Apr 12, 2020

2/2, how I am doing this without vpn-slice.

Basically, I installed the TAP driver from OpenVPN (the "OpenConnect GUI" installer does that, too). I renamed my ethernet connection "Ethernet", and the TAP one "OpenConnect". Also, I have set the DNS suffix "mycompany.org" on the OpenConnect interface.

Then I use an elevated batch file like this:

rem Set default route to "Ethernet" (not sure if that's required)
netsh int ipv4 add route 0.0.0.0/0 "Ethernet" store=active

rem Now call OpenConnect without any `-s` parameter (it probably still uses the default one).

rem Wait for connection to be established
:Wait
ping -n 1 -w 1 intranet | find "mycompany"
if errorlevel 1 goto Wait

rem Lower the OpenConnect interface metric and delete the default route
netsh interface ipv4 set interface "OpenConnect" metric=100 store=active
netsh int ipv4 delete route 0.0.0.0/0 "OpenConnect" store=active

rem set a route to find my company's name servers n1.company.org and n2.company.org
netsh int ipv4 add route xx.yy.zz.0/24 "OpenConnect" store=active

rem set a route for each subnet that I need to connect to at work
netsh int ipv4 add route xx.yy.0.0/16 "OpenConnect" store=active

For split DNS, I have used something like this:
https://github.com/thngkaiyuan/simple-dns-proxy/blob/master/dns_proxy.py
My version runs locally and uses ns1.mycompany.org only to resolve *.mycompany.org, otherwise, it asks 8.8.8.8 for a result. Then,

netsh int ipv4 set dns name="OpenConnect" source=static addr=127.0.0.1

This way, my network traffic is 100% split: The company nameserver sees requests only for *.mycompany.org, and the network only ever sees traffic to the configured subsets.

The part up to here, I would love to do using VPNC env variables to be less fixed on correct naming of interfaces and stuff like this. BUT:

I addition, I also configure SSH tunnels for SAMBA using additional loopback devices and additional HOSTS entries and set a proxy PAC script to route some web traffic through the companies proxy server for IP-based subscription access: in summary, as set of highly company-specific changes that would be a pain to integrate and maintain in vpn-slice - sorry!

Still, I'll be happy to help with the above integration and/or answer questions regarding my specific setup!

@Welsige
Copy link

Welsige commented Jun 23, 2020

Hi @bersbersbers , could you clarify better the DNS part, perhaps posting your DNS version script, edited to mask your data but with commentaries as to where to do changes inside it?

@bersbersbers
Copy link
Author

bersbersbers commented Jun 23, 2020

@Welsige sure, find attached. It's a bit reduced from my full version and I hope I didn't introduce any mistakes. Hard to test with "IP.OF.CMP.NS" :)

split_dns_anon.py.txt

There's three places you need to do changes, lines 16/17 for the company nameserver IP and their suffix; and optionally line 107 to select a different resolver. I experimented a bit with different options:

  • ExternalFirstCompanySecondResolver: for every request, try the external nameserver. Only if that does not give a valid answer, try the company nameserver.
  • ExternalCompanySplitResolver: if name ends with company suffix, use the company nameserver, else use the external nameserver. My current choice.
  • CompanyOnlyResolver: if name ends with company suffix, use the company nameserver, else do not resolve.

If, like me, you use this nameserver only on the VPN interface, what you chose to return has to be somewhat compatible with what the unmodified nameserver returns for the same IPs, as Windows pretty much queries different nameservers on different interfaces simultaneously.

@reicheltp
Copy link

reicheltp commented Oct 29, 2020

If you have access to powershell you can use Windows build in ability to split the DNS and use it only for company domains:

Add-DnsClientNrptRule -Namespace "mycompany.org" -NameServers "xx.yy.zz.1"

@michkot
Copy link

michkot commented Nov 24, 2020

If, like me, you use this nameserver only on the VPN interface, what you chose to return has to be somewhat compatible with what the unmodified nameserver returns for the same IPs, as Windows pretty much queries different nameservers on different interfaces simultaneously.

are you referssing to multihomed dns resolution @bersbersbers ?
https://www.ghacks.net/2017/08/14/turn-off-smart-multi-homed-name-resolution-in-windows/
https://superuser.com/questions/969171/multihomed-windows-10-dns-resolution-timeouts
etc. ? I tried switching it off today and my company's DNS server set by OpenVPN stoped being used at all (and behind-the-VPN services ceased to function shortly after)...

@bersbersbers
Copy link
Author

are you referssing to multihomed dns resolution @bersbersbers ?
https://www.ghacks.net/2017/08/14/turn-off-smart-multi-homed-name-resolution-in-windows/
https://superuser.com/questions/969171/multihomed-windows-10-dns-resolution-timeouts

According to the links you posted, yes - this is exactly what I meant.

I tried switching it off today and my company's DNS server set by OpenVPN stoped being used at all (and behind-the-VPN services ceased to function shortly after)...

I never tried that, but assuming that your native connection has a lower metric, what you observe is what I would expect. Great to know that this is another option (as well as DnsClientNrptRule by @reicheltp).

@michkot
Copy link

michkot commented Nov 24, 2020

I have taken upon me to hold the torch of Windows support hopes (for a while) https://github.com/michkot/vpn-slice/compare/feature-windows_support michkot@8568cf7

Here is a small bridge app to "fit" vpn-slice into to Windows cscript-fixed binary build of openconnect:
https://github.com/michkot/vpnc-script-win--vpn-slice--bridge

@bersbersbers
Copy link
Author

If you have access to powershell you can use Windows build in ability to split the DNS and use it only for company domains:

Add-DnsClientNrptRule -Namespace "mycompany.org" -NameServers "xx.yy.zz.1"

This is great. I had to use

Add-DnsClientNrptRule -Namespace ".mycompany.org" -NameServers "xx.yy.zz.1"

though - note the extra . at the beginning of the namespace.

@bersbersbers
Copy link
Author

If, like me, you use this nameserver only on the VPN interface, what you chose to return has to be somewhat compatible with what the unmodified nameserver returns for the same IPs, as Windows pretty much queries different nameservers on different interfaces simultaneously.

are you referssing to multihomed dns resolution @bersbersbers ?
https://www.ghacks.net/2017/08/14/turn-off-smart-multi-homed-name-resolution-in-windows/
https://superuser.com/questions/969171/multihomed-windows-10-dns-resolution-timeouts

This might have been helpful, too, but it seems the Add-DnsClientNrptRule takes care of that as well. Thanks nonetheless!

@maurerr
Copy link

maurerr commented Oct 26, 2021

alright so this is my take on this subject https://pastebin.com/80Thw5H2 - static subnets and masks lists and dns handled by additional commands as suggested by @reicheltp (thank you very much !) .Saved in the openconnect folder and using it as -s split-script-win.js

@jpfleischer
Copy link

jpfleischer commented Jan 4, 2024

alright so this is my take on this subject https://pastebin.com/80Thw5H2 - static subnets and masks lists and dns handled by additional commands as suggested by @reicheltp (thank you very much !) .Saved in the openconnect folder and using it as -s split-script-win.js

Hi @maurerr this works really well for one of my organizations but not the other. In both cases my public IP never changes to the company, so i know the split is working, but for only one of them i can connect to organization resources. Do you happen to have an updated version of this?

I thought about using the powershell command but i dont know what NameServers is supposed to be?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants