forked from shaba/openuds
advancing on actor
This commit is contained in:
parent
91272fcbb8
commit
2509f41c85
@ -80,13 +80,17 @@ class UDSActorClient(threading.Thread):
|
||||
self._running = True
|
||||
|
||||
time.sleep(0.4) # Wait a bit before sending login
|
||||
# Notify loging and mark it
|
||||
self.api.login(platform.operations.getCurrentUser())
|
||||
|
||||
while self._running:
|
||||
time.sleep(1.1) # Sleeps between loop iterations
|
||||
try:
|
||||
# Notify loging and mark it
|
||||
self.api.login(platform.operations.getCurrentUser())
|
||||
|
||||
self.api.logout(platform.operations.getCurrentUser())
|
||||
while self._running:
|
||||
time.sleep(1.1) # Sleeps between loop iterations
|
||||
|
||||
self.api.logout(platform.operations.getCurrentUser())
|
||||
except Exception as e:
|
||||
logger.error('Error on client loop: %s', e)
|
||||
|
||||
self._listener.stop() # async listener for service
|
||||
|
||||
@ -117,8 +121,11 @@ class UDSActorClient(threading.Thread):
|
||||
ba = QByteArray()
|
||||
buffer = QBuffer(ba)
|
||||
buffer.open(QIODevice.WriteOnly)
|
||||
pixmap.save(buffer)
|
||||
return bytes(ba.toBase64()).decode() # 'result' of JSON will contain base64 of screen
|
||||
pixmap.save(buffer, 'PNG')
|
||||
buffer.close()
|
||||
scrBase64 = bytes(ba.toBase64()).decode()
|
||||
logger.debug('Screenshot length: %s', len(scrBase64))
|
||||
return scrBase64 # 'result' of JSON will contain base64 of screen
|
||||
|
||||
def script(self, script: str) -> typing.Any:
|
||||
tools.ScriptExecutorThread(script).start()
|
||||
|
@ -139,7 +139,10 @@ class HTTPServerThread(threading.Thread):
|
||||
def stop(self) -> None:
|
||||
if self._server:
|
||||
logger.debug('Stopping Http-client Service')
|
||||
self._app.api.unregister(self.url)
|
||||
try:
|
||||
self._app.api.unregister(self.url)
|
||||
except Exception as e:
|
||||
logger.error('Error unregistering on actor service: %s', e)
|
||||
self._server.shutdown()
|
||||
self._server = None
|
||||
|
||||
@ -153,6 +156,9 @@ class HTTPServerThread(threading.Thread):
|
||||
|
||||
# Register using app api
|
||||
logger.debug('Registered %s', self.url)
|
||||
self._app.api.register(self.url)
|
||||
try:
|
||||
self._app.api.register(self.url)
|
||||
except Exception as e:
|
||||
logger.error('Error registering on actor service: %s', e)
|
||||
|
||||
self._server.serve_forever()
|
||||
|
@ -148,6 +148,7 @@ class HTTPServerThread(threading.Thread):
|
||||
os.unlink(self._certFile)
|
||||
except Exception as e:
|
||||
logger.error('Error removing certificate file: %s', e)
|
||||
logger.debug('Http-server stopped')
|
||||
|
||||
def run(self):
|
||||
HTTPServerHandler._service = self._service # pylint: disable=protected-access
|
||||
|
@ -32,7 +32,7 @@ import sys
|
||||
import os
|
||||
import time
|
||||
import atexit
|
||||
from signal import SIGTERM
|
||||
from signal import SIGTERM, SIGKILL
|
||||
|
||||
from udsactor.log import logger
|
||||
|
||||
@ -143,9 +143,14 @@ class Daemon:
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while True:
|
||||
cnt = 10
|
||||
while cnt:
|
||||
cnt -= 1
|
||||
os.kill(pid, SIGTERM)
|
||||
time.sleep(1)
|
||||
|
||||
if not cnt:
|
||||
os.kill(pid, SIGKILL)
|
||||
except OSError as err:
|
||||
if err.errno == 3: # No such process
|
||||
if os.path.exists(self.pidfile):
|
||||
|
@ -83,7 +83,7 @@ class Logger:
|
||||
if self.remoteLogger:
|
||||
self.remoteLogger.log(self.own_token, level, msg)
|
||||
except Exception as e:
|
||||
self.localLogger.log(FATAL, 'Error notifying log to broker: {}'.format(e))
|
||||
self.localLogger.log(DEBUG, 'Log to broker: {}'.format(e))
|
||||
|
||||
self.localLogger.log(level, msg)
|
||||
|
||||
|
@ -180,7 +180,7 @@ class UDSServerApi(UDSApi):
|
||||
if result.ok:
|
||||
return result.json()['result']
|
||||
except requests.ConnectionError as e:
|
||||
raise RESTConnectionError(str(e))
|
||||
raise RESTConnectionError(e)
|
||||
except RESTError:
|
||||
raise
|
||||
except Exception as e:
|
||||
|
@ -124,6 +124,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
self._rebootRequested = True
|
||||
|
||||
def setReady(self) -> None:
|
||||
if not self._isAlive:
|
||||
return
|
||||
|
||||
# First, if postconfig is available, execute it and disable it
|
||||
if self._cfg.post_command:
|
||||
self.execute(self._cfg.post_command, 'postConfig')
|
||||
@ -140,7 +143,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
try:
|
||||
self._certificate = self._api.ready(self._cfg.own_token, self._secret, srvInterface.ip, rest.LISTEN_PORT)
|
||||
except rest.RESTConnectionError as e:
|
||||
logger.info('Error connecting with UDS Broker: %s', e)
|
||||
logger.info('Error connecting with UDS Broker')
|
||||
self.doWait(5000)
|
||||
continue
|
||||
except Exception as e:
|
||||
@ -151,18 +154,24 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
platform.operations.reboot() # On too many errors, simply reboot
|
||||
# Success or any error that is not recoverable (retunerd by UDS). if Error, service will be cleaned in a while.
|
||||
break
|
||||
|
||||
else:
|
||||
logger.error('Could not locate IP address!!!. (Not registered with UDS)')
|
||||
|
||||
# Do not continue if not alive...
|
||||
if not self._isAlive:
|
||||
return
|
||||
# Cleans sensible data
|
||||
if self._cfg.config:
|
||||
self._cfg = self._cfg._replace(config=self._cfg.config._replace(os=None), data=None)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
|
||||
logger.debug('Done setReady')
|
||||
self._startHttpServer()
|
||||
|
||||
def configureMachine(self) -> bool:
|
||||
if not self._isAlive:
|
||||
return False
|
||||
|
||||
# First, if runonce is present, honor it and remove it from config
|
||||
# Return values is "True" for keep service (or daemon) running, False if Stop it.
|
||||
if self._cfg.runonce_command:
|
||||
@ -205,7 +214,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
return True
|
||||
|
||||
def initialize(self) -> bool:
|
||||
if not self._cfg.host: # Not configured
|
||||
if not self._cfg.host or not self._isAlive: # Not configured or not running
|
||||
return False
|
||||
|
||||
# Force time sync, just in case...
|
||||
@ -345,7 +354,12 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
Invoked to wait a bit
|
||||
CAN be OVERRIDEN
|
||||
'''
|
||||
time.sleep(float(miliseconds) / 1000)
|
||||
seconds = miliseconds / 1000.0
|
||||
# So it can be broken by "stop"
|
||||
while self._isAlive and seconds > 1:
|
||||
time.sleep(1)
|
||||
seconds -= 1
|
||||
time.sleep(seconds)
|
||||
|
||||
def notifyStop(self) -> None:
|
||||
'''
|
||||
|
@ -30,13 +30,10 @@
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import typing
|
||||
|
||||
import requests
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.db.models import Q
|
||||
from django.db import transaction
|
||||
|
@ -10,116 +10,101 @@ if typing.TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TIMEOUT = 2
|
||||
|
||||
def notifyPreconnect(userService: 'UserService', userName: str, protocol: str) -> None:
|
||||
'''
|
||||
Notifies a preconnect to an user service
|
||||
'''
|
||||
proxy = userService.deployed_service.proxy
|
||||
class NoActorComms(Exception):
|
||||
pass
|
||||
|
||||
class OldActorVersion(NoActorComms):
|
||||
pass
|
||||
|
||||
def _requestActor(
|
||||
userService: 'UserService',
|
||||
method: str,
|
||||
data: typing.Optional[typing.MutableMapping[str, typing.Any]] = None,
|
||||
minVersion: typing.Optional[str] = None
|
||||
) -> typing.Any:
|
||||
"""
|
||||
Makes a request to actor using "method"
|
||||
if data is None, request is done using GET, else POST
|
||||
if no communications url is provided or no min version, raises a "NoActorComms" exception (or OldActorVersion, derived from NoActorComms)
|
||||
Returns request response value interpreted as json
|
||||
"""
|
||||
url = userService.getCommsUrl()
|
||||
ip, hostname = userService.getConnectionSource()
|
||||
|
||||
if not url:
|
||||
logger.debug('No notification is made because agent does not supports notifications')
|
||||
return
|
||||
logger.warning('No notification is made because agent does not supports notifications: %s', userService.friendly_name)
|
||||
raise NoActorComms('No notification urls for {}'.format(userService.friendly_name))
|
||||
|
||||
url += '/preConnect'
|
||||
minVersion = minVersion or '2.0.0'
|
||||
version = userService.getProperty('actor_version') or '0.0.0'
|
||||
if '-' in version or version < minVersion:
|
||||
logger.warning('Pool %s has old actors (%s)', userService.deployed_service.name, version)
|
||||
raise OldActorVersion('Old actor version {} for {}'.format(version, userService.friendly_name))
|
||||
|
||||
url += '/' + method
|
||||
|
||||
proxy = userService.deployed_service.proxy
|
||||
try:
|
||||
data = {'user': userName, 'protocol': protocol, 'ip': ip, 'hostname': hostname}
|
||||
if proxy is not None:
|
||||
r = proxy.doProxyRequest(url=url, data=data, timeout=2)
|
||||
r = proxy.doProxyRequest(url=url, data=data, timeout=TIMEOUT)
|
||||
else:
|
||||
r = requests.post(
|
||||
url,
|
||||
data=json.dumps(data),
|
||||
headers={'content-type': 'application/json'},
|
||||
verify=False,
|
||||
timeout=2
|
||||
)
|
||||
r = json.loads(r.content)
|
||||
logger.debug('Sent pre-connection to client using %s: %s', url, r)
|
||||
# In fact we ignore result right now
|
||||
except Exception as e:
|
||||
logger.info('preConnection failed: %s. Check connection on destination machine: %s', e, url)
|
||||
|
||||
def checkUuid(userService: 'UserService') -> bool:
|
||||
'''
|
||||
Checks if the uuid of the service is the same of our known uuid on DB
|
||||
'''
|
||||
proxy = userService.deployed_service.proxy
|
||||
|
||||
url = userService.getCommsUrl()
|
||||
|
||||
if not url:
|
||||
logger.debug('No uuid to retrieve because agent does not supports notifications')
|
||||
return True # UUid is valid because it is not supported checking it
|
||||
|
||||
version = userService.getProperty('actor_version') or ''
|
||||
# Just for 2.0 or newer, previous actors will not support this method.
|
||||
# Also externally supported agents will not support this method (as OpenGnsys)
|
||||
if '-' in version or version < '2.0.0':
|
||||
return True
|
||||
|
||||
url += '/uuid'
|
||||
|
||||
try:
|
||||
if proxy:
|
||||
r = proxy.doProxyRequest(url=url, timeout=5)
|
||||
else:
|
||||
r = requests.get(url, verify=False, timeout=5)
|
||||
|
||||
if version >= '3.0.0': # New type of response: {'result': uuid}
|
||||
uuid = r.json()['result']
|
||||
else:
|
||||
uuid = r.json()
|
||||
|
||||
if uuid != userService.uuid:
|
||||
logger.info('The requested machine has uuid %s and the expected was %s', uuid, userService.uuid)
|
||||
return False
|
||||
|
||||
logger.debug('Got uuid from machine: %s %s %s', url, uuid, userService.uuid)
|
||||
# In fact we ignore result right now
|
||||
except Exception as e:
|
||||
logger.error('Get uuid failed: %s. Check connection on destination machine: %s', e, url)
|
||||
|
||||
return True
|
||||
|
||||
def requestScreenshot(userService: 'UserService') -> bytes:
|
||||
"""
|
||||
Returns an screenshot in PNG format (bytes) or empty png if not supported
|
||||
"""
|
||||
png = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
|
||||
proxy = userService.deployed_service.proxy
|
||||
url = userService.getCommsUrl()
|
||||
|
||||
version = userService.getProperty('actor_version') or ''
|
||||
|
||||
# Just for 3.0 or newer, previous actors will not support this method.
|
||||
# Also externally supported agents will not support this method (as OpenGnsys)
|
||||
if '-' in version or version < '3.0.0':
|
||||
url = ''
|
||||
|
||||
if url:
|
||||
try:
|
||||
data: typing.Dict[str, str] = {}
|
||||
if proxy is not None:
|
||||
r = proxy.doProxyRequest(url=url, data=data, timeout=2)
|
||||
if data is None:
|
||||
r = requests.get(url, verify=False, timeout=TIMEOUT)
|
||||
else:
|
||||
r = requests.post(
|
||||
url,
|
||||
data=json.dumps(data),
|
||||
headers={'content-type': 'application/json'},
|
||||
verify=False,
|
||||
timeout=2
|
||||
timeout=TIMEOUT
|
||||
)
|
||||
png = json.loads(r.content)['result']
|
||||
js = r.json()
|
||||
|
||||
# In fact we ignore result right now
|
||||
except Exception as e:
|
||||
logger.error('Get uuid failed: %s. Check connection on destination machine: %s', e, url)
|
||||
if version >= '3.0.0':
|
||||
js = js['result']
|
||||
logger.debug('Requested %s to actor. Url=%s, Result=%s', method, url, js)
|
||||
# In fact we ignore result right now
|
||||
except Exception as e:
|
||||
logger.warning('Request %s failed: %s. Check connection on destination machine: %s', method, e, url)
|
||||
js = None
|
||||
|
||||
return base64.b64decode(png)
|
||||
return js
|
||||
|
||||
def notifyPreconnect(userService: 'UserService', userName: str, protocol: str) -> None:
|
||||
'''
|
||||
Notifies a preconnect to an user service
|
||||
'''
|
||||
ip, hostname = userService.getConnectionSource()
|
||||
try:
|
||||
_requestActor(userService, 'preConnect', {'user': userName, 'protocol': protocol, 'ip': ip, 'hostname': hostname})
|
||||
except NoActorComms:
|
||||
pass # If no preconnect, warning will appear on UDS log
|
||||
|
||||
def checkUuid(userService: 'UserService') -> bool:
|
||||
'''
|
||||
Checks if the uuid of the service is the same of our known uuid on DB
|
||||
'''
|
||||
try:
|
||||
uuid = _requestActor(userService, 'uuid')
|
||||
if uuid != userService.uuid:
|
||||
logger.info('Machine %s do not have expected uuid %s, instead has %s', userService.friendly_name, userService.uuid, uuid)
|
||||
return False
|
||||
except NoActorComms:
|
||||
pass
|
||||
|
||||
return True # Actor does not supports checking
|
||||
|
||||
def requestScreenshot(userService: 'UserService') -> bytes:
|
||||
"""
|
||||
Returns an screenshot in PNG format (bytes) or empty png if not supported
|
||||
"""
|
||||
emptyPng = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
|
||||
try:
|
||||
png = _requestActor(userService, 'screenshot', minVersion='3.0.0') # First valid version with screenshot is 3.0
|
||||
except NoActorComms:
|
||||
png = None
|
||||
|
||||
return base64.b64decode(png or emptyPng)
|
||||
|
||||
|
||||
def sendScript(userService: 'UserService', script: str, forUser: bool = False) -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user