forked from shaba/openuds
Updated Guacamole to only accept authenticated tunnel connections
* Added handshake check BEFORE opening SSL tunnel
This commit is contained in:
parent
29b6613c95
commit
a8a5063083
@ -51,6 +51,7 @@ TUNNEL_LISTENING, TUNNEL_OPENING, TUNNEL_PROCESSING, TUNNEL_ERROR = 0, 1, 2, 3
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ForwardServer(socketserver.ThreadingTCPServer):
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
@ -113,11 +114,15 @@ class ForwardServer(socketserver.ThreadingTCPServer):
|
||||
|
||||
rsocket.connect(self.remote)
|
||||
|
||||
rsocket.sendall(HANDSHAKE_V1) # No response, just the handshake
|
||||
|
||||
context = ssl.create_default_context()
|
||||
|
||||
|
||||
# Do not "recompress" data, use only "base protocol" compression
|
||||
context.options |= ssl.OP_NO_COMPRESSION
|
||||
context.load_verify_locations(tools.getCaCertsFile()) # Load certifi certificates
|
||||
context.load_verify_locations(
|
||||
tools.getCaCertsFile()
|
||||
) # Load certifi certificates
|
||||
|
||||
# If ignore remote certificate
|
||||
if self.check_certificate is False:
|
||||
@ -135,7 +140,7 @@ class ForwardServer(socketserver.ThreadingTCPServer):
|
||||
|
||||
try:
|
||||
with self.connect() as ssl_socket:
|
||||
ssl_socket.sendall(HANDSHAKE_V1 + b'TEST')
|
||||
ssl_socket.sendall(b'TEST')
|
||||
resp = ssl_socket.recv(2)
|
||||
if resp != b'OK':
|
||||
raise Exception({'Invalid tunnelresponse: {resp}'})
|
||||
@ -183,7 +188,7 @@ class Handler(socketserver.BaseRequestHandler):
|
||||
logger.debug('Ticket %s', self.server.ticket)
|
||||
with self.server.connect() as ssl_socket:
|
||||
# Send handhshake + command + ticket
|
||||
ssl_socket.sendall(HANDSHAKE_V1 + b'OPEN' + self.server.ticket.encode())
|
||||
ssl_socket.sendall(b'OPEN' + self.server.ticket.encode())
|
||||
# Check response is OK
|
||||
data = ssl_socket.recv(2)
|
||||
if data != b'OK':
|
||||
|
@ -31,11 +31,11 @@
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from django.conf.urls import url
|
||||
from .views import guacamole, guacamole_authenticated
|
||||
from .views import guacamole
|
||||
|
||||
urlpatterns = [
|
||||
# Authenticated path
|
||||
url(r'^uds/guacamole/auth/(?P<token>[^/]+)/(?P<tunnelId>.+)$', guacamole_authenticated, name='dispatcher.guacamole'),
|
||||
url(r'^uds/guacamole/auth/(?P<token>[^/]+)/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole'),
|
||||
# Non authenticated path. Disabled
|
||||
# url(r'^uds/guacamole/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole.noauth'),
|
||||
]
|
||||
|
@ -54,8 +54,11 @@ def dict2resp(dct):
|
||||
return '\r'.join((k + '\t' + v for k, v in dct.items()))
|
||||
|
||||
|
||||
@auth.trustedSourceRequired
|
||||
def guacamole(request: ExtendedHttpRequestWithUser, tunnelId: str) -> HttpResponse:
|
||||
def guacamole(request: ExtendedHttpRequestWithUser, token: str, tunnelId: str) -> HttpResponse:
|
||||
if not TunnelToken.validateToken(token):
|
||||
logger.error('Invalid token %s from %s', token, request.ip)
|
||||
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
|
||||
# TODO: Check the authId validity
|
||||
logger.debug('Received credentials request for tunnel id %s', tunnelId)
|
||||
|
||||
try:
|
||||
@ -103,10 +106,3 @@ def guacamole(request: ExtendedHttpRequestWithUser, tunnelId: str) -> HttpRespon
|
||||
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
|
||||
|
||||
return HttpResponse(response, content_type=CONTENT_TYPE)
|
||||
|
||||
def guacamole_authenticated(request: ExtendedHttpRequestWithUser, token: str, tunnelId: str) -> HttpResponse:
|
||||
if not TunnelToken.validateToken(token):
|
||||
logger.error('Invalid token %s from %s', token, request.ip)
|
||||
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
|
||||
# TODO: Check the authId validity
|
||||
return guacamole(request, tunnelId)
|
@ -55,6 +55,13 @@ class IPServiceBase(services.Service):
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def getOrder(ipData: str) -> str:
|
||||
try:
|
||||
return ipData.split('~')[0]
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def parent(self) -> 'provider.PhysicalMachinesProvider':
|
||||
return typing.cast('provider.PhysicalMachinesProvider', super().parent())
|
||||
|
||||
|
@ -158,21 +158,6 @@ class Proxy:
|
||||
prettyDest = ''
|
||||
logger.info('CONNECT FROM %s', prettySource)
|
||||
|
||||
try:
|
||||
# First, ensure handshake (simple handshake) and command
|
||||
data: bytes = await source.recv(len(consts.HANDSHAKE_V1))
|
||||
|
||||
if data != consts.HANDSHAKE_V1:
|
||||
logger.error('INVALID HANDSHAKE %s', data)
|
||||
raise Exception()
|
||||
except Exception:
|
||||
if consts.DEBUG:
|
||||
logger.exception('HANDSHAKE')
|
||||
logger.error('HANDSHAKE from %s', address)
|
||||
await source.sendall(b'ERROR_HANDSHAKE')
|
||||
# Closes connection now
|
||||
return
|
||||
|
||||
try:
|
||||
# Handshake correct, get the command (4 bytes)
|
||||
command: bytes = await source.recv(consts.COMMAND_LENGTH)
|
||||
|
@ -40,7 +40,7 @@ ssl_dhparam = /etc/certs/dhparam.pem
|
||||
# http://www.example.com/uds/rest/tunnel/ticket
|
||||
# https://www.example.com:14333/uds/rest/tunnel/ticket
|
||||
uds_server = http://172.27.0.1:8000/uds/rest/tunnel/ticket
|
||||
uds_token = 123456789012345678901234567890123456789012345678
|
||||
uds_token = eBCeFxTBw1IKXCqq-RlncshwWIfrrqxc8y5nehqiqMtRztwD
|
||||
|
||||
# Secret to get access to admin commands (Currently only stats commands). No default for this.
|
||||
# Admin commands and only allowed from "allow" ips
|
||||
|
@ -105,7 +105,25 @@ async def tunnel_proc_async(
|
||||
while True:
|
||||
msg: message.Message = pipe.recv()
|
||||
if msg.command == message.Command.TUNNEL and msg.connection:
|
||||
# Connection done, check for handshake
|
||||
source, address = msg.connection
|
||||
|
||||
try:
|
||||
# First, ensure handshake (simple handshake) and command
|
||||
data: bytes = source.recv(len(consts.HANDSHAKE_V1))
|
||||
|
||||
if data != consts.HANDSHAKE_V1:
|
||||
raise Exception() # Invalid handshake
|
||||
except Exception:
|
||||
if consts.DEBUG:
|
||||
logger.exception('HANDSHAKE')
|
||||
logger.error('HANDSHAKE from %s', address)
|
||||
# Close Source and continue
|
||||
source.close()
|
||||
continue
|
||||
|
||||
return msg.connection
|
||||
|
||||
# Process other messages, and retry
|
||||
except Exception:
|
||||
logger.exception('Receiving data from parent process')
|
||||
|
Loading…
Reference in New Issue
Block a user