URL: https://github.com/freeipa/freeipa/pull/1310 Author: tiran Title: #1310: 6577 ca replica install certupdate2 Action: opened
PR body: """ """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/1310/head:pr1310 git checkout pr1310
From 9520781fb5d198f274cf7ccadec5c434ada69535 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Mon, 30 Oct 2017 17:52:07 +1100 Subject: [PATCH 1/4] CertUpdate: make it easy to invoke from other programs The guts of ipa-certupdate are useful to execute as part of other programs (e.g. as a first step of ipa-ca-install). Refactor ipa_certupdate.CertUpdate to make it easy to do that. In particular, make it possible to use an already-initialised API object. Part of: https://pagure.io/freeipa/issue/6577 --- ipaclient/install/ipa_certupdate.py | 63 ++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py index 06fd079226..32407ad967 100644 --- a/ipaclient/install/ipa_certupdate.py +++ b/ipaclient/install/ipa_certupdate.py @@ -56,30 +56,36 @@ def run(self): api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA) api.finalize() + api.Backend.rpcclient.connect() + self.run_with_api(api) + api.Backend.rpcclient.disconnect() + + @classmethod + def run_with_api(cls, api): + """ + Run the certupdate procedure with the given API object. + + :param api: API object with ldap2/rpcclient backend connected + (such that Commands can be invoked) + + """ server = urlsplit(api.env.jsonrpc_uri).hostname ldap_uri = ipaldap.get_ldap_uri(server) ldap = ipaldap.LDAPClient(ldap_uri) tmpdir = tempfile.mkdtemp(prefix="tmp-") ccache_name = os.path.join(tmpdir, 'ccache') + old_krb5ccname = os.environ.get('KRB5CCNAME') try: principal = str('host/%s@%s' % (api.env.host, api.env.realm)) kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_name) os.environ['KRB5CCNAME'] = ccache_name - api.Backend.rpcclient.connect() try: - result = api.Backend.rpcclient.forward( - 'ca_is_enabled', - version=u'2.107', - ) + result = api.Command.ca_is_enabled(version=u'2.107') ca_enabled = result['result'] except (errors.CommandError, errors.NetworkError): - result = api.Backend.rpcclient.forward( - 'env', - server=True, - version=u'2.0', - ) + result = api.Command.env(server=True, version=u'2.0') ca_enabled = result['result']['enable_ra'] ldap.gssapi_bind() @@ -92,13 +98,16 @@ def run(self): else: lwcas = [] - api.Backend.rpcclient.disconnect() finally: + if old_krb5ccname is None: + del os.environ['KRB5CCNAME'] + else: + os.environ['KRB5CCNAME'] = old_krb5ccname shutil.rmtree(tmpdir) server_fstore = sysrestore.FileStore(paths.SYSRESTORE) if server_fstore.has_files(): - self.update_server(certs) + cls.update_server(certs) try: # pylint: disable=import-error,ipa-forbidden-import from ipaserver.install import cainstance @@ -108,12 +117,13 @@ def run(self): logger.exception( "Failed to add lightweight CA tracking requests") - self.update_client(certs) + cls.update_client(certs) - def update_client(self, certs): - self.update_file(paths.IPA_CA_CRT, certs) - self.update_file(paths.KDC_CA_BUNDLE_PEM, certs) - self.update_file(paths.CA_BUNDLE_PEM, certs) + @classmethod + def update_client(cls, certs): + cls.update_file(paths.IPA_CA_CRT, certs) + cls.update_file(paths.KDC_CA_BUNDLE_PEM, certs) + cls.update_file(paths.CA_BUNDLE_PEM, certs) ipa_db = certdb.NSSDatabase(api.env.nss_dir) @@ -127,19 +137,20 @@ def update_client(self, certs): nickname, ipa_db.secdir, e) break - self.update_db(ipa_db.secdir, certs) + cls.update_db(ipa_db.secdir, certs) tasks.remove_ca_certs_from_systemwide_ca_store() tasks.insert_ca_certs_into_systemwide_ca_store(certs) - def update_server(self, certs): + @classmethod + def update_server(cls, certs): instance = '-'.join(api.env.realm.split('.')) - self.update_db( + cls.update_db( paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance, certs) if services.knownservices.dirsrv.is_running(): services.knownservices.dirsrv.restart(instance) - self.update_db(paths.HTTPD_ALIAS_DIR, certs) + cls.update_db(paths.HTTPD_ALIAS_DIR, certs) if services.knownservices.httpd.is_running(): services.knownservices.httpd.restart() @@ -170,17 +181,19 @@ def update_server(self, certs): logger.debug("modifying certmonger request '%s'", request_id) certmonger.modify(request_id, ca='dogtag-ipa-ca-renew-agent') - self.update_file(paths.CA_CRT, certs) - self.update_file(paths.CACERT_PEM, certs) + cls.update_file(paths.CA_CRT, certs) + cls.update_file(paths.CACERT_PEM, certs) - def update_file(self, filename, certs, mode=0o444): + @staticmethod + def update_file(filename, certs, mode=0o444): certs = (c[0] for c in certs if c[2] is not False) try: x509.write_certificate_list(certs, filename) except Exception as e: logger.error("failed to update %s: %s", filename, e) - def update_db(self, path, certs): + @staticmethod + def update_db(path, certs): db = certdb.NSSDatabase(path) for cert, nickname, trusted, eku in certs: trust_flags = certstore.key_policy_to_trust_flags( From 21fbf7088f9c129f77d7308186e295e2a486c1da Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Mon, 30 Oct 2017 18:05:56 +1100 Subject: [PATCH 2/4] ipa-ca-install: run certupdate as initial step When installing a CA replica, perform a certupdate to ensure that the relevant CA cert is present. This is necessary if the admin has just promoted the topology from CA-less to CA-ful but didn't manually run ipa-certupdate afterwards. Fixes: https://pagure.io/freeipa/issue/6577 --- install/tools/ipa-ca-install | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install index 64478bb0e8..498a5cf114 100755 --- a/install/tools/ipa-ca-install +++ b/install/tools/ipa-ca-install @@ -27,6 +27,7 @@ import tempfile from ipalib.install.kinit import kinit_keytab from ipapython import ipautil +from ipaclient.install.ipa_certupdate import CertUpdate from ipaserver.install import installutils from ipaserver.install.installutils import create_replica_config from ipaserver.install.installutils import check_creds, ReplicaConfig @@ -174,6 +175,16 @@ def install_replica(safe_options, options, filename): not options.skip_conncheck and options.unattended): sys.exit('admin password required') + # Run ipa-certupdate to ensure we have the CA cert. This is + # necessary if the admin has just promoted the topology from + # CA-less to CA-ful, and ipa-certupdate has not been run yet. + CertUpdate.run_with_api(api) + + # CertUpdate restarts DS causing broken pipe on the original + # connection, so reconnect the backend. + api.Backend.ldap2.disconnect() + api.Backend.ldap2.connect() + if options.promote: config = ReplicaConfig() config.ca_host_name = None From a9ad3b5ab72a12144e936faf4bc7160fc10ee209 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Tue, 31 Oct 2017 18:20:15 +1100 Subject: [PATCH 3/4] Run certupdate after promoting to CA-ful deployment After installing a CA in a CA-less installations (using ipa-ca-install), the new CA certificate is not installed in /etc/httpd/alias. This causes communication failure between IPA framework and Dogtag (it cannot verify the Dogtag server certificate). Perform a CertUpdate as the final step when promoting a CA-less deployment to CA-ful. Fixes: https://pagure.io/freeipa/issue/7230 --- install/tools/ipa-ca-install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install index 498a5cf114..65d0b38f65 100755 --- a/install/tools/ipa-ca-install +++ b/install/tools/ipa-ca-install @@ -254,6 +254,10 @@ def install_master(safe_options, options): ca.install_check(True, None, options) ca.install(True, None, options) + # Run ipa-certupdate to add the new CA certificate to + # certificate databases on this server. + logger.info("Updating certificate databases.") + CertUpdate.run_with_api(api) def install(safe_options, options, filename): options.promote = False From 3f368b74d93fe66ef460c3ba225b3ae1e1c05695 Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Tue, 21 Nov 2017 09:05:51 +0100 Subject: [PATCH 4/4] No class/static methods --- install/tools/ipa-ca-install | 5 ++-- ipaclient/install/ipa_certupdate.py | 56 +++++++++++++++++-------------------- ipapython/admintool.py | 3 +- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install index 65d0b38f65..4eaa96bd9f 100755 --- a/install/tools/ipa-ca-install +++ b/install/tools/ipa-ca-install @@ -178,7 +178,7 @@ def install_replica(safe_options, options, filename): # Run ipa-certupdate to ensure we have the CA cert. This is # necessary if the admin has just promoted the topology from # CA-less to CA-ful, and ipa-certupdate has not been run yet. - CertUpdate.run_with_api(api) + CertUpdate(None, None).run() # CertUpdate restarts DS causing broken pipe on the original # connection, so reconnect the backend. @@ -257,7 +257,8 @@ def install_master(safe_options, options): # Run ipa-certupdate to add the new CA certificate to # certificate databases on this server. logger.info("Updating certificate databases.") - CertUpdate.run_with_api(api) + CertUpdate(None, None).run() + def install(safe_options, options, filename): options.promote = False diff --git a/ipaclient/install/ipa_certupdate.py b/ipaclient/install/ipa_certupdate.py index 32407ad967..1b1396013e 100644 --- a/ipaclient/install/ipa_certupdate.py +++ b/ipaclient/install/ipa_certupdate.py @@ -51,24 +51,16 @@ def validate_options(self): super(CertUpdate, self).validate_options(needs_root=True) def run(self): - check_client_configuration() - - api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA) - api.finalize() - - api.Backend.rpcclient.connect() - self.run_with_api(api) - api.Backend.rpcclient.disconnect() - - @classmethod - def run_with_api(cls, api): """ Run the certupdate procedure with the given API object. + """ + check_client_configuration() - :param api: API object with ldap2/rpcclient backend connected - (such that Commands can be invoked) + if not api.isdone('finalize'): + api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA) + api.finalize() + connected = api.Backend.rpcclient.isconnected() - """ server = urlsplit(api.env.jsonrpc_uri).hostname ldap_uri = ipaldap.get_ldap_uri(server) ldap = ipaldap.LDAPClient(ldap_uri) @@ -81,6 +73,9 @@ def run_with_api(cls, api): kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_name) os.environ['KRB5CCNAME'] = ccache_name + if not connected: + api.Backend.rpcclient.connect() + try: result = api.Command.ca_is_enabled(version=u'2.107') ca_enabled = result['result'] @@ -104,10 +99,12 @@ def run_with_api(cls, api): else: os.environ['KRB5CCNAME'] = old_krb5ccname shutil.rmtree(tmpdir) + if not connected: + api.Backend.rpcclient.disconnect() server_fstore = sysrestore.FileStore(paths.SYSRESTORE) if server_fstore.has_files(): - cls.update_server(certs) + self.update_server(certs) try: # pylint: disable=import-error,ipa-forbidden-import from ipaserver.install import cainstance @@ -117,13 +114,12 @@ def run_with_api(cls, api): logger.exception( "Failed to add lightweight CA tracking requests") - cls.update_client(certs) + self.update_client(certs) - @classmethod - def update_client(cls, certs): - cls.update_file(paths.IPA_CA_CRT, certs) - cls.update_file(paths.KDC_CA_BUNDLE_PEM, certs) - cls.update_file(paths.CA_BUNDLE_PEM, certs) + def update_client(self, certs): + self.update_file(paths.IPA_CA_CRT, certs) + self.update_file(paths.KDC_CA_BUNDLE_PEM, certs) + self.update_file(paths.CA_BUNDLE_PEM, certs) ipa_db = certdb.NSSDatabase(api.env.nss_dir) @@ -137,20 +133,20 @@ def update_client(cls, certs): nickname, ipa_db.secdir, e) break - cls.update_db(ipa_db.secdir, certs) + self.update_db(ipa_db.secdir, certs) tasks.remove_ca_certs_from_systemwide_ca_store() tasks.insert_ca_certs_into_systemwide_ca_store(certs) @classmethod - def update_server(cls, certs): + def update_server(self, certs): instance = '-'.join(api.env.realm.split('.')) - cls.update_db( + self.update_db( paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance, certs) if services.knownservices.dirsrv.is_running(): services.knownservices.dirsrv.restart(instance) - cls.update_db(paths.HTTPD_ALIAS_DIR, certs) + self.update_db(paths.HTTPD_ALIAS_DIR, certs) if services.knownservices.httpd.is_running(): services.knownservices.httpd.restart() @@ -181,19 +177,17 @@ def update_server(cls, certs): logger.debug("modifying certmonger request '%s'", request_id) certmonger.modify(request_id, ca='dogtag-ipa-ca-renew-agent') - cls.update_file(paths.CA_CRT, certs) - cls.update_file(paths.CACERT_PEM, certs) + self.update_file(paths.CA_CRT, certs) + self.update_file(paths.CACERT_PEM, certs) - @staticmethod - def update_file(filename, certs, mode=0o444): + def update_file(self,filename, certs, mode=0o444): certs = (c[0] for c in certs if c[2] is not False) try: x509.write_certificate_list(certs, filename) except Exception as e: logger.error("failed to update %s: %s", filename, e) - @staticmethod - def update_db(path, certs): + def update_db(self, path, certs): db = certdb.NSSDatabase(path) for cert, nickname, trusted, eku in certs: trust_flags = certstore.key_policy_to_trust_flags( diff --git a/ipapython/admintool.py b/ipapython/admintool.py index 329e20f374..a1cd198a80 100644 --- a/ipapython/admintool.py +++ b/ipapython/admintool.py @@ -157,7 +157,8 @@ def get_command_class(cls, options, args): def __init__(self, options, args): self.options = options self.args = args - self.safe_options = self.option_parser.get_safe_opts(options) + if args is None: + self.safe_options = self.option_parser.get_safe_opts(options) def execute(self): """Do everything needed after options are parsed
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org