URL: https://github.com/freeipa/freeipa/pull/367 Author: stlaz Title: #367: Remove nsslib from IPA Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/367/head:pr367 git checkout pr367
From 186c84b68e541dabc51707f0bc93f0c69baa2f6e Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Tue, 20 Dec 2016 10:05:36 +0100 Subject: [PATCH 01/12] Remove NSSConnection from the Python RPC module NSSConnection was causing a lot of trouble in the past and there is a lot of logic around it just to make it not fail. What's more, when using NSS to create an SSL connection in FIPS mode, NSS always requires database password which makes the `ipa` command totally unusable. NSSConnection is therefore replaced with Python's httplib.HTTPSConnection which is OpenSSL based. The new HTTPS handling class, IPAHTTPSConnection, is prepared to handle authentication with client certificate for connections to Dogtag server as RA agent. It allows handling even for handling separate client cert/private key in separate files and also for encrypted private key files. https://fedorahosted.org/freeipa/ticket/5695 --- ipalib/config.py | 3 ++ ipalib/constants.py | 1 + ipalib/rpc.py | 70 +++++++--------------------------- ipalib/util.py | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 56 deletions(-) diff --git a/ipalib/config.py b/ipalib/config.py index 20591db..8ecada6 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -493,6 +493,9 @@ def _bootstrap(self, **overrides): if 'nss_dir' not in self: self.nss_dir = self._join('confdir', 'nssdb') + if 'ca_certfile' not in self: + self.ca_certfile = self._join('confdir', 'ca.crt') + # Set plugins_on_demand: if 'plugins_on_demand' not in self: self.plugins_on_demand = (self.context == 'cli') diff --git a/ipalib/constants.py b/ipalib/constants.py index fa20624..82147f3 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -226,6 +226,7 @@ ('conf_default', object), # File containing context independent config ('plugins_on_demand', object), # Whether to finalize plugins on-demand (bool) ('nss_dir', object), # Path to nssdb, default {confdir}/nssdb + ('ca_certfile', object), # Path to CA cert file # Set in Env._finalize_core(): ('in_server', object), # Whether or not running in-server (bool) diff --git a/ipalib/rpc.py b/ipalib/rpc.py index 31ed64e..1ea5d60 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -44,7 +44,7 @@ import gssapi from dns import resolver, rdatatype from dns.exception import DNSException -from nss.error import NSPRError +from ssl import SSLError import six from six.moves import urllib @@ -60,8 +60,7 @@ from ipapython.cookie import Cookie from ipapython.dnsutil import DNSName from ipalib.text import _ -import ipapython.nsslib -from ipapython.nsslib import NSSConnection +from ipalib.util import IPAHTTPSConnection from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \ KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, \ KRB5_REALM_CANT_RESOLVE, KRB5_CC_NOTFOUND, get_principal @@ -470,48 +469,20 @@ def get_host_info(self, host): return (host, extra_headers, x509) + class SSLTransport(LanguageAwareTransport): """Handles an HTTPS transaction to an XML-RPC server.""" - - def get_connection_dbdir(self): - """ - If there is a connections open it may have already initialized - NSS database. Return the database location used by the connection. - """ - for value in context.__dict__.values(): - if not isinstance(value, Connection): - continue - if not isinstance( - getattr(value.conn, '_ServerProxy__transport', None), - SSLTransport): - continue - if hasattr(value.conn._ServerProxy__transport, 'dbdir'): - return value.conn._ServerProxy__transport.dbdir - return None - def make_connection(self, host): host, self._extra_headers, _x509 = self.get_host_info(host) if self._connection and host == self._connection[0]: return self._connection[1] - dbdir = context.nss_dir - connection_dbdir = self.get_connection_dbdir() - - if connection_dbdir: - # If an existing connection is already using the same NSS - # database there is no need to re-initialize. - no_init = dbdir == connection_dbdir - - else: - # If the NSS database is already being used there is no - # need to re-initialize. - no_init = dbdir == ipapython.nsslib.current_dbdir - - conn = NSSConnection(host, 443, dbdir=dbdir, no_init=no_init, - tls_version_min=api.env.tls_version_min, - tls_version_max=api.env.tls_version_max) - self.dbdir=dbdir + conn = IPAHTTPSConnection( + host, 443, + api.env.ca_certfile, + tls_version_min=api.env.tls_version_min, + tls_version_max=api.env.tls_version_max) conn.connect() @@ -891,15 +862,15 @@ def apply_session_cookie(self, url): return session_url def create_connection(self, ccache=None, verbose=None, fallback=None, - delegate=None, nss_dir=None): + delegate=None, ca_certfile=None): if verbose is None: verbose = self.api.env.verbose if fallback is None: fallback = self.api.env.fallback if delegate is None: delegate = self.api.env.delegate - if nss_dir is None: - nss_dir = self.api.env.nss_dir + if ca_certfile is None: + ca_certfile = self.api.env.ca_certfile try: rpc_uri = self.env[self.env_rpc_uri_key] principal = get_principal(ccache_name=ccache) @@ -917,7 +888,7 @@ def create_connection(self, ccache=None, verbose=None, fallback=None, except (errors.CCacheError, ValueError): # No session key, do full Kerberos auth pass - context.nss_dir = nss_dir + context.ca_certfile = ca_certfile urls = self.get_url_list(rpc_uri) serverproxy = None for url in urls: @@ -1027,7 +998,7 @@ def forward(self, name, *args, **kw): error=e.faultString, server=server, ) - except NSPRError as e: + except SSLError as e: raise NetworkError(uri=server, error=str(e)) except ProtocolError as e: # By catching a 401 here we can detect the case where we have @@ -1044,22 +1015,9 @@ def forward(self, name, *args, **kw): # This shouldn't happen if we have a session but it isn't fatal. pass - # Create a new serverproxy with the non-session URI. If there - # is an existing connection we need to save the NSS dbdir so - # we can skip an unnecessary NSS_Initialize() and avoid - # NSS_Shutdown issues. + # Create a new serverproxy with the non-session URI serverproxy = self.create_connection(os.environ.get('KRB5CCNAME'), self.env.verbose, self.env.fallback, self.env.delegate) - - dbdir = None - current_conn = getattr(context, self.id, None) - if current_conn is not None: - dbdir = getattr(current_conn.conn._ServerProxy__transport, 'dbdir', None) - if dbdir is not None: - self.debug('Using dbdir %s' % dbdir) setattr(context, self.id, Connection(serverproxy, self.disconnect)) - if dbdir is not None: - current_conn = getattr(context, self.id, None) - current_conn.conn._ServerProxy__transport.dbdir = dbdir return self.forward(name, *args, **kw) raise NetworkError(uri=server, error=e.errmsg) except socket.error as e: diff --git a/ipalib/util.py b/ipalib/util.py index 1509607..c3bcd6d 100644 --- a/ipalib/util.py +++ b/ipalib/util.py @@ -33,6 +33,7 @@ import dns import encodings import sys +import ssl from weakref import WeakKeyDictionary import netaddr @@ -42,6 +43,12 @@ from netaddr.core import AddrFormatError import six +try: + from httplib import HTTPSConnection +except ImportError: + # Python 3 + from http.client import HTTPSConnection + from ipalib import errors, messages from ipalib.constants import DOMAIN_LEVEL_0 from ipalib.text import _ @@ -51,6 +58,7 @@ from ipapython.dnsutil import resolve_ip_addresses from ipapython.ipa_log_manager import root_logger + if six.PY3: unicode = str @@ -187,6 +195,104 @@ def normalize_zone(zone): return zone +class IPAHTTPSConnection(HTTPSConnection, object): + """ + This class inherits from `object` because HTTPSConnection does not in + Python 2.7. This is to allow the use of super() in its and derived + classes' methods. + """ + + # pylint: disable=no-member + tls_cutoff_map = { + "ssl2": ssl.OP_NO_SSLv2, + "tls1.0": ssl.OP_NO_TLSv1, + "tls1.1": ssl.OP_NO_TLSv1_1, + "tls1.2": ssl.OP_NO_TLSv1_2, + } + # pylint: enable=no-member + + def __init__(self, host, port=HTTPSConnection.default_port, + cafile=None, + client_certfile=None, client_keyfile=None, + keyfile_passwd=None, + tls_version_min="tls1.1", + tls_version_max="tls1.2", + **kwargs): + """ + Set up a client HTTPS connection. + + :param host: The host to connect to + :param port: The port to connect to, defaults to + HTTPSConnection.default_port + :param cafile: A PEM-format file containning the trusted + CA certificates + :param client_certfile: + A PEM-format client certificate file that will be used to + identificate the user to the server. + :param client_keyfile: + A file with the client private key. If this argument is not + supplied, the key will be sought in client_certfile. + :param keyfile_passwd: + A path to the file which stores the password that is used to + encrypt client_keyfile. Leave default value if the keyfile + is not encrypted. + :returns An established HTTPS connection to host:port + """ + if cafile is None: + raise RuntimeError("IPAHTTPSConnection requires cafile argument " + "to perform server certificate verification") + + # pylint: disable=no-member + tls_cutoff = [ + ssl.OP_NO_SSLv2, + ssl.OP_NO_TLSv1, + ssl.OP_NO_TLSv1_1, + ssl.OP_NO_TLSv1_2 + ] + # remove the slice of negating protocol options according to options + min_idx = tls_cutoff.index(self.tls_cutoff_map[tls_version_min]) + max_idx = tls_cutoff.index(self.tls_cutoff_map[tls_version_max]) + tls_use = tls_cutoff[min_idx:max_idx+1] + del(tls_cutoff[min_idx:max_idx+1]) + + # official Python documentation states that the best option to get + # TLSv1 and later is to setup SSLContext with PROTOCOL_SSLv23 + # and then negate the insecure SSLv2 and SSLv3 + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.options |= ( + ssl.OP_ALL | ssl.OP_NO_COMPRESSION | ssl.OP_SINGLE_DH_USE | + ssl.OP_SINGLE_ECDH_USE | ssl.OP_NO_SSLv3 + ) + + # high ciphers without RC4, MD5, TripleDES, pre-shared key + # and secure remote password + ctx.set_ciphers("HIGH:!aNULL:!eNULL:!MD5:!RC4:!3DES:!PSK:!SRP") + + # pylint: enable=no-member + for version in tls_cutoff: + ctx.options |= version + + # make sure the given TLS version is available if Python decides to + # remove it from default TLS flags + for version in tls_use: + ctx.options &= ~version + + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.check_hostname = True + ctx.load_verify_locations(cafile) + + if client_certfile is not None: + if keyfile_passwd is not None: + with open(keyfile_passwd) as pwd_f: + passwd = pwd_f.read() + else: + passwd = None + ctx.load_cert_chain(client_certfile, client_keyfile, passwd) + + super(IPAHTTPSConnection, self).__init__(host, port, context=ctx, + **kwargs) + + def validate_dns_label(dns_label, allow_underscore=False, allow_slash=False): base_chars = 'a-z0-9' extra_chars = '' From c07bb718660a787b971d691e39864566e8c516f0 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Mon, 2 Jan 2017 17:00:00 +0100 Subject: [PATCH 02/12] Move RA agent certificate file export to a different location HTTPS connection to certificate server requires client authentication so we need a file with client certificate and private key prior to its first occurence which happens during migration of certificate profiles to LDAP. https://fedorahosted.org/freeipa/ticket/5695 https://fedorahosted.org/freeipa/ticket/6392 --- install/restart_scripts/renew_ra_cert | 4 ++-- ipaplatform/base/paths.py | 2 +- ipaserver/install/cainstance.py | 5 ++++- ipaserver/install/dogtaginstance.py | 4 ++-- ipaserver/install/ipa_backup.py | 2 +- ipaserver/install/krainstance.py | 7 +------ ipaserver/install/server/upgrade.py | 11 +++++------ ipaserver/plugins/dogtag.py | 2 +- 8 files changed, 17 insertions(+), 20 deletions(-) diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert index d978f94..4dc6c2e 100644 --- a/install/restart_scripts/renew_ra_cert +++ b/install/restart_scripts/renew_ra_cert @@ -29,7 +29,7 @@ import traceback from ipalib.install.kinit import kinit_keytab from ipalib import api -from ipaserver.install import certs, cainstance, krainstance +from ipaserver.install import certs, cainstance, dogtaginstance from ipaplatform.paths import paths @@ -61,7 +61,7 @@ def _main(): cainstance.update_people_entry(dercert) if api.Command.kra_is_enabled()['result']: - krainstance.export_kra_agent_pem() + dogtaginstance.export_ra_agent_pem() finally: shutil.rmtree(tmpdir) api.Backend.ldap2.disconnect() diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 8db9e61..6c35b08 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -138,7 +138,7 @@ class BasePathNamespace(object): ROOT_IPA_CACHE = "/root/.ipa_cache" ROOT_PKI = "/root/.pki" DOGTAG_ADMIN_P12 = "/root/ca-agent.p12" - KRA_AGENT_PEM = "/var/lib/ipa/radb/kra-agent.pem" + RA_AGENT_PEM = "/var/lib/ipa/ra-agent.pem" CACERT_P12 = "/root/cacert.p12" ROOT_IPA_CSR = "/root/ipa.csr" NAMED_PID = "/run/named/named.pid" diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 1b7ada4..233ed50 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -64,7 +64,8 @@ from ipaserver.install import ldapupdate from ipaserver.install import replication from ipaserver.install import sysupgrade -from ipaserver.install.dogtaginstance import DogtagInstance +from ipaserver.install.dogtaginstance import ( + DogtagInstance, export_ra_agent_pem) from ipaserver.plugins import ldap2 # We need to reset the template because the CA uses the regular boot @@ -415,6 +416,8 @@ def configure_instance(self, host_name, dm_password, admin_password, else: self.step("importing RA certificate from PKCS #12 file", lambda: self.import_ra_cert(ra_p12)) + self.step("exporting KRA agent cert", export_ra_agent_pem) + if not ra_only: self.step("importing CA chain to RA certificate database", self.__import_ca_chain) self.step("setting up signing cert profile", self.__setup_sign_profile) diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 968f4b2..6b54359 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -72,7 +72,7 @@ def is_installing_replica(sys_type): return False -def export_kra_agent_pem(): +def export_ra_agent_pem(): """ Export ipaCert with private key for client authentication. """ @@ -90,7 +90,7 @@ def export_kra_agent_pem(): os.chown(filename, 0, pent.pw_gid) os.chmod(filename, 0o440) - os.rename(filename, paths.KRA_AGENT_PEM) + os.rename(filename, paths.RA_AGENT_PEM) class DogtagInstance(service.Service): diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py index 9cf0d4c..9f35bf6 100644 --- a/ipaserver/install/ipa_backup.py +++ b/ipaserver/install/ipa_backup.py @@ -158,7 +158,7 @@ class Backup(admintool.AdminTool): paths.SMB_CONF, paths.SAMBA_KEYTAB, paths.DOGTAG_ADMIN_P12, - paths.KRA_AGENT_PEM, + paths.RA_AGENT_PEM, paths.CACERT_P12, paths.KRACERT_P12, paths.KRB5KDC_KDC_CONF, diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index ec38801..1d823bf 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -36,8 +36,7 @@ from ipaserver.install import cainstance from ipaserver.install import installutils from ipaserver.install import ldapupdate -from ipaserver.install.dogtaginstance import (export_kra_agent_pem, - DogtagInstance) +from ipaserver.install.dogtaginstance import DogtagInstance from ipaserver.plugins import ldap2 from ipapython.ipa_log_manager import log_mgr @@ -118,7 +117,6 @@ def configure_instance(self, realm_name, host_name, dm_password, if not self.clone: self.step("create KRA agent", self.__create_kra_agent) - self.step("exporting KRA agent cert", export_kra_agent_pem) if not ra_only: if promote: self.step("destroying installation admin user", self.teardown_admin) @@ -279,9 +277,6 @@ def __spawn_instance(self): os.remove(cfg_file) shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12) - - export_kra_agent_pem() - self.log.debug("completed creating KRA instance") def __create_kra_agent(self): diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 509f196..71087fe 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -44,7 +44,6 @@ from ipaserver.install import custodiainstance from ipaserver.install import sysupgrade from ipaserver.install import dnskeysyncinstance -from ipaserver.install import krainstance from ipaserver.install import dogtaginstance from ipaserver.install import krbinstance from ipaserver.install import adtrustinstance @@ -1383,10 +1382,10 @@ def fix_trust_flags(): sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) -def export_kra_agent_pem(): +def export_ra_agent_pem(): root_logger.info('[Exporting KRA agent PEM file]') - if sysupgrade.get_upgrade_state('http', 'export_kra_agent_pem'): + if sysupgrade.get_upgrade_state('http', 'export_ra_agent_pem'): root_logger.info("KRA agent PEM file already exported") return @@ -1394,9 +1393,9 @@ def export_kra_agent_pem(): root_logger.info("KRA is not enabled") return - krainstance.export_kra_agent_pem() + dogtaginstance.export_ra_agent_pem() - sysupgrade.set_upgrade_state('http', 'export_kra_agent_pem', True) + sysupgrade.set_upgrade_state('http', 'export_ra_agent_pem', True) def update_mod_nss_protocol(http): @@ -1636,7 +1635,7 @@ def upgrade_configuration(): update_mod_nss_protocol(http) update_mod_nss_cipher_suite(http) fix_trust_flags() - export_kra_agent_pem() + export_ra_agent_pem() update_http_keytab(http) http.configure_gssproxy() http.start() diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 6ff6d29..2156f07 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -2026,7 +2026,7 @@ def get_client(self): str(self.kra_port), 'kra') - connection.set_authentication_cert(paths.KRA_AGENT_PEM) + connection.set_authentication_cert(paths.RA_AGENT_PEM) return KRAClient(connection, crypto) From 3b89508f85c695e031313e6de549d0613209817f Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Wed, 4 Jan 2017 08:41:26 +0100 Subject: [PATCH 03/12] Don't run kra.configure_instance if not necessary If kra should not be set up, don't run the code as it would only prolong the installations. Previously, krainstance configuration would be performed just to export the client certificate and private key to authenticate to certificate server. This is now performed somewhere else therefore there's no need to run KRAInstance.configure_instance. The kra.install() method still performs actions on replicas and we're keeping it in server installer to conform to the installers design. https://fedorahosted.org/freeipa/ticket/5695 --- ipaserver/install/kra.py | 16 +++++----- ipaserver/install/krainstance.py | 65 ++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py index 6f5c22b..eb74452 100644 --- a/ipaserver/install/kra.py +++ b/ipaserver/install/kra.py @@ -69,6 +69,8 @@ def install_check(api, replica_config, options): def install(api, replica_config, options): if replica_config is None: + if not options.setup_kra: + return realm_name = api.env.realm dm_password = options.dm_password host_name = api.env.host @@ -76,7 +78,6 @@ def install(api, replica_config, options): pkcs12_info = None master_host = None - ra_only = not options.setup_kra promote = False else: krafile = os.path.join(replica_config.dir, 'kracert.p12') @@ -96,6 +97,9 @@ def install(api, replica_config, options): " cacert.p12 file not found in replica file") shutil.copy(cafile, krafile) + if not replica_config.setup_kra: + return + realm_name = replica_config.realm_name dm_password = replica_config.dirman_password host_name = replica_config.host_name @@ -103,7 +107,6 @@ def install(api, replica_config, options): pkcs12_info = (krafile,) master_host = replica_config.kra_host_name - ra_only = not replica_config.setup_kra promote = options.promote kra = krainstance.KRAInstance(realm_name) @@ -111,18 +114,15 @@ def install(api, replica_config, options): subject_base=subject_base, pkcs12_info=pkcs12_info, master_host=master_host, - ra_only=ra_only, promote=promote) _service.print_msg("Restarting the directory server") ds = dsinstance.DsInstance() ds.restart() + kra.enable_client_auth_to_db(paths.KRA_CS_CFG_PATH) - if not ra_only: - kra.enable_client_auth_to_db(paths.KRA_CS_CFG_PATH) - - # Restart apache for new proxy config file - services.knownservices.httpd.restart(capture_output=True) + # Restart apache for new proxy config file + services.knownservices.httpd.restart(capture_output=True) def uninstall(standalone): diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index 1d823bf..be17637 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -77,7 +77,7 @@ def __init__(self, realm): def configure_instance(self, realm_name, host_name, dm_password, admin_password, pkcs12_info=None, master_host=None, subject_base=None, subject=None, - ra_only=False, promote=False): + promote=False): """Create a KRA instance. To create a clone, pass in pkcs12_info. @@ -99,38 +99,37 @@ def configure_instance(self, realm_name, host_name, dm_password, self.realm = realm_name self.suffix = ipautil.realm_to_suffix(realm_name) - if not ra_only: - # Confirm that a KRA does not already exist - if self.is_installed(): - raise RuntimeError( - "KRA already installed.") - # Confirm that a Dogtag 10 CA instance already exists - ca = cainstance.CAInstance(self.realm) - if not ca.is_installed(): - raise RuntimeError( - "KRA configuration failed. " - "A Dogtag CA must be installed first") - - if promote: - self.step("creating installation admin user", self.setup_admin) - self.step("configuring KRA instance", self.__spawn_instance) - if not self.clone: - self.step("create KRA agent", - self.__create_kra_agent) - if not ra_only: - if promote: - self.step("destroying installation admin user", self.teardown_admin) - self.step("restarting KRA", self.restart_instance) - self.step("configure certmonger for renewals", - self.configure_certmonger_renewal) - self.step("configure certificate renewals", self.configure_renewal) - self.step("configure HTTP to proxy connections", - self.http_proxy) - if not self.clone: - self.step("add vault container", self.__add_vault_container) - self.step("apply LDAP updates", self.__apply_updates) - - self.step("enabling KRA instance", self.__enable_instance) + # Confirm that a KRA does not already exist + if self.is_installed(): + raise RuntimeError( + "KRA already installed.") + # Confirm that a Dogtag 10 CA instance already exists + ca = cainstance.CAInstance(self.realm) + if not ca.is_installed(): + raise RuntimeError( + "KRA configuration failed. " + "A Dogtag CA must be installed first") + + if promote: + self.step("creating installation admin user", self.setup_admin) + self.step("configuring KRA instance", self.__spawn_instance) + if not self.clone: + self.step("create KRA agent", + self.__create_kra_agent) + if promote: + self.step("destroying installation admin user", + self.teardown_admin) + self.step("restarting KRA", self.restart_instance) + self.step("configure certmonger for renewals", + self.configure_certmonger_renewal) + self.step("configure certificate renewals", self.configure_renewal) + self.step("configure HTTP to proxy connections", + self.http_proxy) + if not self.clone: + self.step("add vault container", self.__add_vault_container) + self.step("apply LDAP updates", self.__apply_updates) + + self.step("enabling KRA instance", self.__enable_instance) self.start_creation(runtime=126) From 9aaaef2ca36bd09711483ecc83a79f78c4411227 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Tue, 3 Jan 2017 09:49:48 +0100 Subject: [PATCH 04/12] Move publishing of CA cert to cainstance creation on master IPAHTTPSConnection which is set up first time in certificate profiles migration to LDAP requires CA cert to be stored in a file. https://fedorahosted.org/freeipa/ticket/5695 --- ipaserver/install/cainstance.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 233ed50..8c9e528 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -424,6 +424,8 @@ def configure_instance(self, host_name, dm_password, admin_password, self.step("setting audit signing renewal to 2 years", self.set_audit_renewal) self.step("restarting certificate server", self.restart_instance) if not self.clone: + self.step("publishing the CA certificate", + lambda: self.publish_ca_cert(paths.IPA_CA_CRT)) self.step("adding RA agent as a trusted user", self.__create_ca_agent) self.step("authorizing RA to modify profiles", configure_profiles_acl) self.step("authorizing RA to manage lightweight CAs", From e5decada75fc993c7df6cf4d84271de7ebbe4cbc Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Tue, 20 Dec 2016 10:23:47 +0100 Subject: [PATCH 05/12] Remove NSSConnection from Dogtag Replaced NSSConnection with Python's httplib.HTTPSConnection. This class is OpenSSL-based. A client certificate with a private key is required to authenticate against the certificate server. We facilitate the RA_AGENT_PEM which already exists. https://fedorahosted.org/freeipa/ticket/5695 --- ipapython/dogtag.py | 20 +++++++------------- ipaserver/install/cainstance.py | 6 ------ ipaserver/install/certs.py | 10 ++-------- ipaserver/plugins/dogtag.py | 38 ++++++++++++-------------------------- 4 files changed, 21 insertions(+), 53 deletions(-) diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py index 01fc5cb..2e2b1c7 100644 --- a/ipapython/dogtag.py +++ b/ipapython/dogtag.py @@ -20,16 +20,16 @@ import collections import xml.dom.minidom -import nss.nss as nss import six # pylint: disable=import-error from six.moves.urllib.parse import urlencode # pylint: enable=import-error from ipalib import api, errors +from ipalib.util import IPAHTTPSConnection from ipalib.errors import NetworkError from ipalib.text import _ -from ipapython import nsslib, ipautil +from ipapython import ipautil from ipapython.ipa_log_manager import root_logger # Python 3 rename. The package is available in "six.moves.http_client", but @@ -131,8 +131,8 @@ def ca_status(ca_host=None): return _parse_ca_status(body) -def https_request(host, port, url, secdir, password, nickname, - method='POST', headers=None, body=None, **kw): +def https_request(host, port, url, cafile, client_certfile, + method='POST', headers=None, body=None, **kw): """ :param method: HTTP request method (defalut: 'POST') :param url: The path (not complete URL!) to post to. @@ -145,15 +145,9 @@ def https_request(host, port, url, secdir, password, nickname, """ def connection_factory(host, port): - no_init = secdir == nsslib.current_dbdir - conn = nsslib.NSSConnection(host, port, dbdir=secdir, no_init=no_init, - tls_version_min=api.env.tls_version_min, - tls_version_max=api.env.tls_version_max) - conn.set_debuglevel(0) - conn.connect() - conn.sock.set_client_auth_data_callback( - nsslib.client_auth_data_callback, - nickname, password, nss.get_default_certdb()) + conn = IPAHTTPSConnection(host, port, cafile, client_certfile, + tls_version_min=api.env.tls_version_min, + tls_version_max=api.env.tls_version_max) return conn if body is None: diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 8c9e528..0f58ffe 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -1567,7 +1567,6 @@ def import_included_profiles(): cn=['certprofiles'], ) - api.Backend.ra_certprofile._read_password() api.Backend.ra_certprofile.override_port = 8443 for (profile_id, desc, store_issued) in dogtag.INCLUDED_PROFILES: @@ -1604,7 +1603,6 @@ def repair_profile_caIPAserviceCert(): This function detects and repairs occurrences of this problem. """ - api.Backend.ra_certprofile._read_password() api.Backend.ra_certprofile.override_port = 8443 profile_id = 'caIPAserviceCert' @@ -1647,8 +1645,6 @@ def migrate_profiles_to_ldap(): """ ensure_ldap_profiles_container() - - api.Backend.ra_certprofile._read_password() api.Backend.ra_certprofile.override_port = 8443 with open(paths.CA_CS_CFG_PATH) as f: @@ -1733,8 +1729,6 @@ def ensure_ipa_authority_entry(): """ # find out authority id, issuer DN and subject DN of IPA CA - # - api.Backend.ra_lightweight_ca._read_password() api.Backend.ra_lightweight_ca.override_port = 8443 with api.Backend.ra_lightweight_ca as lwca: data = lwca.read_ca('host-authority') diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index bca2504..e6b94f5 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -397,12 +397,9 @@ def issue_server_cert(self, certreq_fname, cert_fname): 'xmlOutput': 'true'} # Send the request to the CA - f = open(self.passwd_fname, "r") - password = f.readline() - f.close() result = dogtag.https_request( self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient", - self.secdir, password, "ipaCert", **params) + api.env.ca_certfile, paths.RA_AGENT_PEM, **params) http_status, _http_headers, http_body = result root_logger.debug("CA answer: %s", http_body) @@ -451,12 +448,9 @@ def issue_signing_cert(self, certreq_fname, cert_fname): 'xmlOutput': 'true'} # Send the request to the CA - f = open(self.passwd_fname, "r") - password = f.readline() - f.close() result = dogtag.https_request( self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient", - self.secdir, password, "ipaCert", **params) + api.env.ca_certfile, paths.RA_AGENT_PEM, **params) http_status, _http_headers, http_body = result if http_status != 200: raise RuntimeError("Unable to submit cert request") diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 2156f07..6963c4f 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -1238,30 +1238,18 @@ def _parse_dogtag_error(body): return None def __init__(self, api): + self.ca_cert = api.env.ca_certfile if api.env.in_tree: - self.sec_dir = api.env.dot_ipa + os.sep + 'alias' - self.pwd_file = self.sec_dir + os.sep + '.pwd' + self.client_certfile = os.path.join( + api.env.dot_ipa, 'ra-agent.pem') else: - self.sec_dir = paths.IPA_RADB_DIR - self.pwd_file = os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt') - self.noise_file = self.sec_dir + os.sep + '.noise' - self.ipa_key_size = "2048" - self.ipa_certificate_nickname = "ipaCert" - self.ca_certificate_nickname = "caCert" - self._read_password() + self.client_certfile = paths.RA_AGENT_PEM super(RestClient, self).__init__(api) # session cookie self.override_port = None self.cookie = None - def _read_password(self): - try: - with open(self.pwd_file) as f: - self.password = f.readline().strip() - except IOError: - self.password = '' - @cachedproperty def ca_host(self): """ @@ -1288,9 +1276,8 @@ def __enter__(self): return status, resp_headers, _resp_body = dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, - '/ca/rest/account/login', - self.sec_dir, self.password, self.ipa_certificate_nickname, - method='GET' + '/ca/rest/account/login', self.ca_cert, + self.client_certfile, 'GET' ) cookies = ipapython.cookie.Cookie.parse(resp_headers.get('set-cookie', '')) if status != 200 or len(cookies) == 0: @@ -1302,9 +1289,8 @@ def __exit__(self, exc_type, exc_value, traceback): """Log out of the REST API""" dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, - '/ca/rest/account/logout', - self.sec_dir, self.password, self.ipa_certificate_nickname, - method='GET' + '/ca/rest/account/logout', self.ca_cert, + self.client_certfile, 'GET' ) self.cookie = None @@ -1344,9 +1330,8 @@ def _ssldo(self, method, path, headers=None, body=None, use_session=True): # perform main request status, resp_headers, resp_body = dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, - resource, - self.sec_dir, self.password, self.ipa_certificate_nickname, - method=method, headers=headers, body=body + resource, self.ca_cert, self.client_certfile, + method, headers, body ) if status < 200 or status >= 300: explanation = self._parse_dogtag_error(resp_body) or '' @@ -1426,7 +1411,8 @@ def _sslget(self, url, port, **kw): Perform an HTTPS request """ - return dogtag.https_request(self.ca_host, port, url, self.sec_dir, self.password, self.ipa_certificate_nickname, **kw) + return dogtag.https_request(self.ca_host, port, url, self.ca_cert, + self.client_certfile, **kw) def get_parse_result_xml(self, xml_text, parse_func): ''' From 7b2ff7a1e1864152e467dbe630784f79894fee4a Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Tue, 3 Jan 2017 13:31:01 +0100 Subject: [PATCH 06/12] Remove NSSConnection from otptoken plugin Replace NSSConnection with IPAHTTPSConnection to be able to remove NSSConnection for good. https://fedorahosted.org/freeipa/ticket/5695 --- ipaclient/plugins/otptoken.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ipaclient/plugins/otptoken.py b/ipaclient/plugins/otptoken.py index 885a612..1d57527 100644 --- a/ipaclient/plugins/otptoken.py +++ b/ipaclient/plugins/otptoken.py @@ -25,8 +25,8 @@ from ipalib.messages import add_message, ResultFormattingError from ipalib.plugable import Registry from ipalib.frontend import Local +from ipalib.util import IPAHTTPSConnection from ipapython.dn import DN -from ipapython.nsslib import NSSConnection from ipapython.version import API_VERSION import locale @@ -126,9 +126,7 @@ def __init__(self, **kwargs): def __inner(self, host, **kwargs): tmp = self.__kwargs.copy() tmp.update(kwargs) - # NSSConnection doesn't support timeout argument - tmp.pop('timeout', None) - return NSSConnection(host, **tmp) + return IPAHTTPSConnection(host, **tmp) def https_open(self, req): # pylint: disable=no-member @@ -173,9 +171,10 @@ def forward(self, *args, **kwargs): # Sync the token. # pylint: disable=E1101 - handler = HTTPSHandler(dbdir=api.env.nss_dir, - tls_version_min=api.env.tls_version_min, - tls_version_max=api.env.tls_version_max) + handler = HTTPSHandler( + cafile=api.env.ca_certfile, + tls_version_min=api.env.tls_version_min, + tls_version_max=api.env.tls_version_max) rsp = urllib.request.build_opener(handler).open(sync_uri, query) if rsp.getcode() == 200: status['result'][self.header] = rsp.info().get(self.header, 'unknown') From 88094333c7d0a47fb6e8f9e16c728cd71a2fb1a5 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Wed, 4 Jan 2017 08:47:59 +0100 Subject: [PATCH 07/12] Remove ipapython.nsslib as it is not used anymore Previous changes allowed the removal of nsslib. So long, and thanks for all the fish. https://fedorahosted.org/freeipa/ticket/5695 --- ipapython/nsslib.py | 287 ---------------------------------------------------- 1 file changed, 287 deletions(-) delete mode 100644 ipapython/nsslib.py diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py deleted file mode 100644 index 08d05fc..0000000 --- a/ipapython/nsslib.py +++ /dev/null @@ -1,287 +0,0 @@ -# Authors: Rob Crittenden <rcrit...@redhat.com> -# John Dennis <jden...@redhat.com> -# -# Copyright (C) 2009 Red Hat -# 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 print_function - -import getpass -import socket -from ipapython.ipa_log_manager import root_logger - -from nss.error import NSPRError -import nss.io as io -import nss.nss as nss -import nss.ssl as ssl -import nss.error as error - -# Python 3 rename. The package is available in "six.moves.http_client", but -# pylint cannot handle classes from that alias -try: - import httplib -except ImportError: - # pylint: disable=import-error - import http.client as httplib - -# NSS database currently open -current_dbdir = None - -def auth_certificate_callback(sock, check_sig, is_server, certdb): - cert_is_valid = False - - cert = sock.get_peer_certificate() - - pin_args = sock.get_pkcs11_pin_arg() - if pin_args is None: - pin_args = () - - # Define how the cert is being used based upon the is_server flag. This may - # seem backwards, but isn't. If we're a server we're trying to validate a - # client cert. If we're a client we're trying to validate a server cert. - if is_server: - intended_usage = nss.certificateUsageSSLClient - else: - intended_usage = nss.certificateUsageSSLServer - - try: - # If the cert fails validation it will raise an exception, the errno attribute - # will be set to the error code matching the reason why the validation failed - # and the strerror attribute will contain a string describing the reason. - approved_usage = cert.verify_now(certdb, check_sig, intended_usage, *pin_args) - except Exception as e: - root_logger.error( - 'cert validation failed for "%s" (%s)', cert.subject, - e.strerror) # pylint: disable=no-member - cert_is_valid = False - return cert_is_valid - - root_logger.debug("approved_usage = %s intended_usage = %s", - ', '.join(nss.cert_usage_flags(approved_usage)), - ', '.join(nss.cert_usage_flags(intended_usage))) - - # Is the intended usage a proper subset of the approved usage - cert_is_valid = bool(approved_usage & intended_usage) - - # If this is a server, we're finished - if is_server or not cert_is_valid: - root_logger.debug('cert valid %s for "%s"', cert_is_valid, cert.subject) - return cert_is_valid - - # Certificate is OK. Since this is the client side of an SSL - # connection, we need to verify that the name field in the cert - # matches the desired hostname. This is our defense against - # man-in-the-middle attacks. - - hostname = sock.get_hostname() - try: - # If the cert fails validation it will raise an exception - cert_is_valid = cert.verify_hostname(hostname) - except Exception as e: - root_logger.error('failed verifying socket hostname "%s" matches cert subject "%s" (%s)', - hostname, cert.subject, - e.strerror) # pylint: disable=no-member - cert_is_valid = False - return cert_is_valid - - root_logger.debug('cert valid %s for "%s"', cert_is_valid, cert.subject) - return cert_is_valid - -def client_auth_data_callback(ca_names, chosen_nickname, password, certdb): - cert = None - if chosen_nickname: - try: - cert = nss.find_cert_from_nickname(chosen_nickname, password) - priv_key = nss.find_key_by_any_cert(cert, password) - return cert, priv_key - except NSPRError: - return False - else: - nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER) - for nickname in nicknames: - try: - cert = nss.find_cert_from_nickname(nickname, password) - if cert.check_valid_times(): - if cert.has_signer_in_ca_names(ca_names): - priv_key = nss.find_key_by_any_cert(cert, password) - return cert, priv_key - except NSPRError: - return False - return False - -_af_dict = { - socket.AF_INET: io.PR_AF_INET, - socket.AF_INET6: io.PR_AF_INET6, - socket.AF_UNSPEC: io.PR_AF_UNSPEC -} - -class NSSAddressFamilyFallback(object): - def __init__(self, family): - self.sock_family = family - self.family = self._get_nss_family(self.sock_family) - - def _get_nss_family(self, sock_family): - """ - Translate a family from python socket module to nss family. - """ - try: - return _af_dict[sock_family] - except KeyError: - raise ValueError('Uknown socket family %d\n', sock_family) - - def _create_socket(self): - self.sock = io.Socket(family=self.family) - - def connect_socket(self, host, port): - try: - addr_info = io.AddrInfo(host, family=self.family) - except Exception: - raise NSPRError( - error_code=error.PR_ADDRESS_NOT_SUPPORTED_ERROR, - error_message="Cannot resolve %s using family %s" % (host, - io.addr_family_name(self.family))) - - for net_addr in addr_info: - root_logger.debug("Connecting: %s", net_addr) - net_addr.port = port - self.family = net_addr.family - try: - self._create_socket() - self.sock.connect(net_addr) - return - except Exception as e: - root_logger.debug("Could not connect socket to %s, error: %s", - net_addr, str(e)) - root_logger.debug("Try to continue with next family...") - continue - - raise NSPRError( - error_code=error.PR_ADDRESS_NOT_SUPPORTED_ERROR, - error_message="Could not connect to %s using any address" % host) - - -class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback): - default_port = httplib.HTTPSConnection.default_port - - def __init__(self, host, port=None, strict=None, - dbdir=None, family=socket.AF_UNSPEC, no_init=False, - tls_version_min='tls1.1', tls_version_max='tls1.2'): - """ - :param host: the server to connect to - :param port: the port to use (default is set in HTTPConnection) - :param dbdir: the NSS database directory - :param family: network family to use (default AF_UNSPEC) - :param no_init: do not initialize the NSS database. This requires - that the database has already been initialized or - the request will fail. - :param tls_min_version: mininum version of SSL/TLS supported - :param tls_max_version: maximum version of SSL/TLS supported. - """ - httplib.HTTPConnection.__init__(self, host, port, strict) - NSSAddressFamilyFallback.__init__(self, family) - - root_logger.debug('%s init %s', self.__class__.__name__, host) - - # If initialization is requested, initialize the new database. - if not no_init: - - if nss.nss_is_initialized(): - ssl.clear_session_cache() - try: - nss.nss_shutdown() - except NSPRError as e: - if e.errno != error.SEC_ERROR_NOT_INITIALIZED: - raise e - - if not dbdir: - raise RuntimeError("dbdir is required") - - nss.nss_init(dbdir) - - global current_dbdir - current_dbdir = dbdir - - ssl.set_domestic_policy() - nss.set_password_callback(self.password_callback) - self.tls_version_min = str(tls_version_min) - self.tls_version_max = str(tls_version_max) - - def _create_socket(self): - ssl_enable_renegotiation = getattr( - ssl, 'SSL_ENABLE_RENEGOTIATION', 20) - ssl_require_safe_negotiation = getattr( - ssl,'SSL_REQUIRE_SAFE_NEGOTIATION', 21) - ssl_renegotiate_requires_xtn = getattr( - ssl, 'SSL_RENEGOTIATE_REQUIRES_XTN', 2) - - # Create the socket here so we can do things like let the caller - # override the NSS callbacks - self.sock = ssl.SSLSocket(family=self.family) - self.sock.set_ssl_option(ssl.SSL_SECURITY, True) - self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True) - try: - self.sock.set_ssl_version_range(self.tls_version_min, self.tls_version_max) - except NSPRError: - root_logger.error('Failed to set TLS range to %s, %s' % (self.tls_version_min, self.tls_version_max)) - raise - self.sock.set_ssl_option(ssl_require_safe_negotiation, False) - self.sock.set_ssl_option(ssl_enable_renegotiation, ssl_renegotiate_requires_xtn) - # Provide a callback which notifies us when the SSL handshake is complete - self.sock.set_handshake_callback(self.handshake_callback) - - # Provide a callback to verify the servers certificate - self.sock.set_auth_certificate_callback(auth_certificate_callback, - nss.get_default_certdb()) - self.sock.set_hostname(self.host) - - def password_callback(self, slot, retry, password): - if not retry and password: return password - return getpass.getpass("Enter password for %s: " % slot.token_name) - - def handshake_callback(self, sock): - """ - Verify callback. If we get here then the certificate is ok. - """ - channel = sock.get_ssl_channel_info() - suite = ssl.get_cipher_suite_info(channel.cipher_suite) - root_logger.debug("handshake complete, peer = %s", sock.get_peer_name()) - root_logger.debug('Protocol: %s' % channel.protocol_version_str.upper()) - root_logger.debug('Cipher: %s' % suite.cipher_suite_name) - - def connect(self): - self.connect_socket(self.host, self.port) - - def close(self): - """Close the connection to the HTTP server.""" - if self.sock: - self.sock.close() # close it manually... there may be other refs - self.sock = None - ssl.clear_session_cache() - - def endheaders(self, message=None): - """ - Explicitly close the connection if an error is returned after the - headers are sent. This will likely mean the initial SSL handshake - failed. If this isn't done then the connection is never closed and - subsequent NSS activities will fail with a BUSY error. - """ - try: - # FIXME: httplib uses old-style classes so super doesn't work - httplib.HTTPConnection.endheaders(self, message) - except NSPRError as e: - self.close() - raise e From 952577e4384906fd3150b933bef646cea08493ce Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Fri, 27 Jan 2017 08:58:00 +0100 Subject: [PATCH 08/12] Workaround for certmonger's "Subject" representations If an OpenSSL certificate is requested in Certmonger (CERT_STORAGE == "FILE") the "Subject" field of such Certificate is ordered as received. However, when an NSS certificate is requested, the "Subject" field takes the LDAP order (components get reversed). This is a workaround so that the behavior stays the same. The workaround should be removed when https://fedorahosted.org/certmonger/ticket/62 gets fixed. https://fedorahosted.org/freeipa/ticket/5695 --- install/certmonger/dogtag-ipa-ca-renew-agent-submit | 11 ++++++++++- ipalib/install/certmonger.py | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index 750893d..ee47f61 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -35,6 +35,9 @@ import base64 import contextlib import json +from cryptography import x509 as crypto_x509 +from cryptography.hazmat.backends import default_backend + import six from ipapython import ipautil @@ -64,8 +67,14 @@ if six.PY3: IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca' + def get_nickname(): - subject = os.environ.get('CERTMONGER_REQ_SUBJECT') + # we need to get the subject from a CSR in case we are requesting + # an OpenSSL certificate for which we have to reverse the order of its DN + # components thus changing the CERTMONGER_REQ_SUBJECT + csr = os.environ.get('CERTMONGER_CSR') + csr_obj = crypto_x509.load_pem_x509_csr(csr, default_backend()) + subject = csr_obj.subject if not subject: return None diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py index 951ca9a..999205b 100644 --- a/ipalib/install/certmonger.py +++ b/ipalib/install/certmonger.py @@ -32,6 +32,7 @@ import tempfile from ipalib import api from ipapython.ipa_log_manager import root_logger +from ipapython.dn import DN from ipaplatform.paths import paths from ipaplatform import services @@ -329,6 +330,9 @@ def request_cert( """ if storage == 'FILE': certfile, keyfile = certpath + # This is a workaround for certmonger having different Subject + # representation with NSS and OpenSSL + subject = str(DN(*reversed(DN(subject)))) else: certfile = certpath keyfile = certpath From 7863af1f31609b9275e75a2c87fda8a689dc4be8 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Fri, 27 Jan 2017 09:34:23 +0100 Subject: [PATCH 09/12] Refactor certmonger for OpenSSL certificates Currently, it was only possible to request an SSL certificate via certmonger. Refactored start_tracking methods to allow for OpenSSL certificates tracking. https://fedorahosted.org/freeipa/ticket/5695 --- ipalib/install/certmonger.py | 87 ++++++++++++++++++++++++------------- ipaserver/install/cainstance.py | 6 +-- ipaserver/install/certs.py | 3 +- ipaserver/install/dogtaginstance.py | 6 +-- ipaserver/install/krbinstance.py | 3 +- 5 files changed, 63 insertions(+), 42 deletions(-) diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py index 999205b..7ad19e7 100644 --- a/ipalib/install/certmonger.py +++ b/ipalib/install/certmonger.py @@ -300,7 +300,7 @@ def add_subject(request_id, subject): def request_and_wait_for_cert( - certpath, nickname, subject, principal, passwd_fname=None, + certpath, subject, principal, nickname=None, passwd_fname=None, dns=None, ca='IPA', profile=None, pre_command=None, post_command=None, storage='NSSDB'): """ @@ -308,7 +308,7 @@ def request_and_wait_for_cert( The method also waits for the certificate to be available. """ - reqId = request_cert(certpath, nickname, subject, principal, + reqId = request_cert(certpath, subject, principal, nickname, passwd_fname, dns, ca, profile, pre_command, post_command, storage) state = wait_for_request(reqId, api.env.startup_timeout) @@ -319,7 +319,7 @@ def request_and_wait_for_cert( def request_cert( - certpath, nickname, subject, principal, passwd_fname=None, + certpath, subject, principal, nickname=None, passwd_fname=None, dns=None, ca='IPA', profile=None, pre_command=None, post_command=None, storage='NSSDB'): """ @@ -342,9 +342,11 @@ def request_cert( if not ca_path: raise RuntimeError('{} CA not found'.format(ca)) request_parameters = dict(KEY_STORAGE=storage, CERT_STORAGE=storage, - CERT_LOCATION=certfile, CERT_NICKNAME=nickname, - KEY_LOCATION=keyfile, KEY_NICKNAME=nickname, + CERT_LOCATION=certfile, KEY_LOCATION=keyfile, SUBJECT=subject, CA=ca_path) + if nickname: + request_parameters["CERT_NICKNAME"] = nickname + request_parameters["KEY_NICKNAME"] = nickname if principal: request_parameters['PRINCIPAL'] = [principal] if dns is not None and len(dns) > 0: @@ -378,33 +380,46 @@ def request_cert( return request.obj_if.get_nickname() -def start_tracking(nickname, secdir, password_file=None, command=None): +def start_tracking( + certpath, nickname=None, password_file=None, post_command=None, + storage='NSSDB'): """ - Tell certmonger to track the given certificate nickname in NSS - database in secdir protected by optional password file password_file. + Tell certmonger to track the given certificate in either a file or an NSS + database. The certificate access can be protected by a password_file. - command is an optional parameter which specifies a command for + post_command is an optional parameter which specifies a command for certmonger to run when it renews a certificate. This command must reside in /usr/lib/ipa/certmonger to work with SELinux. Returns certificate nickname. """ + if storage == 'FILE': + certfile, keyfile = certpath + else: + certfile = certpath + keyfile = certpath + cm = _certmonger() - params = {'TRACK': True} - params['cert-nickname'] = nickname - params['cert-database'] = os.path.abspath(secdir) - params['cert-storage'] = 'NSSDB' - params['key-nickname'] = nickname - params['key-database'] = os.path.abspath(secdir) - params['key-storage'] = 'NSSDB' ca_path = cm.obj_if.find_ca_by_nickname('IPA') if not ca_path: raise RuntimeError('IPA CA not found') - params['ca'] = ca_path - if command: - params['cert-postsave-command'] = command + + params = { + 'TRACK': True, + 'CERT_STORAGE': storage, + 'KEY_STORAGE': storage, + 'CERT_LOCATION': certfile, + 'KEY_LOCATION': keyfile, + 'CA': ca_path + } + if nickname: + params['CERT_NICKNAME'] = nickname + params['KEY_NICKNAME'] = nickname if password_file: params['KEY_PIN_FILE'] = os.path.abspath(password_file) + if post_command: + params['cert-postsave-command'] = post_command + result = cm.obj_if.add_request(params) try: if result[0]: @@ -544,8 +559,9 @@ def get_pin(token): return None -def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command, - post_command, profile=None): +def dogtag_start_tracking( + certpath, ca, nickname=None, pin=None, pinfile=None, + pre_command=None, post_command=None, profile=None, storage="NSSDB"): """ Tell certmonger to start tracking a dogtag CA certificate. These are handled differently because their renewal must be done directly @@ -559,20 +575,29 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command, Both commands can be None. """ + if storage == 'FILE': + certfile, keyfile = certpath + else: + certfile = certpath + keyfile = certpath cm = _certmonger() certmonger_cmd_template = paths.CERTMONGER_COMMAND_TEMPLATE - params = {'TRACK': True} - params['cert-nickname'] = nickname - params['cert-database'] = os.path.abspath(secdir) - params['cert-storage'] = 'NSSDB' - params['key-nickname'] = nickname - params['key-database'] = os.path.abspath(secdir) - params['key-storage'] = 'NSSDB' + params = { + 'TRACK': True, + 'CERT_STORAGE': storage, + 'KEY_STORAGE': storage, + 'CERT_LOCATION': certfile, + 'KEY_LOCATION': keyfile + } ca_path = cm.obj_if.find_ca_by_nickname(ca) + if nickname: + params['CERT_NICKNAME'] = nickname + params['KEY_NICKNAME'] = nickname + if ca_path: - params['ca'] = ca_path + params['CA'] = ca_path if pin: params['KEY_PIN'] = pin if pinfile: @@ -622,9 +647,9 @@ def wait_for_request(request_id, timeout=120): return state if __name__ == '__main__': - request_id = request_cert(paths.HTTPD_ALIAS_DIR, "Test", + request_id = request_cert(paths.HTTPD_ALIAS_DIR, "cn=tiger.example.com,O=IPA", - "HTTP/tiger.example....@example.com") + "HTTP/tiger.example....@example.com", "Test") csr = get_request_value(request_id, 'csr') print(csr) stop_tracking(request_id) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 0f58ffe..90883b6 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -996,11 +996,10 @@ def unconfigure_certmonger_renewal_guard(self): def configure_agent_renewal(self): try: certmonger.dogtag_start_tracking( + certpath=paths.IPA_RADB_DIR, ca='dogtag-ipa-ca-renew-agent', nickname='ipaCert', - pin=None, pinfile=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), - secdir=paths.IPA_RADB_DIR, pre_command='renew_ra_cert_pre', post_command='renew_ra_cert') except RuntimeError as e: @@ -1797,9 +1796,8 @@ def add_lightweight_ca_tracking_requests(logger, lwcas): if request_id is None: try: certmonger.dogtag_start_tracking( - secdir=paths.PKI_TOMCAT_ALIAS_DIR, + certpath=paths.PKI_TOMCAT_ALIAS_DIR, pin=certmonger.get_pin('internal'), - pinfile=None, nickname=nickname, ca=ipalib.constants.RENEWAL_CA_NAME, pre_command='stop_pkicad', diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index e6b94f5..15f9545 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -310,7 +310,8 @@ def track_server_cert(self, nickname, principal, password_file=None, command=Non if command is not None and not os.path.isabs(command): command = paths.CERTMONGER_COMMAND_TEMPLATE % (command) try: - request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command) + request_id = certmonger.start_tracking( + self.secdir, nickname, password_file, command) except RuntimeError as e: root_logger.error("certmonger failed starting to track certificate: %s" % str(e)) return diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 6b54359..86c819c 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -303,11 +303,10 @@ def configure_renewal(self): for nickname, profile in self.tracking_reqs: try: certmonger.dogtag_start_tracking( + certpath=self.nss_db, ca='dogtag-ipa-ca-renew-agent', nickname=nickname, pin=pin, - pinfile=None, - secdir=self.nss_db, pre_command='stop_pkicad', post_command='renew_ca_cert "%s"' % nickname, profile=profile) @@ -324,11 +323,10 @@ def track_servercert(self): pin = self.__get_pin() try: certmonger.dogtag_start_tracking( + certpath=self.nss_db, ca='dogtag-ipa-ca-renew-agent', nickname=self.server_cert_name, pin=pin, - pinfile=None, - secdir=self.nss_db, pre_command='stop_pkicad', post_command='renew_ca_cert "%s"' % self.server_cert_name) except RuntimeError as e: diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index 44b3821..8d0d79d 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -361,8 +361,7 @@ def setup_pkinit(self): krbtgt = "krbtgt/" + self.realm + "@" + self.realm certpath = (paths.KDC_CERT, paths.KDC_KEY) try: - reqid = certmonger.request_cert(certpath, u'KDC-Cert', - subject, krbtgt, + reqid = certmonger.request_cert(certpath, subject, krbtgt, dns=self.fqdn, storage='FILE', profile='KDCs_PKINIT_Certs') except dbus.DBusException as e: From 0f7b039752e818751f22ed6ac52bac4f885651a2 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Wed, 1 Feb 2017 09:14:56 +0100 Subject: [PATCH 10/12] Added an OpenSSL handler for Custodia store This is a preparation step to be able to handle sending RA agent certificate over Custodia during domain level 1 replica installation. https://fedorahosted.org/freeipa/ticket/5695 --- ipaserver/secrets/store.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py index b2d724d..1051c50 100644 --- a/ipaserver/secrets/store.py +++ b/ipaserver/secrets/store.py @@ -191,6 +191,67 @@ def import_key(self, value): conn.modify_s('cn=config', mods) +class OpenSSLHandler(DBMAPHandler): + def __init__(self, config, dbmap, nickname=None): + if 'type' not in dbmap or dbmap['type'] != 'OPENSSL': + raise ValueError('Invalid type "{t}", expected OPENSSL' + .format(t=dbmap['type'])) + self.certfile = dbmap['certfile'] + self.keyfile = dbmap.get(['keyfile']) + + def export_key(self): + _fd, tmpfile = tempfile.mkstemp(dir=paths.TMP) + password = ipautil.ipa_generate_password() + args = [ + paths.OPENSSL, + "pkcs12", "-export", + "-in", self.certfile, + "-out", tmpfile, + "-password", "pass:{pwd}".format(pwd=password) + ] + if self.keyfile is not None: + args.extend(["-inkey", self.keyfile]) + + try: + ipautil.run(args, nolog=password) + with open(tmpfile, 'r') as f: + data = f.read() + finally: + os.remove(tmpfile) + return json_encode({'export password': password, + 'pkcs12 data': b64encode(data)}) + + def import_key(self, value): + v = json_decode(value) + data = b64decode(v['pkcs12 data']) + password = v['export password'] + try: + _fd, tmpdata = tempfile.mkstemp(dir=paths.TMP) + with open(tmpdata, 'w') as f: + f.write(data) + + # get the certificate from the file + ipautil.run([paths.OPENSSL, + "pkcs12", + "-in", tmpdata, + "-clcerts", "-nokeys", + "-out", self.certfile, + "-passin", "pass:{pwd}".format(pwd=password)], + nolog=(password)) + + if self.keyfile is not None: + # get the private key from the file + ipautil.run([paths.OPENSSL, + "pkcs12", + "-in", tmpdata, + "-nocerts", "-nodes", + "-out", self.keyfile, + "-passin", "pass:{pwd}".format(pwd=password)], + nolog=(password)) + finally: + os.remove(tmpdata) + + NAME_DB_MAP = { 'ca': { 'type': 'NSSDB', From 911505dd22682bf0a1f69f63e1d129b2200a08d3 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Fri, 13 Jan 2017 09:08:42 +0100 Subject: [PATCH 11/12] Moving ipaCert from HTTPD_ALIAS_DIR The "ipaCert" nicknamed certificate is not required to be in /etc/httpd/alias NSSDB anymore as we were keeping a copy of this file in a separate file anyway. Remove it from there and track only the file. https://fedorahosted.org/freeipa/ticket/5695 --- .../certmonger/dogtag-ipa-ca-renew-agent-submit | 9 +- install/restart_scripts/renew_ra_cert | 30 ++-- ipaplatform/base/paths.py | 2 + ipapython/dogtag.py | 6 +- ipaserver/install/ca.py | 15 +- ipaserver/install/cainstance.py | 72 ++++---- ipaserver/install/certs.py | 6 +- ipaserver/install/custodiainstance.py | 4 + ipaserver/install/dogtaginstance.py | 22 --- ipaserver/install/dsinstance.py | 17 +- ipaserver/install/httpinstance.py | 11 +- ipaserver/install/ipa_replica_prepare.py | 24 ++- ipaserver/install/krainstance.py | 12 +- ipaserver/install/plugins/ca_renewal_master.py | 11 +- ipaserver/install/server/upgrade.py | 187 +++++++++++---------- ipaserver/plugins/dogtag.py | 20 ++- ipaserver/secrets/store.py | 8 +- 17 files changed, 237 insertions(+), 219 deletions(-) diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index ee47f61..c64f7be 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -216,10 +216,11 @@ def request_cert(): syslog.syslog(syslog.LOG_NOTICE, "Forwarding request to dogtag-ipa-renew-agent") - path = paths.DOGTAG_IPA_RENEW_AGENT_SUBMIT - args = [path, '--dbdir', paths.IPA_RADB_DIR] - args.extend(sys.argv[1:]) - args.extend(['--submit-option', "requestor_name=IPA"]) + args = ([paths.DOGTAG_IPA_RENEW_AGENT_SUBMIT, + "--cafile", paths.IPA_CA_CRT, + "--certfile", paths.RA_AGENT_PEM, + "--keyfile", paths.RA_AGENT_KEY] + + sys.argv[1:] + ['--submit-option', "requestor_name=IPA"]) if os.environ.get('CERTMONGER_CA_PROFILE') == 'caCACert': args += ['-N', '-O', 'bypassCAnotafter=true'] result = ipautil.run(args, raiseonerr=False, env=os.environ, diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert index 4dc6c2e..dcbdb55 100644 --- a/install/restart_scripts/renew_ra_cert +++ b/install/restart_scripts/renew_ra_cert @@ -28,14 +28,14 @@ import shutil import traceback from ipalib.install.kinit import kinit_keytab -from ipalib import api -from ipaserver.install import certs, cainstance, dogtaginstance +from ipalib import api, x509 +from ipaserver.install import certs, cainstance from ipaplatform.paths import paths +from cryptography.hazmat.primitives import serialization -def _main(): - nickname = 'ipaCert' +def _main(): api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA) api.finalize() api.Backend.ldap2.connect() @@ -48,20 +48,28 @@ def _main(): os.environ['KRB5CCNAME'] = ccache_filename ca = cainstance.CAInstance(host_name=api.env.host) + ra_certpath = paths.RA_AGENT_PEM if ca.is_renewal_master(): # Fetch the new certificate - db = certs.CertDB(api.env.realm) - dercert = db.get_cert_from_db(nickname, pem=False) - if not dercert: + try: + cert = x509.load_certificate_from_file(ra_certpath) + except IOError as e: + syslog.syslog( + syslog.LOG_ERR, "Can't open '{certpath}': {err}" + .format(certpath=ra_certpath, err=e) + ) + sys.exit(1) + except (TypeError, ValueError): syslog.syslog( - syslog.LOG_ERR, "No certificate %s found." % nickname) + syslog.LOG_ERR, "'{certpath}' is not a valid certificate " + "file".format(certpath=ra_certpath) + ) sys.exit(1) + dercert = cert.public_bytes(serialization.Encoding.DER) + # Load it into dogtag cainstance.update_people_entry(dercert) - - if api.Command.kra_is_enabled()['result']: - dogtaginstance.export_ra_agent_pem() finally: shutil.rmtree(tmpdir) api.Backend.ldap2.disconnect() diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 6c35b08..85c584d 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -139,6 +139,7 @@ class BasePathNamespace(object): ROOT_PKI = "/root/.pki" DOGTAG_ADMIN_P12 = "/root/ca-agent.p12" RA_AGENT_PEM = "/var/lib/ipa/ra-agent.pem" + RA_AGENT_KEY = "/var/lib/ipa/ra-agent.key" CACERT_P12 = "/root/cacert.p12" ROOT_IPA_CSR = "/root/ipa.csr" NAMED_PID = "/run/named/named.pid" @@ -194,6 +195,7 @@ class BasePathNamespace(object): PAM_KRB5_SO_64 = "/usr/lib64/security/pam_krb5.so" DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-ca-renew-agent-submit" DOGTAG_IPA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-renew-agent-submit" + CERTMONGER_DOGTAG_SUBMIT = "/usr/libexec/certmonger/dogtag-submit" IPA_SERVER_GUARD = "/usr/libexec/certmonger/ipa-server-guard" GENERATE_RNDC_KEY = "/usr/libexec/generate-rndc-key.sh" IPA_DNSKEYSYNCD_REPLICA = "/usr/libexec/ipa/ipa-dnskeysync-replica" diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py index 2e2b1c7..72602fe 100644 --- a/ipapython/dogtag.py +++ b/ipapython/dogtag.py @@ -131,8 +131,9 @@ def ca_status(ca_host=None): return _parse_ca_status(body) -def https_request(host, port, url, cafile, client_certfile, - method='POST', headers=None, body=None, **kw): +def https_request( + host, port, url, cafile, client_certfile, client_keyfile, + method='POST', headers=None, body=None, **kw): """ :param method: HTTP request method (defalut: 'POST') :param url: The path (not complete URL!) to post to. @@ -146,6 +147,7 @@ def https_request(host, port, url, cafile, client_certfile, def connection_factory(host, port): conn = IPAHTTPSConnection(host, port, cafile, client_certfile, + client_keyfile, tls_version_min=api.env.tls_version_min, tls_version_max=api.env.tls_version_max) return conn diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index 8e92ef0..d01e426 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -183,8 +183,7 @@ def install_check(standalone, replica_config, options): for db in (cadb, dsdb): for nickname, _trust_flags in db.list_certs(): - if nickname in (certdb.get_ca_nickname(realm_name), - 'ipaCert'): + if nickname == certdb.get_ca_nickname(realm_name): raise ScriptError( "Certificate with nickname %s is present in %s, " "cannot continue." % (nickname, db.secdir)) @@ -317,15 +316,15 @@ def install_step_1(standalone, replica_config, options): cadb = certs.CertDB(realm_name, subject_base=subject_base) dsdb = certs.CertDB(realm_name, nssdir=dirname, subject_base=subject_base) trust_flags = dict(reversed(cadb.list_certs())) - trust_chain = cadb.find_root_cert('ipaCert')[:-1] - for nickname in trust_chain[:-1]: - cert = cadb.get_cert_from_db(nickname, pem=False) + trust_chain = certstore.get_ca_certs( + api.Backend.ldap2, api.env.basedn, api.env.realm, True) + for cert, nickname, _trusted, _ext_key_usage in trust_chain[:-1]: dsdb.add_cert(cert, nickname, trust_flags[nickname]) certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn, cert, nickname, trust_flags[nickname]) - nickname = trust_chain[-1] - cert = cadb.get_cert_from_db(nickname, pem=False) + nickname = trust_chain[-1][1] + cert = trust_chain[-1][0] dsdb.add_cert(cert, nickname, trust_flags[nickname]) certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn, cert, nickname, trust_flags[nickname], @@ -358,6 +357,8 @@ def install_step_1(standalone, replica_config, options): def uninstall(): ca_instance = cainstance.CAInstance(api.env.realm, paths.IPA_RADB_DIR) ca_instance.stop_tracking_certificates() + installutils.remove_file(paths.RA_AGENT_PEM) + installutils.remove_file(paths.RA_AGENT_KEY) if ca_instance.is_configured(): ca_instance.uninstall() diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 90883b6..bb3b90f 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -26,6 +26,7 @@ import ldap import os import pwd +import grp import re import shutil import sys @@ -64,10 +65,11 @@ from ipaserver.install import ldapupdate from ipaserver.install import replication from ipaserver.install import sysupgrade -from ipaserver.install.dogtaginstance import ( - DogtagInstance, export_ra_agent_pem) +from ipaserver.install.dogtaginstance import DogtagInstance from ipaserver.plugins import ldap2 +from cryptography.hazmat.primitives import serialization + # We need to reset the template because the CA uses the regular boot # information INF_TEMPLATE = """ @@ -381,8 +383,7 @@ def configure_instance(self, host_name, dm_password, admin_password, self.external = 2 if self.clone: - cert_db = certs.CertDB(self.realm) - has_ra_cert = (cert_db.get_cert_from_db('ipaCert') != '') + has_ra_cert = os.path.exists(paths.RA_AGENT_PEM) else: has_ra_cert = False @@ -416,7 +417,6 @@ def configure_instance(self, host_name, dm_password, admin_password, else: self.step("importing RA certificate from PKCS #12 file", lambda: self.import_ra_cert(ra_p12)) - self.step("exporting KRA agent cert", export_ra_agent_pem) if not ra_only: self.step("importing CA chain to RA certificate database", self.__import_ca_chain) @@ -661,13 +661,21 @@ def import_ra_cert(self, rafile): Used when setting up replication """ - # Add the new RA cert into the RA database - with tempfile.NamedTemporaryFile(mode="w") as agent_file: - agent_file.write(self.dm_password) - agent_file.flush() - - import_pkcs12( - rafile, agent_file.name, self.ra_agent_db, self.ra_agent_pwd) + # get the private key from the file + ipautil.run([paths.OPENSSL, + "pkcs12", + "-in", rafile, + "-nocerts", "-nodes", + "-out", paths.RA_AGENT_KEY, + "-passin", "pass:"]) + + # get the certificate from the pkcs12 file + ipautil.run([paths.OPENSSL, + "pkcs12", + "-in", rafile, + "-clcerts", "-nokeys", + "-out", paths.RA_AGENT_PEM, + "-passin", "pass:"]) self.configure_agent_renewal() @@ -684,9 +692,8 @@ def __create_ca_agent(self): the appropriate groups for accessing CA services. """ - # get ipaCert certificate - cert_data = base64.b64decode(self.ra_cert) - cert = x509.load_certificate(cert_data, x509.DER) + # get RA certificate + cert_data = self.ra_cert.public_bytes(serialization.Encoding.DER) # connect to CA database server_id = installutils.realm_to_serverid(api.env.realm) @@ -694,7 +701,7 @@ def __create_ca_agent(self): conn = ldap2.ldap2(api, ldap_uri=dogtag_uri) conn.connect(autobind=True) - # create ipara user with ipaCert certificate + # create ipara user with RA certificate user_dn = DN(('uid', "ipara"), ('ou', 'People'), self.basedn) entry = conn.make_entry( user_dn, @@ -707,7 +714,7 @@ def __create_ca_agent(self): userstate=["1"], userCertificate=[cert_data], description=['2;%s;%s;%s' % ( - cert.serial_number, + self.ra_cert.serial_number, DN(self.ca_subject), DN(('CN', 'IPA RA'), self.subject_base))]) conn.add_entry(entry) @@ -796,7 +803,7 @@ def __request_ra_certificate(self): chain = self.__get_ca_chain() data = base64.b64decode(chain) - result = ipautil.run( + ipautil.run( [paths.OPENSSL, "pkcs7", "-inform", @@ -823,23 +830,18 @@ def __request_ra_certificate(self): # The certificate must be requested using caServerCert profile # because this profile does not require agent authentication reqId = certmonger.request_and_wait_for_cert( - certpath=self.ra_agent_db, - nickname='ipaCert', + certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY), principal='host/%s' % self.fqdn, - passwd_fname=self.ra_agent_pwd, subject=str(DN(('CN', 'IPA RA'), self.subject_base)), ca=ipalib.constants.RENEWAL_CA_NAME, profile='caServerCert', pre_command='renew_ra_cert_pre', - post_command='renew_ra_cert') + post_command='renew_ra_cert', + storage="FILE") self.requestId = str(reqId) - result = self.__run_certutil( - ['-L', '-n', 'ipaCert', '-a'], capture_output=True) - self.ra_cert = x509.strip_header(result.output) - self.ra_cert = "\n".join( - line.strip() for line - in self.ra_cert.splitlines() if line.strip()) + self.ra_cert = x509.load_certificate_from_file( + paths.RA_AGENT_PEM) finally: # we can restore the helper parameters certmonger.modify_ca_helper( @@ -851,6 +853,11 @@ def __request_ra_certificate(self): except OSError: pass + apache_gid = grp.getgrnam(constants.HTTPD_USER).gr_gid + for fname in (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY): + os.chown(fname, -1, apache_gid) + os.chmod(fname, 0o440) + def __setup_sign_profile(self): # Tell the profile to automatically issue certs for RAs installutils.set_directive( @@ -996,12 +1003,11 @@ def unconfigure_certmonger_renewal_guard(self): def configure_agent_renewal(self): try: certmonger.dogtag_start_tracking( - certpath=paths.IPA_RADB_DIR, + certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY), ca='dogtag-ipa-ca-renew-agent', - nickname='ipaCert', - pinfile=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), pre_command='renew_ra_cert_pre', - post_command='renew_ra_cert') + post_command='renew_ra_cert', + storage='FILE') except RuntimeError as e: self.log.error( "certmonger failed to start tracking certificate: %s", e) @@ -1018,7 +1024,7 @@ def stop_tracking_certificates(self): certmonger.stop_tracking(self.nss_db, nickname=nickname) try: - certmonger.stop_tracking(paths.IPA_RADB_DIR, nickname='ipaCert') + certmonger.stop_tracking(certfile=paths.RA_AGENT_PEM) except RuntimeError as e: root_logger.error( "certmonger failed to stop tracking certificate: %s", e) diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index 15f9545..8d0af42 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -400,7 +400,8 @@ def issue_server_cert(self, certreq_fname, cert_fname): # Send the request to the CA result = dogtag.https_request( self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient", - api.env.ca_certfile, paths.RA_AGENT_PEM, **params) + api.env.ca_certfile, paths.RA_AGENT_PEM, paths.RA_AGENT_KEY, + **params) http_status, _http_headers, http_body = result root_logger.debug("CA answer: %s", http_body) @@ -451,7 +452,8 @@ def issue_signing_cert(self, certreq_fname, cert_fname): # Send the request to the CA result = dogtag.https_request( self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient", - api.env.ca_certfile, paths.RA_AGENT_PEM, **params) + api.env.ca_certfile, paths.RA_AGENT_PEM, paths.RA_AGENT_KEY, + **params) http_status, _http_headers, http_body = result if http_status != 200: raise RuntimeError("Unable to submit cert request") diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py index a0bb399..7ec0269 100644 --- a/ipaserver/install/custodiainstance.py +++ b/ipaserver/install/custodiainstance.py @@ -112,6 +112,10 @@ def __create_container(self): def import_ra_key(self, master_host_name): cli = self.__CustodiaClient(server=master_host_name) + # please note that ipaCert part has to stay here for historical + # reasons (old servers expect you to ask for ra/ipaCert during + # replication as they store the RA agent cert in an NSS database + # with this nickname) cli.fetch_key('ra/ipaCert') def import_dm_password(self, master_host_name): diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 86c819c..ef73e39 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -24,7 +24,6 @@ import tempfile import traceback import dbus -import pwd from pki.client import PKIConnection import pki.system @@ -72,27 +71,6 @@ def is_installing_replica(sys_type): return False -def export_ra_agent_pem(): - """ - Export ipaCert with private key for client authentication. - """ - fd, filename = tempfile.mkstemp(dir=paths.IPA_RADB_DIR) - os.close(fd) - - args = ["/usr/bin/pki", - "-d", paths.IPA_RADB_DIR, - "-C", os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'), - "client-cert-show", "ipaCert", - "--client-cert", filename] - ipautil.run(args) - - pent = pwd.getpwnam(IPAAPI_USER) - os.chown(filename, 0, pent.pw_gid) - os.chmod(filename, 0o440) - - os.rename(filename, paths.RA_AGENT_PEM) - - class DogtagInstance(service.Service): """ This is the base class for a Dogtag 10+ instance, which uses a diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 9172b65..e3d0d12 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -810,14 +810,15 @@ def __enable_ssl(self): self.nickname, self.principal, dsdb.passwd_fname, 'restart_dirsrv %s' % self.serverid) else: - dsdb.create_from_cacert() - ca_args = ['/usr/libexec/certmonger/dogtag-submit', - '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, - '--dbdir', paths.IPA_RADB_DIR, - '--nickname', 'ipaCert', - '--sslpinfile', os.path.join(paths.IPA_RADB_DIR, - 'pwdfile.txt'), - '--agent-submit'] + dsdb.create_from_cacert(paths.IPA_CA_CRT, passwd=None) + ca_args = [ + paths.CERTMONGER_DOGTAG_SUBMIT, + '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, + '--certfile', paths.RA_AGENT_PEM, + '--keyfile', paths.RA_AGENT_KEY, + '--cafile', paths.IPA_CA_CRT, + '--agent-submit' + ] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper('IPA', helper) try: diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 7317fba..57e4357 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -376,14 +376,13 @@ def __setup_ssl(self): if not self.promote: self.create_password_conf() ca_args = [ - '/usr/libexec/certmonger/dogtag-submit', + paths.CERTMONGER_DOGTAG_SUBMIT, '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, - '--dbdir', paths.IPA_RADB_DIR, - '--nickname', 'ipaCert', - '--sslpinfile', os.path.join(paths.IPA_RADB_DIR, - 'pwdfile.txt'), + '--certfile', paths.RA_AGENT_PEM, + '--keyfile', paths.RA_AGENT_KEY, + '--cafile', paths.IPA_CA_CRT, '--agent-submit' - ] + ] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper('IPA', helper) diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py index ece5f55..5152f62 100644 --- a/ipaserver/install/ipa_replica_prepare.py +++ b/ipaserver/install/ipa_replica_prepare.py @@ -427,7 +427,7 @@ def copy_httpd_certificate(self): self.log.info("Creating SSL certificate for the Web Server") self.export_certdb("httpcert", passwd_fname) - self.log.info("Exporting RA certificate") + self.log.info("Copying RA certificate") self.export_ra_pkcs12() def copy_pkinit_certificate(self): @@ -634,18 +634,16 @@ def export_certdb(self, fname, passwd_fname, is_kdc=False): raise admintool.ScriptError(str(e)) def export_ra_pkcs12(self): - agent_fd, agent_name = tempfile.mkstemp() - os.write(agent_fd, self.dirman_password) - os.close(agent_fd) - - try: - db = certs.CertDB(api.env.realm, host_name=api.env.host) - - if db.has_nickname("ipaCert"): - pkcs12_fname = os.path.join(self.dir, "ra.p12") - db.export_pkcs12(pkcs12_fname, agent_name, "ipaCert") - finally: - os.remove(agent_name) + if (os.path.exists(paths.RA_AGENT_PEM) and + os.path.exists(paths.RA_AGENT_KEY)): + ipautil.run([ + paths.OPENSSL, + "pkcs12", "-export", + "-inkey", paths.RA_AGENT_KEY, + "-in", paths.RA_AGENT_PEM, + "-out", os.path.join(self.dir, "ra.p12"), + "-passout", "pass:" + ]) def update_pki_admin_password(self): dn = DN('uid=admin', 'ou=people', 'o=ipaca') diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index be17637..695b121 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -30,7 +30,6 @@ from ipalib import api from ipalib import x509 from ipaplatform.paths import paths -from ipapython import certdb from ipapython import ipautil from ipapython.dn import DN from ipaserver.install import cainstance @@ -40,6 +39,8 @@ from ipaserver.plugins import ldap2 from ipapython.ipa_log_manager import log_mgr +from cryptography.hazmat.primitives import serialization + # When IPA is installed with DNS support, this CNAME should hold all IPA # replicas with KRA configured IPA_KRA_RECORD = "ipa-kra" @@ -284,10 +285,9 @@ def __create_kra_agent(self): the appropriate groups for accessing KRA services. """ - # get ipaCert certificate - with certdb.NSSDatabase(paths.IPA_RADB_DIR) as ipa_nssdb: - cert_data = ipa_nssdb.get_cert("ipaCert") - cert = x509.load_certificate(cert_data, x509.DER) + # get RA agent certificate + cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM) + cert_data = cert.public_bytes(serialization.Encoding.DER) # connect to KRA database server_id = installutils.realm_to_serverid(api.env.realm) @@ -295,7 +295,7 @@ def __create_kra_agent(self): conn = ldap2.ldap2(api, ldap_uri=dogtag_uri) conn.connect(autobind=True) - # create ipakra user with ipaCert certificate + # create ipakra user with RA agent certificate user_dn = DN(('uid', "ipakra"), ('ou', 'people'), self.basedn) entry = conn.make_entry( user_dn, diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py index 4fa4edb..99503ad 100644 --- a/ipaserver/install/plugins/ca_renewal_master.py +++ b/ipaserver/install/plugins/ca_renewal_master.py @@ -74,17 +74,16 @@ def execute(self, **options): return False, [] criteria = { - 'cert-database': paths.IPA_RADB_DIR, - 'cert-nickname': 'ipaCert', + 'cert-file': paths.RA_AGENT_PEM, } request_id = certmonger.get_request_id(criteria) if request_id is not None: - self.debug("found certmonger request for ipaCert") + self.debug("found certmonger request for RA cert") ca_name = certmonger.get_request_value(request_id, 'ca-name') if ca_name is None: self.warning( - "certmonger request for ipaCert is missing ca_name, " + "certmonger request for RA cert is missing ca_name, " "assuming local CA is renewal slave") return False, [] ca_name = ca_name.strip() @@ -97,11 +96,11 @@ def execute(self, **options): return False, [] else: self.warning( - "certmonger request for ipaCert has unknown ca_name '%s', " + "certmonger request for RA cert has unknown ca_name '%s', " "assuming local CA is renewal slave", ca_name) return False, [] else: - self.debug("certmonger request for ipaCert not found") + self.debug("certmonger request for RA cert not found") config = installutils.get_directive( paths.CA_CS_CFG_PATH, 'subsystem.select', '=') diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 71087fe..a0aaa7d 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -10,6 +10,7 @@ import pwd import fileinput import sys +import tempfile import dns.exception @@ -18,6 +19,10 @@ from six.moves.configparser import SafeConfigParser # pylint: enable=import-error +from cryptography import x509 as crypto_x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.backends import default_backend + from ipalib import api from ipalib.install import certmonger, sysrestore import SSSDConfig @@ -880,77 +885,50 @@ def certificate_renewal_update(ca, ds, http): template = paths.CERTMONGER_COMMAND_TEMPLATE serverid = installutils.realm_to_serverid(api.env.realm) - dirsrv_dir = dsinstance.config_dirname(serverid) # bump version when requests is changed version = 6 - requests = ( - ( - paths.PKI_TOMCAT_ALIAS_DIR, - 'auditSigningCert cert-pki-ca', - 'dogtag-ipa-ca-renew-agent', - template % 'stop_pkicad', - '%s "auditSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'), - None, - ), - ( - paths.PKI_TOMCAT_ALIAS_DIR, - 'ocspSigningCert cert-pki-ca', - 'dogtag-ipa-ca-renew-agent', - template % 'stop_pkicad', - '%s "ocspSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'), - None, - ), - ( - paths.PKI_TOMCAT_ALIAS_DIR, - 'subsystemCert cert-pki-ca', - 'dogtag-ipa-ca-renew-agent', - template % 'stop_pkicad', - '%s "subsystemCert cert-pki-ca"' % (template % 'renew_ca_cert'), - None, - ), - ( - paths.PKI_TOMCAT_ALIAS_DIR, - 'caSigningCert cert-pki-ca', - 'dogtag-ipa-ca-renew-agent', - template % 'stop_pkicad', - '%s "caSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'), - 'ipaCACertRenewal', - ), - ( - paths.IPA_RADB_DIR, - 'ipaCert', - 'dogtag-ipa-ca-renew-agent', - template % 'renew_ra_cert_pre', - template % 'renew_ra_cert', - None, - ), - ( - paths.PKI_TOMCAT_ALIAS_DIR, - 'Server-Cert cert-pki-ca', - 'dogtag-ipa-ca-renew-agent', - template % 'stop_pkicad', - '%s "Server-Cert cert-pki-ca"' % (template % 'renew_ca_cert'), - None, - ), - ( - paths.HTTPD_ALIAS_DIR, - 'Server-Cert', - 'IPA', - None, - template % 'restart_httpd', - None, - ), - ( - dirsrv_dir, - 'Server-Cert', - 'IPA', - None, - '%s %s' % (template % 'restart_dirsrv', serverid), - None, - ), - - ) + requests = [] + for certnick in ('auditSigningCert cert-pki-ca', + 'ocspSigningCert cert-pki-ca', + 'subsystemCert cert-pki-ca', + 'caSigningCert cert-pki-ca', + 'Server-Cert cert-pki-ca', + ): + requests.append( + { + 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, + 'cert-nickname': certnick, + 'ca': 'dogtag-ipa-ca-renew-agent', + 'cert-presave-command': template % 'stop_pkicad', + 'cert-postsave-command': + (template % 'renew_ca_cert "{}"').format(certnick), + } + ) + # set profile for caSigningCert + requests[3]['template-profile'] = 'ipaCACertRenewal' + requests.extend([ + { + 'cert-file': paths.RA_AGENT_PEM, + 'key-file': paths.RA_AGENT_KEY, + 'ca': 'dogtag-ipa-ca-renew-agent', + 'cert-presave-command': template % 'renew_ra_cert_pre', + 'cert-postsave-command': template % 'renew_ra_cert', + }, + { + 'cert-database': paths.HTTPD_ALIAS_DIR, + 'cert-nickname': 'Server-Cert', + 'ca': 'IPA', + 'cert-postsave-command': template % 'restart_httpd', + }, + { + 'cert-database': dsinstance.config_dirname(serverid), + 'cert-nickname': 'Server-Cert', + 'ca': 'IPA', + 'cert-postsave-command': + '%s %s' % (template % 'restart_dirsrv', serverid), + } + ]) root_logger.info("[Update certmonger certificate renewal configuration to " "version %d]" % version) @@ -964,16 +942,7 @@ def certificate_renewal_update(ca, ds, http): # State not set, lets see if we are already configured for request in requests: - nss_dir, nickname, ca_name, pre_command, post_command, profile = request - criteria = { - 'cert-database': nss_dir, - 'cert-nickname': nickname, - 'ca-name': ca_name, - 'template-profile': profile, - 'cert-presave-command': pre_command, - 'cert-postsave-command': post_command, - } - request_id = certmonger.get_request_id(criteria) + request_id = certmonger.get_request_id(request) if request_id is None: break else: @@ -1382,20 +1351,60 @@ def fix_trust_flags(): sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True) -def export_ra_agent_pem(): - root_logger.info('[Exporting KRA agent PEM file]') - - if sysupgrade.get_upgrade_state('http', 'export_ra_agent_pem'): - root_logger.info("KRA agent PEM file already exported") +def update_ra_agent_crt(): + if os.path.exists(paths.RA_AGENT_PEM): return - if not api.Command.kra_is_enabled()['result']: - root_logger.info("KRA is not enabled") + root_logger.info('[Exporting RA agent PEM files]') + _fd, filename = tempfile.mkstemp(dir=paths.VAR_LIB_IPA) + + if sysupgrade.get_upgrade_state('http', 'update_ra_agent_crt'): + root_logger.info("RA agent PEM files already exported") return - dogtaginstance.export_ra_agent_pem() + sysupgrade.set_upgrade_state('http', 'update_ra_agent_crt', True) + + ra_nick = 'ipaCert' + args = [paths.PKI, + "-d", paths.HTTPD_ALIAS_DIR, + "-C", paths.ALIAS_PWDFILE_TXT, + "client-cert-show", ra_nick, + "--client-cert", filename] + ipautil.run(args) + + # get the private key and certificate from the file + with open(filename, 'r') as f: + data = f.read() + cert = crypto_x509.load_pem_x509_certificate( + data, default_backend()) + key = serialization.load_pem_private_key( + data, password=None, backend=default_backend()) + + os.remove(filename) + + with open(paths.RA_AGENT_PEM, 'w') as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + + with open(paths.RA_AGENT_KEY, 'w') as f: + f.write(key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + )) + + pent = pwd.getpwnam(constants.HTTPD_USER) + for pem_file in (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY): + os.chown(pem_file, 0, pent.pw_gid) + os.chmod(pem_file, 0o440) + + if os.path.exists('/etc/httpd/alias/kra-agent.pem'): + os.remove('/etc/httpd/alias/kra-agent.pem') - sysupgrade.set_upgrade_state('http', 'export_ra_agent_pem', True) + httpdb = certs.CertDB(api.env.realm) + oldcert = httpdb.get_cert_from_db(ra_nick) + if oldcert: + certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname=ra_nick) + httpdb.delete_cert(ra_nick) def update_mod_nss_protocol(http): @@ -1635,9 +1644,9 @@ def upgrade_configuration(): update_mod_nss_protocol(http) update_mod_nss_cipher_suite(http) fix_trust_flags() +==== BASE ==== export_ra_agent_pem() - update_http_keytab(http) - http.configure_gssproxy() +==== BASE ==== http.start() uninstall_selfsign(ds, http) diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 6963c4f..5b60c90 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -1241,9 +1241,13 @@ def __init__(self, api): self.ca_cert = api.env.ca_certfile if api.env.in_tree: self.client_certfile = os.path.join( - api.env.dot_ipa, 'ra-agent.pem') + api.env.dot_ipa, 'dogtag-agent.pem') + + self.client_keyfile = os.path.join( + api.env.dot_ipa, 'dogtag-agent.key') else: self.client_certfile = paths.RA_AGENT_PEM + self.client_keyfile = paths.RA_AGENT_KEY super(RestClient, self).__init__(api) # session cookie @@ -1277,7 +1281,8 @@ def __enter__(self): status, resp_headers, _resp_body = dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, '/ca/rest/account/login', self.ca_cert, - self.client_certfile, 'GET' + self.client_certfile, self.client_keyfile, + 'GET' ) cookies = ipapython.cookie.Cookie.parse(resp_headers.get('set-cookie', '')) if status != 200 or len(cookies) == 0: @@ -1290,7 +1295,8 @@ def __exit__(self, exc_type, exc_value, traceback): dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, '/ca/rest/account/logout', self.ca_cert, - self.client_certfile, 'GET' + self.client_certfile, self.client_keyfile, + 'GET' ) self.cookie = None @@ -1331,7 +1337,7 @@ def _ssldo(self, method, path, headers=None, body=None, use_session=True): status, resp_headers, resp_body = dogtag.https_request( self.ca_host, self.override_port or self.env.ca_agent_port, resource, self.ca_cert, self.client_certfile, - method, headers, body + self.client_keyfile, method, headers, body ) if status < 200 or status >= 300: explanation = self._parse_dogtag_error(resp_body) or '' @@ -1412,7 +1418,8 @@ def _sslget(self, url, port, **kw): Perform an HTTPS request """ return dogtag.https_request(self.ca_host, port, url, self.ca_cert, - self.client_certfile, **kw) + self.client_certfile, self.client_keyfile, + **kw) def get_parse_result_xml(self, xml_text, parse_func): ''' @@ -2012,7 +2019,8 @@ def get_client(self): str(self.kra_port), 'kra') - connection.set_authentication_cert(paths.RA_AGENT_PEM) + connection.set_authentication_cert(paths.RA_AGENT_PEM, + paths.RA_AGENT_KEY) return KRAClient(connection, crypto) diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py index 1051c50..78e6b78 100644 --- a/ipaserver/secrets/store.py +++ b/ipaserver/secrets/store.py @@ -266,10 +266,10 @@ def import_key(self, value): 'wrap_nick': 'caSigningCert cert-pki-ca', }, 'ra': { - 'type': 'NSSDB', - 'path': paths.IPA_RADB_DIR, - 'handler': NSSCertDB, - 'pwcallback': HTTPD_password_callback, + 'type': 'OPENSSL', + 'handler': OpenSSLHandler, + 'certfile': paths.RA_AGENT_PEM, + 'keyfile': paths.RA_AGENT_KEY, }, 'dm': { 'type': 'DMLDAP', From d6bfdbb460490e87e93722b7484501a4bf8a0090 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Thu, 2 Feb 2017 14:23:10 +0100 Subject: [PATCH 12/12] REVERT THIS COMMIT ON PKI-BASE UPDATE This commit should be reverted when the following commit gets in pki-base: https://git.fedorahosted.org/cgit/pki.git/commit/?id=71ae20c TODO: when reverting this commit, also bump the pki-base version in freeipa.spec.in --- ipaserver/plugins/dogtag.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 5b60c90..1ef552d 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -2019,8 +2019,11 @@ def get_client(self): str(self.kra_port), 'kra') - connection.set_authentication_cert(paths.RA_AGENT_PEM, - paths.RA_AGENT_KEY) + connection.session.cert = (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY) + # uncomment the following when this commit makes it to release + # https://git.fedorahosted.org/cgit/pki.git/commit/?id=71ae20c + # connection.set_authentication_cert(paths.RA_AGENT_PEM, + # paths.RA_AGENT_KEY) return KRAClient(connection, crypto)
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code