SystemRage / py-kms

KMS Server Emulator written in Python
The Unlicense
2.04k stars 618 forks source link

"SO_REUSEPORT not supported on this platform. Exiting..." in Windows Sandbox (ThunderEX version works) #102

Closed WinkelCode closed 3 years ago

WinkelCode commented 3 years ago

Tested using Python 3.9 and 3.8.6 (x84-64 installers with "add to PATH")

Attempting to run pykms_server.py will result in the error above, I tried manually specifying the local IP (between Windows and Sandbox) but nothing worked, using other random ports didn't either.

This version, although last updated more than a year ago, https://github.com/ThunderEX/py-kms worked without any issues.

simonmicro commented 3 years ago

Hi! First: What a strange platform you use?!

Why? SO_REUSEPORT is an Linux option to allow the reuse of the port from the server DIRECTLY after his end. This is especially useful to allow the py-kms server instantly to restart after e.g. a crash or during development cycle. When this socket option is NOT set, it can take up to several minutes until the port is freed by the OS and ready to use again.

Fix? We could add an parameter to disable this option - BUT for that I would need to reproduce it. And I have no idea what or how the platform you use works.

Help to fix! Open up pykms_Server.py and modify the following line (38):

allow_reuse_address = True

to

allow_reuse_address = False

When this fixes your problem we can proceed to implement a more permanent solution. When not I'll need to dig deeper into our used socket libs...

Looking forward to hear from you!

WinkelCode commented 3 years ago

What a strange platform you use?!

Windows Sandbox is pretty straightforward actually, it's pretty much just an easy to use Windows 10 VM in Hyper-V. It has nice features like copy and pasting stuff into it and being able to communicate with the host (and use the internet) via Hyper-V's vEthernet adapters.

Help to fix! Open up pykms_Server.py and modify the following line (38):

Unfortunately it seems that line is empty and manually adding "allow_reuse_address = False" just results in the same error, I couldn't find "allow_reuse_address" anywhere else in the file either. image

I also tried running it with "-u" and "--no-reuse" but it apparently didn't recognize the parameters (I saw these mentioned in file).

WinkelCode commented 3 years ago

Update: It works!

I read the docs (.com) and found out that the correct syntax is connect -u not just -u.

Launching with py ./pykms_server.py connect -u it works!

The only things that I noticed was an error about tzlocal (which I didn't install and is probably not a big issue) and that nothing got written to the log file (it did when I got the SO_REUSEPORT error so I think it might be error logging only?)

If this isn't an issue with running it on "normal" Windows, a way to make it automatically launch without port reuse is checking if the current user is "WDAGUtilityAccount" which is the username when running in Windows Sandbox.

simonmicro commented 3 years ago

Unfortunately it seems that line is empty and manually adding "allow_reuse_address = False" just results in the same error, I couldn't find "allow_reuse_address" anywhere else in the file either.

May bad, I was in the wrong file / lib.

Launching with py ./pykms_server.py connect -u it works!

Congratulations!

that nothing got written to the log file

The default log level is ERROR - so you may need to lower that.

If this isn't an issue with running it on "normal" Windows, a way to make it automatically launch without port reuse is checking if the current user is "WDAGUtilityAccount" which is the username when running in Windows Sandbox.

I'll look into that, but I can't promise anything :grin:

simonmicro commented 3 years ago

Hey, could you test the following patch for me and confirm it is working (when it does I'll expand the docs and create a PR)?

--- pykms_Server_orig.py    2020-10-07 13:20:20.734492303 +0200
+++ pykms_Server.py 2020-10-07 13:21:11.146097936 +0200
@@ -13,6 +13,7 @@
 import socketserver
 import queue as Queue
 import selectors
+import getpass
 from time import monotonic as time

 import pykms_RpcBind, pykms_RpcRequest
@@ -524,7 +525,7 @@
         log_address = "TCP server listening at %s on port %d" %(srv_config['ip'], srv_config['port'])

         if 'listen' in srv_config:
-                for l, b, r in zip(srv_config['listen'], srv_config['backlog'], srv_config['reuse']):
+                for l, b, r in zip(srv_config['listen'], srv_config['backlog'], srv_config['reuse'] or getpass.getuser() == 'WDAGUtilityAccount'):
                         all_address.append(l + (b,) + (r,))
                         log_address += justify("at %s on port %d" %(l[0], l[1]), indent = 56)
WinkelCode commented 3 years ago

Unfortunately that didn't work either, it simply gave the same error when running it normally but wouldn't even run with connect -u.

Here is a screenshot from a powershell window where I run a couple commands that should show what is going on, it also confirms the result from "getpass.getuser()" in Windows Sandbox:

image

simonmicro commented 3 years ago

Unfortunately that didn't work either, it simply gave the same error when running it normally but wouldn't even run with connect -u.

Here is a screenshot from a powershell window where I run a couple commands that should show what is going on, it also confirms the result from "getpass.getuser()" in Windows Sandbox:

image

Right. I'm an idiot - the line is a for loop and I added a bool for checking. I'll give you an other patch in the next days - sorry for that!

simonmicro commented 3 years ago

@martinsstuff Hey, here is my second attempt - now it should work!

--- pykms_Server_orig.py    2020-10-07 13:20:20.734492303 +0200
+++ pykms_Server.py 2020-10-10 12:50:36.110909231 +0200
@@ -13,6 +13,7 @@
 import socketserver
 import queue as Queue
 import selectors
+import getpass
 from time import monotonic as time

 import pykms_RpcBind, pykms_RpcRequest
@@ -519,7 +520,7 @@
         all_address = [(
                         srv_config['ip'], srv_config['port'],
                         (srv_config['backlog_main'] if 'backlog_main' in srv_config else srv_options['backlog']['def']),
-                        (srv_config['reuse_main'] if 'reuse_main' in srv_config else srv_options['reuse']['def'])
+                        (srv_config['reuse_main'] if 'reuse_main' in srv_config else False if getpass.getuser() == 'WDAGUtilityAccount' else srv_options['reuse']['def'])
                         )]
         log_address = "TCP server listening at %s on port %d" %(srv_config['ip'], srv_config['port'])
WinkelCode commented 3 years ago

Awesome, yes that seems to work! It both runs normally and with connect -u without errors and pykms_Client.py activated against it with no issues.

Small side note: pykms_Client by default tries to activate against 0.0.0.0 which just spits out an error about the address not being valid in its context but manually pointing it towards 127.0.0.1 (for this test case) worked.

It might also be a good idea to point out somewhere, as a comment or somewhere else, why this check is done, WDAGUtilityAccount is usually related to Windows Defender Application Guard and people who don't know this issue here might think it's related to something nefarious. (Well, it's literally for VM detection but for good reasons)