mirror of
https://github.com/retspen/webvirtcloud
synced 2026-03-23 11:04:49 +00:00
Merge 748c167def into aa2a996e3f
This commit is contained in:
commit
72d57ebc21
10 changed files with 245 additions and 33 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from computes.models import Compute
|
from computes.models import Compute
|
||||||
|
|
@ -13,6 +14,8 @@ class ComputeAddTcpForm(forms.Form):
|
||||||
max_length=100)
|
max_length=100)
|
||||||
password = forms.CharField(error_messages={'required': _('No password has been entered')},
|
password = forms.CharField(error_messages={'required': _('No password has been entered')},
|
||||||
max_length=100)
|
max_length=100)
|
||||||
|
gstfsd_key = forms.CharField(max_length=256, required=False)
|
||||||
|
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data['name']
|
name = self.cleaned_data['name']
|
||||||
|
|
@ -41,6 +44,20 @@ class ComputeAddTcpForm(forms.Form):
|
||||||
return hostname
|
return hostname
|
||||||
raise forms.ValidationError(_('This host is already connected'))
|
raise forms.ValidationError(_('This host is already connected'))
|
||||||
|
|
||||||
|
def clean_gstfsd_key(self):
|
||||||
|
gstfsd_key = self.cleaned_data['gstfsd_key']
|
||||||
|
try:
|
||||||
|
data = json.loads(gstfsd_key)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a json object'))
|
||||||
|
if not 'k' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "k" field'))
|
||||||
|
if not 'kty' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "kty" field'))
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a valid json'))
|
||||||
|
return gstfsd_key
|
||||||
|
|
||||||
|
|
||||||
class ComputeAddSshForm(forms.Form):
|
class ComputeAddSshForm(forms.Form):
|
||||||
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
||||||
|
|
@ -49,6 +66,8 @@ class ComputeAddSshForm(forms.Form):
|
||||||
max_length=100)
|
max_length=100)
|
||||||
login = forms.CharField(error_messages={'required': _('No login has been entered')},
|
login = forms.CharField(error_messages={'required': _('No login has been entered')},
|
||||||
max_length=20)
|
max_length=20)
|
||||||
|
gstfsd_key = forms.CharField(max_length=256, required=False)
|
||||||
|
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data['name']
|
name = self.cleaned_data['name']
|
||||||
|
|
@ -77,6 +96,20 @@ class ComputeAddSshForm(forms.Form):
|
||||||
return hostname
|
return hostname
|
||||||
raise forms.ValidationError(_('This host is already connected'))
|
raise forms.ValidationError(_('This host is already connected'))
|
||||||
|
|
||||||
|
def clean_gstfsd_key(self):
|
||||||
|
gstfsd_key = self.cleaned_data['gstfsd_key']
|
||||||
|
try:
|
||||||
|
data = json.loads(gstfsd_key)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a json object'))
|
||||||
|
if not 'k' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "k" field'))
|
||||||
|
if not 'kty' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "kty" field'))
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a valid json'))
|
||||||
|
return gstfsd_key
|
||||||
|
|
||||||
|
|
||||||
class ComputeAddTlsForm(forms.Form):
|
class ComputeAddTlsForm(forms.Form):
|
||||||
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
||||||
|
|
@ -87,6 +120,8 @@ class ComputeAddTlsForm(forms.Form):
|
||||||
max_length=100)
|
max_length=100)
|
||||||
password = forms.CharField(error_messages={'required': _('No password has been entered')},
|
password = forms.CharField(error_messages={'required': _('No password has been entered')},
|
||||||
max_length=100)
|
max_length=100)
|
||||||
|
gstfsd_key = forms.CharField(max_length=256, required=False)
|
||||||
|
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data['name']
|
name = self.cleaned_data['name']
|
||||||
|
|
@ -115,6 +150,20 @@ class ComputeAddTlsForm(forms.Form):
|
||||||
return hostname
|
return hostname
|
||||||
raise forms.ValidationError(_('This host is already connected'))
|
raise forms.ValidationError(_('This host is already connected'))
|
||||||
|
|
||||||
|
def clean_gstfsd_key(self):
|
||||||
|
gstfsd_key = self.cleaned_data['gstfsd_key']
|
||||||
|
try:
|
||||||
|
data = json.loads(gstfsd_key)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a json object'))
|
||||||
|
if not 'k' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "k" field'))
|
||||||
|
if not 'kty' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "kty" field'))
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a valid json'))
|
||||||
|
return gstfsd_key
|
||||||
|
|
||||||
|
|
||||||
class ComputeEditHostForm(forms.Form):
|
class ComputeEditHostForm(forms.Form):
|
||||||
host_id = forms.CharField()
|
host_id = forms.CharField()
|
||||||
|
|
@ -126,6 +175,8 @@ class ComputeEditHostForm(forms.Form):
|
||||||
max_length=100)
|
max_length=100)
|
||||||
password = forms.CharField(max_length=100)
|
password = forms.CharField(max_length=100)
|
||||||
|
|
||||||
|
gstfsd_key = forms.CharField(max_length=256, required=False)
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data['name']
|
name = self.cleaned_data['name']
|
||||||
have_symbol = re.match('[^a-zA-Z0-9._-]+', name)
|
have_symbol = re.match('[^a-zA-Z0-9._-]+', name)
|
||||||
|
|
@ -145,11 +196,28 @@ class ComputeEditHostForm(forms.Form):
|
||||||
raise forms.ValidationError(_('Wrong IP address'))
|
raise forms.ValidationError(_('Wrong IP address'))
|
||||||
return hostname
|
return hostname
|
||||||
|
|
||||||
|
def clean_gstfsd_key(self):
|
||||||
|
gstfsd_key = self.cleaned_data['gstfsd_key']
|
||||||
|
try:
|
||||||
|
data = json.loads(gstfsd_key)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a json object'))
|
||||||
|
if not 'k' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "k" field'))
|
||||||
|
if not 'kty' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "kty" field'))
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a valid json'))
|
||||||
|
return gstfsd_key
|
||||||
|
|
||||||
|
|
||||||
class ComputeAddSocketForm(forms.Form):
|
class ComputeAddSocketForm(forms.Form):
|
||||||
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
||||||
max_length=20)
|
max_length=20)
|
||||||
|
|
||||||
|
gstfsd_key = forms.CharField(max_length=256, required=False)
|
||||||
|
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data['name']
|
name = self.cleaned_data['name']
|
||||||
have_symbol = re.match('[^a-zA-Z0-9._-]+', name)
|
have_symbol = re.match('[^a-zA-Z0-9._-]+', name)
|
||||||
|
|
@ -162,3 +230,17 @@ class ComputeAddSocketForm(forms.Form):
|
||||||
except Compute.DoesNotExist:
|
except Compute.DoesNotExist:
|
||||||
return name
|
return name
|
||||||
raise forms.ValidationError(_('This host is already connected'))
|
raise forms.ValidationError(_('This host is already connected'))
|
||||||
|
|
||||||
|
def clean_gstfsd_key(self):
|
||||||
|
gstfsd_key = self.cleaned_data['gstfsd_key']
|
||||||
|
try:
|
||||||
|
data = json.loads(gstfsd_key)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a json object'))
|
||||||
|
if not 'k' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "k" field'))
|
||||||
|
if not 'kty' in data:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must have a "kty" field'))
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(_('Gstfsd key must be a valid json'))
|
||||||
|
return gstfsd_key
|
||||||
|
|
|
||||||
19
computes/migrations/0002_compute_gstfsd_key.py
Normal file
19
computes/migrations/0002_compute_gstfsd_key.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('computes', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='compute',
|
||||||
|
name='gstfsd_key',
|
||||||
|
field=models.CharField(max_length=256, null=True, blank=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -7,6 +7,7 @@ class Compute(models.Model):
|
||||||
login = models.CharField(max_length=20)
|
login = models.CharField(max_length=20)
|
||||||
password = models.CharField(max_length=14, blank=True, null=True)
|
password = models.CharField(max_length=14, blank=True, null=True)
|
||||||
type = models.IntegerField()
|
type = models.IntegerField()
|
||||||
|
gstfsd_key = models.CharField(max_length=256, blank=True, null=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.hostname
|
return self.hostname
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,12 @@
|
||||||
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
|
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
||||||
|
|
@ -121,6 +127,12 @@
|
||||||
<input type="text" name="login" class="form-control" value="{{ compute.login }}">
|
<input type="text" name="login" class="form-control" value="{{ compute.login }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
||||||
|
|
@ -163,6 +175,12 @@
|
||||||
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
|
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
||||||
|
|
@ -187,6 +205,12 @@
|
||||||
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-z0-9\.\-_]+">
|
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-z0-9\.\-_]+">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
<button type="submit" class="pull-left btn btn-danger" name="host_del">
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@
|
||||||
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
|
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" placeholder="Gstfsd JSON Web Key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||||
|
|
@ -83,6 +89,12 @@
|
||||||
<input type="text" name="login" class="form-control" placeholder="{% trans "Username" %}">
|
<input type="text" name="login" class="form-control" placeholder="{% trans "Username" %}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" placeholder="Gstfsd JSON Web Key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||||
|
|
@ -121,6 +133,12 @@
|
||||||
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
|
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" placeholder="Gstfsd JSON Web Key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||||
|
|
@ -141,6 +159,12 @@
|
||||||
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
|
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-4 control-label">{% trans "Gstfsd key" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" name="gstfsd_key" placeholder="Gstfsd JSON Web Key" class="form-control" value="{{ compute.gstfsd_key }}" maxlength="256">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">
|
<button type="button" class="btn btn-default" data-dismiss="modal">
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ def computes(request):
|
||||||
'status': connection_manager.host_is_up(compute.type, compute.hostname),
|
'status': connection_manager.host_is_up(compute.type, compute.hostname),
|
||||||
'type': compute.type,
|
'type': compute.type,
|
||||||
'login': compute.login,
|
'login': compute.login,
|
||||||
'password': compute.password
|
'password': compute.password,
|
||||||
|
'gstfsd_key': compute.gstfsd_key
|
||||||
})
|
})
|
||||||
return compute_data
|
return compute_data
|
||||||
|
|
||||||
|
|
@ -66,7 +67,8 @@ def computes(request):
|
||||||
hostname=data['hostname'],
|
hostname=data['hostname'],
|
||||||
type=CONN_TCP,
|
type=CONN_TCP,
|
||||||
login=data['login'],
|
login=data['login'],
|
||||||
password=data['password'])
|
password=data['password'],
|
||||||
|
gstfsd_key=data['gstfsd_key'])
|
||||||
new_tcp_host.save()
|
new_tcp_host.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
@ -79,7 +81,8 @@ def computes(request):
|
||||||
new_ssh_host = Compute(name=data['name'],
|
new_ssh_host = Compute(name=data['name'],
|
||||||
hostname=data['hostname'],
|
hostname=data['hostname'],
|
||||||
type=CONN_SSH,
|
type=CONN_SSH,
|
||||||
login=data['login'])
|
login=data['login'],
|
||||||
|
gstfsd_key=data['gstfsd_key'])
|
||||||
new_ssh_host.save()
|
new_ssh_host.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
@ -93,7 +96,8 @@ def computes(request):
|
||||||
hostname=data['hostname'],
|
hostname=data['hostname'],
|
||||||
type=CONN_TLS,
|
type=CONN_TLS,
|
||||||
login=data['login'],
|
login=data['login'],
|
||||||
password=data['password'])
|
password=data['password'],
|
||||||
|
gstfsd_key=data['gstfsd_key'])
|
||||||
new_tls_host.save()
|
new_tls_host.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
@ -107,7 +111,8 @@ def computes(request):
|
||||||
hostname='localhost',
|
hostname='localhost',
|
||||||
type=CONN_SOCKET,
|
type=CONN_SOCKET,
|
||||||
login='',
|
login='',
|
||||||
password='')
|
password='',
|
||||||
|
gstfsd_key=data['gstfsd_key'])
|
||||||
new_socket_host.save()
|
new_socket_host.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
@ -122,6 +127,7 @@ def computes(request):
|
||||||
compute_edit.hostname = data['hostname']
|
compute_edit.hostname = data['hostname']
|
||||||
compute_edit.login = data['login']
|
compute_edit.login = data['login']
|
||||||
compute_edit.password = data['password']
|
compute_edit.password = data['password']
|
||||||
|
compute_edit.gstfsd_key = data['gstfsd_key']
|
||||||
compute_edit.save()
|
compute_edit.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@ import SocketServer
|
||||||
import json
|
import json
|
||||||
import guestfs
|
import guestfs
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
from jwcrypto import jws, jwk, jwe
|
||||||
|
|
||||||
PORT = 16510
|
PORT = 16510
|
||||||
ADDRESS = "0.0.0.0"
|
ADDRESS = "0.0.0.0"
|
||||||
|
SECRET = None
|
||||||
|
|
||||||
|
|
||||||
class MyTCPServer(SocketServer.ThreadingTCPServer):
|
class MyTCPServer(SocketServer.ThreadingTCPServer):
|
||||||
|
|
@ -19,12 +21,20 @@ class MyTCPServer(SocketServer.ThreadingTCPServer):
|
||||||
|
|
||||||
class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
||||||
def handle(self):
|
def handle(self):
|
||||||
# recive data
|
# recive data and check authentcation
|
||||||
data = json.loads(self.request.recv(1024).strip())
|
|
||||||
|
|
||||||
# GuestFS
|
|
||||||
gfs = guestfs.GuestFS(python_return_dict=True)
|
|
||||||
try:
|
try:
|
||||||
|
signed_data = jws.JWS()
|
||||||
|
signed_data.deserialize(self.request.recv(4096).strip())
|
||||||
|
signed_data.verify(SECRET, "HS512")
|
||||||
|
|
||||||
|
encrypted_data = jwe.JWE(algs=["A256KW", "A256CBC-HS512"])
|
||||||
|
encrypted_data.deserialize(signed_data.payload)
|
||||||
|
encrypted_data.decrypt(SECRET)
|
||||||
|
|
||||||
|
data = json.loads(encrypted_data.plaintext)
|
||||||
|
|
||||||
|
# GuestFS
|
||||||
|
gfs = guestfs.GuestFS(python_return_dict=True)
|
||||||
gfs.add_domain(data['vname'])
|
gfs.add_domain(data['vname'])
|
||||||
gfs.launch()
|
gfs.launch()
|
||||||
parts = gfs.list_partitions()
|
parts = gfs.list_partitions()
|
||||||
|
|
@ -51,8 +61,29 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
||||||
pass
|
pass
|
||||||
gfs.shutdown()
|
gfs.shutdown()
|
||||||
gfs.close()
|
gfs.close()
|
||||||
except RuntimeError, err:
|
# we check signature before trying to decrypt so jwe.InvalidJWEData should not be raised ever
|
||||||
|
except (jws.InvalidJWSObject, jwe.InvalidJWEData, RuntimeError, ValueError) as err:
|
||||||
self.request.sendall(json.dumps({'return': 'error', 'message': err.message}))
|
self.request.sendall(json.dumps({'return': 'error', 'message': err.message}))
|
||||||
|
|
||||||
|
except jws.InvalidJWSSignature as err:
|
||||||
|
self.request.sendall(json.dumps({'return': 'error', 'message': (
|
||||||
|
"Fail to verify request signature. Check if you have imported "
|
||||||
|
"the key (/var/lib/gstfsd/SECRET) in WebVirtCloud"
|
||||||
|
)}))
|
||||||
|
|
||||||
|
if not os.path.isfile("/var/lib/gstfsd/SECRET"):
|
||||||
|
try:
|
||||||
|
os.mkdir("/var/lib/gstfsd")
|
||||||
|
except OSError as error:
|
||||||
|
if error.errno != 17: # File exists
|
||||||
|
raise
|
||||||
|
os.chmod("/var/lib/gstfsd", 0700)
|
||||||
|
with open("/var/lib/gstfsd/SECRET", 'w') as f:
|
||||||
|
f.write(jwk.JWK(generate='oct', size=256).export())
|
||||||
|
os.chmod("/var/lib/gstfsd/SECRET", 0600)
|
||||||
|
|
||||||
|
with open("/var/lib/gstfsd/SECRET") as f:
|
||||||
|
SECRET = jwk.JWK(**json.load(f))
|
||||||
|
|
||||||
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)
|
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@ Django==1.8.11
|
||||||
websockify==0.8.0
|
websockify==0.8.0
|
||||||
gunicorn==19.3.0
|
gunicorn==19.3.0
|
||||||
libvirt-python==1.3.2
|
libvirt-python==1.3.2
|
||||||
|
jwcrypto>=0.2.1
|
||||||
#http://github.com/retspen/retspen.github.io/raw/master/libxml2-python-2.9.1.tar.gz
|
#http://github.com/retspen/retspen.github.io/raw/master/libxml2-python-2.9.1.tar.gz
|
||||||
http://git.gnome.org/browse/libxml2/snapshot/libxml2-2.9.1.tar.gz#egg=libxml2-python&subdirectory=python
|
http://git.gnome.org/browse/libxml2/snapshot/libxml2-2.9.1.tar.gz#egg=libxml2-python&subdirectory=python
|
||||||
|
|
|
||||||
|
|
@ -245,10 +245,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="rootpasswd">
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="rootpasswd">
|
||||||
<p>{% trans "You need shut down your instance and enter a new root password." %}</p>
|
<p>{% trans "You need shut down your instance and enter a new root password." %}</p>
|
||||||
<form class="form-inline" method="post" role="form">{% csrf_token %}
|
<form class="form-inline" method="post" role="form" autocomplete="off">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<input type="text" class="form-control input-lg" name="passwd" placeholder="{% trans "Enter Password" %}" maxlength="24">
|
<input type="text" class="form-control input-lg" name="passwd" placeholder="{% trans "Enter Password" %}" maxlength="24" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% ifequal status 5 %}
|
{% ifequal status 5 %}
|
||||||
|
|
@ -257,6 +257,7 @@
|
||||||
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Reset Root Password" %}</button>
|
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Reset Root Password" %}</button>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
</form>
|
</form>
|
||||||
|
<p>{% trans "An empty password disable the root password." %}</p>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="sshkeys">
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="sshkeys">
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import crypt
|
||||||
from string import letters, digits
|
from string import letters, digits
|
||||||
from random import choice
|
from random import choice
|
||||||
from bisect import insort
|
from bisect import insort
|
||||||
|
from jwcrypto import jws, jwk, jwe
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
@ -288,21 +289,34 @@ def instance(request, compute_id, vname):
|
||||||
|
|
||||||
if 'rootpasswd' in request.POST:
|
if 'rootpasswd' in request.POST:
|
||||||
passwd = request.POST.get('passwd', '')
|
passwd = request.POST.get('passwd', '')
|
||||||
passwd_hash = crypt.crypt(passwd, '$6$kgPoiREy')
|
if passwd:
|
||||||
|
passwd_hash = crypt.crypt(passwd, '$6$%s' % ''.join([choice(letters + digits) for i in xrange(8)]))
|
||||||
|
# if password is empty, disable the root password
|
||||||
|
else:
|
||||||
|
passwd_hash = "*"
|
||||||
data = {'action': 'password', 'passwd': passwd_hash, 'vname': vname}
|
data = {'action': 'password', 'passwd': passwd_hash, 'vname': vname}
|
||||||
|
|
||||||
if conn.get_status() == 5:
|
if conn.get_status() == 5:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
if compute.gstfsd_key:
|
||||||
s.connect((compute.hostname, 16510))
|
key = jwk.JWK(**json.loads(compute.gstfsd_key.strip()))
|
||||||
s.send(json.dumps(data))
|
data = jwe.JWE(json.dumps(data), algs=["A256KW", "A256CBC-HS512"])
|
||||||
result = json.loads(s.recv(1024))
|
data.add_recipient(key, header='{"alg":"A256KW","enc":"A256CBC-HS512"}')
|
||||||
s.close()
|
data = jws.JWS(data.serialize())
|
||||||
msg = _("Reset root password")
|
data.add_signature(key, alg="HS512")
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((compute.hostname, 16510))
|
||||||
|
s.send(data.serialize())
|
||||||
|
result = json.loads(s.recv(4096))
|
||||||
|
s.close()
|
||||||
|
msg = _("Reset root password")
|
||||||
|
addlogmsg(request.user.username, instance.name, msg)
|
||||||
|
|
||||||
if result['return'] == 'success':
|
if result['return'] == 'success':
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
|
else:
|
||||||
|
error_messages.append(result.get('message', msg))
|
||||||
else:
|
else:
|
||||||
|
msg = _("Please import the gstfsd key into this compute. It is in /var/lib/gstfsd/SECRET on %s") % compute.name
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
else:
|
else:
|
||||||
msg = _("Please shutdow down your instance and then try again")
|
msg = _("Please shutdow down your instance and then try again")
|
||||||
|
|
@ -314,17 +328,26 @@ def instance(request, compute_id, vname):
|
||||||
data = {'action': 'publickey', 'key': publickey.keypublic, 'vname': vname}
|
data = {'action': 'publickey', 'key': publickey.keypublic, 'vname': vname}
|
||||||
|
|
||||||
if conn.get_status() == 5:
|
if conn.get_status() == 5:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
if compute.gstfsd_key:
|
||||||
s.connect((compute.hostname, 16510))
|
key = jwk.JWK(**json.loads(compute.gstfsd_key.strip()))
|
||||||
s.send(json.dumps(data))
|
data = jwe.JWE(json.dumps(data), algs=["A256KW", "A256CBC-HS512"])
|
||||||
result = json.loads(s.recv(1024))
|
data.add_recipient(key, header='{"alg":"A256KW","enc":"A256CBC-HS512"}')
|
||||||
s.close()
|
data = jws.JWS(data.serialize())
|
||||||
msg = _("Installed new ssh public key %s" % publickey.keyname)
|
data.add_signature(key, alg="HS512")
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.connect((compute.hostname, 16510))
|
||||||
|
s.send(data.serialize())
|
||||||
|
result = json.loads(s.recv(4096))
|
||||||
|
s.close()
|
||||||
|
msg = _("Installed new ssh public key %s" % publickey.keyname)
|
||||||
|
addlogmsg(request.user.username, instance.name, msg)
|
||||||
|
|
||||||
if result['return'] == 'success':
|
if result['return'] == 'success':
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
|
else:
|
||||||
|
error_messages.append(result.get('message', msg))
|
||||||
else:
|
else:
|
||||||
|
msg = _("Please import the gstfsd key into this compute. It is in /var/lib/gstfsd/SECRET on %s") % compute.name
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
else:
|
else:
|
||||||
msg = _("Please shutdow down your instance and then try again")
|
msg = _("Please shutdow down your instance and then try again")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue