pgreco / rpms / ipa

Forked from forks/areguera/rpms/ipa 4 years ago
Clone

Blame SOURCES/0014-Allow_ipaapi_and_Apache_user_to_access_SSSD_IFP_rhbz#1639910.patch

6d47df
From 785c496dceb76a8f628249ce598e0540b1dfec6e Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Tue, 6 Nov 2018 13:57:14 +0100
6d47df
Subject: [PATCH] Allow ipaapi user to access SSSD's info pipe
6d47df
6d47df
For smart card authentication, ipaapi must be able to access to sss-ifp.
6d47df
During installation and upgrade, the ipaapi user is now added to
6d47df
[ifp]allowed_uids.
6d47df
6d47df
The commit also fixes two related issues:
6d47df
6d47df
* The server upgrade code now enables ifp service in sssd.conf. The
6d47df
  existing code modified sssd.conf but never wrote the changes to disk.
6d47df
* sssd_enable_service() no longer fails after it has detected an
6d47df
  unrecognized service.
6d47df
6d47df
Fixes: https://pagure.io/freeipa/issue/7751
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipaclient/install/client.py                | 41 ++++++++++++++++++----
6d47df
 ipaserver/install/server/upgrade.py        | 27 +++++++++-----
6d47df
 ipatests/test_integration/test_commands.py | 31 ++++++++++++++++
6d47df
 3 files changed, 83 insertions(+), 16 deletions(-)
6d47df
6d47df
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
6d47df
index 05255fe61b..f9b003ef57 100644
6d47df
--- a/ipaclient/install/client.py
6d47df
+++ b/ipaclient/install/client.py
6d47df
@@ -35,6 +35,7 @@
6d47df
 # pylint: enable=import-error
6d47df
 
6d47df
 from ipalib import api, errors, x509
6d47df
+from ipalib.constants import IPAAPI_USER
6d47df
 from ipalib.install import certmonger, certstore, service, sysrestore
6d47df
 from ipalib.install import hostname as hostname_
6d47df
 from ipalib.install.kinit import kinit_keytab, kinit_password
6d47df
@@ -914,7 +915,7 @@ def configure_sssd_conf(
6d47df
         domain = sssdconfig.new_domain(cli_domain)
6d47df
 
6d47df
     if options.on_master:
6d47df
-        sssd_enable_service(sssdconfig, 'ifp')
6d47df
+        sssd_enable_ifp(sssdconfig)
6d47df
 
6d47df
     if (
6d47df
         (options.conf_ssh and os.path.isfile(paths.SSH_CONFIG)) or
6d47df
@@ -1018,21 +1019,47 @@ def configure_sssd_conf(
6d47df
     return 0
6d47df
 
6d47df
 
6d47df
-def sssd_enable_service(sssdconfig, service):
6d47df
+def sssd_enable_service(sssdconfig, name):
6d47df
     try:
6d47df
-        sssdconfig.new_service(service)
6d47df
+        sssdconfig.new_service(name)
6d47df
     except SSSDConfig.ServiceAlreadyExists:
6d47df
         pass
6d47df
     except SSSDConfig.ServiceNotRecognizedError:
6d47df
         logger.error(
6d47df
-            "Unable to activate the %s service in SSSD config.", service)
6d47df
+            "Unable to activate the '%s' service in SSSD config.", name)
6d47df
         logger.info(
6d47df
             "Please make sure you have SSSD built with %s support "
6d47df
-            "installed.", service)
6d47df
+            "installed.", name)
6d47df
         logger.info(
6d47df
-            "Configure %s support manually in /etc/sssd/sssd.conf.", service)
6d47df
+            "Configure %s support manually in /etc/sssd/sssd.conf.", name)
6d47df
+        return None
6d47df
 
6d47df
-    sssdconfig.activate_service(service)
6d47df
+    sssdconfig.activate_service(name)
6d47df
+    return sssdconfig.get_service(name)
6d47df
+
6d47df
+
6d47df
+def sssd_enable_ifp(sssdconfig):
6d47df
+    """Enable and configure libsss_simpleifp plugin
6d47df
+    """
6d47df
+    service = sssd_enable_service(sssdconfig, 'ifp')
6d47df
+    if service is None:
6d47df
+        # unrecognized service
6d47df
+        return
6d47df
+
6d47df
+    try:
6d47df
+        uids = service.get_option('allowed_uids')
6d47df
+    except SSSDConfig.NoOptionError:
6d47df
+        uids = set()
6d47df
+    else:
6d47df
+        uids = {s.strip() for s in uids.split(',') if s.strip()}
6d47df
+    # SSSD supports numeric and string UIDs
6d47df
+    # ensure that root is allowed to access IFP, might be 0 or root
6d47df
+    if uids.isdisjoint({'0', 'root'}):
6d47df
+        uids.add('root')
6d47df
+    # allow IPA API to access IFP
6d47df
+    uids.add(IPAAPI_USER)
6d47df
+    service.set_option('allowed_uids', ', '.join(sorted(uids)))
6d47df
+    sssdconfig.save_service(service)
6d47df
 
6d47df
 
6d47df
 def change_ssh_config(filename, changes, sections):
6d47df
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
6d47df
index 96c95b7a07..698afd347e 100644
6d47df
--- a/ipaserver/install/server/upgrade.py
6d47df
+++ b/ipaserver/install/server/upgrade.py
6d47df
@@ -23,7 +23,7 @@
6d47df
 import ipalib.util
6d47df
 import ipalib.errors
6d47df
 from ipaclient.install import timeconf
6d47df
-from ipaclient.install.client import sssd_enable_service
6d47df
+from ipaclient.install.client import sssd_enable_ifp
6d47df
 from ipaplatform import services
6d47df
 from ipaplatform.tasks import tasks
6d47df
 from ipapython import ipautil, version
6d47df
@@ -1408,6 +1408,22 @@ def set_sssd_domain_option(option, value):
6d47df
     sssdconfig.write(paths.SSSD_CONF)
6d47df
 
6d47df
 
6d47df
+def sssd_update():
6d47df
+    sssdconfig = SSSDConfig.SSSDConfig()
6d47df
+    sssdconfig.import_config()
6d47df
+    # upgrade domain
6d47df
+    domain = sssdconfig.get_domain(str(api.env.domain))
6d47df
+    domain.set_option('ipa_server_mode', 'True')
6d47df
+    domain.set_option('ipa_server', api.env.host)
6d47df
+    sssdconfig.save_domain(domain)
6d47df
+    # enable and configure IFP plugin
6d47df
+    sssd_enable_ifp(sssdconfig)
6d47df
+    # write config and restart service
6d47df
+    sssdconfig.write(paths.SSSD_CONF)
6d47df
+    sssd = services.service('sssd', api)
6d47df
+    sssd.restart()
6d47df
+
6d47df
+
6d47df
 def remove_ds_ra_cert(subject_base):
6d47df
     logger.info('[Removing RA cert from DS NSS database]')
6d47df
 
6d47df
@@ -2017,15 +2033,8 @@ def upgrade_configuration():
6d47df
         cainstance.ensure_ipa_authority_entry()
6d47df
 
6d47df
     migrate_to_authselect()
6d47df
-    set_sssd_domain_option('ipa_server_mode', 'True')
6d47df
-    set_sssd_domain_option('ipa_server', api.env.host)
6d47df
 
6d47df
-    sssdconfig = SSSDConfig.SSSDConfig()
6d47df
-    sssdconfig.import_config()
6d47df
-    sssd_enable_service(sssdconfig, 'ifp')
6d47df
-
6d47df
-    sssd = services.service('sssd', api)
6d47df
-    sssd.restart()
6d47df
+    sssd_update()
6d47df
 
6d47df
     krb = krbinstance.KrbInstance(fstore)
6d47df
     krb.fqdn = fqdn
6d47df
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
6d47df
index 640eacfa06..1aa1bb3313 100644
6d47df
--- a/ipatests/test_integration/test_commands.py
6d47df
+++ b/ipatests/test_integration/test_commands.py
6d47df
@@ -20,6 +20,8 @@
6d47df
 from cryptography.hazmat.backends import default_backend
6d47df
 from cryptography import x509
6d47df
 
6d47df
+from ipalib.constants import IPAAPI_USER
6d47df
+
6d47df
 from ipaplatform.paths import paths
6d47df
 
6d47df
 from ipatests.test_integration.base import IntegrationTest
6d47df
@@ -28,6 +30,7 @@
6d47df
 
6d47df
 logger = logging.getLogger(__name__)
6d47df
 
6d47df
+
6d47df
 class TestIPACommand(IntegrationTest):
6d47df
     """
6d47df
     A lot of commands can be executed against a single IPA installation
6d47df
@@ -429,3 +432,31 @@ def test_certificate_out_write_to_file(self):
6d47df
             x509.load_pem_x509_certificate(data, backend=default_backend())
6d47df
 
6d47df
             self.master.run_command(['rm', '-f', filename])
6d47df
+
6d47df
+    def test_sssd_ifp_access_ipaapi(self):
6d47df
+        # check that ipaapi is allowed to access sssd-ifp for smartcard auth
6d47df
+        # https://pagure.io/freeipa/issue/7751
6d47df
+        username = 'admin'
6d47df
+        # get UID for user
6d47df
+        result = self.master.run_command(['ipa', 'user-show', username])
6d47df
+        mo = re.search(r'UID: (\d+)', result.stdout_text)
6d47df
+        assert mo is not None, result.stdout_text
6d47df
+        uid = mo.group(1)
6d47df
+
6d47df
+        cmd = [
6d47df
+            'dbus-send',
6d47df
+            '--print-reply', '--system',
6d47df
+            '--dest=org.freedesktop.sssd.infopipe',
6d47df
+            '/org/freedesktop/sssd/infopipe/Users',
6d47df
+            'org.freedesktop.sssd.infopipe.Users.FindByName',
6d47df
+            'string:{}'.format(username)
6d47df
+        ]
6d47df
+        # test IFP as root
6d47df
+        result = self.master.run_command(cmd)
6d47df
+        assert uid in result.stdout_text
6d47df
+
6d47df
+        # test IFP as ipaapi
6d47df
+        result = self.master.run_command(
6d47df
+            ['sudo', '-u', IPAAPI_USER, '--'] + cmd
6d47df
+        )
6d47df
+        assert uid in result.stdout_text
6d47df
From eb0136ea3438b6fb1145456478f401b9b7467cba Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Fri, 16 Nov 2018 14:11:16 +0100
6d47df
Subject: [PATCH] Remove dead code
6d47df
6d47df
set_sssd_domain_option() is no longer used. Changes are handled by
6d47df
sssd_update().
6d47df
6d47df
See: https://pagure.io/freeipa/issue/7751
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipaserver/install/server/upgrade.py | 9 ---------
6d47df
 1 file changed, 9 deletions(-)
6d47df
6d47df
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
6d47df
index f1e78beb27..71bdd3670c 100644
6d47df
--- a/ipaserver/install/server/upgrade.py
6d47df
+++ b/ipaserver/install/server/upgrade.py
6d47df
@@ -1399,15 +1399,6 @@ def fix_schema_file_syntax():
6d47df
     sysupgrade.set_upgrade_state('ds', 'fix_schema_syntax', True)
6d47df
 
6d47df
 
6d47df
-def set_sssd_domain_option(option, value):
6d47df
-    sssdconfig = SSSDConfig.SSSDConfig()
6d47df
-    sssdconfig.import_config()
6d47df
-    domain = sssdconfig.get_domain(str(api.env.domain))
6d47df
-    domain.set_option(option, value)
6d47df
-    sssdconfig.save_domain(domain)
6d47df
-    sssdconfig.write(paths.SSSD_CONF)
6d47df
-
6d47df
-
6d47df
 def sssd_update():
6d47df
     sssdconfig = SSSDConfig.SSSDConfig()
6d47df
     sssdconfig.import_config()
6d47df
From 415295a6f68f4c797529e19a3f0cf956619d4bed Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Fri, 16 Nov 2018 14:51:23 +0100
6d47df
Subject: [PATCH] Allow HTTPd user to access SSSD IFP
6d47df
6d47df
For smart card and certificate authentication, Apache's
6d47df
mod_lookup_identity module must be able to acess SSSD IFP. The module
6d47df
accesses IFP as Apache user, not as ipaapi user.
6d47df
6d47df
Apache is not allowed to use IFP by default. The update code uses the
6d47df
service's ok-to-auth-as-delegate flag to detect smart card / cert auth.
6d47df
6d47df
See: https://pagure.io/freeipa/issue/7751
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipaclient/install/client.py         | 10 +++++++++-
6d47df
 ipaserver/install/server/upgrade.py | 11 ++++++++++-
6d47df
 2 files changed, 19 insertions(+), 2 deletions(-)
6d47df
6d47df
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
6d47df
index f9b003ef57..6125588802 100644
6d47df
--- a/ipaclient/install/client.py
6d47df
+++ b/ipaclient/install/client.py
6d47df
@@ -47,6 +47,7 @@
6d47df
     verify_host_resolvable,
6d47df
 )
6d47df
 from ipaplatform import services
6d47df
+from ipaplatform.constants import constants
6d47df
 from ipaplatform.paths import paths
6d47df
 from ipaplatform.tasks import tasks
6d47df
 from ipapython import certdb, kernel_keyring, ipaldap, ipautil
6d47df
@@ -1038,8 +1039,13 @@ def sssd_enable_service(sssdconfig, name):
6d47df
     return sssdconfig.get_service(name)
6d47df
 
6d47df
 
6d47df
-def sssd_enable_ifp(sssdconfig):
6d47df
+def sssd_enable_ifp(sssdconfig, allow_httpd=False):
6d47df
     """Enable and configure libsss_simpleifp plugin
