From cd640af37f6d9e93cb378e41348027adbd1c78ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Fri, 8 Oct 2021 12:28:37 +0200 Subject: [PATCH 1/5] Added correcto management of "logout" in case of an unmanaged machine "reboot" --- server/src/uds/REST/methods/actor_v3.py | 49 +++++++++++-------- server/src/uds/core/services/service.py | 20 +++----- .../PhysicalMachines/service_multi.py | 8 ++- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/server/src/uds/REST/methods/actor_v3.py b/server/src/uds/REST/methods/actor_v3.py index 269ad0ef..cd2bbc54 100644 --- a/server/src/uds/REST/methods/actor_v3.py +++ b/server/src/uds/REST/methods/actor_v3.py @@ -269,24 +269,6 @@ class Initialize(ActorV3Action): state__in=[State.USABLE, State.PREPARING], ) - # If no UserService exists, - # is managed (service exists), then it's a "local login" - if not dbFilter.exists() and service: - # The userService does not exists, try to lock the id on the service - serviceInstance = service.getInstance() - lockedId = serviceInstance.lockId(idsList) - if lockedId: - # Return an "generic" result allowing login/logout processing - return ActorV3Action.actorResult( - { - 'own_token': self._params['token'], - 'unique_id': lockedId, - 'os': None, - } - ) - else: # if no lock, return empty result - raise Exception() # Unmanaged host - userService: UserService = next( iter( dbFilter.filter( @@ -657,12 +639,35 @@ class Unmanaged(ActorV3Action): except Exception: return ActorV3Action.actorResult(error='Invalid token') + # Build the possible ids and ask service if it recognizes any of it # If not recognized, will generate anyway the certificate, but will not be saved idsList = [x['ip'] for x in self._params['id']] + [ x['mac'] for x in self._params['id'] ][:10] validId: typing.Optional[str] = service.getValidId(idsList) + + # Check if there is already an assigned user service + # To notify it logout + userService: typing.Optional[UserService] + try: + dbFilter = UserService.objects.filter( + unique_id__in=idsList, + state__in=[State.USABLE, State.PREPARING], + ) + + userService = next( + iter( + dbFilter.filter( + unique_id__in=idsList, + state__in=[State.USABLE, State.PREPARING], + ) + ) + ) + except StopIteration: + userService = None + + # Try to infer the ip from the valid id (that could be an IP or a MAC) ip: str try: ip = next( @@ -681,8 +686,12 @@ class Unmanaged(ActorV3Action): 'password': password, } if validId: - # Notify service of it "just start" action - service.notifyInitialization(validId) + # If id is assigned to an user service, notify "logout" to it + if userService: + Logout.process_logout(userService, 'init') + else: + # If it is not assgined to an user service, notify service + service.notifyInitialization(validId) # Store certificate, secret & port with service if validId service.storeIdInfo( diff --git a/server/src/uds/core/services/service.py b/server/src/uds/core/services/service.py index aacca5fe..842629aa 100644 --- a/server/src/uds/core/services/service.py +++ b/server/src/uds/core/services/service.py @@ -322,22 +322,12 @@ class Service(Module): """ return None - def lockId(self, id: typing.List[str]) -> typing.Optional[str]: - """ - Locks the id, so it cannot be used by a service pool. - - Args: - id (typing.List[str]): Id to lock (list of possible ids) - - Returns: - str: Valid id of locked element, or None if no id found - """ - return None - def processLogin(self, id: str, remote_login: bool) -> None: """ In the case that a login is invoked directly on an actor controlled machine with an service token, this method will be called with provided info by uds actor (parameters) + That is, this method will only be called it UDS does not recognize the invoker, but the invoker + has a valid token and the service has recognized it. (via getValidId) Args: id (str): Id validated through "getValidId" @@ -347,12 +337,13 @@ class Service(Module): def processLogout(self, id: str) -> None: """ - In the case that a login is invoked directly on an actor controlled machine with + In the case that a logout is invoked directly on an actor controlled machine with an service token, this method will be called with provided info by uds actor (parameters) + That is, this method will only be called it UDS does not recognize the invoker, but the invoker + has a valid token and the service has recognized it. (via getValidId) Args: id (str): Id validated through "getValidId" - remote_login (bool): if the login seems to be a remote login """ return @@ -364,6 +355,7 @@ class Service(Module): Args: id (str): Id validated through "getValidId" """ + return def storeIdInfo(self, id: str, data: typing.Any) -> None: self.storage.putPickle('__nfo_' + id, data) diff --git a/server/src/uds/services/PhysicalMachines/service_multi.py b/server/src/uds/services/PhysicalMachines/service_multi.py index 02e4ba84..f3a9aea3 100644 --- a/server/src/uds/services/PhysicalMachines/service_multi.py +++ b/server/src/uds/services/PhysicalMachines/service_multi.py @@ -339,7 +339,7 @@ class IPMachinesService(IPServiceBase): return userServiceInstance.error('IP already assigned') def processLogin(self, id: str, remote_login: bool) -> None: - logger.info('Processing login for %s', id) + logger.debug('Processing login for %s: %s', self, id) # Locate the IP on the storage theIP = IPServiceBase.getIp(id) now = getSqlDatetimeAsUnix() @@ -348,7 +348,11 @@ class IPMachinesService(IPServiceBase): self.storage.putPickle(id, now) # Lock it def processLogout(self, id: str) -> None: - logger.info('Processing logout for %s', id) + logger.debug('Processing logout for %s: %s', self, id) + self.unassignMachine(id) + + def notifyInitialization(self, id: str) -> None: + logger.debug('Notify initialization for %s: %s', self, id) self.unassignMachine(id) def getValidId(self, idsList: typing.Iterable[str]) -> typing.Optional[str]: From fddd54fa9915058040b0f8d416dea0dcbd08d71a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Fri, 8 Oct 2021 12:30:00 +0200 Subject: [PATCH 2/5] Added correcto management of "logout" in case of an unmanaged machine "reboot" --- .../src/uds/services/PhysicalMachines/service_multi.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/src/uds/services/PhysicalMachines/service_multi.py b/server/src/uds/services/PhysicalMachines/service_multi.py index f3a9aea3..dc931ec2 100644 --- a/server/src/uds/services/PhysicalMachines/service_multi.py +++ b/server/src/uds/services/PhysicalMachines/service_multi.py @@ -339,6 +339,9 @@ class IPMachinesService(IPServiceBase): return userServiceInstance.error('IP already assigned') def processLogin(self, id: str, remote_login: bool) -> None: + ''' + Process login for a machine not assigned to any user. + ''' logger.debug('Processing login for %s: %s', self, id) # Locate the IP on the storage theIP = IPServiceBase.getIp(id) @@ -348,10 +351,17 @@ class IPMachinesService(IPServiceBase): self.storage.putPickle(id, now) # Lock it def processLogout(self, id: str) -> None: + ''' + Process logout for a machine not assigned to any user. + ''' logger.debug('Processing logout for %s: %s', self, id) self.unassignMachine(id) def notifyInitialization(self, id: str) -> None: + ''' + Notify that a machine has been initialized. + Normally, this means that + ''' logger.debug('Notify initialization for %s: %s', self, id) self.unassignMachine(id) From 293b7f02ad123852349f389775db9ce27f18471f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Wed, 13 Oct 2021 11:19:44 +0200 Subject: [PATCH 3/5] added small comment for future to actor v3 --- server/src/uds/REST/methods/actor_v3.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/src/uds/REST/methods/actor_v3.py b/server/src/uds/REST/methods/actor_v3.py index cd2bbc54..7eaaf483 100644 --- a/server/src/uds/REST/methods/actor_v3.py +++ b/server/src/uds/REST/methods/actor_v3.py @@ -424,7 +424,7 @@ class Version(ActorV3Action): class LoginLogout(ActorV3Action): name = 'notused' # Not really important, this is not a "leaf" class and will not be directly available - def notifyService(self, login: bool): + def notifyService(self, isLogin: bool): try: # If unmanaged, use Service locator service: 'services.Service' = Service.objects.get( @@ -444,9 +444,11 @@ class LoginLogout(ActorV3Action): if not validId: raise Exception() + # Recover Id Info from service and validId + # idInfo = service.recoverIdInfo(validId) # Notify Service that someone logged in/out - if login: + if isLogin: # Try to guess if this is a remote session is_remote = self._params.get('session_type', '')[:4] in ('xrdp', 'RDP-') service.processLogin(validId, remote_login=is_remote) @@ -518,7 +520,7 @@ class Login(LoginLogout): except Exception: # If unamanaged host, lest do a bit more work looking for a service with the provided parameters... if isManaged: raise - self.notifyService(login=True) + self.notifyService(isLogin=True) return ActorV3Action.actorResult( {'ip': ip, 'hostname': hostname, 'dead_line': deadLine, 'max_idle': maxIdle} @@ -561,7 +563,7 @@ class Logout(LoginLogout): except Exception: # If unamanaged host, lest do a bit more work looking for a service with the provided parameters... if isManaged: raise - self.notifyService(login=False) # Logout notification + self.notifyService(isLogin=False) # Logout notification return ActorV3Action.actorResult('ok') @@ -639,7 +641,6 @@ class Unmanaged(ActorV3Action): except Exception: return ActorV3Action.actorResult(error='Invalid token') - # Build the possible ids and ask service if it recognizes any of it # If not recognized, will generate anyway the certificate, but will not be saved idsList = [x['ip'] for x in self._params['id']] + [ From 741855030f4334ad5559d463304a1c907001951d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Fri, 15 Oct 2021 10:44:22 +0200 Subject: [PATCH 4/5] Removed "prints" :) --- client-py3/full/src/uds/rest.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client-py3/full/src/uds/rest.py b/client-py3/full/src/uds/rest.py index 62eaf11e..f544cab2 100644 --- a/client-py3/full/src/uds/rest.py +++ b/client-py3/full/src/uds/rest.py @@ -183,7 +183,6 @@ class RestApi: def _open( url: str, certErrorCallback: typing.Optional[CertCallbackType] = None ) -> typing.Any: - print('Open') ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE @@ -246,7 +245,6 @@ class RestApi: def getUrl( url: str, certErrorCallback: typing.Optional[CertCallbackType] = None ) -> bytes: - print(url) with RestApi._open(url, certErrorCallback) as response: resp = response.read() From e4b609c4ced698ce4006f1121cdd5476b5047c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Fri, 15 Oct 2021 10:57:43 +0200 Subject: [PATCH 5/5] Fixed key for debian packages on client appimage recipe --- client-py3/full/linux/udsclient-appimage-x86_64.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-py3/full/linux/udsclient-appimage-x86_64.recipe b/client-py3/full/linux/udsclient-appimage-x86_64.recipe index c2f498db..eb7559ba 100644 --- a/client-py3/full/linux/udsclient-appimage-x86_64.recipe +++ b/client-py3/full/linux/udsclient-appimage-x86_64.recipe @@ -31,7 +31,7 @@ AppDir: arch: amd64 sources: - sourceline: 'deb [arch=amd64] http://ftp.de.debian.org/debian/ bullseye main contrib non-free' - key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x04EE7237B7D453EC' + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x648ACFD622F3D138' include: - python3