1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-11 05:17:55 +03:00

Advancing a bit more on server registration, fixing up log level mess

This commit is contained in:
Adolfo Gómez García 2023-07-26 16:47:25 +02:00
parent 3dc3f4ad56
commit 32c88e1543
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
9 changed files with 55 additions and 30 deletions

2
actor

@ -1 +1 @@
Subproject commit a0af4f7189ca40f9b42062273d735393b53eb881
Subproject commit 6364ac80a36b3606f41eb25c02bede8eaf629f8e

View File

@ -48,7 +48,7 @@ logger = logging.getLogger(__name__)
class ActorTokens(ModelHandler):
model = RegisteredServers
model_filter = {'kind': RegisteredServers.ServerType.ACTOR}
model_filter = {'kind': RegisteredServers.ServerType.ACTOR_SERVICE}
table_title = _('Actor tokens')
table_fields = [
@ -65,6 +65,11 @@ class ActorTokens(ModelHandler):
def item_as_dict(self, item: RegisteredServers) -> typing.Dict[str, typing.Any]:
data = item.data or {}
log_level_int = data.get('log_level', 2)
if log_level_int < 10000: # Old log level
log_level = LogLevel.fromActorLevel(log_level_int).name
else:
log_level = LogLevel(log_level_int).name
return {
'id': item.token,
'name': str(_('Token isued by {} from {}')).format(item.username, item.hostname or item.ip),
@ -76,9 +81,7 @@ class ActorTokens(ModelHandler):
'pre_command': data.get('pre_command', ''),
'post_command': data.get('post_command', ''),
'runonce_command': data.get('runonce_command', ''),
'log_level': LogLevel.fromActorLevel(
data.get('log_level', 2)
).name, # ['DEBUG', 'INFO', 'ERROR', 'FATAL'][item.log_level % 4],
'log_level': log_level,
}
def delete(self) -> str:

View File

@ -242,7 +242,7 @@ class Test(ActorV3Action):
Service.objects.get(token=self._params['token'])
else:
RegisteredServers.objects.get(
token=self._params['token'], kind=RegisteredServers.ServerType.ACTOR
token=self._params['token'], kind=RegisteredServers.ServerType.ACTOR_SERVICE
) # Not assigned, because only needs check
clearFailedIp(self._request)
except Exception:
@ -280,18 +280,18 @@ class Register(ActorV3Action):
# Look for a token for this mac. mac is "inside" data, so we must filter first by type and then ensure mac is inside data
# and mac is the requested one
found = False
actorToken: typing.Optional[RegisteredServers] = RegisteredServers.objects.filter(kind=RegisteredServers.ServerType.ACTOR, mac=self._params['mac']).first()
actorToken: typing.Optional[RegisteredServers] = RegisteredServers.objects.filter(kind=RegisteredServers.ServerType.ACTOR_SERVICE, mac=self._params['mac']).first()
if actorToken:
# Update parameters
actorToken.username = self._user.pretty_name
actorToken.ip_from = self._request.ip
actorToken.ip = self._params['ip']
actorToken.hostname = self._params['hostname']
actorToken.log_level = self._params['log_level']
actorToken.data = { # type: ignore
'pre_command': self._params['pre_command'],
'post_command': self._params['post_command'],
'run_once_command': self._params['run_once_command'],
'log_level': self._params['log_level'],
'custom': self._params.get('custom', ''),
}
actorToken.stamp = getSqlDatetime()
@ -306,15 +306,15 @@ class Register(ActorV3Action):
'ip': self._params['ip'],
'ip_version': self._request.ip_version,
'hostname': self._params['hostname'],
'log_level': self._params['log_level'],
'data': { # type: ignore
'pre_command': self._params['pre_command'],
'post_command': self._params['post_command'],
'run_once_command': self._params['run_once_command'],
'log_level': self._params['log_level'],
'custom': self._params.get('custom', ''),
},
'token': RegisteredServers.create_token(),
'kind': RegisteredServers.ServerType.ACTOR,
'kind': RegisteredServers.ServerType.ACTOR_SERVICE,
'os_type': self._params.get('os', KnownOS.UNKNOWN.os_name()),
'mac': self._params['mac'],
'stamp': getSqlDatetime(),
@ -408,9 +408,8 @@ class Initialize(ActorV3Action):
dbFilter = UserService.objects.filter(deployed_service__service=service)
else:
# If not service provided token, use actor tokens
RegisteredServers.objects.get(
token=token, kind=RegisteredServers.ServerType.ACTOR
) # Not assigned, because only needs check
if not RegisteredServers.validateToken(token, RegisteredServers.ServerType.ACTOR_SERVICE):
raise BlockAccess()
# Build the possible ids and make initial filter to match ANY userservice with provided MAC
idsList = [i['mac'] for i in self._params['id'][:5]]
dbFilter = UserService.objects.all()
@ -432,7 +431,7 @@ class Initialize(ActorV3Action):
# Managed by UDS, get initialization data from osmanager and return it
# Set last seen actor version
userService.setProperty('actor_version', self._params['version'])
userService.setActorVersion(self._params['version'])
osData: typing.MutableMapping[str, typing.Any] = {}
osManager = userService.getOsManagerInstance()
if osManager:
@ -444,7 +443,7 @@ class Initialize(ActorV3Action):
service.aliases.create(alias=alias_token)
return initialization_result(userService.uuid, userService.unique_id, osData, alias_token)
except (RegisteredServers.DoesNotExist, Service.DoesNotExist):
except Service.DoesNotExist:
raise BlockAccess() from None
@ -561,7 +560,7 @@ class Version(ActorV3Action):
def action(self) -> typing.MutableMapping[str, typing.Any]:
logger.debug('Version Args: %s, Params: %s', self._args, self._params)
userService = self.getUserService()
userService.setProperty('actor_version', self._params['version'])
userService.setActorVersion(self._params['version'])
userService.logIP(self._params['ip'])
return ActorV3Action.actorResult()
@ -697,13 +696,18 @@ class Log(ActorV3Action):
def action(self) -> typing.MutableMapping[str, typing.Any]:
logger.debug('Args: %s, Params: %s', self._args, self._params)
userService = self.getUserService()
# Adjust loglevel to own, we start on 10000 for OTHER, and received is 0 for OTHER
if userService.getActorVersion() < '4.0.0':
# Adjust loglevel to own, we start on 10000 for OTHER, and received is 0 for OTHER
level = log.LogLevel.fromInt(int(self._params['level']) + 10000)
else:
level = log.LogLevel.fromInt(int(self._params['level']))
log.doLog(
userService,
log.LogLevel.fromInt(int(self._params['level']) + 10000),
level,
self._params['message'],
log.LogSource.ACTOR,
)
return ActorV3Action.actorResult('ok')
@ -720,7 +724,7 @@ class Ticket(ActorV3Action):
try:
# Simple check that token exists
RegisteredServers.objects.get(token=self._params['token'], kind=RegisteredServers.ServerType.ACTOR) # Not assigned, because only needs check
RegisteredServers.objects.get(token=self._params['token'], kind=RegisteredServers.ServerType.ACTOR_SERVICE) # Not assigned, because only needs check
except RegisteredServers.DoesNotExist:
raise BlockAccess() from None # If too many blocks...

View File

@ -38,6 +38,7 @@ from django.utils.translation import gettext_lazy as _
from uds import models
from uds.core.util.model import getSqlDatetimeAsUnix, getSqlDatetime
from uds.core.util.os_detector import KnownOS
from uds.core.util.log import LogLevel
from uds.REST import Handler
from uds.REST.exceptions import RequestError, NotFound
from uds.REST.model import ModelHandler, OK
@ -66,11 +67,12 @@ class ServerRegister(Handler):
# Note that we use IP and HOSTNAME (with type) to identify the server, so if any of them changes, a new token will be created
# MAC is just informative, and data is used to store any other information that may be needed
serverToken = models.RegisteredServers.objects.get(
ip=ip, hostname=self._params['hostname'], kind=self._params['type']
ip=ip, ip_version=ip_version, hostname=self._params['hostname'], kind=self._params['type']
)
# Update parameters
serverToken.username = self._user.pretty_name
serverToken.ip_from = self._request.ip.split('%')[0] # Ensure we do not store zone if IPv6 and present
# Ensure we do not store zone if IPv6 and present
serverToken.ip_from = self._request.ip.split('%')[0]
serverToken.stamp = getSqlDatetime()
serverToken.kind = self._params['type']
serverToken.save()
@ -80,8 +82,10 @@ class ServerRegister(Handler):
username=self._user.pretty_name,
ip_from=self._request.ip.split('%')[0], # Ensure we do not store zone if IPv6 and present
ip=ip,
ip_version=ip_version,
hostname=self._params['hostname'],
token=models.RegisteredServers.create_token(),
log_level=self._params.get('log_level', LogLevel.INFO.value),
stamp=getSqlDatetime(),
kind=self._params['type'],
os_type=typing.cast(str, self._params.get('os', KnownOS.UNKNOWN.os_name())).lower(),
@ -96,7 +100,7 @@ class ServerRegister(Handler):
class ServersTokens(ModelHandler):
model = models.RegisteredServers
model_filter = {
'kind__in': [models.RegisteredServers.ServerType.TUNNEL, models.RegisteredServers.ServerType.OTHER]
'kind__in': [models.RegisteredServers.ServerType.TUNNEL_SERVER, models.RegisteredServers.ServerType.OTHER]
}
path = 'servers'
name = 'tokens'

View File

@ -80,7 +80,7 @@ class TunnelTicket(Handler):
# Take token from url
token = self._args[2][:48]
if not models.RegisteredServers.validateToken(token, serverType=models.RegisteredServers.ServerType.TUNNEL):
if not models.RegisteredServers.validateToken(token, serverType=models.RegisteredServers.ServerType.TUNNEL_SERVER):
if self._args[1][:4] == 'stop':
# "Discard" invalid stop requests, because Applications does not like them.
# RDS connections keep alive for a while after the application is finished,
@ -161,6 +161,6 @@ class TunnelRegister(ServerRegister):
# Just a compatibility method for old tunnel servers
def post(self) -> typing.MutableMapping[str, typing.Any]:
self._params['type'] = models.RegisteredServers.ServerType.TUNNEL
self._params['type'] = models.RegisteredServers.ServerType.TUNNEL_SERVER
self._params['os'] = self._params.get('os', KnownOS.LINUX.os_name()) # Legacy tunnels are always linux
return super().post()

View File

@ -58,7 +58,7 @@ def dict2resp(dct: typing.Mapping[typing.Any, typing.Any]) -> str:
@auth.trustedSourceRequired
def guacamole(request: ExtendedHttpRequestWithUser, token: str, tunnelId: str) -> HttpResponse:
if not RegisteredServers.validateToken(token, serverType=RegisteredServers.ServerType.TUNNEL):
if not RegisteredServers.validateToken(token, serverType=RegisteredServers.ServerType.TUNNEL_SERVER):
logger.error('Invalid token %s from %s', token, request.ip)
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
logger.debug('Received credentials request for tunnel id %s', tunnelId)

View File

@ -107,6 +107,11 @@ class Migration(migrations.Migration):
name="listen_port",
field=models.IntegerField(default=43910),
),
migrations.AddField(
model_name="registeredservers",
name="log_level",
field=models.IntegerField(default=50000),
),
migrations.RunPython(
migrate_old_data,
revert_old_data,

View File

@ -36,6 +36,7 @@ from uds.core.util.os_detector import KnownOS
from django.db import models
from uds.core.util.request import ExtendedHttpRequest
from uds.core.util.log import LogLevel
from .consts import MAX_DNS_NAME_LENGTH, MAX_IPV6_LENGTH
@ -61,8 +62,8 @@ class RegisteredServers(models.Model):
"""
class ServerType(enum.IntEnum):
TUNNEL = 1
ACTOR = 2
TUNNEL_SERVER = 1
ACTOR_SERVICE = 2
APP_SERVER = 3
OTHER = 99
@ -71,8 +72,8 @@ class RegisteredServers(models.Model):
def path(self) -> str:
return {
RegisteredServers.ServerType.TUNNEL: 'tunnel',
RegisteredServers.ServerType.ACTOR: 'actor',
RegisteredServers.ServerType.TUNNEL_SERVER: 'tunnel',
RegisteredServers.ServerType.ACTOR_SERVICE: 'actor',
RegisteredServers.ServerType.APP_SERVER: 'app',
RegisteredServers.ServerType.OTHER: 'other',
}[self]
@ -96,11 +97,13 @@ class RegisteredServers(models.Model):
# Note that a server can register itself several times, so we can have several entries
# for the same server, but with different types.
# (So, for example, an APP_SERVER can be also a TUNNEL_SERVER, because will use both APP API and TUNNEL API)
kind = models.IntegerField(default=ServerType.TUNNEL.value)
kind = models.IntegerField(default=ServerType.TUNNEL_SERVER.value)
# os type of server (linux, windows, etc..)
os_type = models.CharField(max_length=32, default=KnownOS.UNKNOWN.os_name())
# mac address of registered server, if any. Important for VDI actor servers mainly, informative for others
mac = models.CharField(max_length=32, default=MAC_UNKNOWN, db_index=True)
# Log level, so we can filter messages for this server
log_level = models.IntegerField(default=LogLevel.ERROR.value)
# Extra data, for custom server type use (i.e. actor keeps command related data here)
data = models.JSONField(null=True, blank=True, default=None)

View File

@ -627,6 +627,12 @@ class UserService(UUIDModel):
def getLoggedIP(self) -> str:
return self.getProperty('ip') or '0.0.0.0' # nosec: no binding address
def setActorVersion(self, version: typing.Optional[str] = None) -> None:
self.setProperty('actor_version', version)
def getActorVersion(self) -> str:
return self.getProperty('actor_version') or '0.0.0'
def isValidPublication(self) -> bool:
"""