6d47df
+
6d47df
+    Allow the ``ipaapi`` user to access IFP. In case allow_httpd is true,
6d47df
+    the Apache HTTPd user is also allowed to access IFP. For smart card
6d47df
+    authentication, mod_lookup_identity must be allowed to access user
6d47df
+    information.
6d47df
     """
6d47df
     service = sssd_enable_service(sssdconfig, 'ifp')
6d47df
     if service is None:
6d47df
@@ -1058,6 +1064,8 @@ def sssd_enable_ifp(sssdconfig):
6d47df
         uids.add('root')
6d47df
     # allow IPA API to access IFP
6d47df
     uids.add(IPAAPI_USER)
6d47df
+    if allow_httpd:
6d47df
+        uids.add(constants.HTTPD_USER)
6d47df
     service.set_option('allowed_uids', ', '.join(sorted(uids)))
6d47df
     sssdconfig.save_service(service)
6d47df
 
6d47df
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
6d47df
index 71bdd3670c..4de7fd974d 100644
6d47df
--- a/ipaserver/install/server/upgrade.py
6d47df
+++ b/ipaserver/install/server/upgrade.py
6d47df
@@ -1407,8 +1407,17 @@ def sssd_update():
6d47df
     domain.set_option('ipa_server_mode', 'True')
6d47df
     domain.set_option('ipa_server', api.env.host)
6d47df
     sssdconfig.save_domain(domain)
6d47df
+    # check if service has ok_to_auth_as_delegate
6d47df
+    service = 'HTTP/{}'.format(api.env.host)
6d47df
+    result = api.Command.service_show(service, all=True)
6d47df
+    flag = result['result'].get('ipakrboktoauthasdelegate', False)
6d47df
+    if flag:
6d47df
+        logger.debug(
6d47df
+            "%s has ok_to_auth_as_delegate, allow Apache to access IFP",
6d47df
+            services
6d47df
+        )
6d47df
     # enable and configure IFP plugin
6d47df
-    sssd_enable_ifp(sssdconfig)
6d47df
+    sssd_enable_ifp(sssdconfig, allow_httpd=flag)
6d47df
     # write config and restart service
6d47df
     sssdconfig.write(paths.SSSD_CONF)
6d47df
     sssd = services.service('sssd', api)
6d47df
From d7d17ece57ae1322c8368b7853f24d56b1d6a150 Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Fri, 16 Nov 2018 14:54:32 +0100
6d47df
Subject: [PATCH] Smart card auth advise: Allow Apache user
6d47df
6d47df
Modify the smard card auth advise script to use sssd_enable_ifp() in
6d47df
order to allow Apache to access SSSD IFP.
6d47df
6d47df
See: https://pagure.io/freeipa/issue/7751
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipaserver/advise/plugins/smart_card_auth.py | 21 ++++++++++++++++++++-
6d47df
 1 file changed, 20 insertions(+), 1 deletion(-)
6d47df
6d47df
diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py
6d47df
index 97e23303b0..9a7a315ed5 100644
6d47df
--- a/ipaserver/advise/plugins/smart_card_auth.py
6d47df
+++ b/ipaserver/advise/plugins/smart_card_auth.py
6d47df
@@ -105,6 +105,7 @@ class config_server_for_smart_card_auth(common_smart_card_auth_config):
6d47df
     ssl_conf = paths.HTTPD_SSL_CONF
6d47df
     ssl_ocsp_directive = OCSP_DIRECTIVE
6d47df
     kdc_service_name = services.knownservices.krb5kdc.systemd_name
6d47df
+    httpd_service_name = services.knownservices.httpd.systemd_name
6d47df
 
6d47df
     def get_info(self):
6d47df
         self.log.exit_on_nonroot_euid()
6d47df
@@ -117,6 +118,7 @@ def get_info(self):
6d47df
         self.record_httpd_ocsp_status()
6d47df
         self.check_and_enable_pkinit()
6d47df
         self.enable_ok_to_auth_as_delegate_on_http_principal()
6d47df
+        self.allow_httpd_ifp()
6d47df
         self.upload_smartcard_ca_certificates_to_systemwide_db()
6d47df
         self.install_smart_card_signing_ca_certs()
6d47df
         self.update_ipa_ca_certificate_store()
6d47df
@@ -183,7 +185,9 @@ def _format_command(self, fmt_line, directive, filename):
6d47df
 
6d47df
     def restart_httpd(self):
6d47df
         self.log.comment('finally restart apache')
6d47df
-        self.log.command('systemctl restart httpd')
6d47df
+        self.log.command(
6d47df
+            'systemctl restart {}'.format(self.httpd_service_name)
6d47df
+        )
6d47df
 
6d47df
     def record_httpd_ocsp_status(self):
6d47df
         self.log.comment('store the OCSP upgrade state')
6d47df
@@ -214,6 +218,21 @@ def enable_ok_to_auth_as_delegate_on_http_principal(self):
6d47df
             ["Failed to set OK_AS_AUTH_AS_DELEGATE flag on HTTP principal"]
6d47df
         )
6d47df
 
6d47df
+    def allow_httpd_ifp(self):
6d47df
+        self.log.comment('Allow Apache to access SSSD IFP')
6d47df
+        self.log.exit_on_failed_command(
6d47df
+            '{} -c "import SSSDConfig; '
6d47df
+            'from ipaclient.install.client import sssd_enable_ifp; '
6d47df
+            'from ipaplatform.paths import paths; '
6d47df
+            'c = SSSDConfig.SSSDConfig(); '
6d47df
+            'c.import_config(); '
6d47df
+            'sssd_enable_ifp(c, allow_httpd=True); '
6d47df
+            'c.write(paths.SSSD_CONF)"'.format(sys.executable),
6d47df
+            ['Failed to modify SSSD config']
6d47df
+        )
6d47df
+        self.log.comment('Restart sssd')
6d47df
+        self.log.command('systemctl restart sssd')
6d47df
+
6d47df
     def restart_kdc(self):
6d47df
         self.log.exit_on_failed_command(
6d47df
             'systemctl restart {}'.format(self.kdc_service_name),
6d47df
From b56db8daa704782c44683412b85a454654eabc19 Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Mon, 19 Nov 2018 14:19:16 +0100
6d47df
Subject: [PATCH] Log stderr in run_command
6d47df
6d47df
pytest_multihost's run_command() does not log stderr when a command
6d47df
fails. Wrap the function call to log stderr so it's easier to debug
6d47df
failing tests.
6d47df
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipatests/pytest_ipa/integration/host.py | 19 +++++++++++++++++++
6d47df
 1 file changed, 19 insertions(+)
6d47df
6d47df
diff --git a/ipatests/pytest_ipa/integration/host.py b/ipatests/pytest_ipa/integration/host.py
6d47df
index 28f6e2cd32..6aed58ae96 100644
6d47df
--- a/ipatests/pytest_ipa/integration/host.py
6d47df
+++ b/ipatests/pytest_ipa/integration/host.py
6d47df
@@ -18,6 +18,7 @@
6d47df
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
6d47df
 
6d47df
 """Host class for integration testing"""
6d47df
+import subprocess
6d47df
 
6d47df
 import pytest_multihost.host
6d47df
 
6d47df
@@ -60,6 +61,24 @@ def to_env(self, **kwargs):
6d47df
         from ipatests.pytest_ipa.integration.env_config import host_to_env
6d47df
         return host_to_env(self, **kwargs)
6d47df
 
6d47df
+    def run_command(self, argv, set_env=True, stdin_text=None,
6d47df
+                    log_stdout=True, raiseonerr=True,
6d47df
+                    cwd=None, bg=False, encoding='utf-8'):
6d47df
+        # Wrap run_command to log stderr on raiseonerr=True
6d47df
+        result = super().run_command(
6d47df
+            argv, set_env=set_env, stdin_text=stdin_text,
6d47df
+            log_stdout=log_stdout, raiseonerr=False, cwd=cwd, bg=bg,
6d47df
+            encoding=encoding
6d47df
+        )
6d47df
+        if result.returncode and raiseonerr:
6d47df
+            result.log.error('stderr: %s', result.stderr_text)
6d47df
+            raise subprocess.CalledProcessError(
6d47df
+                result.returncode, argv,
6d47df
+                result.stdout_text, result.stderr_text
6d47df
+            )
6d47df
+        else:
6d47df
+            return result
6d47df
+
6d47df
 
6d47df
 class WinHost(pytest_multihost.host.WinHost):
6d47df
     """
