mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
DNSSEC: remove keys purged by OpenDNSSEC from master HSM from LDAP
Key purging has to be only only after key metadata purging so ipa-dnskeysyncd on replices does not fail while dereferencing non-existing keys. https://fedorahosted.org/freeipa/ticket/5334 Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
parent
6bdc18d0c5
commit
ddf7397a4b
@ -383,7 +383,10 @@ def master2ldap_master_keys_sync(log, ldapkeydb, localhsm):
|
||||
ldapkeydb.flush()
|
||||
|
||||
def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
|
||||
# synchroniza zone keys
|
||||
"""add and update zone key material from local HSM to LDAP
|
||||
|
||||
No key material will be removed, only new keys will be added or updated.
|
||||
Key removal is hanled by master2ldap_zone_keys_purge()."""
|
||||
log = log.getChild('master2ldap_zone_keys')
|
||||
keypairs_ldap = ldapkeydb.zone_keypairs
|
||||
log.debug("zone keys in LDAP: %s", hex_set(keypairs_ldap))
|
||||
@ -392,10 +395,10 @@ def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
|
||||
privkeys_local = localhsm.zone_privkeys
|
||||
log.debug("zone keys in local HSM: %s", hex_set(privkeys_local))
|
||||
|
||||
assert set(pubkeys_local) == set(privkeys_local), \
|
||||
"IDs of private and public keys for DNS zones in local HSM does " \
|
||||
"not match to key pairs: %s vs. %s" % \
|
||||
(hex_set(pubkeys_local), hex_set(privkeys_local))
|
||||
assert set(pubkeys_local) == set(privkeys_local), (
|
||||
"IDs of private and public keys for DNS zones in local HSM does "
|
||||
"not match to key pairs: %s vs. %s" %
|
||||
(hex_set(pubkeys_local), hex_set(privkeys_local)))
|
||||
|
||||
new_keys = set(pubkeys_local) - set(keypairs_ldap)
|
||||
log.debug("new zone keys in local HSM: %s", hex_set(new_keys))
|
||||
@ -416,6 +419,29 @@ def master2ldap_zone_keys_sync(log, ldapkeydb, localhsm):
|
||||
sync_set_metadata_2ldap(log, privkeys_local, keypairs_ldap)
|
||||
ldapkeydb.flush()
|
||||
|
||||
def master2ldap_zone_keys_purge(log, ldapkeydb, localhsm):
|
||||
"""purge removed key material from LDAP (but not metadata)
|
||||
|
||||
Keys which are present in LDAP but not in local HSM will be removed.
|
||||
Key metadata must be removed first so references to removed key material
|
||||
are removed before actually removing the keys."""
|
||||
keypairs_ldap = ldapkeydb.zone_keypairs
|
||||
log.debug("zone keys in LDAP: %s", hex_set(keypairs_ldap))
|
||||
|
||||
pubkeys_local = localhsm.zone_pubkeys
|
||||
privkeys_local = localhsm.zone_privkeys
|
||||
log.debug("zone keys in local HSM: %s", hex_set(privkeys_local))
|
||||
assert set(pubkeys_local) == set(privkeys_local), \
|
||||
"IDs of private and public keys for DNS zones in local HSM does " \
|
||||
"not match to key pairs: %s vs. %s" % \
|
||||
(hex_set(pubkeys_local), hex_set(privkeys_local))
|
||||
|
||||
deleted_key_ids = set(keypairs_ldap) - set(pubkeys_local)
|
||||
log.debug("zone keys deleted from local HSM but present in LDAP: %s",
|
||||
hex_set(deleted_key_ids))
|
||||
for zkey_id in deleted_key_ids:
|
||||
keypairs_ldap[zkey_id].schedule_deletion()
|
||||
ldapkeydb.flush()
|
||||
|
||||
def hex_set(s):
|
||||
out = set()
|
||||
@ -595,7 +621,7 @@ ldap.gssapi_bind()
|
||||
log.debug('Connected')
|
||||
|
||||
|
||||
### DNSSEC master: key synchronization
|
||||
### DNSSEC master: key material upload & synchronization (but not deletion)
|
||||
ldapkeydb = LdapKeyDB(log, ldap, DN(('cn', 'keys'), ('cn', 'sec'),
|
||||
ipalib.api.env.container_dns,
|
||||
ipalib.api.env.basedn))
|
||||
@ -607,7 +633,7 @@ master2ldap_master_keys_sync(log, ldapkeydb, localhsm)
|
||||
master2ldap_zone_keys_sync(log, ldapkeydb, localhsm)
|
||||
|
||||
|
||||
### DNSSEC master: DNSSEC key metadata upload
|
||||
### DNSSEC master: DNSSEC key metadata upload & synchronization & deletion
|
||||
# command receive is delayed so the command will stay in socket queue until
|
||||
# the problem with LDAP server or HSM is fixed
|
||||
try:
|
||||
@ -661,6 +687,11 @@ try:
|
||||
for zone_row in db.execute("SELECT name FROM zones"):
|
||||
sync_zone(log, ldap, dns_dn, zone_row['name'])
|
||||
|
||||
### DNSSEC master: DNSSEC key material purging
|
||||
# references to old key material were removed above in sync_zone()
|
||||
# so now we can purge old key material from LDAP
|
||||
master2ldap_zone_keys_purge(log, ldapkeydb, localhsm)
|
||||
|
||||
except Exception as ex:
|
||||
msg = "ipa-ods-exporter exception: %s" % traceback.format_exc(ex)
|
||||
log.exception(ex)
|
||||
|
@ -104,40 +104,56 @@ def get_default_attrs(object_classes):
|
||||
result.update(defaults[cls])
|
||||
return result
|
||||
|
||||
|
||||
class Key(collections.MutableMapping):
|
||||
"""abstraction to hide LDAP entry weirdnesses:
|
||||
- non-normalized attribute names
|
||||
- boolean attributes returned as strings
|
||||
- planned entry deletion prevents subsequent use of the instance
|
||||
"""
|
||||
def __init__(self, entry, ldap, ldapkeydb):
|
||||
self.entry = entry
|
||||
self._delentry = None # indicates that object was deleted
|
||||
self.ldap = ldap
|
||||
self.ldapkeydb = ldapkeydb
|
||||
self.log = ldap.log.getChild(__name__)
|
||||
|
||||
def __assert_not_deleted(self):
|
||||
assert self.entry and not self._delentry, (
|
||||
"attempt to use to-be-deleted entry %s detected"
|
||||
% self._delentry.dn)
|
||||
|
||||
def __getitem__(self, key):
|
||||
self.__assert_not_deleted()
|
||||
val = self.entry.single_value[key]
|
||||
if key.lower() in bool_attr_names:
|
||||
val = ldap_bool(val)
|
||||
return val
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__assert_not_deleted()
|
||||
self.entry[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.__assert_not_deleted()
|
||||
del self.entry[key]
|
||||
|
||||
def __iter__(self):
|
||||
"""generates list of ipa names of all PKCS#11 attributes present in the object"""
|
||||
self.__assert_not_deleted()
|
||||
for ipa_name in list(self.entry.keys()):
|
||||
lowercase = ipa_name.lower()
|
||||
if lowercase in attrs_name2id:
|
||||
yield lowercase
|
||||
|
||||
def __len__(self):
|
||||
self.__assert_not_deleted()
|
||||
return len(self.entry)
|
||||
|
||||
def __repr__(self):
|
||||
if self._delentry:
|
||||
return 'deleted entry: %s' % repr(self._delentry)
|
||||
|
||||
sanitized = dict(self.entry)
|
||||
for attr in ['ipaPrivateKey', 'ipaPublicKey', 'ipk11publickeyinfo']:
|
||||
if attr in sanitized:
|
||||
@ -152,6 +168,49 @@ class Key(collections.MutableMapping):
|
||||
if self.get(attr, empty) == default_attrs[attr]:
|
||||
del self[attr]
|
||||
|
||||
def _update_key(self):
|
||||
"""remove default values from LDAP entry and write back changes"""
|
||||
if self._delentry:
|
||||
self._delete_key()
|
||||
return
|
||||
|
||||
self._cleanup_key()
|
||||
|
||||
try:
|
||||
self.ldap.update_entry(self.entry)
|
||||
except ipalib.errors.EmptyModlist:
|
||||
pass
|
||||
|
||||
def _delete_key(self):
|
||||
"""remove key metadata entry from LDAP
|
||||
|
||||
After calling this, the python object is no longer valid and all
|
||||
subsequent method calls on it will fail.
|
||||
"""
|
||||
assert not self.entry, (
|
||||
"Key._delete_key() called before Key.schedule_deletion()")
|
||||
assert self._delentry, "Key._delete_key() called more than once"
|
||||
self.log.debug('deleting key id 0x%s DN %s from LDAP',
|
||||
hexlify(self._delentry.single_value['ipk11id']),
|
||||
self._delentry.dn)
|
||||
self.ldap.delete_entry(self._delentry)
|
||||
self._delentry = None
|
||||
self.ldap = None
|
||||
self.ldapkeydb = None
|
||||
|
||||
def schedule_deletion(self):
|
||||
"""schedule key deletion from LDAP
|
||||
|
||||
Calling schedule_deletion() will make this object incompatible with
|
||||
normal Key. After that the object must not be read or modified.
|
||||
Key metadata will be actually deleted when LdapKeyDB.flush() is called.
|
||||
"""
|
||||
assert not self._delentry, (
|
||||
"Key.schedule_deletion() called more than once")
|
||||
self._delentry = self.entry
|
||||
self.entry = None
|
||||
|
||||
|
||||
class ReplicaKey(Key):
|
||||
# TODO: object class assert
|
||||
def __init__(self, entry, ldap, ldapkeydb):
|
||||
@ -238,21 +297,12 @@ class LdapKeyDB(AbstractHSM):
|
||||
self._update_keys()
|
||||
return keys
|
||||
|
||||
def _update_key(self, key):
|
||||
"""remove default values from LDAP entry and write back changes"""
|
||||
key._cleanup_key()
|
||||
|
||||
try:
|
||||
self.ldap.update_entry(key.entry)
|
||||
except ipalib.errors.EmptyModlist:
|
||||
pass
|
||||
|
||||
def _update_keys(self):
|
||||
for cache in [self.cache_masterkeys, self.cache_replica_pubkeys_wrap,
|
||||
self.cache_zone_keypairs]:
|
||||
self.cache_zone_keypairs]:
|
||||
if cache:
|
||||
for key in cache.values():
|
||||
self._update_key(key)
|
||||
key._update_key()
|
||||
|
||||
def flush(self):
|
||||
"""write back content of caches to LDAP"""
|
||||
|
Loading…
Reference in New Issue
Block a user