forked from shaba/openuds
advancing on new v3 actor
This commit is contained in:
parent
a8b94325e5
commit
ced7226f81
@ -72,7 +72,7 @@ class PublicProvider(handler.Handler):
|
|||||||
logger.debug('Received Pre connection')
|
logger.debug('Received Pre connection')
|
||||||
if 'user' not in self._params or 'protocol' not in self._params:
|
if 'user' not in self._params or 'protocol' not in self._params:
|
||||||
raise Exception('Invalid preConnect parameters')
|
raise Exception('Invalid preConnect parameters')
|
||||||
return self._service.preConnect(self._params['user'], self._params['protocol'])
|
return self._service.preConnect(self._params['user'], self._params['protocol'], self._params.get('ip', 'unknown'), self._params.get('hostname', 'unknown'))
|
||||||
|
|
||||||
def get_information(self) -> typing.Any:
|
def get_information(self) -> typing.Any:
|
||||||
# Return something useful? :)
|
# Return something useful? :)
|
||||||
|
@ -51,10 +51,10 @@ class UDSActorSvc(daemon.Daemon, CommonService):
|
|||||||
daemon.Daemon.__init__(self, '/var/run/udsactor.pid')
|
daemon.Daemon.__init__(self, '/var/run/udsactor.pid')
|
||||||
CommonService.__init__(self)
|
CommonService.__init__(self)
|
||||||
|
|
||||||
|
# Captures signals so we can stop gracefully
|
||||||
signal.signal(signal.SIGINT, self.markForExit)
|
signal.signal(signal.SIGINT, self.markForExit)
|
||||||
signal.signal(signal.SIGTERM, self.markForExit)
|
signal.signal(signal.SIGTERM, self.markForExit)
|
||||||
|
|
||||||
|
|
||||||
def markForExit(self, signum, frame) -> None:
|
def markForExit(self, signum, frame) -> None:
|
||||||
self._isAlive = False
|
self._isAlive = False
|
||||||
|
|
||||||
|
@ -90,12 +90,12 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
|
|||||||
# Ensures exists destination folder
|
# Ensures exists destination folder
|
||||||
dirname = os.path.dirname(CONFIGFILE)
|
dirname = os.path.dirname(CONFIGFILE)
|
||||||
if not os.path.exists(dirname):
|
if not os.path.exists(dirname):
|
||||||
os.mkdir(dirname, mode=0o700) # Will create only if route to path already exists, for example, /etc (that must... :-))
|
os.mkdir(dirname, mode=0o755) # Will create only if route to path already exists, for example, /etc (that must... :-))
|
||||||
|
|
||||||
with open(CONFIGFILE, 'w') as f:
|
with open(CONFIGFILE, 'w') as f:
|
||||||
cfg.write(f)
|
cfg.write(f)
|
||||||
|
|
||||||
os.chmod(CONFIGFILE, 0o0600) # Ensure only readable by root
|
os.chmod(CONFIGFILE, 0o0644) # Ensure only readable by root
|
||||||
|
|
||||||
|
|
||||||
def useOldJoinSystem() -> bool:
|
def useOldJoinSystem() -> bool:
|
||||||
|
@ -58,34 +58,6 @@ class RESTUserServiceNotFoundError(RESTError):
|
|||||||
class RESTOsManagerError(RESTError):
|
class RESTOsManagerError(RESTError):
|
||||||
ERRCODE = 4
|
ERRCODE = 4
|
||||||
|
|
||||||
|
|
||||||
# Disable warnings log messages
|
|
||||||
try:
|
|
||||||
import urllib3 # @UnusedImport @UnresolvedImport
|
|
||||||
except Exception:
|
|
||||||
from requests.packages import urllib3 # @Reimport @UnresolvedImport
|
|
||||||
|
|
||||||
try:
|
|
||||||
urllib3.disable_warnings() # @UndefinedVariable
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
except Exception:
|
|
||||||
pass # In fact, isn't too important, but will log warns to logging file
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
|
|
||||||
def ensureResultIsOk(result: typing.Any) -> None:
|
|
||||||
if 'error' not in result:
|
|
||||||
return
|
|
||||||
|
|
||||||
for i in (RESTInvalidKeyError, RESTUnmanagedHostError, RESTUserServiceNotFoundError, RESTOsManagerError):
|
|
||||||
if result['error'] == i.ERRCODE:
|
|
||||||
raise i(result['result'])
|
|
||||||
|
|
||||||
err = RESTError(result['result'])
|
|
||||||
err.ERRCODE = result['error']
|
|
||||||
raise err
|
|
||||||
|
|
||||||
|
|
||||||
class REST:
|
class REST:
|
||||||
def __init__(self, host: str, validateCert: bool) -> None:
|
def __init__(self, host: str, validateCert: bool) -> None:
|
||||||
self.host = host
|
self.host = host
|
||||||
@ -93,7 +65,7 @@ class REST:
|
|||||||
self.url = "https://{}/uds/rest/".format(self.host)
|
self.url = "https://{}/uds/rest/".format(self.host)
|
||||||
# Disable logging requests messages except for errors, ...
|
# Disable logging requests messages except for errors, ...
|
||||||
logging.getLogger("requests").setLevel(logging.CRITICAL)
|
logging.getLogger("requests").setLevel(logging.CRITICAL)
|
||||||
# Tries to disable all warnings
|
logging.getLogger("urllib3").setLevel(logging.ERROR)
|
||||||
try:
|
try:
|
||||||
warnings.simplefilter("ignore") # Disables all warnings
|
warnings.simplefilter("ignore") # Disables all warnings
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -103,6 +75,24 @@ class REST:
|
|||||||
def _headers(self) -> typing.MutableMapping[str, str]:
|
def _headers(self) -> typing.MutableMapping[str, str]:
|
||||||
return {'content-type': 'application/json'}
|
return {'content-type': 'application/json'}
|
||||||
|
|
||||||
|
def _actorPost(
|
||||||
|
self,
|
||||||
|
method: str, # i.e. 'initialize', 'ready', ....
|
||||||
|
payLoad: typing.MutableMapping[str, typing.Any],
|
||||||
|
headers: typing.Optional[typing.MutableMapping[str, str]] = None
|
||||||
|
) -> typing.Any:
|
||||||
|
headers = headers or self._headers
|
||||||
|
try:
|
||||||
|
result = requests.post(self.url + 'actor/v2/' + method, data=json.dumps(payLoad), headers=headers, verify=self.validateCert)
|
||||||
|
if result.ok:
|
||||||
|
return result.json()['result']
|
||||||
|
except requests.ConnectionError as e:
|
||||||
|
raise RESTConnectionError(str(e))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise RESTError(result.content)
|
||||||
|
|
||||||
def _login(self, auth: str, username: str, password: str) -> typing.MutableMapping[str, str]:
|
def _login(self, auth: str, username: str, password: str) -> typing.MutableMapping[str, str]:
|
||||||
try:
|
try:
|
||||||
# First, try to login
|
# First, try to login
|
||||||
@ -120,7 +110,6 @@ class REST:
|
|||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|
||||||
|
|
||||||
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
|
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
|
||||||
try:
|
try:
|
||||||
result = requests.get(self.url + 'auth/auths', headers=self._headers, verify=self.validateCert, timeout=4)
|
result = requests.get(self.url + 'auth/auths', headers=self._headers, verify=self.validateCert, timeout=4)
|
||||||
@ -137,7 +126,6 @@ class REST:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def register( #pylint: disable=too-many-arguments, too-many-locals
|
def register( #pylint: disable=too-many-arguments, too-many-locals
|
||||||
self,
|
self,
|
||||||
auth: str,
|
auth: str,
|
||||||
@ -186,36 +174,31 @@ class REST:
|
|||||||
'version': VERSION,
|
'version': VERSION,
|
||||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces]
|
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces]
|
||||||
}
|
}
|
||||||
try:
|
r = self._actorPost('initialize', payload)
|
||||||
result = requests.post(self.url + 'actor/v2/initialize', data=json.dumps(payload), headers=self._headers, verify=self.validateCert)
|
os = r['os']
|
||||||
if result.ok:
|
return types.InitializationResultType(
|
||||||
r = result.json()['result']
|
own_token=r['own_token'],
|
||||||
os = r['os']
|
unique_id=r['unique_id'].lower() if r['unique_id'] else None,
|
||||||
return types.InitializationResultType(
|
max_idle=r['max_idle'],
|
||||||
own_token=r['own_token'],
|
os=types.ActorOsConfigurationType(
|
||||||
unique_id=r['unique_id'].lower() if r['unique_id'] else None,
|
action=os['action'],
|
||||||
max_idle=r['max_idle'],
|
name=os['name'],
|
||||||
os=types.ActorOsConfigurationType(
|
username=os.get('username'),
|
||||||
action=os['action'],
|
password=os.get('password'),
|
||||||
name=os['name'],
|
new_password=os.get('new_password'),
|
||||||
username=os.get('username'),
|
ad=os.get('ad'),
|
||||||
password=os.get('password'),
|
ou=os.get('ou')
|
||||||
new_password=os.get('new_password'),
|
) if r['os'] else None
|
||||||
ad=os.get('ad'),
|
)
|
||||||
ou=os.get('ou')
|
|
||||||
) if r['os'] else None
|
|
||||||
)
|
|
||||||
except requests.ConnectionError as e:
|
|
||||||
raise RESTConnectionError(str(e))
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
raise RESTError(result.content)
|
def ready(self, own_token: str, secret: str, ip: str) -> None:
|
||||||
|
payload = {
|
||||||
def ready(self, own_token: str, secret: str, interfaces: typing.Iterable[types.InterfaceInfoType]) -> None:
|
'token': own_token,
|
||||||
# TODO: implement ready
|
'secret': secret,
|
||||||
return
|
'ip': ip
|
||||||
|
}
|
||||||
|
self._actorPost('ready', payload) # Ignores result...
|
||||||
|
|
||||||
def notifyIpChange(self, own_token: str, secret: str, ip: str) -> None:
|
def notifyIpChange(self, own_token: str, secret: str, ip: str) -> None:
|
||||||
# TODO: implement notifyIpChange
|
# In fact, notifyingIpChange is same as ready right now
|
||||||
return
|
self.ready(own_token, secret, ip)
|
||||||
|
@ -60,6 +60,8 @@ class CommonService:
|
|||||||
_isAlive: bool = True
|
_isAlive: bool = True
|
||||||
_rebootRequested: bool = False
|
_rebootRequested: bool = False
|
||||||
_loggedIn = False
|
_loggedIn = False
|
||||||
|
_cachedInteface: typing.Optional[types.InterfaceInfoType] = None
|
||||||
|
|
||||||
_cfg: types.ActorConfigurationType
|
_cfg: types.ActorConfigurationType
|
||||||
_api: rest.REST
|
_api: rest.REST
|
||||||
_interfaces: typing.List[types.InterfaceInfoType]
|
_interfaces: typing.List[types.InterfaceInfoType]
|
||||||
@ -83,6 +85,19 @@ class CommonService:
|
|||||||
|
|
||||||
socket.setdefaulttimeout(20)
|
socket.setdefaulttimeout(20)
|
||||||
|
|
||||||
|
def serviceInterfaceInfo(self, interfaces: typing.Optional[typing.List[types.InterfaceInfoType]] = None) -> typing.Optional[types.InterfaceInfoType]:
|
||||||
|
"""
|
||||||
|
returns the inteface with unique_id mac or first interface or None if no interfaces...
|
||||||
|
"""
|
||||||
|
interfaces = interfaces or self._interfaces
|
||||||
|
if self._cfg.config and interfaces:
|
||||||
|
try:
|
||||||
|
return next(x for x in self._interfaces if x.mac.lower() == self._cfg.config.unique_id)
|
||||||
|
except StopIteration:
|
||||||
|
return interfaces[0]
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def reboot(self) -> None:
|
def reboot(self) -> None:
|
||||||
self._rebootRequested = True
|
self._rebootRequested = True
|
||||||
|
|
||||||
@ -94,8 +109,13 @@ class CommonService:
|
|||||||
platform.store.writeConfig(self._cfg)
|
platform.store.writeConfig(self._cfg)
|
||||||
|
|
||||||
if self._cfg.own_token and self._interfaces:
|
if self._cfg.own_token and self._interfaces:
|
||||||
self._api.ready(self._cfg.own_token, self._secret, self._interfaces)
|
srvInterface = self.serviceInterfaceInfo()
|
||||||
# Cleans sensible data
|
if srvInterface:
|
||||||
|
self._api.ready(self._cfg.own_token, self._secret, srvInterface.ip)
|
||||||
|
else:
|
||||||
|
logger.error('Could not locate IP address!!!. (Not registered with UDS)')
|
||||||
|
|
||||||
|
# Cleans sensible data
|
||||||
if self._cfg.config:
|
if self._cfg.config:
|
||||||
self._cfg = self._cfg._replace(config=self._cfg.config._replace(os=None), data=None)
|
self._cfg = self._cfg._replace(config=self._cfg.config._replace(os=None), data=None)
|
||||||
platform.store.writeConfig(self._cfg)
|
platform.store.writeConfig(self._cfg)
|
||||||
@ -183,17 +203,10 @@ class CommonService:
|
|||||||
# Not enouth data do check
|
# Not enouth data do check
|
||||||
return
|
return
|
||||||
|
|
||||||
unique_id = self._cfg.config.unique_id
|
|
||||||
def locateMac(interfaces: typing.Iterable[types.InterfaceInfoType]) -> typing.Optional[types.InterfaceInfoType]:
|
|
||||||
try:
|
|
||||||
return next(x for x in interfaces if x.mac.lower() == unique_id)
|
|
||||||
except StopIteration:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
old: types.InterfaceInfoType = locateMac(self._interfaces)
|
old = self.serviceInterfaceInfo()
|
||||||
new: types.InterfaceInfoType = locateMac(platform.operations.getNetworkInfo())
|
new = self.serviceInterfaceInfo(platform.operations.getNetworkInfo())
|
||||||
if not new:
|
if not new or not old:
|
||||||
raise Exception('No ip currently available for {}'.format(self._cfg.config.unique_id))
|
raise Exception('No ip currently available for {}'.format(self._cfg.config.unique_id))
|
||||||
if old.ip != new.ip:
|
if old.ip != new.ip:
|
||||||
self._api.notifyIpChange(self._cfg.own_token, self._secret, new.ip)
|
self._api.notifyIpChange(self._cfg.own_token, self._secret, new.ip)
|
||||||
@ -265,7 +278,7 @@ class CommonService:
|
|||||||
'''
|
'''
|
||||||
logger.info('Service is being stopped')
|
logger.info('Service is being stopped')
|
||||||
|
|
||||||
def preConnect(self, userName: str, protocol: str) -> str: # pylint: disable=unused-argument
|
def preConnect(self, userName: str, protocol: str, ip: str, hostname: str) -> str: # pylint: disable=unused-argument
|
||||||
'''
|
'''
|
||||||
Invoked when received a PRE Connection request via REST
|
Invoked when received a PRE Connection request via REST
|
||||||
Base preconnect executes the preconnect command
|
Base preconnect executes the preconnect command
|
||||||
|
@ -147,7 +147,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
|
|||||||
logger.info('Using multiple step join because configuration requests to do so')
|
logger.info('Using multiple step join because configuration requests to do so')
|
||||||
self.multiStepJoin(name, domain, ou, account, password)
|
self.multiStepJoin(name, domain, ou, account, password)
|
||||||
|
|
||||||
def preConnect(self, userName: str, protocol: str) -> str:
|
def preConnect(self, userName: str, protocol: str, ip: str, hostname: str) -> str:
|
||||||
logger.debug('Pre connect invoked')
|
logger.debug('Pre connect invoked')
|
||||||
|
|
||||||
if protocol == 'rdp': # If connection is not using rdp, skip adding user
|
if protocol == 'rdp': # If connection is not using rdp, skip adding user
|
||||||
@ -176,7 +176,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
|
|||||||
self._user = None
|
self._user = None
|
||||||
logger.debug('User {} already in group'.format(userName))
|
logger.debug('User {} already in group'.format(userName))
|
||||||
|
|
||||||
return super().preConnect(userName, protocol)
|
return super().preConnect(userName, protocol, ip, hostname)
|
||||||
|
|
||||||
def ovLogon(self, username: str, password: str) -> str:
|
def ovLogon(self, username: str, password: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -76,7 +76,7 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
|
|||||||
except Exception:
|
except Exception:
|
||||||
key = wreg.CreateKeyEx(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
|
key = wreg.CreateKeyEx(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
|
||||||
|
|
||||||
fixRegistryPermissions(key.handle)
|
# fixRegistryPermissions(key.handle)
|
||||||
|
|
||||||
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config)) # @UndefinedVariable
|
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config)) # @UndefinedVariable
|
||||||
wreg.CloseKey(key) # @UndefinedVariable
|
wreg.CloseKey(key) # @UndefinedVariable
|
||||||
|
@ -41,7 +41,8 @@ from uds.models import (
|
|||||||
UserService
|
UserService
|
||||||
)
|
)
|
||||||
|
|
||||||
from uds.core import VERSION
|
#from uds.core import VERSION
|
||||||
|
from uds.core.managers import userServiceManager
|
||||||
from uds.core.util.state import State
|
from uds.core.util.state import State
|
||||||
from uds.core.util.cache import Cache
|
from uds.core.util.cache import Cache
|
||||||
from uds.core.util.config import GlobalConfig
|
from uds.core.util.config import GlobalConfig
|
||||||
@ -56,49 +57,65 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
ALLOWED_FAILS = 5
|
ALLOWED_FAILS = 5
|
||||||
|
|
||||||
def actorResult(result: typing.Any = None, error: typing.Optional[str] = None) -> typing.MutableMapping[str, typing.Any]:
|
class BlockAccess(Exception):
|
||||||
result = result or ''
|
pass
|
||||||
res = {'result': result, 'stamp': getSqlDatetimeAsUnix()}
|
|
||||||
if error:
|
|
||||||
res['error'] = error
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
# Helpers
|
||||||
def checkBlockedIp(ip: str)-> None:
|
def checkBlockedIp(ip: str)-> None:
|
||||||
cache = Cache('actorv2')
|
cache = Cache('actorv2')
|
||||||
fails = cache.get(ip) or 0
|
fails = cache.get(ip) or 0
|
||||||
if fails > ALLOWED_FAILS:
|
if fails > ALLOWED_FAILS:
|
||||||
logger.info('Access to actor from %s is blocked for %s seconds since last fail', ip, GlobalConfig.LOGIN_BLOCK.getInt())
|
logger.info('Access to actor from %s is blocked for %s seconds since last fail', ip, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||||
raise Exception()
|
raise BlockAccess()
|
||||||
|
|
||||||
def incFailedIp(ip: str) -> None:
|
def incFailedIp(ip: str) -> None:
|
||||||
cache = Cache('actorv2')
|
cache = Cache('actorv2')
|
||||||
fails = (cache.get(ip) or 0) + 1
|
fails = (cache.get(ip) or 0) + 1
|
||||||
cache.put(ip, fails, GlobalConfig.LOGIN_BLOCK.getInt())
|
cache.put(ip, fails, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||||
|
|
||||||
|
|
||||||
# Enclosed methods under /actor path
|
|
||||||
class ActorV2(Handler):
|
|
||||||
"""
|
|
||||||
Processes actor requests
|
|
||||||
"""
|
|
||||||
authenticated = False # Actor requests are not authenticated by REST api (except register)
|
|
||||||
path = 'actor'
|
|
||||||
name = 'v2'
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
"""
|
|
||||||
Processes get requests
|
|
||||||
"""
|
|
||||||
logger.debug('Actor args for GET: %s', self._args)
|
|
||||||
|
|
||||||
return actorResult({'version': VERSION, 'required': '3.0.0'})
|
|
||||||
|
|
||||||
class ActorV2Action(Handler):
|
class ActorV2Action(Handler):
|
||||||
authenticated = False # Actor requests are not authenticated normally
|
authenticated = False # Actor requests are not authenticated normally
|
||||||
path = 'actor/v2'
|
path = 'actor/v2'
|
||||||
|
|
||||||
def get(self):
|
@staticmethod
|
||||||
return actorResult(VERSION)
|
def actorResult(result: typing.Any = None, error: typing.Optional[str] = None) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
result = result or ''
|
||||||
|
res = {'result': result, 'stamp': getSqlDatetimeAsUnix()}
|
||||||
|
if error:
|
||||||
|
res['error'] = error
|
||||||
|
return res
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setCommsUrl(userService: UserService, ip: str, secret: str):
|
||||||
|
url = 'https://{}/actor/{}'.format(userService.getLoggedIP(), secret)
|
||||||
|
userService.setCommsUrl(url)
|
||||||
|
|
||||||
|
def getUserService(self) -> UserService:
|
||||||
|
'''
|
||||||
|
Looks for an userService and, if not found, raises a BlockAccess request
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
return UserService.objects.get(uuid=self._params['token'])
|
||||||
|
except UserService.DoesNotExist:
|
||||||
|
raise BlockAccess()
|
||||||
|
|
||||||
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
return ActorV2Action.actorResult(error='Base action invoked')
|
||||||
|
|
||||||
|
def post(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
try:
|
||||||
|
checkBlockedIp(self._request.ip) # pylint: disable=protected-access
|
||||||
|
result = self.action()
|
||||||
|
logger.debug('Action result: %s', result)
|
||||||
|
return result
|
||||||
|
except BlockAccess:
|
||||||
|
# For blocking attacks
|
||||||
|
incFailedIp(self._request.ip) # pylint: disable=protected-access
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Posting')
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise AccessDenied('Access denied')
|
||||||
|
|
||||||
class ActorV2Register(ActorV2Action):
|
class ActorV2Register(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
@ -136,7 +153,7 @@ class ActorV2Register(ActorV2Action):
|
|||||||
token=secrets.token_urlsafe(36),
|
token=secrets.token_urlsafe(36),
|
||||||
stamp=getSqlDatetime()
|
stamp=getSqlDatetime()
|
||||||
)
|
)
|
||||||
return actorResult(actorToken.token)
|
return ActorV2Action.actorResult(actorToken.token)
|
||||||
|
|
||||||
class ActorV2Initiialize(ActorV2Action):
|
class ActorV2Initiialize(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
@ -145,17 +162,7 @@ class ActorV2Initiialize(ActorV2Action):
|
|||||||
"""
|
"""
|
||||||
name = 'initialize'
|
name = 'initialize'
|
||||||
|
|
||||||
def get(self) -> typing.MutableMapping[str, typing.Any]:
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
"""
|
|
||||||
Processes get requests. Basically checks if this is a "postThoughGet" for OpenGnsys or similar
|
|
||||||
"""
|
|
||||||
if self._args[0] == 'PostThoughGet':
|
|
||||||
self._args = self._args[1:] # Remove first argument
|
|
||||||
return self.post()
|
|
||||||
|
|
||||||
raise RequestError('Invalid request')
|
|
||||||
|
|
||||||
def post(self) -> typing.MutableMapping[str, typing.Any]:
|
|
||||||
"""
|
"""
|
||||||
Initialize method expect a json POST with this fields:
|
Initialize method expect a json POST with this fields:
|
||||||
* version: str -> Actor version
|
* version: str -> Actor version
|
||||||
@ -183,7 +190,6 @@ class ActorV2Initiialize(ActorV2Action):
|
|||||||
# First, validate token...
|
# First, validate token...
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
try:
|
try:
|
||||||
checkBlockedIp(self._request.ip) # Raises an exception if ip is temporarily blocked
|
|
||||||
ActorToken.objects.get(token=self._params['token']) # Not assigned, because only needs check
|
ActorToken.objects.get(token=self._params['token']) # Not assigned, because only needs check
|
||||||
# Valid actor token, now validate access allowed. That is, look for a valid mac from the ones provided.
|
# Valid actor token, now validate access allowed. That is, look for a valid mac from the ones provided.
|
||||||
try:
|
try:
|
||||||
@ -195,7 +201,7 @@ class ActorV2Initiialize(ActorV2Action):
|
|||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info('Unmanaged host request: %s, %s', self._params, e)
|
logger.info('Unmanaged host request: %s, %s', self._params, e)
|
||||||
return actorResult({
|
return ActorV2Action.actorResult({
|
||||||
'own_token': None,
|
'own_token': None,
|
||||||
'max_idle': None,
|
'max_idle': None,
|
||||||
'unique_id': None,
|
'unique_id': None,
|
||||||
@ -207,44 +213,88 @@ class ActorV2Initiialize(ActorV2Action):
|
|||||||
userService.setProperty('actor_version', self._params['version'])
|
userService.setProperty('actor_version', self._params['version'])
|
||||||
maxIdle = None
|
maxIdle = None
|
||||||
osData: typing.MutableMapping[str, typing.Any] = {}
|
osData: typing.MutableMapping[str, typing.Any] = {}
|
||||||
if userService.deployed_service.osmanager:
|
osManager: typing.Optional['osmanagers.OSManager'] = userService.getOsManager()
|
||||||
osManager: 'osmanagers.OSManager' = userService.deployed_service.osmanager.getInstance()
|
if osManager:
|
||||||
maxIdle = osManager.maxIdle()
|
maxIdle = osManager.maxIdle()
|
||||||
logger.debug('Max idle: %s', maxIdle)
|
logger.debug('Max idle: %s', maxIdle)
|
||||||
osData = osManager.actorData(userService)
|
osData = osManager.actorData(userService)
|
||||||
|
|
||||||
return actorResult({
|
return ActorV2Action.actorResult({
|
||||||
'own_token': userService.uuid,
|
'own_token': userService.uuid,
|
||||||
'unique_id': userService.unique_id,
|
'unique_id': userService.unique_id,
|
||||||
'max_idle': maxIdle,
|
'max_idle': maxIdle,
|
||||||
'os': osData
|
'os': osData
|
||||||
})
|
})
|
||||||
except ActorToken.DoesNotExist:
|
except ActorToken.DoesNotExist:
|
||||||
incFailedIp(self._request.ip) # For blocking attacks
|
raise BlockAccess()
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
raise AccessDenied('Access denied')
|
|
||||||
|
class ActorV2Ready(ActorV2Action):
|
||||||
|
"""
|
||||||
|
Notifies the user service is ready
|
||||||
|
"""
|
||||||
|
name = 'ready'
|
||||||
|
|
||||||
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
"""
|
||||||
|
Initialize method expect a json POST with this fields:
|
||||||
|
* token: str -> Valid Actor "own_token" (if invalid, will return an error).
|
||||||
|
Currently it is the same as user service uuid, but this could change
|
||||||
|
* secret: Secret for commsUrl for actor
|
||||||
|
* ip: ip accesible by uds
|
||||||
|
"""
|
||||||
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
|
userService = self.getUserService()
|
||||||
|
# Stores known IP and notifies it to deployment
|
||||||
|
userService.logIP(self._params['ip'])
|
||||||
|
userServiceInstance = userService.getInstance()
|
||||||
|
userServiceInstance.setIp(self._params['ip'])
|
||||||
|
userService.updateData(userServiceInstance)
|
||||||
|
|
||||||
|
# Store communications url also
|
||||||
|
ActorV2Action.setCommsUrl(userService, self._params['ip'], self._params['secret'])
|
||||||
|
|
||||||
|
if userService.os_state != State.USABLE:
|
||||||
|
userService.setOsState(State.USABLE)
|
||||||
|
# Notify osManager or readyness if has os manager
|
||||||
|
osManager: typing.Optional['osmanagers.OSManager'] = userService.getOsManager()
|
||||||
|
|
||||||
|
if osManager:
|
||||||
|
osManager.toReady(userService)
|
||||||
|
userServiceManager().notifyReadyFromOsManager(userService, '')
|
||||||
|
|
||||||
|
return ActorV2Action.actorResult('ok')
|
||||||
|
|
||||||
class ActorV2Login(ActorV2Action):
|
class ActorV2Login(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
Notifies user logged out
|
Notifies user logged id
|
||||||
"""
|
"""
|
||||||
name = 'login'
|
name = 'login'
|
||||||
|
|
||||||
def post(self):
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
return actorResult('ok')
|
userService = self.getUserService()
|
||||||
|
osManager: typing.Optional['osmanagers.OSManager'] = userService.getOsManager()
|
||||||
|
if osManager:
|
||||||
|
osManager.loggedIn(userService, self._params.get('username') or '')
|
||||||
|
|
||||||
|
ip, hostname = userService.getConnectionSource()
|
||||||
|
deadLine = userService.deployed_service.getDeadline()
|
||||||
|
return ActorV2Action.actorResult({
|
||||||
|
'ip': ip,
|
||||||
|
'hostname': hostname,
|
||||||
|
'dead_line': deadLine
|
||||||
|
})
|
||||||
|
|
||||||
class ActorV2Logout(ActorV2Action):
|
class ActorV2Logout(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
Notifies user logged in
|
Notifies user logged out
|
||||||
"""
|
"""
|
||||||
name = 'logout'
|
name = 'logout'
|
||||||
|
|
||||||
def post(self):
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
return actorResult('ok')
|
return ActorV2Action.actorResult('ok')
|
||||||
|
|
||||||
class ActorV2Log(ActorV2Action):
|
class ActorV2Log(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
@ -252,36 +302,9 @@ class ActorV2Log(ActorV2Action):
|
|||||||
"""
|
"""
|
||||||
name = 'log'
|
name = 'log'
|
||||||
|
|
||||||
def post(self):
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
return actorResult('ok')
|
return ActorV2Action.actorResult('ok')
|
||||||
|
|
||||||
class ActorV2Ready(ActorV2Action):
|
|
||||||
"""
|
|
||||||
Notifies the service is ready
|
|
||||||
"""
|
|
||||||
name = 'ready'
|
|
||||||
|
|
||||||
def setCommsUrl(self, userService: UserService, secret: str):
|
|
||||||
url = 'https://{}/actor/{}'.format(userService.getLoggedIP(), secret)
|
|
||||||
userService.setCommsUrl(url)
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
|
||||||
return actorResult('ok')
|
|
||||||
|
|
||||||
class ActorV2IpChange(ActorV2Action):
|
|
||||||
"""
|
|
||||||
Notifies an IP change
|
|
||||||
"""
|
|
||||||
name = 'ipchange'
|
|
||||||
|
|
||||||
def post(self):
|
|
||||||
"""
|
|
||||||
Records the ip change, and also fix notifyComms url
|
|
||||||
"""
|
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
|
||||||
return actorResult('ok')
|
|
||||||
|
|
||||||
class ActorV2Ticket(ActorV2Action):
|
class ActorV2Ticket(ActorV2Action):
|
||||||
"""
|
"""
|
||||||
@ -289,6 +312,29 @@ class ActorV2Ticket(ActorV2Action):
|
|||||||
"""
|
"""
|
||||||
name = 'ticket'
|
name = 'ticket'
|
||||||
|
|
||||||
def post(self):
|
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
return actorResult('ok')
|
return ActorV2Action.actorResult('ok')
|
||||||
|
|
||||||
|
class ActorV2Notify(ActorV2Action):
|
||||||
|
name = 'notify'
|
||||||
|
|
||||||
|
def post(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
raise AccessDenied('Access denied')
|
||||||
|
|
||||||
|
def get(self) -> typing.MutableMapping[str, typing.Any]:
|
||||||
|
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||||
|
if 'action' not in self._params or 'token' not in self._params or self._params['action'] not in ('login', 'logout'):
|
||||||
|
# Requested login or logout
|
||||||
|
raise RequestError('Invalid parameters')
|
||||||
|
|
||||||
|
try:
|
||||||
|
checkBlockedIp(self._request.ip) # pylint: disable=protected-access
|
||||||
|
userService = UserService.objects.get(uuid=self._params['token'])
|
||||||
|
# TODO: finish this
|
||||||
|
return ActorV2Action.actorResult('ok')
|
||||||
|
except UserService.DoesNotExist:
|
||||||
|
# For blocking attacks
|
||||||
|
incFailedIp(self._request.ip) # pylint: disable=protected-access
|
||||||
|
|
||||||
|
raise AccessDenied('Access denied')
|
||||||
|
@ -455,6 +455,8 @@ class UserServiceManager:
|
|||||||
'''
|
'''
|
||||||
proxy = userService.deployed_service.proxy
|
proxy = userService.deployed_service.proxy
|
||||||
url = userService.getCommsUrl()
|
url = userService.getCommsUrl()
|
||||||
|
ip, hostname = userService.getConnectionSource()
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
logger.debug('No notification is made because agent does not supports notifications')
|
logger.debug('No notification is made because agent does not supports notifications')
|
||||||
return
|
return
|
||||||
@ -462,7 +464,7 @@ class UserServiceManager:
|
|||||||
url += '/preConnect'
|
url += '/preConnect'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = {'user': userName, 'protocol': protocol}
|
data = {'user': userName, 'protocol': protocol, 'ip': ip, 'hostname': hostname}
|
||||||
if proxy is not None:
|
if proxy is not None:
|
||||||
r = proxy.doProxyRequest(url=url, data=data, timeout=2)
|
r = proxy.doProxyRequest(url=url, data=data, timeout=2)
|
||||||
else:
|
else:
|
||||||
|
@ -259,7 +259,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
|||||||
return (self.src_ip, self.src_hostname)
|
return (self.src_ip, self.src_hostname)
|
||||||
|
|
||||||
def getOsManager(self) -> typing.Optional['OSManager']:
|
def getOsManager(self) -> typing.Optional['OSManager']:
|
||||||
return self.deployed_service.osmanager
|
return self.deployed_service.osmanager.getInstance()
|
||||||
|
|
||||||
def needsOsManager(self) -> bool:
|
def needsOsManager(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -71,7 +71,7 @@ class WinDomainOsManager(WindowsOsManager):
|
|||||||
grp = gui.TextField(length=64, label=_('Machine Group'), order=7, tooltip=_('Group to which add machines on creation. If empty, no group will be used. (experimental)'), tab=_('Advanced'))
|
grp = gui.TextField(length=64, label=_('Machine Group'), order=7, tooltip=_('Group to which add machines on creation. If empty, no group will be used. (experimental)'), tab=_('Advanced'))
|
||||||
removeOnExit = gui.CheckBoxField(label=_('Machine clean'), order=8, tooltip=_('If checked, UDS will try to remove the machine from the domain USING the provided credentials'), tab=_('Advanced'), defvalue=gui.TRUE)
|
removeOnExit = gui.CheckBoxField(label=_('Machine clean'), order=8, tooltip=_('If checked, UDS will try to remove the machine from the domain USING the provided credentials'), tab=_('Advanced'), defvalue=gui.TRUE)
|
||||||
serverHint = gui.TextField(length=64, label=_('Server Hint'), order=9, tooltip=_('In case of several AD servers, which one is preferred'), tab=_('Advanced'))
|
serverHint = gui.TextField(length=64, label=_('Server Hint'), order=9, tooltip=_('In case of several AD servers, which one is preferred'), tab=_('Advanced'))
|
||||||
ssl = gui.CheckBoxField(label=_('Use SSL'), order=10, tooltip=_('If checked, a ssl connection to Active Directory will be used'), tab=_('Advanced'))
|
ssl = gui.CheckBoxField(label=_('Use SSL'), order=10, tooltip=_('If checked, a ssl connection to Active Directory will be used'), tab=_('Advanced'), defvalue=gui.TRUE)
|
||||||
|
|
||||||
# Inherits base "onLogout"
|
# Inherits base "onLogout"
|
||||||
onLogout = WindowsOsManager.onLogout
|
onLogout = WindowsOsManager.onLogout
|
||||||
|
Loading…
x
Reference in New Issue
Block a user