6d47df
From 97776d2c4eed5de73780476bb11a635a2e47ebc5 Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Wed, 21 Nov 2018 10:00:20 +0100
6d47df
Subject: [PATCH] Test smart card advise scripts
6d47df
6d47df
Create and execute the server and client smart card advise scripts.
6d47df
6d47df
See: See: https://pagure.io/freeipa/issue/7751
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipatests/prci_definitions/gating.yaml    |  2 +-
6d47df
 ipatests/test_integration/test_advise.py | 99 ++++++++++++++++++------
6d47df
 2 files changed, 75 insertions(+), 26 deletions(-)
6d47df
6d47df
diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml
6d47df
index 7a9b612dea..13f8851d02 100644
6d47df
--- a/ipatests/prci_definitions/gating.yaml
6d47df
+++ b/ipatests/prci_definitions/gating.yaml
6d47df
@@ -157,7 +157,7 @@ jobs:
6d47df
         test_suite: test_integration/test_advise.py
6d47df
         template: *ci-master-f29
6d47df
         timeout: 3600
6d47df
-        topology: *master_1repl
6d47df
+        topology: *master_1repl_1client
6d47df
 
6d47df
   fedora-29/test_testconfig:
6d47df
     requires: [fedora-29/build]
6d47df
diff --git a/ipatests/test_integration/test_advise.py b/ipatests/test_integration/test_advise.py
6d47df
index 3b821c8797..b548614922 100644
6d47df
--- a/ipatests/test_integration/test_advise.py
6d47df
+++ b/ipatests/test_integration/test_advise.py
6d47df
@@ -21,11 +21,17 @@
6d47df
 # pylint: disable=no-member
6d47df
 
6d47df
 import re
6d47df
+
6d47df
+from ipalib.constants import IPAAPI_USER
6d47df
+from ipaplatform.paths import paths
6d47df
+from ipaplatform.constants import constants
6d47df
+
6d47df
+from ipatests.create_external_ca import ExternalCA
6d47df
 from ipatests.pytest_ipa.integration import tasks
6d47df
 from ipatests.test_integration.base import IntegrationTest
6d47df
 
6d47df
 
6d47df
-def run_advice(master, advice_id, advice_regex, raiseerr):
6d47df
+def run_advice(master, advice_id, advice_regex, raiseerr=True):
6d47df
     # Obtain the advice from the server
6d47df
     tasks.kinit_admin(master)
6d47df
     result = master.run_command(['ipa-advise', advice_id],
6d47df
@@ -43,28 +49,38 @@ class TestAdvice(IntegrationTest):
6d47df
     """
6d47df
     Tests ipa-advise output.
6d47df
     """
6d47df
-    advice_id = None
6d47df
-    raiseerr = None
6d47df
-    advice_regex = ''
6d47df
     topology = 'line'
6d47df
+    num_replicas = 0
6d47df
+    num_clients = 1
6d47df
+
6d47df
+    def execute_advise(self, host, advice_id, *args):
6d47df
+        # ipa-advise script is only available on a server
6d47df
+        tasks.kinit_admin(self.master)
6d47df
+        advice = self.master.run_command(['ipa-advise', advice_id])
6d47df
+        # execute script on host (client or master)
6d47df
+        if host is not self.master:
6d47df
+            tasks.kinit_admin(host)
6d47df
+        filename = tasks.upload_temp_contents(host, advice.stdout_text)
6d47df
+        cmd = ['sh', filename]
6d47df
+        cmd.extend(args)
6d47df
+        try:
6d47df
+            result = host.run_command(cmd)
6d47df
+        finally:
6d47df
+            host.run_command(['rm', '-f', filename])
6d47df
+        return advice, result
6d47df
 
6d47df
     def test_invalid_advice(self):
6d47df
         advice_id = r'invalid-advise-param'
6d47df
         advice_regex = r"invalid[\s]+\'advice\'.*"
6d47df
-        raiseerr = False
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
-
6d47df
+        run_advice(self.master, advice_id, advice_regex, raiseerr=False)
6d47df
 
6d47df
     def test_advice_FreeBSDNSSPAM(self):
6d47df
         advice_id = 'config-freebsd-nss-pam-ldapd'
6d47df
         advice_regex = r"\#\!\/bin\/sh.*" \
6d47df
                        r"pkg_add[\s]+\-r[\s]+nss\-pam\-ldapd[\s]+curl.*" \
6d47df
                        r"\/usr\/local\/etc\/rc\.d\/nslcd[\s]+restart"
6d47df
-        raiseerr = True
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
 
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
 
6d47df
     def test_advice_GenericNSSPAM(self):
6d47df
         advice_id = 'config-generic-linux-nss-pam-ldapd'
6d47df
@@ -75,20 +91,16 @@ def test_advice_GenericNSSPAM(self):
6d47df
             r"service[\s]+nscd[\s]+stop[\s]+\&\&[\s]+service[\s]+"
6d47df
             r"nslcd[\s]+restart"
6d47df
         )
6d47df
-        raiseerr = True
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
 
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
 
6d47df
     def test_advice_GenericSSSDBefore19(self):
6d47df
         advice_id = r'config-generic-linux-sssd-before-1-9'
6d47df
         advice_regex = r"\#\!\/bin\/sh.*" \
6d47df
                        r"apt\-get[\s]+\-y[\s]+install sssd curl openssl.*" \
6d47df
                        r"service[\s]+sssd[\s]+start"
6d47df
-        raiseerr = True
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
 
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
 
6d47df
     def test_advice_RedHatNSS(self):
6d47df
         advice_id = 'config-redhat-nss-ldap'
6d47df
@@ -100,10 +112,8 @@ def test_advice_RedHatNSS(self):
6d47df
             r"[\s]+\-\-enableldapauth[\s]+"
6d47df
             r"\-\-ldapserver=.*[\s]+\-\-ldapbasedn=.*"
6d47df
         )
6d47df
-        raiseerr = True
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
 
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
 
6d47df
     def test_advice_RedHatNSSPAM(self):
6d47df
         advice_id = 'config-redhat-nss-pam-ldapd'
6d47df
@@ -113,10 +123,8 @@ def test_advice_RedHatNSSPAM(self):
6d47df
                        r"authconfig[\s]+\-\-updateall[\s]+\-\-enableldap"\
6d47df
                        r"[\s]+\-\-enableldaptls[\s]+\-\-enableldapauth[\s]+" \
6d47df
                        r"\-\-ldapserver=.*[\s]+\-\-ldapbasedn=.*"
6d47df
-        raiseerr = True
6d47df
-
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
 
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
 
6d47df
     def test_advice_RedHatSSSDBefore19(self):
6d47df
         advice_id = 'config-redhat-sssd-before-1-9'
6d47df
@@ -125,6 +133,47 @@ def test_advice_RedHatSSSDBefore19(self):
6d47df
             r"yum[\s]+install[\s]+\-y[\s]+sssd[\s]+authconfig[\s]+"
6d47df
             r"curl[\s]+openssl.*service[\s]+sssd[\s]+start"
6d47df
         )
6d47df
-        raiseerr = True
6d47df
 
6d47df
-        run_advice(self.master, advice_id, advice_regex, raiseerr)
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
+
6d47df
+    # trivial checks
6d47df
+    def test_advice_enable_admins_sudo(self):
6d47df
+        advice_id = 'enable_admins_sudo'
6d47df
+        advice_regex = r"\#\!\/bin\/sh.*"
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
+
6d47df
+    def test_advice_config_server_for_smart_card_auth(self):
6d47df
+        advice_id = 'config_server_for_smart_card_auth'
6d47df
+        advice_regex = r"\#\!\/bin\/sh.*"
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
+
6d47df
+        ca_pem = ExternalCA().create_ca()
6d47df
+        ca_file = tasks.upload_temp_contents(self.master, ca_pem)
6d47df
+        try:
6d47df
+            self.execute_advise(self.master, advice_id, ca_file)
6d47df
+        except Exception:
6d47df
+            # debug: sometimes ipa-certupdate times out in
6d47df
+            # "Resubmitting certmonger request"
6d47df
+            self.master.run_command(['getcert', 'list'])
6d47df
+            raise
6d47df
+        finally:
6d47df
+            self.master.run_command(['rm', '-f', ca_file])
6d47df
+        sssd_conf = self.master.get_file_contents(
6d47df
+            paths.SSSD_CONF, encoding='utf-8'
6d47df
+        )
6d47df
+        assert constants.HTTPD_USER in sssd_conf
6d47df
+        assert IPAAPI_USER in sssd_conf
6d47df
+
6d47df
+    def test_advice_config_client_for_smart_card_auth(self):
6d47df
+        advice_id = 'config_client_for_smart_card_auth'
6d47df
+        advice_regex = r"\#\!\/bin\/sh.*"
6d47df
+        run_advice(self.master, advice_id, advice_regex)
6d47df
+
6d47df
+        client = self.clients[0]
6d47df
+
6d47df
+        ca_pem = ExternalCA().create_ca()
6d47df
+        ca_file = tasks.upload_temp_contents(client, ca_pem)
6d47df
+        try:
6d47df
+            self.execute_advise(client, advice_id, ca_file)
6d47df
+        finally:
6d47df
+            client.run_command(['rm', '-f', ca_file])
6d47df
From 6ed90a2ac08c070e8e5c47a1eb3c52d7d30cabb8 Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Wed, 21 Nov 2018 10:44:55 +0100
6d47df
Subject: [PATCH] Add install/remove package helpers to advise
6d47df
6d47df
The smart card advise scripts assume that yum is installed. However
6d47df
Fedora has dnf and the yum wrapper is not installed by default.
6d47df
Installation and removal of packages is now provided by two helper
6d47df
methods that detect the package manager.
6d47df
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipaserver/advise/base.py                    | 36 +++++++++++++++++++++
6d47df
 ipaserver/advise/plugins/smart_card_auth.py | 24 +++++++-------
6d47df
 2 files changed, 47 insertions(+), 13 deletions(-)
6d47df
6d47df
diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py
6d47df
index 07b1431e84..ec65113e34 100644
6d47df
--- a/ipaserver/advise/base.py
6d47df
+++ b/ipaserver/advise/base.py
6d47df
@@ -227,6 +227,7 @@ def __init__(self):
6d47df
         self.content = []
6d47df
         self.prefix = '# '
6d47df
         self.options = None
6d47df
+        self.pkgmgr_detected = False
6d47df
         self._indentation_tracker = _IndentationTracker(
6d47df
             spaces_per_indent=DEFAULT_INDENTATION_INCREMENT)
6d47df
 
6d47df
@@ -312,6 +313,41 @@ def exit_on_predicate(self, predicate, error_message_lines):
6d47df
 
6d47df
             self.command('exit 1')
6d47df
 
6d47df
+    def detect_pkgmgr(self):
6d47df
+        self.commands_on_predicate(
6d47df
+            'which yum >/dev/null',
6d47df
+            commands_to_run_when_true=['PKGMGR=yum'],
6d47df
+            commands_to_run_when_false=['PKGMGR=dnf']
6d47df
+        )
6d47df
+        self.pkgmgr_detected = True
6d47df
+
6d47df
+    def install_packages(self, names, error_message_lines):
6d47df
+        assert isinstance(names, list)
6d47df
+        self.detect_pkgmgr()
6d47df
+        self.command('rpm -qi {} > /dev/null'.format(' '.join(names)))
6d47df
+        self.commands_on_predicate(
6d47df
+            '[ "$?" -ne "0" ]',
6d47df
+            ['$PKGMGR install -y {}'.format(' '.join(names))]
6d47df
+        )
6d47df
+        self.exit_on_predicate(
6d47df
+            '[ "$?" -ne "0" ]',
6d47df
+            error_message_lines
6d47df
+        )
6d47df
+
6d47df
+    def remove_package(self, name, error_message_lines):
6d47df
+        # remove only supports one package name
6d47df
+        assert ' ' not in name
6d47df
+        self.detect_pkgmgr()
6d47df
+        self.command('rpm -qi {} > /dev/null'.format(name))
6d47df
+        self.commands_on_predicate(
6d47df
+            '[ "$?" -eq "0" ]',
6d47df
+            ['$PKGMGR remove -y {} || exit 1'.format(name)]
6d47df
+        )
6d47df
+        self.exit_on_predicate(
6d47df
+            '[ "$?" -ne "0" ]',
6d47df
+            error_message_lines
6d47df
+        )
6d47df
+
6d47df
     @contextmanager
6d47df
     def unbranched_if(self, predicate):
6d47df
         with self._compound_statement(UnbranchedIfStatement, predicate):
6d47df
diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py
6d47df
index 9a7a315ed5..411124f935 100644
6d47df
--- a/ipaserver/advise/plugins/smart_card_auth.py
6d47df
+++ b/ipaserver/advise/plugins/smart_card_auth.py
6d47df
@@ -135,9 +135,10 @@ def resolve_ipaca_records(self):
6d47df
 
