mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-10 01:17:59 +03:00
Added Support for SSH through HTML5
This commit is contained in:
parent
b9a01e686f
commit
6bb4c3bd5e
@ -45,6 +45,7 @@ NX = 'nx'
|
||||
X11 = 'x11'
|
||||
X2GO = 'x2go' # Based on NX
|
||||
NICEDCV = 'nicedcv'
|
||||
SSH = 'ssh'
|
||||
OTHER = 'other'
|
||||
|
||||
GENERIC = (RDP, RGS, VNC, NX, X11, X2GO, PCOIP, NICEDCV, OTHER)
|
||||
GENERIC = (RDP, RGS, VNC, NX, X11, X2GO, PCOIP, NICEDCV, SSH, OTHER)
|
||||
|
34
server/src/uds/transports/HTML5SSH/__init__.py
Normal file
34
server/src/uds/transports/HTML5SSH/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
|
||||
from .html5ssh import HTML5SSHTransport
|
BIN
server/src/uds/transports/HTML5SSH/html5ssh.png
Normal file
BIN
server/src/uds/transports/HTML5SSH/html5ssh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
259
server/src/uds/transports/HTML5SSH/html5ssh.py
Normal file
259
server/src/uds/transports/HTML5SSH/html5ssh.py
Normal file
@ -0,0 +1,259 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2022 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
|
||||
from uds.core.ui import gui
|
||||
|
||||
from uds.core import transports
|
||||
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds import models
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core import Module
|
||||
from django.http import HttpRequest # pylint: disable=ungrouped-imports
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class HTML5SSHTransport(transports.Transport):
|
||||
"""
|
||||
Provides access via SSH to service.
|
||||
"""
|
||||
|
||||
typeName = _('HTML5 SSH')
|
||||
typeType = 'HTML5SSHTransport'
|
||||
typeDescription = _('SSH protocol using HTML5 client')
|
||||
iconFile = 'html5ssh.png'
|
||||
|
||||
ownLink = True
|
||||
supportedOss = OsDetector.allOss
|
||||
protocol = transports.protocols.SSH
|
||||
group = transports.TUNNELED_GROUP
|
||||
|
||||
guacamoleServer = gui.TextField(
|
||||
label=_('Tunnel Server'),
|
||||
order=1,
|
||||
tooltip=_(
|
||||
'Host of the tunnel server (use http/https & port if needed) as accesible from users'
|
||||
),
|
||||
defvalue='https://',
|
||||
length=64,
|
||||
required=True,
|
||||
tab=gui.TUNNEL_TAB,
|
||||
)
|
||||
|
||||
username = gui.TextField(
|
||||
label=_('Username'),
|
||||
order=20,
|
||||
tooltip=_('Username for VNC connection authentication.'),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
password = gui.PasswordField(
|
||||
label=_('Password'),
|
||||
order=21,
|
||||
tooltip=_('Password for VNC connection authentication'),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
sshPrivateKey = gui.TextField(
|
||||
label=_('SSH Private Key'),
|
||||
order=22,
|
||||
tooltip=_(
|
||||
'Private key for SSH authentication. If not provided, password authentication is used.'
|
||||
),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
sshPassphrase = gui.PasswordField(
|
||||
label=_('SSH Private Key Passphrase'),
|
||||
order=23,
|
||||
tooltip=_(
|
||||
'Passphrase for SSH private key if it is required. If not provided, but it is needed, user will be prompted for it.'
|
||||
),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
|
||||
sshCommand = gui.TextField(
|
||||
label=_('SSH Command'),
|
||||
order=30,
|
||||
tooltip=_(
|
||||
'Command to execute on the remote server. If not provided, an interactive shell will be executed.'
|
||||
),
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
sshPort = gui.NumericField(
|
||||
length=40,
|
||||
label=_('SSH Server port'),
|
||||
defvalue='22',
|
||||
order=22,
|
||||
tooltip=_('Port of the SSH server.'),
|
||||
required=True,
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
sshHostKey = gui.TextField(
|
||||
label=_('SSH Host Key'),
|
||||
order=41,
|
||||
tooltip=_(
|
||||
'Host key of the SSH server. If not provided, no verification of host identity is done.'
|
||||
),
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
serverKeepAlive = gui.NumericField(
|
||||
length=3,
|
||||
label=_('Server Keep Alive'),
|
||||
defvalue='30',
|
||||
order=42,
|
||||
tooltip=_(
|
||||
'Time in seconds between keep alive messages sent to server. If not provided, no keep alive messages are sent.'
|
||||
),
|
||||
required=True,
|
||||
minValue=0,
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
|
||||
ticketValidity = gui.NumericField(
|
||||
length=3,
|
||||
label=_('Ticket Validity'),
|
||||
defvalue='60',
|
||||
order=90,
|
||||
tooltip=_(
|
||||
'Allowed time, in seconds, for HTML5 client to reload data from UDS Broker. The default value of 60 is recommended.'
|
||||
),
|
||||
required=True,
|
||||
minValue=60,
|
||||
tab=gui.ADVANCED_TAB,
|
||||
)
|
||||
forceNewWindow = gui.ChoiceField(
|
||||
order=91,
|
||||
label=_('Force new HTML Window'),
|
||||
tooltip=_('Select windows behavior for new connections on HTML5'),
|
||||
required=True,
|
||||
values=[
|
||||
gui.choiceItem(
|
||||
gui.FALSE,
|
||||
_('Open every connection on the same window, but keeps UDS window.'),
|
||||
),
|
||||
gui.choiceItem(
|
||||
gui.TRUE, _('Force every connection to be opened on a new window.')
|
||||
),
|
||||
gui.choiceItem(
|
||||
'overwrite',
|
||||
_('Override UDS window and replace it with the connection.'),
|
||||
),
|
||||
],
|
||||
defvalue=gui.FALSE,
|
||||
tab=gui.ADVANCED_TAB,
|
||||
)
|
||||
|
||||
def initialize(self, values: 'Module.ValuesType'):
|
||||
if not values:
|
||||
return
|
||||
# Strip spaces
|
||||
# Remove trailing / (one or more) from url if it exists from "guacamoleServer" field
|
||||
self.guacamoleServer.value = self.guacamoleServer.value.strip().rstrip('/')
|
||||
if self.guacamoleServer.value[0:4] != 'http':
|
||||
raise transports.Transport.ValidationException(
|
||||
_('The server must be http or https')
|
||||
)
|
||||
|
||||
def isAvailableFor(self, userService: 'models.UserService', ip: str) -> bool:
|
||||
"""
|
||||
Checks if the transport is available for the requested destination ip
|
||||
Override this in yours transports
|
||||
"""
|
||||
logger.debug('Checking availability for %s', ip)
|
||||
ready = self.cache.get(ip)
|
||||
if not ready:
|
||||
# Check again for readyness
|
||||
if self.testServer(userService, ip, self.sshPort.value) is True:
|
||||
self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
|
||||
return True
|
||||
self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
|
||||
return ready == 'Y'
|
||||
|
||||
def getLink( # pylint: disable=too-many-locals
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest',
|
||||
) -> str:
|
||||
# Build params dict
|
||||
params = {
|
||||
'protocol': 'ssh',
|
||||
'hostname': ip,
|
||||
'port': str(self.sshPort.num()),
|
||||
}
|
||||
|
||||
# Optional numeric keep alive. If less than 2, it is not sent
|
||||
if self.serverKeepAlive.num() >= 2:
|
||||
params['server-alive-interval'] = str(self.serverKeepAlive.num())
|
||||
|
||||
# Add optional parameters (strings only)
|
||||
for i in (
|
||||
('username', self.username),
|
||||
('password', self.password),
|
||||
('private-key', self.sshPrivateKey),
|
||||
('passphrase', self.sshPassphrase),
|
||||
('command', self.sshCommand),
|
||||
('host-key', self.sshHostKey),
|
||||
):
|
||||
if i[1].value.strip():
|
||||
params[i[0]] = i[1].value.strip()
|
||||
|
||||
logger.debug('SSH Params: %s', params)
|
||||
|
||||
scrambler = cryptoManager().randomString(32)
|
||||
ticket = models.TicketStore.create(params, validity=self.ticketValidity.num())
|
||||
|
||||
onw = ''
|
||||
if self.forceNewWindow.value == gui.TRUE:
|
||||
onw = 'o_n_w={}'
|
||||
elif self.forceNewWindow.value == 'overwrite':
|
||||
onw = 'o_s_w=yes'
|
||||
onw = onw.format(hash(transport.name))
|
||||
|
||||
return str(
|
||||
"{}/guacamole/#/?data={}.{}{}".format(
|
||||
self.guacamoleServer.value, ticket, scrambler, onw
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user