From e9b57bfcf702affbad4a8b7c09f75f71c250ef68 Mon Sep 17 00:00:00 2001 From: kendarorg Date: Wed, 2 Jun 2021 08:46:12 +0200 Subject: [PATCH] LDAP Integration ( https://github.com/retspen/webvirtcloud/issues/243 ) (#443) * Added ldap support * Update * Added logging * Update * Working * Working * Working * Working * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Check for ldap3 existence * Add eol Co-authored-by: Kendar --- Dockerfile | 3 + README.md | 46 +++++++++++- conf/requirements.txt | 1 + webvirtcloud/ldapbackend.py | 113 ++++++++++++++++++++++++++++++ webvirtcloud/settings.py.template | 22 ++++++ 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 webvirtcloud/ldapbackend.py diff --git a/Dockerfile b/Dockerfile index 91f9f12..4582751 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,9 @@ RUN apt-get update -qqy \ nginx \ pkg-config \ gcc \ + libldap2-dev \ + libssl-dev \ + libsasl2-dev \ libsasl2-modules \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* diff --git a/README.md b/README.md index 54f826f..d0dc575 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)])) ### Install WebVirtCloud panel (Ubuntu 18.04+ LTS) ```bash -sudo apt-get -y install git virtualenv python3-virtualenv python3-dev python3-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs +sudo apt-get -y install git virtualenv python3-virtualenv python3-dev python3-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs libsasl2-dev libldap2-dev libssl-dev git clone https://github.com/retspen/webvirtcloud cd webvirtcloud cp webvirtcloud/settings.py.template webvirtcloud/settings.py @@ -97,7 +97,7 @@ Go to http://serverip and you should see the login screen. ```bash sudo yum -y install epel-release -sudo yum -y install python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml git python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs +sudo yum -y install python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml git python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs libsasl2-dev libldap2-dev libssl-dev ``` #### Creating directories and cloning repo @@ -380,6 +380,48 @@ Run tests python manage.py test ``` +## LDAP Configuration + +The example settings are based on an OpenLDAP server with groups defined as "cn" of class "groupOfUniqueNames" + +Enable LDAP + +```bash +sudo sed -i "s/LDAP_ENABLED = False/LDAP_ENABLED = True/g"" /srv/webvirtcloud/webvirtcloud/settings.py +``` + +Set the LDAP server name and root DN + +```bash +sudo sed -i "s/LDAP_URL = ''/LDAP_URL = 'myldap.server.com'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +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 + +```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 +``` + +Set the attribute that will be used to find the username, i usually use the cn + +```bash +sudo sed -i "s/LDAP_USER_UID_PREFIX = ''/LDAP_USER_UID_PREFIX = 'cn'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +``` + +You can now create the filters to retrieve the users for the various group. This will be used during the user creation only + +```bash +sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_ADMINS = ''/LDAP_SEARCH_GROUP_FILTER_ADMINS = 'memberOf=cn=admins,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_STAFF = ''/LDAP_SEARCH_GROUP_FILTER_STAFF = 'memberOf=cn=staff,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +sudo sed -i "s/LDAP_SEARCH_GROUP_FILTER_USERS = ''/LDAP_SEARCH_GROUP_FILTER_USERS = 'memberOf=cn=users,dc=kendar,dc=org'/g"" /srv/webvirtcloud/webvirtcloud/settings.py +``` + +Now when you login with an LDAP user it will be assigned the rights defined. The user will be authenticated then with ldap and authorized through the WebVirtCloud permissions. + +If you'd like to move a user from ldap to WebVirtCloud, just change its password from the UI and (eventually) remove from the group in ldap + ## Screenshots Instance Detail: diff --git a/conf/requirements.txt b/conf/requirements.txt index d9dd802..8bcb55d 100644 --- a/conf/requirements.txt +++ b/conf/requirements.txt @@ -12,3 +12,4 @@ qrcode==6.1 rwlock==0.0.7 websockify==0.9.0 zipp==3.4.0 +ldap3==2.9.0 diff --git a/webvirtcloud/ldapbackend.py b/webvirtcloud/ldapbackend.py new file mode 100644 index 0000000..2d38e8f --- /dev/null +++ b/webvirtcloud/ldapbackend.py @@ -0,0 +1,113 @@ +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.models import User +from django.conf import settings +from accounts.models import UserAttributes, UserInstance, UserSSHKey +from django.contrib.auth.models import Permission +from logs.models import Logs +import uuid + +try: + from ldap3 import Server, Connection, ALL + #/srv/webvirtcloud/ldap/ldapbackend.py + class LdapAuthenticationBackend(ModelBackend): + + def get_LDAP_user(self, username, password, filterString): + print('get_LDAP_user') + try: + server = Server(settings.LDAP_URL, port=settings.LDAP_PORT, + use_ssl=settings.USE_SSL,get_info=ALL) + connection = Connection(server, + settings.LDAP_MASTER_DN, + settings.LDAP_MASTER_PW, auto_bind=True) + + connection.search(settings.LDAP_ROOT_DN, + '(&({attr}={login})({filter}))'.format( + attr=settings.LDAP_USER_UID_PREFIX, + login=username, + filter=filterString), attributes=['*']) + + if len(connection.response) == 0: + print('get_LDAP_user-no response') + return None + specificUser = connection.response[0] + userDn = str(specificUser.get('raw_dn'),'utf-8') + with Connection(server, + userDn, + password) as con: + return username + return None + + def authenticate(self, request, username=None, password=None, **kwargs): + if not settings.LDAP_ENABLED: + return None + print("authenticate_ldap") + # Get the user information from the LDAP if he can be authenticated + isAdmin = False + isStaff = 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: + return None + else: + isStaff = True + else: + isAdmin = True + isStaff = True + + try: + user = User.objects.get(username=username) + attributes = UserAttributes.objects.get(user=user) + # TODO VERIFY + except User.DoesNotExist: + print("authenticate-create new user") + user = User(username=username) + user.is_active = True + user.is_staff = isStaff + user.is_superuser = isAdmin + user.set_password(uuid.uuid4().hex) + user.save() + maxInstances = 1 + maxCpus = 1 + maxMemory = 128 + maxDiskSize = 1 + if isStaff: + maxMemory = 2048 + maxDiskSize = 20 + permission = Permission.objects.get(codename='clone_instances') + user.user_permissions.add(permission) + if isAdmin: + maxInstances = -1 + maxCpus = -1 + maxMemory = -1 + maxDiskSize = -1 + permission = Permission.objects.get(codename='clone_instances') + user.user_permissions.add(permission) + user.save() + UserAttributes.objects.create( + user=user, + max_instances=maxInstances, + max_cpus=maxCpus, + max_memory=maxMemory, + max_disk_size=maxDiskSize, + ) + user.save() + + print("authenticate-user created") + return user + + def get_user(self, user_id): + if not settings.LDAP_ENABLED: + return None + print("get_user_ldap") + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + print("get_user-user not found") + return None +except: + class LdapAuthenticationBackend(ModelBackend): + def authenticate(self, request, username=None, password=None, **kwargs): + return None + def get_user(self, user_id): + return None diff --git a/webvirtcloud/settings.py.template b/webvirtcloud/settings.py.template index 37ca195..4dab092 100644 --- a/webvirtcloud/settings.py.template +++ b/webvirtcloud/settings.py.template @@ -95,6 +95,7 @@ DATABASES = { AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", + "webvirtcloud.ldapbackend.LdapAuthenticationBackend", ] LOGIN_URL = "/accounts/login/" @@ -212,3 +213,24 @@ SHOW_PROFILE_EDIT_PASSWORD = True OTP_ENABLED = False LOGIN_REQUIRED_IGNORE_VIEW_NAMES = ["accounts:email_otp"] + +LDAP_ENABLED = False +LDAP_URL = '' +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 = '' +## The root dn (e.g. dc=kendar,dc=org) +LDAP_ROOT_DN = '' +## Queries to identify the users, i use groupOfUniqueNames on openldap + +## e.g. memberOf=cn=admins,cn=staff,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 +LDAP_SEARCH_GROUP_FILTER_STAFF = '' +## e.g. memberOf=cn=webvirtcloud,ou=groups,dc=kendar,dc=org +LDAP_SEARCH_GROUP_FILTER_USERS = '' + +## The user name prefix to identify the user name (e.g. cn) +LDAP_USER_UID_PREFIX = ''