Skip to content
Snippets Groups Projects
Commit aa23e734 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Added preliminary support for tasks auth password and refactored computing conf approach.

parent 2bed0517
No related branches found
No related tags found
No related merge requests found
Showing
with 191 additions and 70 deletions
......@@ -49,6 +49,22 @@ while read env_var; do
fi
done
#---------------------
# Password
#---------------------
if [ "x$AUTH_PASS" != "x" ]; then
echo "[INFO] Setting up VNC password..."
mkdir -p /metauser/.vnc
/opt/tigervnc/usr/bin/vncpasswd -f <<< $AUTH_PASS > /metauser/.vnc/passwd
chmod 600 /metauser/.vnc/passwd
export VNC_AUTH=True
else
echo "[INFO] Not setting up any VNC password"
fi
#---------------------
# Entrypoint command
#---------------------
......
#!/bin/bash
# Exec TigerVNC server
if [ "x$VNC_AUTH" == "xTrue" ]; then
/opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes vncauth,tlsvnc -xstartup /opt/tigervnc/xstartup
else
/opt/tigervnc/usr/bin/vncserver :0 -SecurityTypes None -xstartup /opt/tigervnc/xstartup
fi
# Check it is running. If it is not, exit
while true
......
#!/bin/bash
docker tag rosetta/metadesktop localhost:5000/rosetta/metadesktop
docker push localhost:5000/rosetta/metadesktop
from django.contrib import admin
from .models import Profile, LoginToken, Task, Container, Computing, ComputingSysConf, ComputingUserConf
from .models import Profile, LoginToken, Task, Container, Computing, ComputingSysConf, ComputingUserConf, Keys
admin.site.register(Profile)
admin.site.register(LoginToken)
......@@ -9,3 +9,4 @@ admin.site.register(Container)
admin.site.register(Computing)
admin.site.register(ComputingSysConf)
admin.site.register(ComputingUserConf)
admin.site.register(Keys)
......@@ -2,7 +2,7 @@
import inspect
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponse
from django.http import HttpResponse, HttpResponseRedirect
from .utils import format_exception, log_user_activity
from .exceptions import ErrorMessage, ConsistencyException
......
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from ...models import Profile, Container, Computing, ComputingSysConf, ComputingUserConf
from ...models import Profile, Container, Computing, ComputingSysConf, ComputingUserConf, Keys
class Command(BaseCommand):
help = 'Adds the admin superuser with \'a\' password.'
......@@ -32,6 +32,13 @@ class Command(BaseCommand):
print('Creating testuser profile')
Profile.objects.create(user=testuser, authtoken='129aac94-284a-4476-953c-ffa4349b4a50')
# Create default keys
print('Creating testuser defualt keys')
Keys.objects.create(user = testuser,
default = True,
private_key_file = '/rosetta/.ssh/id_rsa',
public_key_file = '/rosetta/.ssh/id_rsa.pub')
# Public containers
public_containers = Container.objects.filter(user=None)
......@@ -46,7 +53,8 @@ class Command(BaseCommand):
image = 'rosetta/metadesktop',
type = 'docker',
registry = 'docker_local',
service_ports = '8590')
service_ports = '8590',
require_pass = True)
# MetaDesktop Singularity
Container.objects.create(user = None,
......@@ -54,7 +62,8 @@ class Command(BaseCommand):
image = 'rosetta/metadesktop',
type = 'singularity',
registry = 'docker_local',
service_ports = '8590')
service_ports = '8590',
require_pass = True)
# Astrocook
Container.objects.create(user = None,
......@@ -87,37 +96,57 @@ class Command(BaseCommand):
else:
print('Creating demo computing resources containers...')
# Local computing resource
#==============================
# Local remote computing
#==============================
Computing.objects.create(user = None,
name = 'Local',
type = 'local')
type = 'local',
require_sys_conf = False,
require_user_conf = False,
require_user_keys = False)
# Demo remote computing resource
#==============================
# Demo remote computing
#==============================
demo_remote_computing = Computing.objects.create(user = None,
name = 'Demo remote',
type = 'remote',
requires_sys_conf = True,
requires_user_conf = False)
# Create demo remote sys computing conf
require_sys_conf = True,
require_user_conf = False,
require_user_keys = False)
ComputingSysConf.objects.create(computing = demo_remote_computing,
data = {'host': 'slurmclusterworker-one'})
# Create demo remote user computing conf
ComputingUserConf.objects.create(user = testuser,
computing = demo_remote_computing,
data = {'user': 'testuser',
'id_rsa': '/rosetta/.ssh/id_rsa',
'id_rsa.pub': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2n4wiLiRmE1sla5+w0IW3wwPW/mqhhkm7IyCBS+rGTgnts7xsWcxobvamNdD6KSLNnjFZbBb7Yaf/BvWrwQgdqIFVU3gRWHYzoU6js+lKtBjd0e2DAVGivWCKEkSGLx7zhx7uH/Jt8kyZ4NaZq0p5+SFHBzePdR/1rURd8G8+G3OaCPKqP+JQT4RMUQHC5SNRJLcK1piYdmhDiYEyuQG4FlStKCWLCXeUY2EVirNMeQIfOgbUHJsVjH07zm1y8y7lTWDMWVZOnkG6Ap5kB+n4l1eWbslOKgDv29JTFOMU+bvGvYZh70lmLK7Hg4CMpXVgvw5VF9v97YiiigLwvC7wasBHaASwH7wUqakXYhdGFxJ23xVMSLnvJn4S++4L8t8bifRIVqhT6tZCPOU4fdOvJKCRjKrf7gcW/E33ovZFgoOCJ2vBLIh9N9ME0v7tG15JpRtgIBsCXwLcl3tVyCZJ/eyYMbc3QJGsbcPGb2CYRjDbevPCQlNavcMdlyrNIke7VimM5aW8OBJKVh5wCNRpd9XylrKo1cZHYxu/c5Lr6VUZjLpxDlSz+IuTn4VE7vmgHNPnXdlxRKjLHG/FZrZTSCWFEBcRoSa/hysLSFwwDjKd9nelOZRNBvJ+NY48vA8ixVnk4WAMlR/5qhjTRam66BVysHeRcbjJ2IGjwTJC5Q== rosetta@rosetta.platform'})
#==============================
# Demo remote (auth) computing
#==============================
demo_remote_auth_computing = Computing.objects.create(user = None,
name = 'Demo remote (auth)',
type = 'remote',
require_sys_conf = True,
require_user_conf = True,
require_user_keys = True)
ComputingSysConf.objects.create(computing = demo_remote_auth_computing,
data = {'host': 'slurmclusterworker-one'})
ComputingUserConf.objects.create(user = testuser,
computing = demo_remote_auth_computing,
data = {'user': 'testuser'})
# Demo slurm computing resource
#==============================
# Demo Slurm computing
#==============================
demo_slurm_computing = Computing.objects.create(user = None,
name = 'Demo Slurm',
type = 'slurm',
requires_sys_conf = True,
requires_user_conf = True)
require_sys_conf = True,
require_user_conf = True,
require_user_keys = True)
# Create demo slurm sys computing conf
ComputingSysConf.objects.create(computing = demo_slurm_computing,
......@@ -126,10 +155,6 @@ class Command(BaseCommand):
# Create demo slurm user computing conf
ComputingUserConf.objects.create(user = testuser,
computing = demo_slurm_computing,
data = {'user': 'testuser',
'id_rsa': '/rosetta/.ssh/id_rsa',
'id_rsa.pub': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2n4wiLiRmE1sla5+w0IW3wwPW/mqhhkm7IyCBS+rGTgnts7xsWcxobvamNdD6KSLNnjFZbBb7Yaf/BvWrwQgdqIFVU3gRWHYzoU6js+lKtBjd0e2DAVGivWCKEkSGLx7zhx7uH/Jt8kyZ4NaZq0p5+SFHBzePdR/1rURd8G8+G3OaCPKqP+JQT4RMUQHC5SNRJLcK1piYdmhDiYEyuQG4FlStKCWLCXeUY2EVirNMeQIfOgbUHJsVjH07zm1y8y7lTWDMWVZOnkG6Ap5kB+n4l1eWbslOKgDv29JTFOMU+bvGvYZh70lmLK7Hg4CMpXVgvw5VF9v97YiiigLwvC7wasBHaASwH7wUqakXYhdGFxJ23xVMSLnvJn4S++4L8t8bifRIVqhT6tZCPOU4fdOvJKCRjKrf7gcW/E33ovZFgoOCJ2vBLIh9N9ME0v7tG15JpRtgIBsCXwLcl3tVyCZJ/eyYMbc3QJGsbcPGb2CYRjDbevPCQlNavcMdlyrNIke7VimM5aW8OBJKVh5wCNRpd9XylrKo1cZHYxu/c5Lr6VUZjLpxDlSz+IuTn4VE7vmgHNPnXdlxRKjLHG/FZrZTSCWFEBcRoSa/hysLSFwwDjKd9nelOZRNBvJ+NY48vA8ixVnk4WAMlR/5qhjTRam66BVysHeRcbjJ2IGjwTJC5Q== rosetta@rosetta.platform'})
data = {'user': 'testuser'})
......@@ -75,6 +75,8 @@ class Container(models.Model):
registry = models.CharField('Container registry', max_length=255, blank=False, null=False)
service_ports = models.CharField('Container service ports', max_length=36, blank=True, null=True)
#private = models.BooleanField('Container is private and needs auth to be pulled from the registry')
require_user = models.BooleanField(default=False)
require_pass = models.BooleanField(default=False)
def __str__(self):
return str('Container of type "{}" with image "{}" with service ports "{}" from registry "{}" of user "{}"'.format(self.type, self.image, self.service_ports, self.registry, self.user))
......@@ -103,11 +105,17 @@ class Computing(models.Model):
name = models.CharField('Computing Name', max_length=255, blank=False, null=False)
type = models.CharField('Computing Type', max_length=255, blank=False, null=False)
requires_sys_conf = models.BooleanField(default=False)
requires_user_conf = models.BooleanField(default=False)
require_sys_conf = models.BooleanField(default=False)
require_user_conf = models.BooleanField(default=False)
require_user_keys = models.BooleanField(default=False)
def __str__(self):
if self.user:
return str('Computing Resource "{}" of user "{}"'.format(self.name, self.user))
else:
return str('Computing Resource "{}"'.format(self.name))
@property
def id(self):
......@@ -201,6 +209,10 @@ class ComputingSysConf(models.Model):
def id(self):
return str(self.uuid).split('-')[0]
def __str__(self):
return str('Computing sys conf for {} with id "{}"'.format(self.computing, self.id))
class ComputingUserConf(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
......@@ -210,7 +222,7 @@ class ComputingUserConf(models.Model):
@property
def id(self):
return str(self.uuid).split('-')[0]
return str('Computing sys conf for {} with id "{}" of user "{}"'.format(self.computing, self.id, self.user))
#=========================
......@@ -234,7 +246,7 @@ class Task(models.Model):
# Auth
auth_user = models.CharField('Task auth user', max_length=36, blank=True, null=True)
auth_password = models.CharField('Task auth password', max_length=36, blank=True, null=True)
auth_pass = models.CharField('Task auth pass', max_length=36, blank=True, null=True)
access_method = models.CharField('Task access method', max_length=36, blank=True, null=True)
def save(self, *args, **kwargs):
......@@ -279,5 +291,23 @@ class Task(models.Model):
return str(self.uuid).split('-')[0]
#=========================
# Keys
#=========================
class Keys(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, related_name='+', on_delete=models.CASCADE, null=False)
private_key_file = models.CharField('Private key file', max_length=4096, blank=False, null=False)
public_key_file = models.CharField('Public key file', max_length=4096, blank=False, null=False)
default = models.BooleanField('Default keys?', default=False)
def __str__(self):
return str('Keys with id "{}" of user "{}"'.format(self.id, self.user))
@property
def id(self):
return str(self.uuid).split('-')[0]
from .models import TaskStatuses
from .models import TaskStatuses, Keys
from .utils import os_shell
from .exceptions import ErrorMessage, ConsistencyException
......@@ -23,6 +23,10 @@ def start_task(task):
# Init run command #--cap-add=NET_ADMIN --cap-add=NET_RAW
run_command = 'sudo docker run --network=rosetta_default --name rosetta-task-{}'.format( task.id)
# Pass if any
if task.auth_pass:
run_command += ' -eAUTH_PASS={} '.format(task.auth_pass)
# Data volume
run_command += ' -v {}/task-{}:/data'.format(TASK_DATA_DIR, task.id)
......@@ -67,16 +71,28 @@ def start_task(task):
host = task.computing.get_conf_param('host')
# Get id_rsa
id_rsa_file = task.computing.get_conf_param('id_rsa')
if not id_rsa_file:
raise Exception('This computing requires an id_rsa file but cannot find any')
#id_rsa_file = task.computing.get_conf_param('id_rsa')
#if not id_rsa_file:
# raise Exception('This computing requires an id_rsa file but cannot find any')
# Get user keys
if task.computing.require_user_keys:
user_keys = Keys.objects.get(user=task.user, default=True)
else:
raise NotImplementedError('Remote tasks not requiring keys are not yet supported')
# 1) Run the container on the host (non blocking)
if task.container.type == 'singularity':
run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(id_rsa_file, host)
run_command += '"export SINGULARITY_NOHTTPS=true && '
# Set pass if any
if task.auth_pass:
authstring = ' export SINGULARITYENV_AUTH_PASS={} && '.format(task.auth_pass)
else:
authstring = ''
run_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} '.format(user_keys.private_key_file, host)
run_command += '"export SINGULARITY_NOHTTPS=true && {} '.format(authstring)
run_command += 'exec nohup singularity run --pid --writable-tmpfs --containall --cleanenv '
# Set registry
......@@ -139,14 +155,17 @@ def stop_task(task):
elif task.computing.type == 'remote':
# Get user keys
if task.computing.require_user_keys:
user_keys = Keys.objects.get(user=task.user, default=True)
else:
raise NotImplementedError('Remote tasks not requiring keys are not yet supported')
# Get computing host
host = task.computing.get_conf_param('host')
# Get id_rsa
id_rsa_file = task.computing.get_conf_param('id_rsa')
# Stop the task remotely
stop_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} "kill -9 {}"'.format(id_rsa_file, host, task.pid)
stop_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} "kill -9 {}"'.format(user_keys.private_key_file, host, task.pid)
logger.debug(stop_command)
out = os_shell(stop_command, capture=True)
if out.exit_code != 0:
......
......@@ -26,6 +26,12 @@
<td>{{ task.created }}</td>
</tr>
<tr>
<td><b>Running on</b></td>
<td>{{ task.computing }}</td>
</tr>
<!-- <tr>
<td><b>Task pid</b></td>
<td>{{ task.pid}}</td>
......@@ -46,6 +52,14 @@
<td>{{ task.tunnel_port }}</td>
</tr>
{% if task.auth_pass %}
<tr>
<td><b>Task Auth pass</b></td>
<td>{{ task.auth_pass }}</td>
</tr>
{% endif %}
<tr>
<td><b>Operations</b></td>
......
......@@ -18,20 +18,15 @@
<hr/>
{% if data.computing %}
{% include "components/computing.html" with computing=data.computin %}
{% include "components/computing.html" with computing=data.computing %}
{% else %}
{% for computing in data.platform_computings %}
{% for computing in data.computings %}
{% include "components/computing.html" with computing=computing %}
<br />
{% endfor %}
{% for computing in data.user_computings %}
{% include "components/computing.html" with computing=computing %}
<br />
{% endfor %}
<br />
<a href="/add_computing">Add new...</a>
......
......@@ -74,23 +74,28 @@
<form action="/create_task/" method="POST">
{% csrf_token %}
<input type="hidden" name="step" value="two" />
<input type="hidden" name="task_uuid" value="{{ data.task_uuid }}" />
<input type="hidden" name="task_uuid" value="{{ data.task.uuid }}" />
<table class="dashboard" style="max-width:700px">
{% if data.task.container.require_user %}
<tr>
<td><b>Task user</b></td>
<td>
<input type="text" name="auth_user" value="" placeholder="" size="23" />
</td>
</tr>
{% endif %}
{% if data.task.container.require_pass %}
<tr>
<td><b>Task password</b></td>
<td valign="top"><b>Set task password</b></td>
<td>
<input type="password" name="auth_password" value="" placeholder="" size="23" />
<input type="password" name="auth_password" value="" placeholder="" size="23" /><br>
<font size=-1>Use a non-sensitive password as it will be stored in plain text. 6 chars min.</font>
</td>
</tr>
{% endif %}
<tr>
<td><b>Access method</b></td><td>
......
......@@ -6,7 +6,7 @@ from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User
from django.shortcuts import redirect
from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing
from .models import Profile, LoginToken, Task, TaskStatuses, Container, Computing, Keys
from .utils import send_email, format_exception, timezonize, os_shell, booleanize, debug_param
from .decorators import public_view, private_view
from .tasks import start_task, stop_task
......@@ -435,7 +435,7 @@ def create_task(request):
# Set step and task uuid
data['step'] = 'two'
data['task_uuid'] = task.uuid
data['task'] = task
elif step == 'two':
......@@ -444,10 +444,13 @@ def create_task(request):
task = _task_cache[task_uuid]
# Add auth
task.task_auth_user = request.POST.get('auth_user', None)
task.task_auth_password = request.POST.get('auth_password', None)
task.task_access_method = request.POST.get('access_method', None)
task.auth_user = request.POST.get('auth_user', None)
task.auth_pass = request.POST.get('auth_password', None)
task.access_method = request.POST.get('access_method', None)
# Cheks
if len(task.auth_pass) < 6:
raise ErrorMessage('Task password must be at least 6 chars')
# Add auth and/or computing parameters to the task if any
# TODO... (i..e num cores)
......@@ -524,7 +527,11 @@ def task_log(request):
host = task.computing.get_conf_param('host')
# Get id_rsa
id_rsa_file = task.computing.get_conf_param('id_rsa')
if task.computing.require_user_keys:
user_keys = Keys.objects.get(user=task.user, default=True)
id_rsa_file = user_keys.private_key_file
else:
raise NotImplementedError('temote with no keys not yet')
# View the Singularity container log
view_log_command = 'ssh -i {} -4 -o StrictHostKeyChecking=no {} "cat /tmp/{}.log"'.format(id_rsa_file, host, task.uuid)
......@@ -682,10 +689,10 @@ def computings(request):
data={}
data['user'] = request.user
data['profile'] = Profile.objects.get(user=request.user)
data['title'] = 'Add computing'
data['title'] = 'Computing resources'
data['name'] = request.POST.get('name',None)
data['computings'] = list(Computing.objects.filter(user=None)) + Computing.objects.filter(user=request.user)
data['computings'] = list(Computing.objects.filter(user=None)) + list(Computing.objects.filter(user=request.user))
# Attach user conf in any
for computing in data['computings']:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment