URL: https://github.com/freeipa/freeipa/pull/4536 Author: tiran Title: #4536: [Backport][ipa-4-8] Support for SUSE/openSUSE ipaplatform Action: opened
PR body: """ This PR was opened automatically because PR #4532 was pushed to master and backport to ipa-4-8 is required. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/4536/head:pr4536 git checkout pr4536
From 50fd8058894cffb6e2573e0c88c42c7c2b82bffa Mon Sep 17 00:00:00 2001 From: Stasiek Michalski <stas...@michalski.cc> Date: Wed, 15 Apr 2020 03:12:16 +0200 Subject: [PATCH] Support for SUSE/openSUSE ipaplatform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Howard Guo <h...@suse.com> Co-authored-by: Daniel Molkentin <dmolken...@suse.com> Co-authored-by: Marcus Rückert <da...@nordisch.org> --- ipaplatform/setup.py | 3 +- ipaplatform/suse/__init__.py | 7 ++ ipaplatform/suse/constants.py | 22 ++++ ipaplatform/suse/paths.py | 87 +++++++++++++++ ipaplatform/suse/services.py | 194 ++++++++++++++++++++++++++++++++++ ipaplatform/suse/tasks.py | 46 ++++++++ 6 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 ipaplatform/suse/__init__.py create mode 100644 ipaplatform/suse/constants.py create mode 100644 ipaplatform/suse/paths.py create mode 100644 ipaplatform/suse/services.py create mode 100644 ipaplatform/suse/tasks.py diff --git a/ipaplatform/setup.py b/ipaplatform/setup.py index 69101794ec..c2e656f3d3 100644 --- a/ipaplatform/setup.py +++ b/ipaplatform/setup.py @@ -38,7 +38,8 @@ "ipaplatform.debian", "ipaplatform.fedora", "ipaplatform.redhat", - "ipaplatform.rhel" + "ipaplatform.rhel", + "ipaplatform.suse" ], install_requires=[ "cffi", diff --git a/ipaplatform/suse/__init__.py b/ipaplatform/suse/__init__.py new file mode 100644 index 0000000000..99ce58f60c --- /dev/null +++ b/ipaplatform/suse/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (C) 2020 FreeIPA Contributors, see COPYING for license +# + +""" +This module contains SUSE specific platform files. +""" diff --git a/ipaplatform/suse/constants.py b/ipaplatform/suse/constants.py new file mode 100644 index 0000000000..559943b37e --- /dev/null +++ b/ipaplatform/suse/constants.py @@ -0,0 +1,22 @@ +# +# Copyright (C) 2020 FreeIPA Contributors, see COPYING for license +# + +""" +This SUSE OS family base platform module exports default platform +related constants for the SUSE OS family-based systems. +""" + +# Fallback to default path definitions +from ipaplatform.base.constants import BaseConstantsNamespace + + +class SuseConstantsNamespace(BaseConstantsNamespace): + HTTPD_USER = "wwwrun" + HTTPD_GROUP = "www" + # Don't have it yet + SSSD_USER = "root" + TLS_HIGH_CIPHERS = None + + +constants = SuseConstantsNamespace() diff --git a/ipaplatform/suse/paths.py b/ipaplatform/suse/paths.py new file mode 100644 index 0000000000..2f01bc2c46 --- /dev/null +++ b/ipaplatform/suse/paths.py @@ -0,0 +1,87 @@ +# +# Copyright (C) 2020 FreeIPA Contributors, see COPYING for license +# + +""" +This SUSE OS family base platform module exports default filesystem paths as +common in SUSE OS family-based systems. +""" + +from ipaplatform.base.paths import BasePathNamespace + + +class SusePathNamespace(BasePathNamespace): + BIN_HOSTNAMECTL = "/usr/bin/hostnamectl" + SYSTEMCTL = "/usr/bin/systemctl" + ETC_HTTPD_DIR = "/etc/apache2" + HTTPD_ALIAS_DIR = "/etc/apache2/ipa" + GSSAPI_SESSION_KEY = "/etc/apache2/ipa/ipasession.key" + HTTPD_CONF_D_DIR = "/etc/apache2/conf.d/" + HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/apache2/conf.d/ipa-kdc-proxy.conf" + HTTPD_IPA_PKI_PROXY_CONF = "/etc/apache2/conf.d/ipa-pki-proxy.conf" + HTTPD_IPA_REWRITE_CONF = "/etc/apache2/conf.d/ipa-rewrite.conf" + HTTPD_IPA_CONF = "/etc/apache2/conf.d/ipa.conf" + HTTPD_NSS_CONF = "/etc/apache2/conf.d/nss.conf" + HTTPD_SSL_CONF = "/etc/apache2/conf.d/ssl.conf" + HTTPD_SSL_SITE_CONF = "/etc/apache2/conf.d/ssl.conf" + HTTPD_PASSWORD_CONF = "/etc/apache2/ipa/password.conf" + NAMED_CUSTOM_CONFIG = "/etc/named.d/ipa-ext.conf" + NAMED_VAR_DIR = "/var/lib/named" + NAMED_MANAGED_KEYS_DIR = "/var/lib/named/dyn" + IPA_P11_KIT = "/etc/pki/trust/ipa.p11-kit" + # Those files are only here to be able to configure them, we copy those in + # rpm spec to fillupdir + SYSCONFIG_HTTPD = "/etc/sysconfig/apache2" + SYSCONFIG_NAMED = "/etc/sysconfig/named-named" + SYSCONFIG_NTPD = "/etc/sysconfig/ntp" + SYSCONF_NETWORK = "/etc/sysconfig/network/config" + SYSTEMD_SYSTEM_HTTPD_D_DIR = "/etc/systemd/system/apache2.service.d/" + SYSTEMD_SYSTEM_HTTPD_IPA_CONF = ( + "/etc/systemd/system/apache2.service.d/ipa.conf" + ) + CERTMONGER_COMMAND_TEMPLATE = "/usr/lib/ipa/certmonger/%s" + CHROMIUM_BROWSER = "/usr/bin/chromium" + BIN_NISDOMAINNAME = "/bin/nisdomainname" + BIND_LDAP_DNS_IPA_WORKDIR = "/var/lib/named/dyndb-ldap/ipa/" + BIND_LDAP_DNS_ZONE_WORKDIR = "/var/lib/named/dyndb-ldap/ipa/master/" + PAM_KRB5_SO = "/lib/security/pam_krb5.so" + PAM_KRB5_SO_64 = PAM_KRB5_SO + # openSUSE still uses lib for libexec, this will change when we don't + # anymore + LIBEXEC_CERTMONGER_DIR = "/usr/lib/certmonger" + DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT = ( + "/usr/lib/certmonger/dogtag-ipa-ca-renew-agent-submit" + ) + DOGTAG_IPA_RENEW_AGENT_SUBMIT = ( + "/usr/lib/certmonger/dogtag-ipa-renew-agent-submit" + ) + CERTMONGER_DOGTAG_SUBMIT = "/usr/lib/certmonger/dogtag-submit" + IPA_SERVER_GUARD = "/usr/lib/certmonger/ipa-server-guard" + GENERATE_RNDC_KEY = "/usr/lib/generate-rndc-key.sh" + LIBEXEC_IPA_DIR = "/usr/lib/ipa" + IPA_DNSKEYSYNCD_REPLICA = "/usr/lib/ipa/ipa-dnskeysync-replica" + IPA_DNSKEYSYNCD = "/usr/lib/ipa/ipa-dnskeysyncd" + IPA_HTTPD_KDCPROXY = "/usr/lib/ipa/ipa-httpd-kdcproxy" + IPA_ODS_EXPORTER = "/usr/lib/ipa/ipa-ods-exporter" + IPA_PKI_RETRIEVE_KEY = "/usr/lib/ipa/ipa-pki-retrieve-key" + IPA_HTTPD_PASSWD_READER = "/usr/lib/ipa/ipa-httpd-pwdreader" + IPA_PKI_WAIT_RUNNING = "/usr/lib/ipa/ipa-pki-wait-running" + DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11" + VAR_KERBEROS_KRB5KDC_DIR = "/var/lib/kerberos/krb5kdc/" + VAR_KRB5KDC_K5_REALM = "/var/lib/kerberos/krb5kdc/.k5." + CACERT_PEM = "/var/lib/kerberos/krb5kdc/cacert.pem" + KRB5KDC_KADM5_ACL = "/var/lib/kerberos/krb5kdc/kadm5.acl" + KRB5KDC_KADM5_KEYTAB = "/var/lib/kerberos/krb5kdc/kadm5.keytab" + KRB5KDC_KDC_CONF = "/var/lib/kerberos/krb5kdc/kdc.conf" + KDC_CERT = "/var/lib/kerberos/krb5kdc/kdc.crt" + KDC_KEY = "/var/lib/kerberos/krb5kdc/kdc.key" + NAMED_RUN = "/var/lib/named/data/named.run" + IPA_CUSTODIA_HANDLER = "/usr/lib/ipa/custodia" + WSGI_PREFIX_DIR = "/run/apache2/wsgi" + KDESTROY = "/usr/lib/mit/bin/kdestroy" + BIN_KVNO = "/usr/lib/mit/bin/kvno" + UPDATE_CA_TRUST = "/usr/sbin/update-ca-certificates" + AUTHSELECT = "/usr/bin/authselect" + + +paths = SusePathNamespace() diff --git a/ipaplatform/suse/services.py b/ipaplatform/suse/services.py new file mode 100644 index 0000000000..df11dd741c --- /dev/null +++ b/ipaplatform/suse/services.py @@ -0,0 +1,194 @@ +# +# Copyright (C) 2020 FreeIPA Contributors, see COPYING for license +# + +import os +import logging +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__) + +suse_system_units = dict( + (x, "%s.service" % x) for x in base_services.wellknownservices +) +suse_system_units["httpd"] = "apache2.service" + +suse_system_units["dirsrv"] = "dirsrv@.service" +suse_system_units["pki-tomcatd"] = "pki-tomcatd@pki-tomcat.service" +suse_system_units["pki_tomcatd"] = suse_system_units["pki-tomcatd"] +suse_system_units["ipa-otpd"] = "ipa-otpd.socket" +suse_system_units["ipa-dnskeysyncd"] = "ipa-dnskeysyncd.service" +suse_system_units["named-regular"] = "named.service" +suse_system_units["named-pkcs11"] = "named.service" +suse_system_units["named"] = "named.service" +suse_system_units["ods-enforcerd"] = "ods-enforcerd.service" +suse_system_units["ods_enforcerd"] = suse_system_units["ods-enforcerd"] +suse_system_units["ods-signerd"] = "ods-signerd.service" +suse_system_units["ods_signerd"] = suse_system_units["ods-signerd"] + + +class SuseService(base_services.SystemdService): + system_units = suse_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: + systemd_name = "%s.service" % (service_name) + super().__init__(service_name, systemd_name, api) + + +class SuseDirectoryService(SuseService): + 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().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().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 SuseIPAService(SuseService): + # Credits to upstream developer + def enable(self, instance_name=""): + super().enable(instance_name) + self.restart(instance_name) + + +class SuseCAService(SuseService): + # Credits to upstream developer + 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().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 + + +def suse_service_class_factory(name, api): + if name == "dirsrv": + return SuseDirectoryService(name, api) + if name == "ipa": + return SuseIPAService(name, api) + if name in ("pki-tomcatd", "pki_tomcatd"): + return SuseCAService(name, api) + return SuseService(name, api) + + +class SuseServices(base_services.KnownServices): + def service_class_factory(self, name, api=None): + return suse_service_class_factory(name, api) + + # Credits to upstream developer + def __init__(self): + # pylint: disable=ipa-forbidden-import + import ipalib + + # pylint: enable=ipa-forbidden-import + services = dict() + for s in base_services.wellknownservices: + services[s] = self.service_class_factory(s, ipalib.api) + super().__init__(services) + + +timedate_services = ["ntpd"] +service = suse_service_class_factory +knownservices = SuseServices() diff --git a/ipaplatform/suse/tasks.py b/ipaplatform/suse/tasks.py new file mode 100644 index 0000000000..3885c2600a --- /dev/null +++ b/ipaplatform/suse/tasks.py @@ -0,0 +1,46 @@ +# +# Copyright (C) 2020 FreeIPA Contributors, see COPYING for license +# + +""" +This module contains default SUSE OS family-specific implementations of +system tasks. +""" + +import logging + +from ipaplatform.paths import paths +from ipaplatform.redhat.tasks import RedHatTaskNamespace + +logger = logging.getLogger(__name__) + + +class SuseTaskNamespace(RedHatTaskNamespace): + def restore_context(self, filepath, force=False): + pass # FIXME: Implement after libexec move + + def check_selinux_status(self, restorecon=paths.RESTORECON): + pass # FIXME: Implement after libexec move + + def set_nisdomain(self, nisdomain): + nis_variable = "NETCONFIG_NIS_STATIC_DOMAIN" + try: + with open(paths.SYSCONF_NETWORK, "r") as f: + content = [ + line + for line in f + if not line.strip().upper().startswith(nis_variable) + ] + except IOError: + content = [] + + content.append("{}={}\n".format(nis_variable, nisdomain)) + + with open(paths.SYSCONF_NETWORK, "w") as f: + f.writelines(content) + + def set_selinux_booleans(self, required_settings, backup_func=None): + return False # FIXME: Implement after libexec move + + +tasks = SuseTaskNamespace()
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org