Commit 07311cb4 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Add https proxy for tasks mechanism stub.

parent 15bdf157
Loading
Loading
Loading
Loading
+102 −3
Original line number Diff line number Diff line
@@ -518,10 +518,10 @@ def hash_string_to_int(string):


#================================
#  Tunnel setup
#  Tunnel (and proxy) setup
#================================

def setup_tunnel(task):
def setup_tunnel_and_proxy(task):

    # Importing here instead of on top avoids circular dependencies problems when loading booleanize in settings
    from .models import Task, KeyPair, TaskStatuses
@@ -546,8 +546,107 @@ def setup_tunnel(task):
        task.tcp_tunnel_port = tcp_tunnel_port
        task.save()
        
    # Setup the proxy now.
    # Some info about the various SSL switches: https://serverfault.com/questions/577616/using-https-between-apache-loadbalancer-and-backends

    # Check if the tunnel is active and if not create it
    # Esnure conf directory exists
    if not os.path.exists('/shared/etc_apache2_sites_enabled'):
        os.makedirs('/shared/etc_apache2_sites_enabled')

    # Set conf file name
    apache_conf_file = '/shared/etc_apache2_sites_enabled/{}.conf'.format(task.uuid)

    # Check if proxy conf exists 
    if not os.path.exists(apache_conf_file):

        # Write conf file
        logger.debug('Writing task proxy conf to {}'.format(apache_conf_file))
    
        websocket_protocol = 'wss' if task.container.interface_protocol == 'https' else 'ws'
        task_proxy_host = os.environ.get('TASK_PROXY_HOST', 'localhost')
        apache_conf_content = '''
#---------------------------
#  Task interface proxy 
#---------------------------

#<Location /desktop/{0}/>
#AuthType Basic
#AuthName "Restricted area"
#AuthUserFile /shared/reyns/etc_apache2_sites_enabled/'''+str(task.uuid)+'''.htpasswd
#Require valid-user  

#ProxyPass http://desktop-{0}:8590/
#ProxyPassReverse http://desktop-{0}:8590/
#</Location>     

#<Location /sessions/{1}>
#ProxyPass ws://desktop-{0}:8590/websockify
#ProxyPassReverse ws://desktop-{0}:8590/websockify
#</Location>

Listen '''+str(task.tcp_tunnel_port)+'''
<VirtualHost _default_:'''+str(task.tcp_tunnel_port)+'''>
    
    ServerName  '''+task_proxy_host+'''
    ServerAdmin admin@rosetta
    
    SSLEngine on
    SSLCertificateFile /root/certificates/rosetta_platform/rosetta_platform.crt
    SSLCertificateKeyFile /root/certificates/rosetta_platform/rosetta_platform.key
    SSLCACertificateFile /root/certificates/rosetta_platform/rosetta_platform.ca-bundle
    
    SSLProxyEngine On
    SSLProxyVerify none 
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off  

    BrowserMatch "MSIE [2-6]" \
        nokeepalive ssl-unclean-shutdown \
        downgrade-1.0 force-response-1.0
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

    # Use RewriteEngine to handle websocket connection upgrades
    RewriteEngine On
    RewriteCond %{HTTP:Connection} Upgrade [NC]
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteRule /(.*) '''+str(websocket_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/$1 [P,L]

    <Location "/">
      # preserve Host header to avoid cross-origin problems
      ProxyPreserveHost on
      # proxy to the port
      ProxyPass         '''+str(task.container.interface_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/
      ProxyPassReverse  '''+str(task.container.interface_protocol)+'''://webapp:'''+str(task.tcp_tunnel_port)+'''/
    </Location>

</VirtualHost>

'''
        with open(apache_conf_file, 'w') as f:
            f.write(apache_conf_content)
    
    # Now check conf exist on proxy
    logger.debug('Checking if conf is enabled on proxy service')
    out = os_shell('ssh -o StrictHostKeyChecking=no proxy "[ -e /etc/apache2/sites-enabled/{}.conf ]"'.format(task.uuid), capture=True)

    if out.exit_code == 1:
  
        logger.debug('Conf not enabled on proxy service, linkig it and reloading Apache conf')
  
        # Link on proxy since conf does not exist
        out = os_shell('ssh -o StrictHostKeyChecking=no proxy "sudo ln -s /shared/etc_apache2_sites_enabled/{0}.conf /etc/apache2/sites-enabled/{0}.conf"'.format(task.uuid), capture=True)
        if out.exit_code != 0:
            logger.error(out.stderr)
            raise ErrorMessage('Somthing went wrong when activating the task proxy conf')        
        
        # Reload apache conf on Proxy
        out = os_shell('ssh -o StrictHostKeyChecking=no proxy "sudo apache2ctl graceful"', capture=True)
        if out.exit_code != 0:
            logger.error(out.stderr) 
            raise ErrorMessage('Somthing went wrong when loading the task proxy conf')        


    # Check if the tunnel is (still) active and if not create it
    logger.debug('Checking if task "{}" has a running tunnel'.format(task))

    out = os_shell('ps -ef | grep ":{}:{}:{}" | grep -v grep'.format(task.tcp_tunnel_port, task.interface_ip, task.interface_port), capture=True)
+18 −9
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.db.models import Q
from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, KeyPair, ComputingSysConf, ComputingUserConf, Text
from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel, finalize_user_creation
from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param, get_tunnel_host, random_username, setup_tunnel_and_proxy, finalize_user_creation
from .decorators import public_view, private_view
from .exceptions import ErrorMessage

@@ -341,7 +341,7 @@ def tasks(request):
            elif action=='connect':
                
                # First ensure that the tunnel is setu up
                setup_tunnel(task)
                setup_tunnel_and_proxy(task)

                # Then, redirect to the task through the tunnel
                tunnel_host = get_tunnel_host()
@@ -972,16 +972,25 @@ def direct_connection_handler(request, uuid):
        raise ErrorMessage('You do not have access to this task.')

    # First ensure that the tunnel is setu up
    setup_tunnel(task)
    setup_tunnel_and_proxy(task)
    
    # Set task proxy host
    task_proxy_host = settings.TASK_PROXY_HOST

    # Then, redirect to the task through the tunnel
    tunnel_host = get_tunnel_host()
    if task.requires_proxy:
        if task.requires_proxy_auth and task.auth_token:
            user = request.user.email
            password = task.auth_token
        return redirect('{}://{}:{}@{}:{}'.format(task.container.interface_protocol, user, password, tunnel_host, task.tcp_tunnel_port))
            redirect_string = 'https://{}:{}@{}:{}'.format(user, password, task_proxy_host, task.tcp_tunnel_port)        
        else:
        return redirect('{}://{}:{}'.format(task.container.interface_protocol, tunnel_host,task.tcp_tunnel_port))
            redirect_string = 'https://{}:{}'.format(task_proxy_host, task.tcp_tunnel_port)       
    else:
        redirect_string = '{}://{}:{}'.format(task.container.interface_protocol, tunnel_host,task.tcp_tunnel_port)
    
    logger.debug('Task direct connect redirect: "{}"'.format(redirect_string))
    return redirect(redirect_string)
        
    

@@ -999,7 +1008,7 @@ def sharable_link_handler(request, short_uuid):
        raise ErrorMessage('You do not have access to this task.')

    # First ensure that the tunnel is setu up
    setup_tunnel(task)
    setup_tunnel_and_proxy(task)

    # Then, redirect to the task through the tunnel
    tunnel_host = get_tunnel_host()
+2 −0
Original line number Diff line number Diff line
@@ -230,6 +230,8 @@ LOCAL_USER_DATA_DIR = os.environ.get('LOCAL_USER_DATA_DIR', '/data')
# Invitation code if any
INVITATION_CODE = os.environ.get('INVITATION_CODE', None)

# Task proxy host (WARNING: direct use of the env var in utils.py)
TASK_PROXY_HOST = os.environ.get('TASK_PROXY_HOST', 'localhost')

#===============================
#  Auth