代码拉取完成,页面将自动刷新
同步操作将从 OpenCloudOS Stream/freeipa 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/authconfig.py freeipa-4.10.1/ipaplatform/opencloudos/authconfig.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/authconfig.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/authconfig.py 2023-04-26 19:36:26.929138784 +0800
@@ -0,0 +1,375 @@
+# Copyright (C) 2023 OpenCloudOS
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import
+import logging
+import six
+import abc
+
+from ipaplatform.paths import paths
+from ipapython import ipautil
+from ipapython.admintool import ScriptError
+import os
+import time
+
+FILES_TO_NOT_BACKUP = ['passwd', 'group', 'shadow', 'gshadow']
+
+logger = logging.getLogger(__name__)
+
+
+def get_auth_tool():
+ return OpenCloudOSAuthSelect()
+
+
[email protected]_metaclass(abc.ABCMeta)
+class OpenCloudOSAuthToolBase:
+
+ @abc.abstractmethod
+ def configure(self, sssd, mkhomedir, statestore, sudo=True):
+ pass
+
+ @abc.abstractmethod
+ def unconfigure(self, fstore, statestore,
+ was_sssd_installed,
+ was_sssd_configured):
+ pass
+
+ @abc.abstractmethod
+ def backup(self, path):
+ """
+ Backup the system authentication resources configuration
+ :param path: directory where the backup will be stored
+ """
+
+ @abc.abstractmethod
+ def restore(self, path):
+ """
+ Restore the system authentication resources configuration from a backup
+ :param path: directory where the backup is stored
+ """
+
+ @abc.abstractmethod
+ def set_nisdomain(self, nisdomain):
+ pass
+
+
+class OpenCloudOSAuthSelect(OpenCloudOSAuthToolBase):
+
+ def _get_authselect_current_output(self):
+ try:
+ current = ipautil.run(
+ [paths.AUTHSELECT, "current", "--raw"])
+ except ipautil.CalledProcessError:
+ logger.debug("Current configuration not managed by authselect")
+ return None
+
+ return current.raw_output.decode()
+
+ def _parse_authselect_output(self, output_text=None):
+ """
+ Parses the output_text to extract the profile and options.
+ When no text is provided, runs the 'authselect profile' command to
+ generate the text to be parsed.
+ """
+ if output_text is None:
+ output_text = self._get_authselect_current_output()
+ if output_text is None:
+ return None
+
+ output_text = output_text.strip()
+ if not output_text:
+ return None
+ output_items = output_text.split(' ')
+ profile = output_items[0]
+ features = output_items[1:]
+ return profile, features
+
+ def configure(self, sssd, mkhomedir, statestore, sudo=True,
+ subid=False):
+ # In the statestore, the following keys are used for the
+ # 'authselect' module:
+ # Old method:
+ # profile: name of the profile configured pre-installation
+ # features_list: list of features configured pre-installation
+ # mkhomedir: True if installation was called with --mkhomedir
+ # profile and features_list are used when reverting to the
+ # pre-install state
+ # New method:
+ # backup: name of the authselect backup
+ backup_name = "pre_ipaclient_{}".format(time.strftime("%Y%m%d%H%M%S"))
+ statestore.backup_state('authselect', 'backup', backup_name)
+
+ cmd = [paths.AUTHSELECT, "select", "sssd"]
+ if mkhomedir:
+ cmd.append("with-mkhomedir")
+ statestore.backup_state('authselect', 'mkhomedir', True)
+ if sudo:
+ cmd.append("with-sudo")
+ if subid:
+ cmd.append("with-subid")
+ cmd.append("--force")
+ cmd.append("--backup={}".format(backup_name))
+
+ ipautil.run(cmd)
+
+ def unconfigure(
+ self, fstore, statestore, was_sssd_installed, was_sssd_configured
+ ):
+ # If the installation failed before doing the authselect part
+ # nothing to do here
+ complete = statestore.get_state('installation', 'complete')
+ if complete is not None and not complete and \
+ not statestore.has_state('authselect'):
+ return
+
+ if not statestore.has_state('authselect'):
+ logger.warning(
+ "WARNING: Unable to revert to the pre-installation state "
+ "('authconfig' tool has been deprecated in favor of "
+ "'authselect'). The default sssd profile will be used "
+ "instead.")
+ # Build the equivalent command line that will be displayed
+ # to the user
+ # This is a copy-paste of unconfigure code, except that it
+ # creates the command line but does not actually call it
+ authconfig = OpenCloudOSAuthConfig()
+ authconfig.prepare_unconfigure(
+ fstore, statestore, was_sssd_installed, was_sssd_configured)
+ args = authconfig.build_args()
+ logger.warning(
+ "The authconfig arguments would have been: authconfig %s",
+ " ".join(args))
+
+ profile = 'sssd'
+ features = []
+ else:
+ profile = statestore.restore_state('authselect', 'profile')
+ if not profile:
+ profile = 'sssd'
+ features_state = statestore.restore_state(
+ 'authselect', 'features_list'
+ )
+ statestore.delete_state('authselect', 'mkhomedir')
+ # https://pagure.io/freeipa/issue/8054
+ if fstore.has_file(paths.NSSWITCH_CONF):
+ logger.info("Restoring user-nsswitch.conf")
+ fstore.restore_file(paths.NSSWITCH_CONF)
+ # only non-empty features, https://pagure.io/freeipa/issue/7776
+ if features_state is not None:
+ features = [
+ f.strip() for f in features_state.split(' ') if f.strip()
+ ]
+ else:
+ features = []
+
+ backup = statestore.restore_state('authselect', 'backup')
+ if backup:
+ cmd = [paths.AUTHSELECT, "backup-restore", backup]
+ ipautil.run(cmd)
+ else:
+ cmd = [paths.AUTHSELECT, "select", profile]
+ cmd.extend(features)
+ cmd.append("--force")
+ ipautil.run(cmd)
+
+ def backup(self, path):
+ current = self._get_authselect_current_output()
+ if current is None:
+ return
+
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+ with open(os.path.join(path, "authselect.backup"), 'w') as f:
+ f.write(current)
+
+ def restore(self, path):
+ with open(os.path.join(path, "authselect.backup"), "r") as f:
+ cfg = self._parse_authselect_output(f.read())
+
+ if cfg:
+ profile = cfg[0]
+ cmd = [paths.AUTHSELECT, "select", profile]
+ cmd.extend(cfg[1])
+ cmd.append("--force")
+ ipautil.run(cmd)
+
+ def set_nisdomain(self, nisdomain):
+ try:
+ with open(paths.SYSCONF_NETWORK, 'r') as f:
+ content = [
+ line for line in f
+ if not line.strip().upper().startswith('NISDOMAIN')
+ ]
+ except IOError:
+ content = []
+
+ content.append("NISDOMAIN={}\n".format(nisdomain))
+
+ with open(paths.SYSCONF_NETWORK, 'w') as f:
+ f.writelines(content)
+
+
+# OpenCloudOSAuthConfig concrete class definition to be removed later
+# when agreed on exact path to migrate to authselect
+class OpenCloudOSAuthConfig(OpenCloudOSAuthToolBase):
+ """
+ AuthConfig class implements system-independent interface to configure
+ system authentication resources. In OpenCloudOS systems this is done with
+ authconfig(8) utility.
+
+ AuthConfig class is nothing more than a tool to gather configuration
+ options and execute their processing. These options then converted by
+ an actual implementation to series of a system calls to appropriate
+ utilities performing real configuration.
+
+ If you need to re-use existing AuthConfig instance for multiple runs,
+ make sure to call 'AuthConfig.reset()' between the runs.
+ """
+
+ def __init__(self):
+ self.parameters = {}
+
+ def enable(self, option):
+ self.parameters[option] = True
+ return self
+
+ def disable(self, option):
+ self.parameters[option] = False
+ return self
+
+ def add_option(self, option):
+ self.parameters[option] = None
+ return self
+
+ def add_parameter(self, option, value):
+ self.parameters[option] = [value]
+ return self
+
+ def reset(self):
+ self.parameters = {}
+ return self
+
+ def build_args(self):
+ args = []
+
+ for (option, value) in self.parameters.items():
+ if type(value) is bool:
+ if value:
+ args.append("--enable%s" % (option))
+ else:
+ args.append("--disable%s" % (option))
+ elif type(value) in (tuple, list):
+ args.append("--%s" % (option))
+ args.append("%s" % (value[0]))
+ elif value is None:
+ args.append("--%s" % (option))
+ else:
+ args.append("--%s%s" % (option, value))
+
+ return args
+
+ def execute(self, update=True):
+ if update:
+ self.add_option("update")
+
+ args = self.build_args()
+ try:
+ ipautil.run([paths.AUTHCONFIG] + args)
+ except ipautil.CalledProcessError:
+ raise ScriptError("Failed to execute authconfig command")
+
+ def configure(self, sssd, mkhomedir, statestore, sudo=True):
+ if sssd:
+ statestore.backup_state('authconfig', 'sssd', True)
+ statestore.backup_state('authconfig', 'sssdauth', True)
+ self.enable("sssd")
+ self.enable("sssdauth")
+ else:
+ statestore.backup_state('authconfig', 'ldap', True)
+ self.enable("ldap")
+ self.enable("forcelegacy")
+
+ statestore.backup_state('authconfig', 'krb5', True)
+ self.enable("krb5")
+ self.add_option("nostart")
+
+ if mkhomedir:
+ statestore.backup_state('authconfig', 'mkhomedir', True)
+ self.enable("mkhomedir")
+
+ self.execute()
+ self.reset()
+
+ def prepare_unconfigure(self, fstore, statestore,
+ was_sssd_installed,
+ was_sssd_configured):
+ if statestore.has_state('authconfig'):
+ # disable only those configurations that we enabled during install
+ for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
+ cnf = statestore.restore_state('authconfig', conf)
+ # Do not disable sssd, as this can cause issues with its later
+ # uses. Remove it from statestore however, so that it becomes
+ # empty at the end of uninstall process.
+ if cnf and conf != 'sssd':
+ self.disable(conf)
+ else:
+ # There was no authconfig status store
+ # It means the code was upgraded after original install
+ # Fall back to old logic
+ self.disable("ldap")
+ self.disable("krb5")
+ if not(was_sssd_installed and was_sssd_configured):
+ # Only disable sssdauth. Disabling sssd would cause issues
+ # with its later uses.
+ self.disable("sssdauth")
+ self.disable("mkhomedir")
+
+ def unconfigure(self, fstore, statestore,
+ was_sssd_installed,
+ was_sssd_configured):
+ self.prepare_unconfigure(
+ fstore, statestore, was_sssd_installed, was_sssd_configured)
+ self.execute()
+ self.reset()
+
+ def backup(self, path):
+ try:
+ ipautil.run([paths.AUTHCONFIG, "--savebackup", path])
+ except ipautil.CalledProcessError:
+ raise ScriptError("Failed to execute authconfig command")
+
+ # do not backup these files since we don't want to mess with
+ # users/groups during restore. Authconfig doesn't seem to mind about
+ # having them deleted from backup dir
+ files_to_remove = [os.path.join(path, f) for f in FILES_TO_NOT_BACKUP]
+ for filename in files_to_remove:
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+
+ def restore(self, path):
+ try:
+ ipautil.run([paths.AUTHCONFIG, "--restorebackup", path])
+ except ipautil.CalledProcessError:
+ raise ScriptError("Failed to execute authconfig command")
+
+ def set_nisdomain(self, nisdomain):
+ # Let authconfig setup the permanent configuration
+ self.reset()
+ self.add_parameter("nisdomain", nisdomain)
+ self.execute()
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/constants.py freeipa-4.10.1/ipaplatform/opencloudos/constants.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/constants.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/constants.py 2023-04-26 19:35:43.296150586 +0800
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
+#
+
+'''
+This OpenCloudOS family base platform module exports default platform
+related constants for the OpenCloudOS family-based systems.
+'''
+
+# Fallback to default path definitions
+from __future__ import absolute_import
+
+from ipaplatform.base.constants import BaseConstantsNamespace, User, Group
+
+
+__all__ = ("constants", "User", "Group")
+
+
+class OpenCloudOSConstantsNamespace(BaseConstantsNamespace):
+ # Use system-wide crypto policy
+ TLS_HIGH_CIPHERS = None
+
+
+constants = OpenCloudOSConstantsNamespace()
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/__init__.py freeipa-4.10.1/ipaplatform/opencloudos/__init__.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/__init__.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/__init__.py 2023-04-26 19:35:49.185148994 +0800
@@ -0,0 +1,19 @@
+# Copyright (C) 2023 OpenCloudOS
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This module contains OpenCloudOS family specific platform files.
+'''
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/paths.py freeipa-4.10.1/ipaplatform/opencloudos/paths.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/paths.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/paths.py 2023-04-26 19:35:56.773146940 +0800
@@ -0,0 +1,45 @@
+# Copyright (C) 2023 OpenCloudOS
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This OpenCloudOS family base platform module exports default filesystem paths as
+common in OpenCloudOS family-based systems.
+'''
+
+from __future__ import absolute_import
+
+import sys
+
+# Fallback to default path definitions
+from ipaplatform.base.paths import BasePathNamespace
+
+
+class OpenCloudOSPathNamespace(BasePathNamespace):
+ CRYPTO_POLICY_OPENSSLCNF_FILE = (
+ '/etc/crypto-policies/back-ends/opensslcnf.config'
+ )
+ # https://docs.python.org/2/library/platform.html#cross-platform
+ if sys.maxsize > 2**32:
+ LIBSOFTHSM2_SO = BasePathNamespace.LIBSOFTHSM2_SO_64
+ PAM_KRB5_SO = BasePathNamespace.PAM_KRB5_SO_64
+ BIND_LDAP_SO = BasePathNamespace.BIND_LDAP_SO_64
+ AUTHCONFIG = '/usr/sbin/authconfig'
+ AUTHSELECT = '/usr/bin/authselect'
+ SYSCONF_NETWORK = '/etc/sysconfig/network'
+ NSSWITCH_CONF = '/etc/authselect/user-nsswitch.conf'
+
+
+paths = OpenCloudOSPathNamespace()
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/services.py freeipa-4.10.1/ipaplatform/opencloudos/services.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/services.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/services.py 2023-04-26 19:28:06.687274308 +0800
@@ -0,0 +1,238 @@
+# Copyright (C) 2023 OpenCloudOS
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""
+Contains OpenCloudOS family-specific service class implementations.
+"""
+
+from __future__ import absolute_import
+
+import logging
+import os
+import time
+import contextlib
+
+from ipaplatform.base import services as base_services
+
+from ipapython import ipautil, dogtag
+from ipaplatform.paths import paths
+
+logger = logging.getLogger(__name__)
+
+# Mappings from service names as FreeIPA code references to these services
+# to their actual systemd service names
+
+# For beginning just remap names to add .service
+# As more services will migrate to systemd, unit names will deviate and
+# mapping will be kept in this dictionary
+opencloudos_system_units = dict((x, "%s.service" % x)
+ for x in base_services.wellknownservices)
+
+opencloudos_system_units['rpcgssd'] = 'rpc-gssd.service'
+opencloudos_system_units['rpcidmapd'] = 'nfs-idmapd.service'
+opencloudos_system_units['domainname'] = 'nis-domainname.service'
+
+# Rewrite dirsrv and pki-tomcatd services as they support instances via separate
+# service generator. To make this working, one needs to have both [email protected]
+# and foo.target -- the latter is used when request should be coming for
+# all instances (like stop). systemd, unfortunately, does not allow one
+# to request action for all service instances at once if only [email protected]
+# unit is available. To add more, if any of those services need to be
+# started/stopped automagically, one needs to manually create symlinks in
+# /etc/systemd/system/foo.target.wants/ (look into systemd.py's enable()
+# code).
+
+opencloudos_system_units['dirsrv'] = '[email protected]'
+# Our PKI instance is [email protected]
+opencloudos_system_units['pki-tomcatd'] = '[email protected]'
+opencloudos_system_units['pki_tomcatd'] = opencloudos_system_units['pki-tomcatd']
+opencloudos_system_units['ipa-otpd'] = 'ipa-otpd.socket'
+opencloudos_system_units['ipa-dnskeysyncd'] = 'ipa-dnskeysyncd.service'
+opencloudos_system_units['named-regular'] = 'named.service'
+opencloudos_system_units['named-pkcs11'] = 'named-pkcs11.service'
+opencloudos_system_units['named'] = opencloudos_system_units['named-pkcs11']
+opencloudos_system_units['named-conflict'] = opencloudos_system_units['named-regular']
+opencloudos_system_units['ods-enforcerd'] = 'ods-enforcerd.service'
+opencloudos_system_units['ods_enforcerd'] = opencloudos_system_units['ods-enforcerd']
+opencloudos_system_units['ods-signerd'] = 'ods-signerd.service'
+opencloudos_system_units['ods_signerd'] = opencloudos_system_units['ods-signerd']
+opencloudos_system_units['gssproxy'] = 'gssproxy.service'
+
+
+# Service classes that implement OpenCloudOS family-specific behaviour
+
+class OpenCloudOSService(base_services.SystemdService):
+ system_units = opencloudos_system_units
+
+ def __init__(self, service_name, api=None):
+ systemd_name = service_name
+ if service_name in self.system_units:
+ systemd_name = self.system_units[service_name]
+ else:
+ if '.' not in service_name:
+ # if service_name does not have a dot, it is not foo.service
+ # and not a foo.target. Thus, not correct service name for
+ # systemd, default to foo.service style then
+ systemd_name = "%s.service" % (service_name)
+ super(OpenCloudOSService, self).__init__(service_name, systemd_name, api)
+
+
+class OpenCloudOSDirectoryService(OpenCloudOSService):
+
+ def is_installed(self, instance_name):
+ file_path = "{}/{}-{}".format(paths.ETC_DIRSRV, "slapd", instance_name)
+ return os.path.exists(file_path)
+
+ def restart(self, instance_name="", capture_output=True, wait=True,
+ ldapi=False):
+ # We need to explicitly enable instances to install proper symlinks as
+ # dirsrv.target.wants/ dependencies. Standard systemd service class does it
+ # on enable() method call. Unfortunately, ipa-server-install does not do
+ # explicit dirsrv.enable() because the service startup is handled by ipactl.
+ #
+ # If we wouldn't do this, our instances will not be started as systemd would
+ # not have any clue about instances (PKI-IPA and the domain we serve)
+ # at all. Thus, hook into dirsrv.restart().
+
+ if instance_name:
+ elements = self.systemd_name.split("@")
+
+ srv_etc = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
+ self.systemd_name)
+ srv_tgt = os.path.join(paths.ETC_SYSTEMD_SYSTEM_DIR,
+ self.SYSTEMD_SRV_TARGET % (elements[0]))
+ srv_lnk = os.path.join(srv_tgt,
+ self.service_instance(instance_name))
+
+ if not os.path.exists(srv_etc):
+ self.enable(instance_name)
+ elif not os.path.samefile(srv_etc, srv_lnk):
+ os.unlink(srv_lnk)
+ os.symlink(srv_etc, srv_lnk)
+
+ with self.__wait(instance_name, wait, ldapi) as wait:
+ super(OpenCloudOSDirectoryService, self).restart(
+ instance_name, capture_output=capture_output, wait=wait)
+
+ def start(self, instance_name="", capture_output=True, wait=True,
+ ldapi=False):
+ with self.__wait(instance_name, wait, ldapi) as wait:
+ super(OpenCloudOSDirectoryService, self).start(
+ instance_name, capture_output=capture_output, wait=wait)
+
+ @contextlib.contextmanager
+ def __wait(self, instance_name, wait, ldapi):
+ if ldapi:
+ instance_name = self.service_instance(instance_name)
+ if instance_name.endswith('.service'):
+ instance_name = instance_name[:-8]
+ if instance_name.startswith('dirsrv'):
+ # this is intentional, return the empty string if the instance
+ # name is 'dirsrv'
+ instance_name = instance_name[7:]
+ if not instance_name:
+ ldapi = False
+ if ldapi:
+ yield False
+ socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % instance_name
+ ipautil.wait_for_open_socket(socket_name,
+ self.api.env.startup_timeout)
+ else:
+ yield wait
+
+
+class OpenCloudOSIPAService(OpenCloudOSService):
+ # Enforce restart of IPA services when we do enable it
+ # This gets around the fact that after ipa-server-install systemd thinks
+ # ipa.service is not yet started but all services were actually started
+ # already.
+ def enable(self, instance_name=""):
+ super(OpenCloudOSIPAService, self).enable(instance_name)
+ self.restart(instance_name)
+
+
+class OpenCloudOSCAService(OpenCloudOSService):
+ def wait_until_running(self):
+ logger.debug('Waiting until the CA is running')
+ timeout = float(self.api.env.startup_timeout)
+ op_timeout = time.time() + timeout
+ while time.time() < op_timeout:
+ try:
+ # check status of CA instance on this host, not remote ca_host
+ status = dogtag.ca_status(self.api.env.host)
+ except Exception as e:
+ status = 'check interrupted due to error: %s' % e
+ logger.debug('The CA status is: %s', status)
+ if status == 'running':
+ break
+ logger.debug('Waiting for CA to start...')
+ time.sleep(1)
+ else:
+ raise RuntimeError('CA did not start in %ss' % timeout)
+
+ def is_running(self, instance_name="", wait=True):
+ if instance_name:
+ return super(OpenCloudOSCAService, self).is_running(instance_name)
+ try:
+ status = dogtag.ca_status()
+ if status == 'running':
+ return True
+ elif status == 'starting' and wait:
+ # Exception is raised if status is 'starting' even after wait
+ self.wait_until_running()
+ return True
+ except Exception as e:
+ logger.debug(
+ 'Failed to check CA status: %s', e
+ )
+ return False
+
+
+# Function that constructs proper OpenCloudOS family-specific server classes for
+# services of specified name
+
+def opencloudos_service_class_factory(name, api=None):
+ if name == 'dirsrv':
+ return OpenCloudOSDirectoryService(name, api)
+ if name == 'ipa':
+ return OpenCloudOSIPAService(name, api)
+ if name in ('pki-tomcatd', 'pki_tomcatd'):
+ return OpenCloudOSCAService(name, api)
+ return OpenCloudOSService(name, api)
+
+
+# Magicdict containing OpenCloudOSService instances.
+
+class OpenCloudOSServices(base_services.KnownServices):
+ def __init__(self):
+ # pylint: disable=ipa-forbidden-import
+ import ipalib # FixMe: break import cycle
+ # pylint: enable=ipa-forbidden-import
+ services = dict()
+ for s in base_services.wellknownservices:
+ services[s] = self.service_class_factory(s, ipalib.api)
+ # Call base class constructor. This will lock services to read-only
+ super(OpenCloudOSServices, self).__init__(services)
+
+ def service_class_factory(self, name, api=None):
+ return opencloudos_service_class_factory(name, api)
+
+# Objects below are expected to be exported by platform module
+
+timedate_services = base_services.timedate_services
+service = opencloudos_service_class_factory
+knownservices = OpenCloudOSServices()
diff -Naur freeipa-4.10.1.orig/ipaplatform/opencloudos/tasks.py freeipa-4.10.1/ipaplatform/opencloudos/tasks.py
--- freeipa-4.10.1.orig/ipaplatform/opencloudos/tasks.py 1970-01-01 08:00:00.000000000 +0800
+++ freeipa-4.10.1/ipaplatform/opencloudos/tasks.py 2023-04-26 19:32:37.029200977 +0800
@@ -0,0 +1,771 @@
+# Copyright (C) 2023 OpenCloudOS
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This module contains default OpenCloudOS family-specific implementations of
+system tasks.
+'''
+from __future__ import print_function, absolute_import
+
+import ctypes
+import logging
+import os
+from pathlib import Path
+import socket
+import traceback
+import errno
+import urllib
+import subprocess
+import sys
+import textwrap
+
+from ctypes.util import find_library
+from functools import total_ordering
+from subprocess import CalledProcessError
+
+from pyasn1.error import PyAsn1Error
+
+from ipapython import directivesetter
+from ipapython import ipautil
+import ipapython.errors
+
+from ipaplatform.constants import constants
+from ipaplatform.paths import paths
+from ipaplatform.opencloudos.authconfig import get_auth_tool
+from ipaplatform.base.tasks import BaseTaskNamespace
+
+logger = logging.getLogger(__name__)
+
+
+# /etc/pkcs11/modules override
+# base filename, module, list of disabled-in
+# 'p11-kit-proxy' disables proxying of module, see man(5) pkcs11.conf
+PKCS11_MODULES = [
+ ('softhsm2', paths.LIBSOFTHSM2_SO, ['p11-kit-proxy']),
+]
+
+
+NM_IPA_CONF = textwrap.dedent("""
+ # auto-generated by IPA installer
+ [main]
+ dns={dnsprocessing}
+
+ [global-dns]
+ searches={searches}
+
+ [global-dns-domain-*]
+ servers={servers}
+""")
+
+
+@total_ordering
+class IPAVersion:
+ _rpmvercmp_func = None
+
+ @classmethod
+ def _rpmvercmp(cls, a, b):
+ """Lazy load and call librpm's rpmvercmp
+ """
+ rpmvercmp_func = cls._rpmvercmp_func
+ if rpmvercmp_func is None:
+ librpm = ctypes.CDLL(find_library('rpm'))
+ rpmvercmp_func = librpm.rpmvercmp
+ # int rpmvercmp(const char *a, const char *b)
+ rpmvercmp_func.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
+ rpmvercmp_func.restype = ctypes.c_int
+ cls._rpmvercmp_func = rpmvercmp_func
+ return rpmvercmp_func(a, b)
+
+ def __init__(self, version):
+ self._version = version
+ self._bytes = version.encode('utf-8')
+
+ @property
+ def version(self):
+ return self._version
+
+ def __eq__(self, other):
+ if not isinstance(other, IPAVersion):
+ return NotImplemented
+ return self._rpmvercmp(self._bytes, other._bytes) == 0
+
+ def __lt__(self, other):
+ if not isinstance(other, IPAVersion):
+ return NotImplemented
+ return self._rpmvercmp(self._bytes, other._bytes) < 0
+
+ def __hash__(self):
+ return hash(self._version)
+
+
+class OpenCloudOSTaskNamespace(BaseTaskNamespace):
+
+ def restore_context(self, filepath, force=False):
+ """Restore SELinux security context on the given filepath.
+
+ SELinux equivalent is /path/to/restorecon <filepath>
+ restorecon's return values are not reliable so we have to
+ ignore them (BZ #739604).
+
+ ipautil.run() will do the logging.
+ """
+ restorecon = paths.SBIN_RESTORECON
+ if not self.is_selinux_enabled() or not os.path.exists(restorecon):
+ return
+
+ # Force reset of context to match file_context for customizable
+ # files, and the default file context, changing the user, role,
+ # range portion as well as the type.
+ args = [restorecon]
+ if force:
+ args.append('-F')
+ args.append(filepath)
+ ipautil.run(args, raiseonerr=False)
+
+ def is_selinux_enabled(self):
+ """Check if SELinux is available and enabled
+ """
+ try:
+ ipautil.run([paths.SELINUXENABLED])
+ except ipautil.CalledProcessError:
+ # selinuxenabled returns 1 if not enabled
+ return False
+ except OSError:
+ # selinuxenabled binary not available
+ return False
+ else:
+ return True
+
+ def check_selinux_status(self, restorecon=paths.RESTORECON):
+ """
+ We don't have a specific package requirement for policycoreutils
+ which provides restorecon. This is because we don't require
+ SELinux on client installs. However if SELinux is enabled then
+ this package is required.
+
+ This function returns nothing but may raise a Runtime exception
+ if SELinux is enabled but restorecon is not available.
+ """
+ if not self.is_selinux_enabled():
+ return False
+
+ if not os.path.exists(restorecon):
+ raise RuntimeError('SELinux is enabled but %s does not exist.\n'
+ 'Install the policycoreutils package and start '
+ 'the installation again.' % restorecon)
+ return True
+
+ def check_ipv6_stack_enabled(self):
+ """Checks whether IPv6 kernel module is loaded.
+
+ Function checks if /proc/net/if_inet6 is present. If IPv6 stack is
+ enabled, it exists and contains the interfaces configuration.
+
+ :raises: RuntimeError when IPv6 stack is disabled
+ """
+ if not os.path.exists(paths.IF_INET6):
+ raise RuntimeError(
+ "IPv6 stack has to be enabled in the kernel and some "
+ "interface has to have ::1 address assigned. Typically "
+ "this is 'lo' interface. If you do not wish to use IPv6 "
+ "globally, disable it on the specific interfaces in "
+ "sysctl.conf except 'lo' interface.")
+
+ try:
+ localhost6 = ipautil.CheckedIPAddress('::1', allow_loopback=True)
+ if localhost6.get_matching_interface() is None:
+ raise ValueError("no interface for ::1 address found")
+ except ValueError:
+ raise RuntimeError(
+ "IPv6 stack is enabled in the kernel but there is no "
+ "interface that has ::1 address assigned. Add ::1 address "
+ "resolution to 'lo' interface. You might need to enable IPv6 "
+ "on the interface 'lo' in sysctl.conf.")
+
+ def detect_container(self):
+ """Check if running inside a container
+
+ :returns: container runtime or None
+ :rtype: str, None
+ """
+ try:
+ output = subprocess.check_output(
+ [paths.SYSTEMD_DETECT_VIRT, '--container'],
+ stderr=subprocess.STDOUT
+ )
+ except subprocess.CalledProcessError as e:
+ if e.returncode == 1:
+ # No container runtime detected
+ return None
+ else:
+ raise
+ else:
+ return output.decode('utf-8').strip()
+
+ def restore_pre_ipa_client_configuration(self, fstore, statestore,
+ was_sssd_installed,
+ was_sssd_configured):
+
+ auth_config = get_auth_tool()
+ auth_config.unconfigure(
+ fstore, statestore, was_sssd_installed, was_sssd_configured
+ )
+
+ def set_nisdomain(self, nisdomain):
+ try:
+ with open(paths.SYSCONF_NETWORK, 'r') as f:
+ content = [
+ line for line in f
+ if not line.strip().upper().startswith('NISDOMAIN')
+ ]
+ except IOError:
+ content = []
+
+ content.append("NISDOMAIN={}\n".format(nisdomain))
+
+ with open(paths.SYSCONF_NETWORK, 'w') as f:
+ f.writelines(content)
+
+ def modify_nsswitch_pam_stack(self, sssd, mkhomedir, statestore,
+ sudo=True, subid=False):
+ auth_config = get_auth_tool()
+ auth_config.configure(sssd, mkhomedir, statestore, sudo, subid)
+
+ def is_nosssd_supported(self):
+ # The flag --no-sssd is not supported any more for rhel-based distros
+ return False
+
+ def backup_auth_configuration(self, path):
+ auth_config = get_auth_tool()
+ auth_config.backup(path)
+
+ def restore_auth_configuration(self, path):
+ auth_config = get_auth_tool()
+ auth_config.restore(path)
+
+ def migrate_auth_configuration(self, statestore):
+ """
+ Migrate the pam stack configuration from authconfig to an authselect
+ profile.
+ """
+ # Check if mkhomedir was enabled during installation
+ mkhomedir = statestore.get_state('authconfig', 'mkhomedir')
+
+ # Force authselect 'sssd' profile
+ authselect_cmd = [paths.AUTHSELECT, "select", "sssd", "with-sudo"]
+ if mkhomedir:
+ authselect_cmd.append("with-mkhomedir")
+ authselect_cmd.append("--force")
+ ipautil.run(authselect_cmd)
+
+ # Remove all remaining keys from the authconfig module
+ for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
+ statestore.restore_state('authconfig', conf)
+
+ # Create new authselect module in the statestore
+ statestore.backup_state('authselect', 'profile', 'sssd')
+ statestore.backup_state(
+ 'authselect', 'features_list', '')
+ statestore.backup_state('authselect', 'mkhomedir', bool(mkhomedir))
+
+ def reload_systemwide_ca_store(self):
+ try:
+ ipautil.run([paths.UPDATE_CA_TRUST])
+ except CalledProcessError as e:
+ logger.error(
+ "Could not update systemwide CA trust database: %s", e)
+ return False
+ else:
+ logger.info("Systemwide CA database updated.")
+ return True
+
+ def platform_insert_ca_certs(self, ca_certs):
+ return any([
+ self.write_p11kit_certs(paths.IPA_P11_KIT, ca_certs),
+ self.remove_ca_certificates_bundle(
+ paths.SYSTEMWIDE_IPA_CA_CRT
+ ),
+ ])
+
+ def write_p11kit_certs(self, filename, ca_certs):
+ # pylint: disable=ipa-forbidden-import
+ from ipalib import x509 # FixMe: break import cycle
+ from ipalib.errors import CertificateError
+ # pylint: enable=ipa-forbidden-import
+
+ path = Path(filename)
+ try:
+ f = open(path, 'w')
+ except IOError:
+ logger.error("Failed to open %s", path)
+ raise
+
+ with f:
+ f.write("# This file was created by IPA. Do not edit.\n"
+ "\n")
+
+ try:
+ os.fchmod(f.fileno(), 0o644)
+ except IOError:
+ logger.error("Failed to set mode of %s", path)
+ raise
+
+ has_eku = set()
+ for cert, nickname, trusted, _ext_key_usage in ca_certs:
+ try:
+ subject = cert.subject_bytes
+ issuer = cert.issuer_bytes
+ serial_number = cert.serial_number_bytes
+ public_key_info = cert.public_key_info_bytes
+ except (PyAsn1Error, ValueError, CertificateError):
+ logger.error(
+ "Failed to decode certificate \"%s\"", nickname)
+ raise
+
+ label = urllib.parse.quote(nickname)
+ subject = urllib.parse.quote(subject)
+ issuer = urllib.parse.quote(issuer)
+ serial_number = urllib.parse.quote(serial_number)
+ public_key_info = urllib.parse.quote(public_key_info)
+
+ obj = ("[p11-kit-object-v1]\n"
+ "class: certificate\n"
+ "certificate-type: x-509\n"
+ "certificate-category: authority\n"
+ "label: \"%(label)s\"\n"
+ "subject: \"%(subject)s\"\n"
+ "issuer: \"%(issuer)s\"\n"
+ "serial-number: \"%(serial_number)s\"\n"
+ "x-public-key-info: \"%(public_key_info)s\"\n" %
+ dict(label=label,
+ subject=subject,
+ issuer=issuer,
+ serial_number=serial_number,
+ public_key_info=public_key_info))
+ if trusted is True:
+ obj += "trusted: true\n"
+ elif trusted is False:
+ obj += "x-distrusted: true\n"
+ obj += "{pem}\n\n".format(
+ pem=cert.public_bytes(x509.Encoding.PEM).decode('ascii'))
+
+ f.write(obj)
+
+ if (cert.extended_key_usage is not None and
+ public_key_info not in has_eku):
+ try:
+ ext_key_usage = cert.extended_key_usage_bytes
+ except PyAsn1Error:
+ logger.error(
+ "Failed to encode extended key usage for \"%s\"",
+ nickname)
+ raise
+ value = urllib.parse.quote(ext_key_usage)
+ obj = ("[p11-kit-object-v1]\n"
+ "class: x-certificate-extension\n"
+ "label: \"ExtendedKeyUsage for %(label)s\"\n"
+ "x-public-key-info: \"%(public_key_info)s\"\n"
+ "object-id: 2.5.29.37\n"
+ "value: \"%(value)s\"\n\n" %
+ dict(label=label,
+ public_key_info=public_key_info,
+ value=value))
+ f.write(obj)
+ has_eku.add(public_key_info)
+
+ return True
+
+ def platform_remove_ca_certs(self):
+ return any([
+ self.remove_ca_certificates_bundle(paths.IPA_P11_KIT),
+ self.remove_ca_certificates_bundle(paths.SYSTEMWIDE_IPA_CA_CRT),
+ ])
+
+ def remove_ca_certificates_bundle(self, filename):
+ path = Path(filename)
+ if not path.is_file():
+ return False
+
+ try:
+ path.unlink()
+ except Exception:
+ logger.error("Could not remove %s", path)
+ raise
+
+ return True
+
+ def backup_hostname(self, fstore, statestore):
+ filepath = paths.ETC_HOSTNAME
+ if os.path.exists(filepath):
+ fstore.backup_file(filepath)
+
+ # store old hostname
+ old_hostname = socket.gethostname()
+ statestore.backup_state('network', 'hostname', old_hostname)
+
+ def restore_hostname(self, fstore, statestore):
+ old_hostname = statestore.restore_state('network', 'hostname')
+
+ if old_hostname is not None:
+ try:
+ self.set_hostname(old_hostname)
+ except ipautil.CalledProcessError as e:
+ logger.debug("%s", traceback.format_exc())
+ logger.error(
+ "Failed to restore this machine hostname to %s (%s).",
+ old_hostname, e
+ )
+
+ filepath = paths.ETC_HOSTNAME
+ if fstore.has_file(filepath):
+ fstore.restore_file(filepath)
+
+ def set_selinux_booleans(self, required_settings, backup_func=None):
+ def get_setsebool_args(changes):
+ args = [paths.SETSEBOOL, "-P"]
+ args.extend(["%s=%s" % update for update in changes.items()])
+
+ return args
+
+ if not self.is_selinux_enabled():
+ return False
+
+ updated_vars = {}
+ failed_vars = {}
+ for setting, state in required_settings.items():
+ if state is None:
+ continue
+ try:
+ result = ipautil.run(
+ [paths.GETSEBOOL, setting],
+ capture_output=True
+ )
+ original_state = result.output.split()[2]
+ if backup_func is not None:
+ backup_func(setting, original_state)
+
+ if original_state != state:
+ updated_vars[setting] = state
+ except ipautil.CalledProcessError as e:
+ logger.error("Cannot get SELinux boolean '%s': %s", setting, e)
+ failed_vars[setting] = state
+
+ if updated_vars:
+ args = get_setsebool_args(updated_vars)
+ try:
+ ipautil.run(args)
+ except ipautil.CalledProcessError:
+ failed_vars.update(updated_vars)
+
+ if failed_vars:
+ raise ipapython.errors.SetseboolError(
+ failed=failed_vars,
+ command=' '.join(get_setsebool_args(failed_vars)))
+
+ return True
+
+ def parse_ipa_version(self, version):
+ """
+ :param version: textual version
+ :return: object implementing proper __cmp__ method for version compare
+ """
+ return IPAVersion(version)
+
+ def configure_httpd_service_ipa_conf(self):
+ """Create systemd config for httpd service to work with IPA
+ """
+ if not os.path.exists(paths.SYSTEMD_SYSTEM_HTTPD_D_DIR):
+ os.mkdir(paths.SYSTEMD_SYSTEM_HTTPD_D_DIR, 0o755)
+
+ ipautil.copy_template_file(
+ os.path.join(paths.USR_SHARE_IPA_DIR, 'ipa-httpd.conf.template'),
+ paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF,
+ dict(
+ KDCPROXY_CONFIG=paths.KDCPROXY_CONFIG,
+ IPA_HTTPD_KDCPROXY=paths.IPA_HTTPD_KDCPROXY,
+ KRB5CC_HTTPD=paths.KRB5CC_HTTPD,
+ )
+ )
+
+ os.chmod(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, 0o644)
+ self.restore_context(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF)
+ self.systemd_daemon_reload()
+
+ def systemd_daemon_reload(self):
+ """Tell systemd to reload config files"""
+ ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"])
+
+ def configure_http_gssproxy_conf(self, ipauser):
+ ipautil.copy_template_file(
+ os.path.join(paths.USR_SHARE_IPA_DIR, 'gssproxy.conf.template'),
+ paths.GSSPROXY_CONF,
+ dict(
+ HTTP_KEYTAB=paths.HTTP_KEYTAB,
+ HTTPD_USER=constants.HTTPD_USER,
+ IPAAPI_USER=ipauser,
+ SWEEPER_SOCKET=paths.IPA_CCACHE_SWEEPER_GSSPROXY_SOCK,
+ )
+ )
+
+ os.chmod(paths.GSSPROXY_CONF, 0o600)
+ self.restore_context(paths.GSSPROXY_CONF)
+
+ def configure_httpd_wsgi_conf(self):
+ """Configure WSGI for correct Python version
+
+ See https://pagure.io/freeipa/issue/7394
+ """
+ conf = paths.HTTPD_IPA_WSGI_MODULES_CONF
+ if sys.version_info.major == 2:
+ wsgi_module = constants.MOD_WSGI_PYTHON2
+ else:
+ wsgi_module = constants.MOD_WSGI_PYTHON3
+
+ if conf is None or wsgi_module is None:
+ logger.info("Nothing to do for configure_httpd_wsgi_conf")
+ return
+
+ confdir = os.path.dirname(conf)
+ if not os.path.isdir(confdir):
+ os.makedirs(confdir)
+
+ ipautil.copy_template_file(
+ os.path.join(
+ paths.USR_SHARE_IPA_DIR, 'ipa-httpd-wsgi.conf.template'
+ ),
+ conf,
+ dict(WSGI_MODULE=wsgi_module)
+ )
+
+ os.chmod(conf, 0o644)
+ self.restore_context(conf)
+
+ def remove_httpd_service_ipa_conf(self):
+ """Remove systemd config for httpd service of IPA"""
+ try:
+ os.unlink(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ logger.debug(
+ 'Trying to remove %s but file does not exist',
+ paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF
+ )
+ else:
+ logger.error(
+ 'Error removing %s: %s',
+ paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e
+ )
+ return
+
+ self.systemd_daemon_reload()
+
+ def configure_httpd_protocol(self):
+ # use default crypto policy for SSLProtocol
+ directivesetter.set_directive(
+ paths.HTTPD_SSL_CONF, 'SSLProtocol', None, False
+ )
+
+ def set_hostname(self, hostname):
+ ipautil.run([paths.BIN_HOSTNAMECTL, 'set-hostname', hostname])
+
+ def is_fips_enabled(self):
+ """
+ Checks whether this host is FIPS-enabled.
+
+ Returns a boolean indicating if the host is FIPS-enabled, i.e. if the
+ file /proc/sys/crypto/fips_enabled contains a non-0 value. Otherwise,
+ or if the file /proc/sys/crypto/fips_enabled does not exist,
+ the function returns False.
+ """
+ try:
+ with open(paths.PROC_FIPS_ENABLED, 'r') as f:
+ if f.read().strip() != '0':
+ return True
+ except IOError:
+ # Consider that the host is not fips-enabled if the file does not
+ # exist
+ pass
+ return False
+
+ def setup_httpd_logging(self):
+ directivesetter.set_directive(paths.HTTPD_SSL_CONF,
+ 'ErrorLog',
+ 'logs/error_log', False)
+ directivesetter.set_directive(paths.HTTPD_SSL_CONF,
+ 'TransferLog',
+ 'logs/access_log', False)
+
+ def configure_dns_resolver(self, nameservers, searchdomains, *,
+ resolve1_enabled=False, fstore=None):
+ """Configure global DNS resolver (e.g. /etc/resolv.conf)
+
+ :param nameservers: list of IP addresses
+ :param searchdomains: list of search domaons
+ :param fstore: optional file store for backup
+ """
+ assert nameservers and isinstance(nameservers, list)
+ assert searchdomains and isinstance(searchdomains, list)
+
+ super().configure_dns_resolver(
+ nameservers=nameservers,
+ searchdomains=searchdomains,
+ resolve1_enabled=resolve1_enabled,
+ fstore=fstore
+ )
+
+ # break circular import
+ from ipaplatform.services import knownservices
+
+ if fstore is not None and not fstore.has_file(paths.RESOLV_CONF):
+ fstore.backup_file(paths.RESOLV_CONF)
+
+ nm = knownservices['NetworkManager']
+ nm_enabled = nm.is_enabled()
+ if nm_enabled:
+ logger.debug(
+ "Network Manager is enabled, write %s",
+ paths.NETWORK_MANAGER_IPA_CONF
+ )
+ # write DNS override and reload network manager to have it create
+ # a new resolv.conf. The file is prefixed with ``zzz`` to
+ # make it the last file. Global dns options do not stack and last
+ # man standing wins.
+ if resolve1_enabled:
+ # push DNS configuration to systemd-resolved
+ dnsprocessing = "systemd-resolved"
+ else:
+ # update /etc/resolv.conf
+ dnsprocessing = "default"
+
+ cfg = NM_IPA_CONF.format(
+ dnsprocessing=dnsprocessing,
+ servers=','.join(nameservers),
+ searches=','.join(searchdomains)
+ )
+ with open(paths.NETWORK_MANAGER_IPA_CONF, 'w') as f:
+ os.fchmod(f.fileno(), 0o644)
+ f.write(cfg)
+ # reload NetworkManager
+ nm.reload_or_restart()
+
+ if not resolve1_enabled and not nm_enabled:
+ # no NM running, no systemd-resolved detected
+ # fall back to /etc/resolv.conf
+ logger.debug(
+ "Neither Network Manager nor systemd-resolved are enabled, "
+ "write %s directly.", paths.RESOLV_CONF
+ )
+ cfg = [
+ "# auto-generated by IPA installer",
+ "search {}".format(' '.join(searchdomains)),
+ ]
+ for nameserver in nameservers:
+ cfg.append("nameserver {}".format(nameserver))
+ with open(paths.RESOLV_CONF, 'w') as f:
+ f.write('\n'.join(cfg))
+
+ def unconfigure_dns_resolver(self, fstore=None):
+ """Unconfigure global DNS resolver (e.g. /etc/resolv.conf)
+
+ :param fstore: optional file store for restore
+ """
+ super().unconfigure_dns_resolver(fstore=fstore)
+ # break circular import
+ from ipaplatform.services import knownservices
+
+ nm = knownservices['NetworkManager']
+ if os.path.isfile(paths.NETWORK_MANAGER_IPA_CONF):
+ os.unlink(paths.NETWORK_MANAGER_IPA_CONF)
+ if nm.is_enabled():
+ nm.reload_or_restart()
+
+ def configure_pkcs11_modules(self, fstore):
+ """Disable global p11-kit configuration for NSS
+ """
+ filenames = []
+ for name, module, disabled_in in PKCS11_MODULES:
+ filename = os.path.join(
+ paths.ETC_PKCS11_MODULES_DIR,
+ "{}.module".format(name)
+ )
+ if os.path.isfile(filename):
+ # Only back up if file is not yet backed up and it does not
+ # look like a file that is generated by IPA.
+ with open(filename) as f:
+ content = f.read()
+ is_ipa_file = "IPA" in content
+ if not is_ipa_file and not fstore.has_file(filename):
+ logger.debug("Backing up existing '%s'.", filename)
+ fstore.backup_file(filename)
+
+ with open(filename, "w") as f:
+ f.write("# created by IPA installer\n")
+ f.write("module: {}\n".format(module))
+ # see man(5) pkcs11.conf
+ f.write("disable-in: {}\n".format(", ".join(disabled_in)))
+ os.fchmod(f.fileno(), 0o644)
+ self.restore_context(filename)
+ logger.debug("Created PKCS#11 module config '%s'.", filename)
+ filenames.append(filename)
+
+ return filenames
+
+ def restore_pkcs11_modules(self, fstore):
+ """Restore global p11-kit configuration for NSS
+ """
+ filenames = []
+ for name, _module, _disabled_in in PKCS11_MODULES:
+ filename = os.path.join(
+ paths.ETC_PKCS11_MODULES_DIR,
+ "{}.module".format(name)
+ )
+ try:
+ os.unlink(filename)
+ except OSError:
+ pass
+ else:
+ filenames.append(filename)
+
+ if fstore.has_file(filename):
+ fstore.restore_file(filename)
+
+ return filenames
+
+ def get_pkcs11_modules(self):
+ """Return the list of module config files setup by IPA
+ """
+ return tuple(os.path.join(paths.ETC_PKCS11_MODULES_DIR,
+ "{}.module".format(name))
+ for name, _module, _disabled in PKCS11_MODULES)
+
+ def enable_sssd_sudo(self, _fstore):
+ """sudo enablement is handled by authselect"""
+
+ def disable_ldap_automount(self, statestore):
+ """Disable ldap-based automount"""
+ super(OpenCloudOSTaskNamespace, self).disable_ldap_automount(statestore)
+
+ authselect_cmd = [paths.AUTHSELECT, "disable-feature",
+ "with-custom-automount"]
+ try:
+ ipautil.run(authselect_cmd)
+ except ipautil.CalledProcessError:
+ logger.info("Unable to disable with-custom-automount feature")
+ logger.info("It may happen if the configuration was done "
+ "using authconfig instead of authselect")
+
+
+tasks = OpenCloudOSTaskNamespace()
diff -Naur freeipa-4.10.1.orig/ipaplatform/setup.py freeipa-4.10.1/ipaplatform/setup.py
--- freeipa-4.10.1.orig/ipaplatform/setup.py 2023-04-26 16:38:55.347662250 +0800
+++ freeipa-4.10.1/ipaplatform/setup.py 2023-04-26 16:39:37.408653916 +0800
@@ -40,8 +40,9 @@
"ipaplatform.redhat",
"ipaplatform.rhel",
"ipaplatform.rhel_container",
- "ipaplatform.suse"
- ],
+ "ipaplatform.suse",
+ "ipaplatform.opencloudos"
+ ],
install_requires=[
"cffi",
# "ipalib", # circular dependency
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。