From c817d3e61a2d5d5326ad2bf11975d7dbdf2bad88 Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Mon, 13 Jun 2022 09:58:46 +0200 Subject: [PATCH 1/6] Add permission can view instances --- console/views.py | 2 +- instances/models.py | 1 + instances/templates/allinstances.html | 2 +- instances/views.py | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/console/views.py b/console/views.py index 7a6691b..81f5465 100644 --- a/console/views.py +++ b/console/views.py @@ -41,7 +41,7 @@ def console(request): host = int(temptoken[0]) uuid = temptoken[1] - if not request.user.is_superuser: + if not request.user.is_superuser and not request.user.has_perm("instances.view_instances"): try: userInstance = UserInstance.objects.get( instance__compute_id=host, instance__uuid=uuid, user__id=request.user.id diff --git a/instances/models.py b/instances/models.py index 68d2ecc..83aa54f 100644 --- a/instances/models.py +++ b/instances/models.py @@ -214,6 +214,7 @@ class PermissionSet(models.Model): permissions = [ ('clone_instances', 'Can clone instances'), ('passwordless_console', _('Can access console without password')), + ('view_instances', 'Can view instances'), ] managed = False diff --git a/instances/templates/allinstances.html b/instances/templates/allinstances.html index e69bc22..095d99c 100644 --- a/instances/templates/allinstances.html +++ b/instances/templates/allinstances.html @@ -25,7 +25,7 @@ {% endif %} {% endfor %}
- {% if app_settings.VIEW_INSTANCES_LIST_STYLE == 'grouped' and request.user.is_superuser %} + {% if app_settings.VIEW_INSTANCES_LIST_STYLE == 'grouped' and request.user.is_superuser or 'instances.view_instances' in perms %} {% include 'allinstances_index_grouped.html' %} {% else %} {% include 'allinstances_index_nongrouped.html' %} diff --git a/instances/views.py b/instances/views.py index aed5b28..6436684 100644 --- a/instances/views.py +++ b/instances/views.py @@ -46,7 +46,7 @@ def index(request): for compute in computes: utils.refr(compute) - if request.user.is_superuser: + if request.user.is_superuser or request.user.has_perm("instances.view_instances"): instances = Instance.objects.all().prefetch_related("userinstance_set") else: instances = Instance.objects.filter(userinstance__user=request.user).prefetch_related("userinstance_set") @@ -237,7 +237,7 @@ def get_instance(user, pk): instance = get_object_or_404(Instance, pk=pk) user_instances = user.userinstance_set.all().values_list("instance", flat=True) - if user.is_superuser or instance.id in user_instances: + if user.is_superuser or user.has_perm("instances.view_instances") or instance.id in user_instances: return instance else: raise Http404() From de2dce7573d8b513ee433982335aa94ab751ae4d Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Tue, 14 Jun 2022 15:10:33 +0200 Subject: [PATCH 2/6] Added Technicians group --- .../0003_create_group_technicians.py | 15 ++++++++ webvirtcloud/ldapbackend.py | 35 ++++++++++++++----- webvirtcloud/settings.py.template | 6 ++-- 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 admin/migrations/0003_create_group_technicians.py diff --git a/admin/migrations/0003_create_group_technicians.py b/admin/migrations/0003_create_group_technicians.py new file mode 100644 index 0000000..d5b1cae --- /dev/null +++ b/admin/migrations/0003_create_group_technicians.py @@ -0,0 +1,15 @@ +from django.db import models, migrations + +def apply_migration(apps, schema_editor): + Group = apps.get_model('auth', 'Group') + Group.objects.create(name='Technicians') + +class Migration(migrations.Migration): + + dependencies = [ + ('admin', '0002_auto_20200609_0830'), + ] + + operations = [ + migrations.RunPython(apply_migration) + ] diff --git a/webvirtcloud/ldapbackend.py b/webvirtcloud/ldapbackend.py index c81707c..7af45f8 100644 --- a/webvirtcloud/ldapbackend.py +++ b/webvirtcloud/ldapbackend.py @@ -1,5 +1,5 @@ from django.contrib.auth.backends import ModelBackend -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group from django.conf import settings from accounts.models import UserAttributes, UserInstance, UserSSHKey from django.contrib.auth.models import Permission @@ -44,21 +44,36 @@ try: # Get the user information from the LDAP if he can be authenticated isAdmin = False isStaff = False + isTechnician = False if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_ADMINS) is None: - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_STAFF) is None: - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_USERS) is None: - print("User does not belong to any search group. Check LDAP_SEARCH_GROUP_FILTER in settings.") - return None - else: - isStaff = True + if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_STAFF) is None: + if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_TECHNICIANS) is None: + if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_USERS) is None: + print("User does not belong to any search group. Check LDAP_SEARCH_GROUP_FILTER in settings.") + return None + else: + isTechnician = True + else: + isStaff = True else: - isAdmin = True - isStaff = True + isAdmin = True + isStaff = True + + techniciansGroup = Group.objects.get(name='Technicians') try: user = User.objects.get(username=username) attributes = UserAttributes.objects.get(user=user) + user.is_staff = isStaff + user.is_superuser = isAdmin + if isTechnician is False and user.groups.filter(name='Technicians').exists(): + user.groups.remove(techniciansGroup) + elif isTechnician is True and user.groups.filter(name='Technicians').exists() is False: + user.groups.add(techniciansGroup) + else: + print("The user is already in the Technicians group") + user.save() # TODO VERIFY except User.DoesNotExist: print("authenticate-create new user: {}".format(username)) @@ -68,6 +83,8 @@ try: user.is_superuser = isAdmin user.set_password(uuid.uuid4().hex) user.save() + if isTechnician is True: + user.groups.add(techniciansGroup) maxInstances = 1 maxCpus = 1 maxMemory = 128 diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 75b6131..b16bd5e 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -269,10 +269,12 @@ LDAP_ROOT_DN = '' ## Queries to identify the users, i use groupOfUniqueNames on openldap ### PLEASE BE SURE memberOf overlay is activated on slapd -## e.g. memberOf=cn=admins,cn=staff,cn=webvirtcloud,ou=groups,dc=kendar,dc=org +## e.g. memberOf=cn=admins,cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org LDAP_SEARCH_GROUP_FILTER_ADMINS = '' -## e.g. memberOf=cn=staff,cn=webvirtcloud,ou=groups,dc=kendar,dc=org +## e.g. memberOf=cn=staff,cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org LDAP_SEARCH_GROUP_FILTER_STAFF = '' +## e.g. memberOf=cn=technicians,cn=webvirtcloud,ou=groups,dc=kendar,dc=org +LDAP_SEARCH_GROUP_FILTER_TECHNICIANS = '' ## e.g. memberOf=cn=webvirtcloud,ou=groups,dc=kendar,dc=org LDAP_SEARCH_GROUP_FILTER_USERS = '' From 32eb91f53f173194338421cd6dc11660de01becb Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Tue, 14 Jun 2022 15:41:38 +0200 Subject: [PATCH 3/6] Automatic input first name last name and email with ldap --- webvirtcloud/ldapbackend.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/webvirtcloud/ldapbackend.py b/webvirtcloud/ldapbackend.py index 7af45f8..15c0834 100644 --- a/webvirtcloud/ldapbackend.py +++ b/webvirtcloud/ldapbackend.py @@ -30,8 +30,11 @@ try: return None specificUser = connection.response[0] userDn = str(specificUser.get('raw_dn'),'utf-8') + userGivenName = connection.entries[0].givenName + userSn = connection.entries[0].sn + userMail = connection.entries[0].mail with Connection(server, userDn, password) as con: - return username + return username, userGivenName, userSn, userMail except Exception as e: print("LDAP Exception: {}".format(e)) return None @@ -46,10 +49,14 @@ try: isStaff = False isTechnician = False - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_ADMINS) is None: - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_STAFF) is None: - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_TECHNICIANS) is None: - if self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_USERS) is None: + requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_ADMINS) + if requeteLdap is None: + requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_STAFF) + if requeteLdap is None: + requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_TECHNICIANS) + if requeteLdap is None: + requeteLdap = self.get_LDAP_user(username, password, settings.LDAP_SEARCH_GROUP_FILTER_USERS) + if requeteLdap is None: print("User does not belong to any search group. Check LDAP_SEARCH_GROUP_FILTER in settings.") return None else: @@ -78,6 +85,9 @@ try: except User.DoesNotExist: print("authenticate-create new user: {}".format(username)) user = User(username=username) + user.first_name = requeteLdap[1] + user.last_name = requeteLdap[2] + user.email = requeteLdap[3] user.is_active = True user.is_staff = isStaff user.is_superuser = isAdmin From 02b02d3321c98fb0a1e9cc3dbe6af293f78cb219 Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Wed, 15 Jun 2022 13:51:56 +0200 Subject: [PATCH 4/6] Added DRBD status --- instances/models.py | 1 + .../templates/allinstances_index_grouped.html | 5 +++ .../allinstances_index_nongrouped.html | 4 ++ instances/views.py | 43 +++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/instances/models.py b/instances/models.py index 83aa54f..5d4f021 100644 --- a/instances/models.py +++ b/instances/models.py @@ -28,6 +28,7 @@ class Instance(models.Model): uuid = models.CharField(_('uuid'), max_length=36, db_index=True) is_template = models.BooleanField(_('is template'), default=False) created = models.DateTimeField(_('created'), auto_now_add=True) + drbd = models.CharField(_('drbd'), max_length=24, default="None") objects = InstanceManager() diff --git a/instances/templates/allinstances_index_grouped.html b/instances/templates/allinstances_index_grouped.html index ef5e119..a6c3bc6 100644 --- a/instances/templates/allinstances_index_grouped.html +++ b/instances/templates/allinstances_index_grouped.html @@ -7,6 +7,7 @@ {% trans "Name" %}
{% trans "Description" %} {% trans "User"%} {% trans "Status" %} + {% trans "Role/Disk" %} {% trans "VCPU" %} {% trans "Memory" %} {% trans "Actions" %} & {% trans "Mem Usage" %} @@ -27,6 +28,7 @@ {% trans "Connected" %} + {{ compute.cpu_count }} {{ compute.ram_size|filesizeformat }} @@ -62,6 +64,9 @@ {% trans "Suspended" %} {% endif %} + + {% if instance.drbd == "Primary/OK" or instance.drbd == "Secondary/OK" %}{% else %}{% endif %}{{ instance.drbd }} + {{ instance.proxy.instance.info.3 }} {{ instance.cur_memory }} MB diff --git a/instances/templates/allinstances_index_nongrouped.html b/instances/templates/allinstances_index_nongrouped.html index e32e29d..114b6b8 100644 --- a/instances/templates/allinstances_index_nongrouped.html +++ b/instances/templates/allinstances_index_nongrouped.html @@ -7,6 +7,7 @@ {% trans 'Host' %}
{% trans 'User' %} {% endif %} {% trans 'Status' %} + {% trans 'Role/Disk' %} {% trans 'VCPU' %} {% trans 'Memory' %} {% trans 'Actions' %} @@ -44,6 +45,9 @@ {% if instance.proxy.instance.info.0 == 3 %}{% trans "Suspended" %}{% endif %} + + {% if instance.drbd == "Primary/OK" or instance.drbd == "Secondary/OK" %}{% else %}{% endif %}{{ instance.drbd }} + {{ instance.proxy.instance.info.3 }} {{ instance.cur_memory }} MB diff --git a/instances/views.py b/instances/views.py index 6436684..b227046 100644 --- a/instances/views.py +++ b/instances/views.py @@ -4,6 +4,7 @@ import os import re import socket import time +import subprocess from bisect import insort from accounts.models import UserInstance, UserSSHKey @@ -127,6 +128,9 @@ def instance(request, pk): storages_host = sorted(instance.proxy.get_storages(True)) net_models_host = instance.proxy.get_network_models() + instance.drbd = drbd_status(request, pk) + instance.save() + return render(request, "instance.html", locals()) @@ -134,6 +138,45 @@ def status(request, pk): instance = get_instance(request.user, pk) return JsonResponse({"status": instance.proxy.get_status()}) +def drbd_status(request, pk): + instance = get_instance(request.user, pk) + result = "None DRBD" + + if instance.compute.type == 2: + conn = instance.compute.login + "@" + instance.compute.hostname + remoteDrbdStatus = subprocess.run(["ssh", conn, "sudo", "drbdadm", "status", "&&", "exit"], stdout=subprocess.PIPE, text=True) + + if remoteDrbdStatus.stdout: + try: + instanceFindDrbd = re.compile(instance.name + '[_]*[A-Z]* role:(.+?)\n disk:(.+?)\n', re.IGNORECASE) + instanceDrbd = instanceFindDrbd.findall(remoteDrbdStatus.stdout) + + primaryCount = 0 + secondaryCount = 0 + statusDisk = "OK" + + for disk in instanceDrbd: + if disk[0] == "Primary": + primaryCount = primaryCount + 1 + elif disk[0] == "Secondary": + secondaryCount = secondaryCount + 1 + if disk[1] != "UpToDate": + statusDisk = "NOK" + + if primaryCount > 0 and secondaryCount > 0: + statusRole = "NOK" + else: + if primaryCount > secondaryCount: + statusRole = "Primary" + else: + statusRole = "Secondary" + + result = statusRole + "/" + statusDisk + + except: + print("Error to get drbd role and status") + + return result def stats(request, pk): instance = get_instance(request.user, pk) From e26a114c44c2008351dd9c5198d5795c4674265d Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Wed, 15 Jun 2022 17:08:16 +0200 Subject: [PATCH 5/6] Added ldap password encryption --- README.md | 14 ++++++++++++-- webvirtcloud/.dec_ldap_pwd.sh | 18 ++++++++++++++++++ webvirtcloud/settings.py.template | 4 +++- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100755 webvirtcloud/.dec_ldap_pwd.sh diff --git a/README.md b/README.md index a829d15..f917721 100644 --- a/README.md +++ b/README.md @@ -397,11 +397,21 @@ sudo sed -i "s/LDAP_URL = ''/LDAP_URL = 'myldap.server.com'/g"" /srv/webvirtclou sudo sed -i "s/LDAP_ROOT_DN = ''/LDAP_ROOT_DN = 'dc=server,dc=com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py ``` -Set the user that has browse access to LDAP and its password +Set the passphrase to decrypt the password +```bash +sudo sed -i "s/pass:MYPASSPHRASE/pass:MYTRUEPASSPHRASE/g" /srv/webvirtcloud/webvirtcloud/.dec_ldap_pwd.sh +``` + +Encrypt the password +```bash +echo MYPASSWORD | openssl enc -pbkdf2 -salt -pass pass:MYTRUEPASSPHRASE | base64 +``` + +Set the user that has browse access to LDAP and its password encrypted ```bash sudo sed -i "s/LDAP_MASTER_DN = ''/LDAP_MASTER_DN = 'cn=admin,ou=users,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py -sudo sed -i "s/LDAP_MASTER_PW = ''/LDAP_MASTER_PW = 'password'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +sudo sed -i "s/LDAP_MASTER_PW_ENC = ''/LDAP_MASTER_PW_ENC = 'MYPASSWORDENCRYPTED'/g"" /srv/webvirtcloud/webvirtcloud/settings.py ``` Set the attribute that will be used to find the username, i usually use the cn diff --git a/webvirtcloud/.dec_ldap_pwd.sh b/webvirtcloud/.dec_ldap_pwd.sh new file mode 100755 index 0000000..2cda920 --- /dev/null +++ b/webvirtcloud/.dec_ldap_pwd.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +##### + +# + +# LDAP PASSWORD DECRYPTION SCRIPT + +# + +# + +##### + +ENC_PASSWD=$1 + +echo $(echo $ENC_PASSWD | base64 -d | openssl enc -pbkdf2 -salt -d -pass pass:MYPASSPHRASE ) + diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index b16bd5e..984f2a1 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -3,6 +3,7 @@ Django settings for webvirtcloud project. """ +import subprocess from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -263,7 +264,8 @@ LDAP_PORT = 389 USE_SSL = False ## The user with search rights on ldap. (e.g cn=admin,dc=kendar,dc=org) LDAP_MASTER_DN = '' -LDAP_MASTER_PW = '' +LDAP_MASTER_PW_ENC = '' +LDAP_MASTER_PW = subprocess.Popen(["bash", str(BASE_DIR) + "/webvirtcloud/.dec_ldap_pwd.sh", LDAP_MASTER_PW_ENC],stdout=subprocess.PIPE, text=True).stdout.read().strip('\n') ## The root dn (e.g. dc=kendar,dc=org) LDAP_ROOT_DN = '' ## Queries to identify the users, i use groupOfUniqueNames on openldap From 052610dd215b761399577b6d555ecdc5b90a0536 Mon Sep 17 00:00:00 2001 From: Info-IIG Date: Thu, 16 Jun 2022 14:55:08 +0200 Subject: [PATCH 6/6] Added permission can snapshot instances --- instances/models.py | 1 + instances/templates/instance.html | 2 +- instances/views.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/instances/models.py b/instances/models.py index 5d4f021..147b099 100644 --- a/instances/models.py +++ b/instances/models.py @@ -216,6 +216,7 @@ class PermissionSet(models.Model): ('clone_instances', 'Can clone instances'), ('passwordless_console', _('Can access console without password')), ('view_instances', 'Can view instances'), + ('snapshot_instances', 'Can snapshot instances'), ] managed = False diff --git a/instances/templates/instance.html b/instances/templates/instance.html index a1b88b4..cb18556 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -93,7 +93,7 @@ {% trans "Resize" %} - {% if allow_admin_or_not_template %} + {% if allow_admin_or_not_template and 'instances.snapshot_instances' in perms %}