mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-07-29 15:55:47 -05:00
Use one Custodia peer to retrieve all secrets
Fix 994f71ac8a was incomplete. Under some
circumstancs the DM hash and CA keys were still retrieved from two different
machines.
Custodia client now uses a single remote to upload keys and download all
secrets.
Fixes: https://pagure.io/freeipa/issue/7518
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Simo Sorce <ssorce@redhat.com>
This commit is contained in:
+1
-1
@@ -60,7 +60,7 @@ jobs:
|
|||||||
build_url: '{fedora-28/build_url}'
|
build_url: '{fedora-28/build_url}'
|
||||||
test_suite: test_integration/test_external_ca.py::TestExternalCA test_integration/test_external_ca.py::TestSelfExternalSelf test_integration/test_external_ca.py::TestExternalCAInstall
|
test_suite: test_integration/test_external_ca.py::TestExternalCA test_integration/test_external_ca.py::TestSelfExternalSelf test_integration/test_external_ca.py::TestExternalCAInstall
|
||||||
template: *ci-master-f28
|
template: *ci-master-f28
|
||||||
timeout: 3600
|
timeout: 4200
|
||||||
topology: *master_1repl_1client
|
topology: *master_1repl_1client
|
||||||
|
|
||||||
fedora-28/test_topologies:
|
fedora-28/test_topologies:
|
||||||
|
|||||||
@@ -280,7 +280,6 @@ def install_step_0(standalone, replica_config, options, custodia):
|
|||||||
cafile = os.path.join(replica_config.dir, 'cacert.p12')
|
cafile = os.path.join(replica_config.dir, 'cacert.p12')
|
||||||
if options.promote:
|
if options.promote:
|
||||||
custodia.get_ca_keys(
|
custodia.get_ca_keys(
|
||||||
replica_config.ca_host_name,
|
|
||||||
cafile,
|
cafile,
|
||||||
replica_config.dirman_password)
|
replica_config.dirman_password)
|
||||||
|
|
||||||
|
|||||||
@@ -763,7 +763,7 @@ class CAInstance(DogtagInstance):
|
|||||||
self.configure_agent_renewal()
|
self.configure_agent_renewal()
|
||||||
|
|
||||||
def __import_ra_key(self):
|
def __import_ra_key(self):
|
||||||
self._custodia.import_ra_key(self.master_host)
|
self._custodia.import_ra_key()
|
||||||
self.__set_ra_cert_perms()
|
self.__set_ra_cert_perms()
|
||||||
|
|
||||||
self.configure_agent_renewal()
|
self.configure_agent_renewal()
|
||||||
|
|||||||
@@ -71,40 +71,45 @@ def get_custodia_instance(config, mode):
|
|||||||
if mode == CustodiaModes.CA_PEER:
|
if mode == CustodiaModes.CA_PEER:
|
||||||
# In case we install replica with CA, prefer CA host as source for
|
# In case we install replica with CA, prefer CA host as source for
|
||||||
# all Custodia secret material.
|
# all Custodia secret material.
|
||||||
custodia_master = config.ca_host_name
|
custodia_peer = config.ca_host_name
|
||||||
elif mode == CustodiaModes.KRA_PEER:
|
elif mode == CustodiaModes.KRA_PEER:
|
||||||
custodia_master = config.kra_host_name
|
custodia_peer = config.kra_host_name
|
||||||
elif mode == CustodiaModes.MASTER_PEER:
|
elif mode == CustodiaModes.MASTER_PEER:
|
||||||
custodia_master = config.master_host_name
|
custodia_peer = config.master_host_name
|
||||||
elif mode == CustodiaModes.STANDALONE:
|
elif mode == CustodiaModes.STANDALONE:
|
||||||
custodia_master = None
|
custodia_peer = None
|
||||||
else:
|
else:
|
||||||
custodia_master = None
|
custodia_peer = None
|
||||||
|
|
||||||
if custodia_master is None:
|
if custodia_peer is None:
|
||||||
# use ldapi with local dirsrv instance
|
# use ldapi with local dirsrv instance
|
||||||
logger.info("Custodia uses LDAPI.")
|
logger.info("Custodia uses LDAPI.")
|
||||||
ldap_uri = None
|
|
||||||
else:
|
else:
|
||||||
logger.info("Custodia uses '%s' as master peer.", custodia_master)
|
logger.info("Custodia uses '%s' as master peer.", custodia_peer)
|
||||||
ldap_uri = 'ldap://{}'.format(custodia_master)
|
|
||||||
|
|
||||||
return CustodiaInstance(
|
return CustodiaInstance(
|
||||||
host_name=config.host_name,
|
host_name=config.host_name,
|
||||||
realm=config.realm_name,
|
realm=config.realm_name,
|
||||||
ldap_uri=ldap_uri
|
custodia_peer=custodia_peer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustodiaInstance(SimpleServiceInstance):
|
class CustodiaInstance(SimpleServiceInstance):
|
||||||
def __init__(self, host_name=None, realm=None, ldap_uri=None):
|
def __init__(self, host_name=None, realm=None, custodia_peer=None):
|
||||||
super(CustodiaInstance, self).__init__("ipa-custodia")
|
super(CustodiaInstance, self).__init__("ipa-custodia")
|
||||||
self.config_file = paths.IPA_CUSTODIA_CONF
|
self.config_file = paths.IPA_CUSTODIA_CONF
|
||||||
self.server_keys = paths.IPA_CUSTODIA_KEYS
|
self.server_keys = paths.IPA_CUSTODIA_KEYS
|
||||||
self.ldap_uri = ldap_uri
|
self.custodia_peer = custodia_peer
|
||||||
self.fqdn = host_name
|
self.fqdn = host_name
|
||||||
self.realm = realm
|
self.realm = realm
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ldap_uri(self):
|
||||||
|
if self.custodia_peer is None:
|
||||||
|
return installutils.realm_to_ldapi_uri(self.realm)
|
||||||
|
else:
|
||||||
|
return "ldap://{}".format(self.custodia_peer)
|
||||||
|
|
||||||
def __config_file(self):
|
def __config_file(self):
|
||||||
template_file = os.path.basename(self.config_file) + '.template'
|
template_file = os.path.basename(self.config_file) + '.template'
|
||||||
template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file)
|
template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file)
|
||||||
@@ -124,7 +129,7 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
ipautil.flush_sync(f)
|
ipautil.flush_sync(f)
|
||||||
|
|
||||||
def create_instance(self):
|
def create_instance(self):
|
||||||
if self.ldap_uri is None or self.ldap_uri.startswith('ldapi://'):
|
if self.ldap_uri.startswith('ldapi://'):
|
||||||
# local case, ensure container exists
|
# local case, ensure container exists
|
||||||
self.step("Making sure custodia container exists",
|
self.step("Making sure custodia container exists",
|
||||||
self.__create_container)
|
self.__create_container)
|
||||||
@@ -195,25 +200,24 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
updater = ldapupdate.LDAPUpdate(sub_dict=sub_dict)
|
updater = ldapupdate.LDAPUpdate(sub_dict=sub_dict)
|
||||||
updater.update([os.path.join(paths.UPDATES_DIR, '73-custodia.update')])
|
updater.update([os.path.join(paths.UPDATES_DIR, '73-custodia.update')])
|
||||||
|
|
||||||
def import_ra_key(self, master_host_name):
|
def import_ra_key(self):
|
||||||
cli = self._get_custodia_client(server=master_host_name)
|
cli = self._get_custodia_client()
|
||||||
# please note that ipaCert part has to stay here for historical
|
# please note that ipaCert part has to stay here for historical
|
||||||
# reasons (old servers expect you to ask for ra/ipaCert during
|
# reasons (old servers expect you to ask for ra/ipaCert during
|
||||||
# replication as they store the RA agent cert in an NSS database
|
# replication as they store the RA agent cert in an NSS database
|
||||||
# with this nickname)
|
# with this nickname)
|
||||||
cli.fetch_key('ra/ipaCert')
|
cli.fetch_key('ra/ipaCert')
|
||||||
|
|
||||||
def import_dm_password(self, master_host_name):
|
def import_dm_password(self):
|
||||||
cli = self._get_custodia_client(server=master_host_name)
|
cli = self._get_custodia_client()
|
||||||
cli.fetch_key('dm/DMHash')
|
cli.fetch_key('dm/DMHash')
|
||||||
|
|
||||||
def _wait_keys(self, host, timeout=300):
|
def _wait_keys(self, timeout=300):
|
||||||
ldap_uri = 'ldap://%s' % host
|
|
||||||
deadline = int(time.time()) + timeout
|
deadline = int(time.time()) + timeout
|
||||||
logger.info("Waiting up to %s seconds to see our keys "
|
logger.info("Waiting up to %s seconds to see our keys "
|
||||||
"appear on host: %s", timeout, host)
|
"appear on host %s", timeout, self.ldap_uri)
|
||||||
|
|
||||||
konn = KEMLdap(ldap_uri)
|
konn = KEMLdap(self.ldap_uri)
|
||||||
saved_e = None
|
saved_e = None
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -223,8 +227,11 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
if saved_e is None:
|
if saved_e is None:
|
||||||
# FIXME: Change once there's better way to show this
|
# FIXME: Change once there's better way to show this
|
||||||
# message in installer output,
|
# message in installer output,
|
||||||
print(" Waiting for keys to appear on host: {}, please "
|
print(
|
||||||
"wait until this has completed.".format(host))
|
" Waiting for keys to appear on host: {}, please "
|
||||||
|
"wait until this has completed.".format(
|
||||||
|
self.ldap_uri)
|
||||||
|
)
|
||||||
# log only once for the same error
|
# log only once for the same error
|
||||||
if not isinstance(e, type(saved_e)):
|
if not isinstance(e, type(saved_e)):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -234,23 +241,25 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
raise RuntimeError("Timed out trying to obtain keys.")
|
raise RuntimeError("Timed out trying to obtain keys.")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
def _get_custodia_client(self, server):
|
def _get_custodia_client(self):
|
||||||
|
if self.custodia_peer is None:
|
||||||
|
raise ValueError("Can't replicate secrets without Custodia peer")
|
||||||
# Before we attempt to fetch keys from this host, make sure our public
|
# Before we attempt to fetch keys from this host, make sure our public
|
||||||
# keys have been replicated there.
|
# keys have been replicated there.
|
||||||
self._wait_keys(server)
|
self._wait_keys()
|
||||||
|
|
||||||
return CustodiaClient(
|
return CustodiaClient(
|
||||||
client_service='host@{}'.format(self.fqdn),
|
client_service='host@{}'.format(self.fqdn),
|
||||||
keyfile=self.server_keys, keytab=paths.KRB5_KEYTAB,
|
keyfile=self.server_keys, keytab=paths.KRB5_KEYTAB,
|
||||||
server=server, realm=self.realm
|
server=self.custodia_peer, realm=self.realm
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data):
|
def _get_keys(self, cacerts_file, cacerts_pwd, data):
|
||||||
# Fetch all needed certs one by one, then combine them in a single
|
# Fetch all needed certs one by one, then combine them in a single
|
||||||
# PKCS12 file
|
# PKCS12 file
|
||||||
prefix = data['prefix']
|
prefix = data['prefix']
|
||||||
certlist = data['list']
|
certlist = data['list']
|
||||||
cli = self._get_custodia_client(server=ca_host)
|
cli = self._get_custodia_client()
|
||||||
|
|
||||||
with NSSDatabase(None) as tmpdb:
|
with NSSDatabase(None) as tmpdb:
|
||||||
tmpdb.create_db()
|
tmpdb.create_db()
|
||||||
@@ -287,23 +296,23 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
'-o', cacerts_file
|
'-o', cacerts_file
|
||||||
])
|
])
|
||||||
|
|
||||||
def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd):
|
def get_ca_keys(self, cacerts_file, cacerts_pwd):
|
||||||
certlist = ['caSigningCert cert-pki-ca',
|
certlist = ['caSigningCert cert-pki-ca',
|
||||||
'ocspSigningCert cert-pki-ca',
|
'ocspSigningCert cert-pki-ca',
|
||||||
'auditSigningCert cert-pki-ca',
|
'auditSigningCert cert-pki-ca',
|
||||||
'subsystemCert cert-pki-ca']
|
'subsystemCert cert-pki-ca']
|
||||||
data = {'prefix': 'ca',
|
data = {'prefix': 'ca',
|
||||||
'list': certlist}
|
'list': certlist}
|
||||||
self._get_keys(ca_host, cacerts_file, cacerts_pwd, data)
|
self._get_keys(cacerts_file, cacerts_pwd, data)
|
||||||
|
|
||||||
def get_kra_keys(self, ca_host, cacerts_file, cacerts_pwd):
|
def get_kra_keys(self, cacerts_file, cacerts_pwd):
|
||||||
certlist = ['auditSigningCert cert-pki-kra',
|
certlist = ['auditSigningCert cert-pki-kra',
|
||||||
'storageCert cert-pki-kra',
|
'storageCert cert-pki-kra',
|
||||||
'subsystemCert cert-pki-ca',
|
'subsystemCert cert-pki-ca',
|
||||||
'transportCert cert-pki-kra']
|
'transportCert cert-pki-kra']
|
||||||
data = {'prefix': 'ca',
|
data = {'prefix': 'ca',
|
||||||
'list': certlist}
|
'list': certlist}
|
||||||
self._get_keys(ca_host, cacerts_file, cacerts_pwd, data)
|
self._get_keys(cacerts_file, cacerts_pwd, data)
|
||||||
|
|
||||||
def __start(self):
|
def __start(self):
|
||||||
super(CustodiaInstance, self).__start()
|
super(CustodiaInstance, self).__start()
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ def install(api, replica_config, options, custodia):
|
|||||||
paths.KRB5_KEYTAB,
|
paths.KRB5_KEYTAB,
|
||||||
ccache)
|
ccache)
|
||||||
custodia.get_kra_keys(
|
custodia.get_kra_keys(
|
||||||
replica_config.kra_host_name,
|
|
||||||
krafile,
|
krafile,
|
||||||
replica_config.dirman_password)
|
replica_config.dirman_password)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1113,9 +1113,9 @@ def uninstall(installer):
|
|||||||
dsinstance.DsInstance(fstore=fstore).uninstall()
|
dsinstance.DsInstance(fstore=fstore).uninstall()
|
||||||
if _server_trust_ad_installed:
|
if _server_trust_ad_installed:
|
||||||
adtrustinstance.ADTRUSTInstance(fstore).uninstall()
|
adtrustinstance.ADTRUSTInstance(fstore).uninstall()
|
||||||
# ldap_uri isn't used, but IPAKEMKeys parses /etc/ipa/default.conf
|
# realm isn't used, but IPAKEMKeys parses /etc/ipa/default.conf
|
||||||
# otherwise, see https://pagure.io/freeipa/issue/7474 .
|
# otherwise, see https://pagure.io/freeipa/issue/7474 .
|
||||||
custodiainstance.CustodiaInstance(ldap_uri='ldapi://invalid').uninstall()
|
custodiainstance.CustodiaInstance(realm='REALM.INVALID').uninstall()
|
||||||
otpdinstance.OtpdInstance().uninstall()
|
otpdinstance.OtpdInstance().uninstall()
|
||||||
tasks.restore_hostname(fstore, sstore)
|
tasks.restore_hostname(fstore, sstore)
|
||||||
fstore.restore_all_files()
|
fstore.restore_all_files()
|
||||||
|
|||||||
@@ -1515,7 +1515,7 @@ def install(installer):
|
|||||||
krb.restart()
|
krb.restart()
|
||||||
|
|
||||||
if promote:
|
if promote:
|
||||||
custodia.import_dm_password(config.master_host_name)
|
custodia.import_dm_password()
|
||||||
promote_sssd(config.host_name)
|
promote_sssd(config.host_name)
|
||||||
promote_openldap_conf(config.host_name, config.master_host_name)
|
promote_openldap_conf(config.host_name, config.master_host_name)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user