DNSSEC: Add ability to trigger full data synchronization to ipa-ods-exporter.

New exporter's command 'ipa-full-update' will resynchronize all zone
keys from ODS database to LDAP.

This command holds database lock for the whole time to avoid race
conditions so it should be used only in special cases, e.g. during
master server migration.

https://fedorahosted.org/freeipa/ticket/4657

Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
Petr Spacek 2015-06-26 18:21:38 +02:00 committed by Tomas Babej
parent 6a8fb04460
commit 579d30571b

View File

@ -157,49 +157,42 @@ def get_ldap_keys(ldap, zone_dn):
return ldap_keys return ldap_keys
def get_ods_keys(zone_name): def get_ods_keys(zone_name):
# Open DB directly and read key timestamps etc. # get zone ID
with ods_db_lock(): cur = db.execute("SELECT id FROM zones WHERE LOWER(name)=LOWER(?)",
db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB, (zone_name,))
isolation_level="EXCLUSIVE") rows = cur.fetchall()
db.row_factory = sqlite3.Row assert len(rows) == 1, "exactly one DNS zone should exist in ODS DB"
db.execute('BEGIN') zone_id = rows[0][0]
# get zone ID # get all keys for given zone ID
cur = db.execute("SELECT id FROM zones WHERE LOWER(name)=LOWER(?)", cur = db.execute("SELECT kp.HSMkey_id, kp.generate, kp.algorithm, dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, dnsk.keytype "
(zone_name,)) "FROM keypairs AS kp JOIN dnsseckeys AS dnsk ON kp.id = dnsk.id "
rows = cur.fetchall() "WHERE dnsk.zone_id = ?", (zone_id,))
assert len(rows) == 1, "exactly one DNS zone should exist in ODS DB" keys = {}
zone_id = rows[0][0] for row in cur:
key_data = sql2datetimes(row)
if 'idnsSecKeyDelete' in key_data \
and key_data['idnsSecKeyDelete'] > datetime.now():
continue # ignore deleted keys
# get all keys for given zone ID key_data.update(sql2ldap_flags(row['keytype']))
cur = db.execute("SELECT kp.HSMkey_id, kp.generate, kp.algorithm, dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, dnsk.keytype " log.debug("%s", key_data)
"FROM keypairs AS kp JOIN dnsseckeys AS dnsk ON kp.id = dnsk.id " assert key_data.get('idnsSecKeyZONE', None) == 'TRUE', \
"WHERE dnsk.zone_id = ?", (zone_id,)) 'unexpected key type 0x%x' % row['keytype']
keys = {} if key_data.get('idnsSecKeySEP', 'FALSE') == 'TRUE':
for row in cur: key_type = 'KSK'
key_data = sql2datetimes(row) else:
if 'idnsSecKeyDelete' in key_data \ key_type = 'ZSK'
and key_data['idnsSecKeyDelete'] > datetime.now():
continue # ignore deleted keys
key_data.update(sql2ldap_flags(row['keytype'])) key_data.update(sql2ldap_algorithm(row['algorithm']))
log.debug("%s", key_data) key_id = "%s-%s-%s" % (key_type,
assert key_data.get('idnsSecKeyZONE', None) == 'TRUE', \ datetime2ldap(key_data['idnsSecKeyCreated']),
'unexpected key type 0x%x' % row['keytype'] row['HSMkey_id'])
if key_data.get('idnsSecKeySEP', 'FALSE') == 'TRUE':
key_type = 'KSK'
else:
key_type = 'ZSK'
key_data.update(sql2ldap_algorithm(row['algorithm'])) key_data.update(sql2ldap_keyid(row['HSMkey_id']))
key_id = "%s-%s-%s" % (key_type, keys[key_id] = key_data
datetime2ldap(key_data['idnsSecKeyCreated']),
row['HSMkey_id'])
key_data.update(sql2ldap_keyid(row['HSMkey_id'])) return keys
keys[key_id] = key_data
return keys
def sync_set_metadata_2ldap(log, source_set, target_set): def sync_set_metadata_2ldap(log, source_set, target_set):
"""sync metadata from source key set to target key set in LDAP """sync metadata from source key set to target key set in LDAP
@ -365,11 +358,16 @@ def parse_command(cmd):
'HSM synchronization finished, exiting.', 'HSM synchronization finished, exiting.',
None) None)
elif cmd == 'ipa-full-update':
return (None,
'Synchronization of all zones requested.',
None)
elif not cmd.startswith('update '): elif not cmd.startswith('update '):
return (0, return (0,
'Command "%s" is not supported by IPA; ' 'Command "%s" is not supported by IPA; '
'HSM synchronization was finished and the command ' 'HSM synchronization was finished and the command '
'will be ignored.\n' % cmd, 'will be ignored.' % cmd,
None) None)
else: else:
@ -394,6 +392,7 @@ def cmd2ods_zone_name(cmd):
return zone_name return zone_name
def sync_zone(log, ldap, dns_dn, zone_name): def sync_zone(log, ldap, dns_dn, zone_name):
log.debug('synchronizing zone "%s"', zone_name)
ods_keys = get_ods_keys(zone_name) ods_keys = get_ods_keys(zone_name)
ods_keys_id = set(ods_keys.keys()) ods_keys_id = set(ods_keys.keys())
@ -530,6 +529,19 @@ if exitcode is not None:
else: else:
log.debug(msg) log.debug(msg)
sync_zone(log, ldap, dns_dn, zone_name) # Open DB directly and read key timestamps etc.
with ods_db_lock():
db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB,
isolation_level="EXCLUSIVE")
db.row_factory = sqlite3.Row
db.execute('BEGIN')
if zone_name is not None:
# only one zone should be processed
sync_zone(log, ldap, dns_dn, zone_name)
else:
# process all zones
for zone_row in db.execute("SELECT name FROM zones"):
sync_zone(log, ldap, dns_dn, zone_row['name'])
log.debug('Done') log.debug('Done')