From 03d705639a6e0a18874dd6708bfe43f39acef47a Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:00:51 +0300 Subject: [PATCH 01/13] Add Linux Active Directory OS manager --- .../uds/osmanagers/LinuxOsManager/__init__.py | 1 + .../LinuxOsManager/linux_ad_osmanager.py | 255 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py diff --git a/server/src/uds/osmanagers/LinuxOsManager/__init__.py b/server/src/uds/osmanagers/LinuxOsManager/__init__.py index 5e367fc6e..2d264027d 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/__init__.py +++ b/server/src/uds/osmanagers/LinuxOsManager/__init__.py @@ -41,6 +41,7 @@ from uds.core import VERSION from .linux_osmanager import LinuxOsManager from .linux_randompass_osmanager import LinuxRandomPassManager +from .linux_ad_osmanager import LinuxOsADManager downloadsManager().registerDownloadable( 'udsactor_{version}_all.deb'.format(version=VERSION), diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py new file mode 100644 index 000000000..8a41e84d4 --- /dev/null +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012-2023 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +@author: Alexander Burmatov, thatman at altlinux dot org +""" +import codecs +import logging +import typing + +from django.utils.translation import gettext_noop as _, gettext_lazy + +from uds.core.ui import gui +from uds.core import exceptions +from uds.core.managers import cryptoManager + +from .linux_osmanager import LinuxOsManager + +if typing.TYPE_CHECKING: + from uds.models.user_service import UserService + + +logger = logging.getLogger(__name__) + + +class LinuxOsADManager(LinuxOsManager): + typeName = _('Linux OS Active Directory Manager') + typeType = 'LinuxADManager' + typeDescription = _('Os Manager to control Linux virtual machines with active directory') + iconFile = 'losmanager.png' + + domain = gui.TextField( + length=64, + label=_('Domain'), + order=1, + tooltip=_( + 'Domain to join machines to (use FQDN form, Netbios name not supported for most operations)' + ), + required=True, + ) + account = gui.TextField( + length=64, + label=_('Account'), + order=2, + tooltip=_('Account with rights to add machines to domain'), + required=True, + ) + password = gui.PasswordField( + length=64, + label=_('Password'), + order=3, + tooltip=_('Password of the account'), + required=True, + ) + ou = gui.TextField( + length=256, + label=_('OU'), + order=4, + tooltip=_( + 'Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local' + ), + ) + clientSoftware = gui.ChoiceField( + label=_('Client software'), + order=5, + tooltip=_('Use specific client software'), + values=[ + {'id': 'automatically', 'text': gettext_lazy('Automatically')}, + {'id': 'sssd', 'text': gettext_lazy('SSSD')}, + {'id': 'winbind', 'text': gettext_lazy('Winbind')}, + ], + tab=_('Advanced'), + defvalue='automatically', + ) + membershipSoftware = gui.ChoiceField( + label=_('Membership software'), + order=6, + tooltip=_('Use specific membership software'), + values=[ + {'id': 'automatically', 'text': gettext_lazy('Automatically')}, + {'id': 'samba', 'text': gettext_lazy('Samba')}, + {'id': 'adcli', 'text': gettext_lazy('adcli')}, + ], + tab=_('Advanced'), + defvalue='automatically', + ) + removeOnExit = gui.CheckBoxField( + label=_('Machine clean'), + order=7, + tooltip=_( + 'If checked, UDS will try to remove the machine from the domain USING the provided credentials' + ), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + ssl = gui.CheckBoxField( + label=_('Use SSL'), + order=8, + tooltip=_('If checked, a ssl connection to Active Directory will be used'), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + automaticIdMapping = gui.CheckBoxField( + label=_('Automatic ID mapping'), + order=9, + tooltip=_('If checked, automatic ID mapping'), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + + # Inherits base "onLogout" + onLogout = LinuxOsManager.onLogout + idle = LinuxOsManager.idle + deadLine = LinuxOsManager.deadLine + + _domain: str + _ou: str + _account: str + _password: str + _removeOnExit: str + _ssl: str + _automaticIdMapping: str + _clientSoftware: str + _serverSoftware: str + _membershipSoftware: str + + def __init__(self, environment, values): + super(LinuxOsADManager, self).__init__(environment, values) + if values: + if values['domain'] == '': + raise exceptions.ValidationError(_('Must provide a domain!')) + if values['account'] == '': + raise exceptions.ValidationError( + _('Must provide an account to add machines to domain!') + ) + if values['password'] == '': + raise exceptions.ValidationError( + _('Must provide a password for the account!') + ) + self._domain = values['domain'] + self._account = values['account'] + self._password = values['password'] + self._ou = values['ou'].strip() + self._clientSoftware = values['clientSoftware'] + self._serverSoftware = 'active-directory' + self._membershipSoftware = values['membershipSoftware'] + self._removeOnExit = 'y' if values['removeOnExit'] else 'n' + self._ssl = 'y' if values['ssl'] else 'n' + self._automaticIdMapping = 'y' if values['automaticIdMapping'] else 'n' + else: + self._domain = '' + self._account = '' + self._password = '' # nosec: no encoded password + self._ou = '' + self._clientSoftware = '' + self._serverSoftware = 'active-directory' + self._membershipSoftware = '' + self._removeOnExit = 'n' + self._ssl = 'n' + self._automaticIdMapping = 'n' + + def actorData( + self, userService: 'UserService' + ) -> typing.MutableMapping[str, typing.Any]: + return { + 'action': 'rename_ad', + 'name': userService.getName(), + 'ad': self._domain, + 'username': self._account, + 'password': self._password, + 'ou': self._ou, + 'clientSoftware': self._clientSoftware, + 'serverSoftware': self._serverSoftware, + 'membershipSoftware': self._membershipSoftware, + 'ssl': self._ssl, + 'automaticIdMapping': self._automaticIdMapping, + 'isPersistent': 'y' if self.isPersistent() else 'n', + } + + def marshal(self) -> bytes: + """ + Serializes the os manager data so we can store it in database + """ + base = super().marshal() + return '\t'.join( + [ + 'v1', + self._domain, + self._account, + cryptoManager().encrypt(self._password), + self._ou, + self._clientSoftware, + self._serverSoftware, + self._membershipSoftware, + self._removeOnExit, + self._ssl, + self._automaticIdMapping, + codecs.encode(base, 'hex').decode(), + ] + ).encode('utf8') + + def unmarshal(self, data: bytes) -> None: + values = data.decode('utf8').split('\t') + if values[0] in ('v1'): + self._domain = values[1] + self._account = values[2] + self._password = cryptoManager().decrypt(values[3]) + self._ou = values[4] + self._clientSoftware = values[5] + self._serverSoftware = values[6] + self._membershipSoftware = values[7] + self._removeOnExit = values[8] + self._ssl = values[9] + self._automaticIdMapping = values[10] + super().unmarshal(codecs.decode(values[11].encode(), 'hex')) + + def valuesDict(self) -> gui.ValuesDictType: + dct = super().valuesDict() + dct['domain'] = self._domain + dct['account'] = self._account + dct['password'] = self._password + dct['ou'] = self._ou + dct['clientSoftware'] = self._clientSoftware + dct['serverSoftware'] = self._serverSoftware + dct['membershipSoftware'] = self._membershipSoftware + dct['removeOnExit'] = self._removeOnExit == 'y' + dct['ssl'] = self._ssl == 'y' + dct['automaticIdMapping'] = self._automaticIdMapping == 'y' + return dct \ No newline at end of file From 20fdd56ca2c30054551666bf57b27bf34918c2bc Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:03:21 +0300 Subject: [PATCH 02/13] Add Linux FreeIPA OS manager --- .../uds/osmanagers/LinuxOsManager/__init__.py | 1 + .../LinuxOsManager/linux_freeipa_osmanager.py | 240 ++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py diff --git a/server/src/uds/osmanagers/LinuxOsManager/__init__.py b/server/src/uds/osmanagers/LinuxOsManager/__init__.py index 2d264027d..35b666a4e 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/__init__.py +++ b/server/src/uds/osmanagers/LinuxOsManager/__init__.py @@ -42,6 +42,7 @@ from uds.core import VERSION from .linux_osmanager import LinuxOsManager from .linux_randompass_osmanager import LinuxRandomPassManager from .linux_ad_osmanager import LinuxOsADManager +from .linux_freeipa_osmanager import LinuxOsFreeIPAManager downloadsManager().registerDownloadable( 'udsactor_{version}_all.deb'.format(version=VERSION), diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py new file mode 100644 index 000000000..fc6e9e569 --- /dev/null +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012-2023 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +@author: Alexander Burmatov, thatman at altlinux dot org +""" +import codecs +import logging +import typing + +from django.utils.translation import gettext_noop as _, gettext_lazy + +from uds.core.ui import gui +from uds.core import exceptions +from uds.core.managers import cryptoManager + +from .linux_osmanager import LinuxOsManager + +if typing.TYPE_CHECKING: + from uds.models.user_service import UserService + + +logger = logging.getLogger(__name__) + + +class LinuxOsFreeIPAManager(LinuxOsManager): + typeName = _('Linux OS FreeIPA Manager') + typeType = 'LinuxFreeIPAManager' + typeDescription = _('Os Manager to control Linux virtual machines with FreeIPA') + iconFile = 'losmanager.png' + + domain = gui.TextField( + length=64, + label=_('Domain'), + order=1, + tooltip=_( + 'Domain to join machines to (use FQDN form, Netbios name not supported for most operations)' + ), + required=True, + ) + account = gui.TextField( + length=64, + label=_('Account'), + order=2, + tooltip=_('Account with rights to add machines to domain'), + required=True, + ) + password = gui.PasswordField( + length=64, + label=_('Password'), + order=3, + tooltip=_('Password of the account'), + required=True, + ) + clientSoftware = gui.ChoiceField( + label=_('Client software'), + order=4, + tooltip=_('Use specific client software'), + values=[ + {'id': 'automatically', 'text': gettext_lazy('Automatically')}, + {'id': 'sssd', 'text': gettext_lazy('SSSD')}, + {'id': 'winbind', 'text': gettext_lazy('Winbind')}, + ], + tab=_('Advanced'), + defvalue='automatically', + ) + membershipSoftware = gui.ChoiceField( + label=_('Membership software'), + order=5, + tooltip=_('Use specific membership software'), + values=[ + {'id': 'automatically', 'text': gettext_lazy('Automatically')}, + {'id': 'samba', 'text': gettext_lazy('Samba')}, + {'id': 'adcli', 'text': gettext_lazy('adcli')}, + ], + tab=_('Advanced'), + defvalue='automatically', + ) + removeOnExit = gui.CheckBoxField( + label=_('Machine clean'), + order=6, + tooltip=_( + 'If checked, UDS will try to remove the machine from the domain USING the provided credentials' + ), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + ssl = gui.CheckBoxField( + label=_('Use SSL'), + order=7, + tooltip=_('If checked, a ssl connection to Active Directory will be used'), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + automaticIdMapping = gui.CheckBoxField( + label=_('Automatic ID mapping'), + order=8, + tooltip=_('If checked, automatic ID mapping'), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + + # Inherits base "onLogout" + onLogout = LinuxOsManager.onLogout + idle = LinuxOsManager.idle + deadLine = LinuxOsManager.deadLine + + _domain: str + _account: str + _password: str + _removeOnExit: str + _ssl: str + _automaticIdMapping: str + _clientSoftware: str + _serverSoftware: str + _membershipSoftware: str + + def __init__(self, environment, values): + super(LinuxOsFreeIPAManager, self).__init__(environment, values) + if values: + if values['domain'] == '': + raise exceptions.ValidationError(_('Must provide a domain!')) + if values['account'] == '': + raise exceptions.ValidationError( + _('Must provide an account to add machines to domain!') + ) + if values['password'] == '': + raise exceptions.ValidationError( + _('Must provide a password for the account!') + ) + self._domain = values['domain'] + self._account = values['account'] + self._password = values['password'] + self._clientSoftware = values['clientSoftware'] + self._serverSoftware = 'ipa' + self._membershipSoftware = values['membershipSoftware'] + self._removeOnExit = 'y' if values['removeOnExit'] else 'n' + self._ssl = 'y' if values['ssl'] else 'n' + self._automaticIdMapping = 'y' if values['automaticIdMapping'] else 'n' + else: + self._domain = '' + self._account = '' + self._password = '' # nosec: no encoded password + self._clientSoftware = '' + self._serverSoftware = 'ipa' + self._membershipSoftware = '' + self._removeOnExit = 'n' + self._ssl = 'n' + self._automaticIdMapping = 'n' + + def actorData( + self, userService: 'UserService' + ) -> typing.MutableMapping[str, typing.Any]: + return { + 'action': 'rename_ad', + 'name': userService.getName(), + 'ad': self._domain, + 'username': self._account, + 'password': self._password, + 'clientSoftware': self._clientSoftware, + 'serverSoftware': self._serverSoftware, + 'membershipSoftware': self._membershipSoftware, + 'ssl': self._ssl, + 'automaticIdMapping': self._automaticIdMapping, + 'isPersistent': 'y' if self.isPersistent() else 'n', + } + + def marshal(self) -> bytes: + """ + Serializes the os manager data so we can store it in database + """ + base = super().marshal() + return '\t'.join( + [ + 'v1', + self._domain, + self._account, + cryptoManager().encrypt(self._password), + self._clientSoftware, + self._serverSoftware, + self._membershipSoftware, + self._removeOnExit, + self._ssl, + self._automaticIdMapping, + codecs.encode(base, 'hex').decode(), + ] + ).encode('utf8') + + def unmarshal(self, data: bytes) -> None: + values = data.decode('utf8').split('\t') + if values[0] in ('v1'): + self._domain = values[1] + self._account = values[2] + self._password = cryptoManager().decrypt(values[3]) + self._clientSoftware = values[4] + self._serverSoftware = values[5] + self._membershipSoftware = values[6] + self._removeOnExit = values[7] + self._ssl = values[8] + self._automaticIdMapping = values[9] + super().unmarshal(codecs.decode(values[10].encode(), 'hex')) + + def valuesDict(self) -> gui.ValuesDictType: + dct = super().valuesDict() + dct['domain'] = self._domain + dct['account'] = self._account + dct['password'] = self._password + dct['clientSoftware'] = self._clientSoftware + dct['serverSoftware'] = self._serverSoftware + dct['membershipSoftware'] = self._membershipSoftware + dct['removeOnExit'] = self._removeOnExit == 'y' + dct['ssl'] = self._ssl == 'y' + dct['automaticIdMapping'] = self._automaticIdMapping == 'y' + return dct \ No newline at end of file From 6bc6f2d1710097c0891a23a93a0fcfbe59c41c15 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:06:16 +0300 Subject: [PATCH 03/13] Update documentation for actorData method New parameters needed to join Linux machines to the domain have been added. --- server/src/uds/core/osmanagers/osmanager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/uds/core/osmanagers/osmanager.py b/server/src/uds/core/osmanagers/osmanager.py index 1aedf502b..2833f1163 100644 --- a/server/src/uds/core/osmanagers/osmanager.py +++ b/server/src/uds/core/osmanagers/osmanager.py @@ -117,6 +117,12 @@ class OSManager(Module): 'ou': 'ou' # or '' if default ou 'username': 'userwithaddmachineperms@domain.xxxx' 'password': 'passwordForTheUserWithPerms', + 'clientSoftware': 'sssd' or 'winbind' or 'automatically' if linux os manager, + 'serverSoftware': 'active-directory' or 'ipa' if linux os manager, + 'membershipSoftware': 'samba' or 'adcli' or 'automatically' if linux os manager, + 'ssl': 'n' or 'y' if linux os manager, + 'automaticIdMapping': 'n' or 'y' if linux os manager, + 'isPersistent': 'n' or 'y' if linux os manager, } * rename vm, do NOT ADD to AD, and change password for an user { From 7f274ad5b2f43cb0e82dadebfd9f6876965eb7b8 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:15:13 +0300 Subject: [PATCH 04/13] Add operation for join to the domain Joining the domain takes place using the realm utility. It was chosen because it works in various Linux distributions, so it is universal. It is necessary to add a dependency to the realm utility for the actor. --- actor/src/udsactor/linux/operations.py | 42 +++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index 6b2d7bae1..cbfc2015c 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -193,11 +193,43 @@ def renameComputer(newName: str) -> bool: return True # Always reboot right now. Not much slower but much more convenient -def joinDomain( - domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False -): - pass - +def joinDomain( # pylint: disable=unused-argument, too-many-arguments + name: str, + domain: str, + ou: str, + account: str, + password: str, + client_software: str, + server_software: str, + membership_software: str, + ssl: bool, + automatic_id_mapping: bool + ) -> None: + if server_software == 'ipa': + try: + hostname = getComputerName() + domain[domain.index('.'):] + command = f'hostnamectl set-hostname {hostname}' + subprocess.run(command, shell=True) + except Exception as e: + logger.error(f'Error set hostname for freeeipa: {e}') + try: + command = f'realm join -U {account} ' + if client_software and client_software != 'automatically': + command += f'--client-software={client_software} ' + if server_software: + command += f'--server-software={server_software} ' + if membership_software and membership_software != 'automatically': + command += f'--membership-software={membership_software} ' + if ou and server_software !='ipa': + command += f'--computer-ou="{ou}" ' + if ssl == 'y': + command += '--use-ldaps ' + if automatic_id_mapping == 'n': + command += '--automatic-id-mapping=no ' + command += domain + subprocess.run(command, input=password.encode(), shell=True) + except Exception as e: + logger.error(f'Error join machine to domain {name}: {e}') def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None: ''' From b6ad6d80d59e6b9382153e389c4e530b0e83ff2a Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:26:02 +0300 Subject: [PATCH 05/13] Add operation for leave from the domain --- actor/src/udsactor/linux/operations.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index cbfc2015c..9de35d555 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -231,6 +231,30 @@ def joinDomain( # pylint: disable=unused-argument, too-many-arguments except Exception as e: logger.error(f'Error join machine to domain {name}: {e}') +def leaveDomain( + domain: str, + account: str, + password: str, + client_software: str, + server_software: str, + ) -> None: + if server_software == 'ipa': + try: + command = f'hostnamectl set-hostname {getComputerName()}' + subprocess.run(command, shell=True) + except Exception as e: + logger.error(f'Error set hostname for leave freeeipa domain: {e}') + try: + command = f'realm leave -U {account} ' + if client_software and client_software != 'automatically': + command += f'--client-software={client_software} ' + if server_software: + command += f'--server-software={server_software} ' + command += domain + subprocess.run(command, input=password.encode(), shell=True) + except Exception as e: + logger.error(f'Error leave machine from domain {domain}: {e}') + def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None: ''' Simple password change for user using command line From 28ce61c98322d84aec9e2cc4f1e35151a8636d29 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:36:28 +0300 Subject: [PATCH 06/13] Type extension for actor configuration Parameters have been added that are given by the server to join the Linux machines to the domain. --- actor/src/udsactor/types.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py index 8f849b3f3..16dd41448 100644 --- a/actor/src/udsactor/types.py +++ b/actor/src/udsactor/types.py @@ -24,6 +24,12 @@ class ActorOsConfigurationType(typing.NamedTuple): new_password: typing.Optional[str] = None ad: typing.Optional[str] = None ou: typing.Optional[str] = None + clientSoftware: typing.Optional[str] = None + serverSoftware: typing.Optional[str] = None + membershipSoftware: typing.Optional[str] = None + ssl: typing.Optional[str] = None + automaticIdMapping: typing.Optional[str] = None + isPersistent: typing.Optional[str] = None class ActorDataConfigurationType(typing.NamedTuple): unique_id: typing.Optional[str] = None From 47dc43f601d345642bf14a642ea2c20f0aa5ba46 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:42:55 +0300 Subject: [PATCH 07/13] Extension of the request for initialization Extension of the request for initialization according to the change in the configuration type of the actor. --- actor/src/udsactor/rest.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 4c8fcf35c..6b5009493 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -268,6 +268,12 @@ class UDSServerApi(UDSApi): new_password=os.get('new_password'), ad=os.get('ad'), ou=os.get('ou'), + clientSoftware = os.get('clientSoftware'), + serverSoftware = os.get('serverSoftware'), + membershipSoftware = os.get('membershipSoftware'), + ssl = os.get('ssl'), + automaticIdMapping = os.get('automaticIdMapping'), + isPersistent = os.get('isPersistent'), ) if r['os'] else None, From d6e2373de4e52f79152f4b30cac4f4b364e28ddc Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 15:59:01 +0300 Subject: [PATCH 08/13] Redefining a function to join to the domain Depending on which machine (Linux or Windows) the actor is for, a different number of parameters are passed to this function. --- actor/src/udsactor/linux/service.py | 21 +++++++++++++++++++-- actor/src/udsactor/service.py | 28 +++++++++++++++++++++------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index 2d345bba4..24b9efd71 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -32,6 +32,7 @@ import signal from . import daemon +from . import operations from ..log import logger from ..service import CommonService @@ -60,10 +61,26 @@ class UDSActorSvc(daemon.Daemon, CommonService): domain: str, ou: str, account: str, - password: str + password: str, + client_software: str, + server_software: str, + membership_software: str, + ssl: bool, + automatic_id_mapping: bool ) -> None: - logger.info('Join domain is not supported on linux platforms right now. Just renaming.') self.rename(name) + logger.debug(f'Starting joining domain {domain} with name {name}') + operations.joinDomain(name, \ + domain, \ + ou, \ + account, \ + password, \ + client_software, \ + server_software, \ + membership_software, \ + ssl, \ + automatic_id_mapping + ) def run(self) -> None: logger.debug('Running Daemon: {}'.format(self._isAlive)) diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index 492caf7e7..12267943c 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -235,13 +235,27 @@ class CommonService: # pylint: disable=too-many-instance-attributes osData.new_password, ) elif osData.action == 'rename_ad': - self.joinDomain( - osData.name, - osData.ad or '', - osData.ou or '', - osData.username or '', - osData.password or '', - ) + if not osData.serverSoftware: + self.joinDomain( + osData.name, + osData.ad or '', + osData.ou or '', + osData.username or '', + osData.password or '', + ) + else: + self.joinDomain( + osData.name, + osData.ad or '', + osData.ou or '', + osData.username or '', + osData.password or '', + osData.clientSoftware or '', + osData.serverSoftware or '', + osData.membershipSoftware or '', + osData.ssl or '', + osData.automaticIdMapping or '' + ) if self._rebootRequested: try: From 2d303623012b1609a4cf6e3c3feb47de46d3a226 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 16:30:54 +0300 Subject: [PATCH 09/13] Saving actor config for non-persistent machines This is necessary to leave a non-persistent machine from the domain. --- actor/src/udsactor/service.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index 12267943c..4ed636567 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -194,10 +194,15 @@ class CommonService: # pylint: disable=too-many-instance-attributes # Cleans sensible data if self._cfg.config: - self._cfg = self._cfg._replace( - config=self._cfg.config._replace(os=None), data=None - ) - platform.store.writeConfig(self._cfg) + try: + isPersistent = self._cfg.config.os.isPersistent == 'y' + except: + isPersistent = True + if isPersistent: + self._cfg = self._cfg._replace( + config=self._cfg.config._replace(os=None), data=None + ) + platform.store.writeConfig(self._cfg) logger.info('Service ready') From f3ba042140b46a119e876f5d4606b50bf33ca44d Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 16:35:09 +0300 Subject: [PATCH 10/13] Changing the function when the service is stopped If the machine is non-persistent, it will be leave from the domain when the service is stopped. --- actor/src/udsactor/linux/service.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index 24b9efd71..ea8da395e 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -82,6 +82,22 @@ class UDSActorSvc(daemon.Daemon, CommonService): automatic_id_mapping ) + def finish(self) -> None: + try: + if self._cfg.config and self._cfg.config.os: + osData = self._cfg.config.os + if osData.action == 'rename_ad' and osData.isPersistent == 'n' : + operations.leaveDomain( + osData.ad or '', + osData.username or '', + osData.password or '', + osData.clientSoftware or '', + osData.serverSoftware or '', + ) + except Exception as e: + logger.error(f'Got exception operating machine: {e}') + super().finish() + def run(self) -> None: logger.debug('Running Daemon: {}'.format(self._isAlive)) set_proctitle('UDSActorDaemon') From 277d9f9b883ecca541c0fb25266f242ef3ffb656 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 16:39:37 +0300 Subject: [PATCH 11/13] Changing the service file This is necessary for the correct stop of the service when the machine is shutdowned or rebooted (with the leave from the domain of non-persistent machines). --- actor/linux/debian/udsactor.service | 1 + 1 file changed, 1 insertion(+) diff --git a/actor/linux/debian/udsactor.service b/actor/linux/debian/udsactor.service index 9fc67c44e..aab21e5dc 100644 --- a/actor/linux/debian/udsactor.service +++ b/actor/linux/debian/udsactor.service @@ -3,6 +3,7 @@ Description=UDS Broker actor After=network.target [Service] +KillMode=mixed Type=simple User=root Group=root From f8a9a9d633c1bf5589a32ac58f368d39e8bf3971 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 16:47:47 +0300 Subject: [PATCH 12/13] Update copyright date --- actor/src/udsactor/linux/operations.py | 2 +- actor/src/udsactor/linux/service.py | 2 +- actor/src/udsactor/rest.py | 2 +- actor/src/udsactor/service.py | 2 +- server/src/uds/core/osmanagers/osmanager.py | 2 +- server/src/uds/osmanagers/LinuxOsManager/__init__.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index 9de35d555..292a52bf3 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014-2019 Virtual Cable S.L. +# Copyright (c) 2014-2023 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index ea8da395e..f315de632 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014-2019 Virtual Cable S.L. +# Copyright (c) 2014-2023 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 6b5009493..018f1f546 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2019-2021 Virtual Cable S.L.U. +# Copyright (c) 2019-2023 Virtual Cable S.L.U. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index 4ed636567..8b8a3119d 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014-2019 Virtual Cable S.L. +# Copyright (c) 2014-2023 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, diff --git a/server/src/uds/core/osmanagers/osmanager.py b/server/src/uds/core/osmanagers/osmanager.py index 2833f1163..7e3207531 100644 --- a/server/src/uds/core/osmanagers/osmanager.py +++ b/server/src/uds/core/osmanagers/osmanager.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2012-2022 Virtual Cable S.L.U. +# Copyright (c) 2012-2023 Virtual Cable S.L.U. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, diff --git a/server/src/uds/osmanagers/LinuxOsManager/__init__.py b/server/src/uds/osmanagers/LinuxOsManager/__init__.py index 35b666a4e..2259fb314 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/__init__.py +++ b/server/src/uds/osmanagers/LinuxOsManager/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2012-2022 Virtual Cable S.L.U. +# Copyright (c) 2012-2023 Virtual Cable S.L.U. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, From d8f41a048eca9642b439faf60189b1b1ec129005 Mon Sep 17 00:00:00 2001 From: Alexander Burmatov Date: Wed, 17 May 2023 16:49:26 +0300 Subject: [PATCH 13/13] Updating information about authors --- actor/src/udsactor/linux/operations.py | 1 + actor/src/udsactor/linux/service.py | 1 + actor/src/udsactor/rest.py | 1 + actor/src/udsactor/service.py | 1 + server/src/uds/core/osmanagers/osmanager.py | 1 + server/src/uds/osmanagers/LinuxOsManager/__init__.py | 1 + 6 files changed, 6 insertions(+) diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index 292a52bf3..fede39bbd 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -27,6 +27,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org ''' # pylint: disable=invalid-name import configparser diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index f315de632..b57d2897e 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -28,6 +28,7 @@ ''' @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org ''' import signal diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 018f1f546..3cee28c86 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -27,6 +27,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org ''' # pylint: disable=invalid-name import warnings diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index 8b8a3119d..e0ea7503e 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -27,6 +27,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org ''' # pylint: disable=invalid-name diff --git a/server/src/uds/core/osmanagers/osmanager.py b/server/src/uds/core/osmanagers/osmanager.py index 7e3207531..1186a3433 100644 --- a/server/src/uds/core/osmanagers/osmanager.py +++ b/server/src/uds/core/osmanagers/osmanager.py @@ -29,6 +29,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org """ import typing diff --git a/server/src/uds/osmanagers/LinuxOsManager/__init__.py b/server/src/uds/osmanagers/LinuxOsManager/__init__.py index 2259fb314..63f41fa66 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/__init__.py +++ b/server/src/uds/osmanagers/LinuxOsManager/__init__.py @@ -29,6 +29,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com +@author: Alexander Burmatov, thatman at altlinux dot org """ import os.path import typing