6d47df
         self.log.comment('make sure bind-utils are installed so that we can '
6d47df
                          'dig for ipa-ca records')
6d47df
-        self.log.exit_on_failed_command(
6d47df
-            'yum install -y bind-utils',
6d47df
-            ['Failed to install bind-utils'])
6d47df
+        self.log.install_packages(
6d47df
+            ['bind-utils'],
6d47df
+            ['Failed to install bind-utils']
6d47df
+        )
6d47df
 
6d47df
         self.log.comment('make sure ipa-ca records are resolvable, '
6d47df
                          'otherwise error out and instruct')
6d47df
@@ -272,26 +273,23 @@ def get_info(self):
6d47df
         self.restart_sssd()
6d47df
 
6d47df
     def check_and_remove_pam_pkcs11(self):
6d47df
-        self.log.command('rpm -qi pam_pkcs11 > /dev/null')
6d47df
-        self.log.commands_on_predicate(
6d47df
-            '[ "$?" -eq "0" ]',
6d47df
-            [
6d47df
-                'yum remove -y pam_pkcs11'
6d47df
-            ]
6d47df
+        self.log.remove_package(
6d47df
+            'pam_pkcs11',
6d47df
+            ['Could not remove pam_pkcs11 package']
6d47df
         )
6d47df
 
6d47df
     def install_opensc_and_dconf_packages(self):
6d47df
         self.log.comment(
6d47df
             'authconfig often complains about missing dconf, '
6d47df
             'install it explicitly')
6d47df
-        self.log.exit_on_failed_command(
6d47df
-            'yum install -y {} dconf'.format(self.opensc_module_name.lower()),
6d47df
+        self.log.install_packages(
6d47df
+            [self.opensc_module_name.lower(), 'dconf'],
6d47df
             ['Could not install OpenSC package']
6d47df
         )
6d47df
 
6d47df
     def install_krb5_client_dependencies(self):
6d47df
-        self.log.exit_on_failed_command(
6d47df
-            'yum install -y krb5-pkinit-openssl',
6d47df
+        self.log.install_packages(
6d47df
+            ['krb5-pkinit-openssl'],
6d47df
             ['Failed to install Kerberos client PKINIT extensions.']
6d47df
         )
6d47df
 
6d47df
From e05ce4a20d2395179580db7e3db75c601c8f364c Mon Sep 17 00:00:00 2001
6d47df
From: Christian Heimes <cheimes@redhat.com>
6d47df
Date: Thu, 13 Dec 2018 14:40:44 +0100
6d47df
Subject: [PATCH] Python 2 compatibility
6d47df
6d47df
Make new test helpers and test code compatible with Python 2.7.
6d47df
6d47df
See: https://pagure.io/freeipa/issue/7751
6d47df
Signed-off-by: Christian Heimes <cheimes@redhat.com>
6d47df
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
6d47df
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
6d47df
---
6d47df
 ipatests/pytest_ipa/integration/host.py  | 4 ++--
6d47df
 ipatests/test_integration/test_advise.py | 2 ++
6d47df
 2 files changed, 4 insertions(+), 2 deletions(-)
6d47df
6d47df
diff --git a/ipatests/pytest_ipa/integration/host.py b/ipatests/pytest_ipa/integration/host.py
6d47df
index 6aed58ae96..eb05872467 100644
6d47df
--- a/ipatests/pytest_ipa/integration/host.py
6d47df
+++ b/ipatests/pytest_ipa/integration/host.py
6d47df
@@ -65,7 +65,7 @@ def run_command(self, argv, set_env=True, stdin_text=None,
6d47df
                     log_stdout=True, raiseonerr=True,
6d47df
                     cwd=None, bg=False, encoding='utf-8'):
6d47df
         # Wrap run_command to log stderr on raiseonerr=True
6d47df
-        result = super().run_command(
6d47df
+        result = super(Host, self).run_command(
6d47df
             argv, set_env=set_env, stdin_text=stdin_text,
6d47df
             log_stdout=log_stdout, raiseonerr=False, cwd=cwd, bg=bg,
6d47df
             encoding=encoding
6d47df
@@ -74,7 +74,7 @@ def run_command(self, argv, set_env=True, stdin_text=None,
6d47df
             result.log.error('stderr: %s', result.stderr_text)
6d47df
             raise subprocess.CalledProcessError(
6d47df
                 result.returncode, argv,
6d47df
-                result.stdout_text, result.stderr_text
6d47df
+                result.stderr_text
6d47df
             )
6d47df
         else:
6d47df
             return result
6d47df
diff --git a/ipatests/test_integration/test_advise.py b/ipatests/test_integration/test_advise.py
6d47df
index b548614922..761f278238 100644
6d47df
--- a/ipatests/test_integration/test_advise.py
6d47df
+++ b/ipatests/test_integration/test_advise.py
6d47df
@@ -20,6 +20,8 @@
6d47df
 # FIXME: Pylint errors
6d47df
 # pylint: disable=no-member
6d47df
 
6d47df
+from __future__ import absolute_import
6d47df
+
6d47df
 import re
6d47df
 
6d47df
 from ipalib.constants import IPAAPI_USER