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

Reply via email to