2019-06-21 05:05:19 -05:00
|
|
|
#!/usr/bin/python3
|
2014-10-19 10:04:40 -05:00
|
|
|
#
|
|
|
|
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
|
|
|
|
#
|
|
|
|
"""
|
|
|
|
Download keys from LDAP to local HSM.
|
|
|
|
|
|
|
|
This program should be run only on replicas, not on DNSSEC masters.
|
|
|
|
"""
|
|
|
|
|
2015-07-20 09:04:07 -05:00
|
|
|
from gssapi.exceptions import GSSError
|
2017-05-24 08:42:23 -05:00
|
|
|
import logging
|
2014-10-19 10:04:40 -05:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import ipalib
|
2019-04-09 09:58:35 -05:00
|
|
|
from ipalib import errors
|
2017-03-29 11:53:11 -05:00
|
|
|
from ipalib.constants import SOFTHSM_DNSSEC_TOKEN_LABEL
|
2016-11-23 10:40:47 -06:00
|
|
|
from ipalib.install.kinit import kinit_keytab
|
2014-10-19 10:04:40 -05:00
|
|
|
from ipapython.dn import DN
|
2017-05-24 09:35:07 -05:00
|
|
|
from ipapython.ipa_log_manager import standard_logging_setup
|
2022-05-09 10:11:09 -05:00
|
|
|
from ipapython.ipautil import get_config_debug
|
2014-10-19 10:04:40 -05:00
|
|
|
from ipapython import ipaldap
|
|
|
|
from ipaplatform.paths import paths
|
2016-11-22 10:55:10 -06:00
|
|
|
from ipaserver.dnssec.abshsm import (sync_pkcs11_metadata,
|
|
|
|
ldap2p11helper_api_params,
|
|
|
|
wrappingmech_name2id)
|
2017-08-25 08:45:24 -05:00
|
|
|
from ipaserver.dnssec.ldapkeydb import LdapKeyDB, str_hexlify
|
2016-11-22 10:55:10 -06:00
|
|
|
from ipaserver.dnssec.localhsm import LocalHSM
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
logger = logging.getLogger(os.path.basename(__file__))
|
|
|
|
|
2014-10-19 10:04:40 -05:00
|
|
|
DAEMONNAME = 'ipa-dnskeysyncd'
|
|
|
|
PRINCIPAL = None # not initialized yet
|
|
|
|
WORKDIR = '/tmp'
|
|
|
|
|
|
|
|
def hex_set(s):
|
|
|
|
out = set()
|
|
|
|
for i in s:
|
2017-08-25 08:45:24 -05:00
|
|
|
out.add("0x%s" % str_hexlify(i))
|
2014-10-19 10:04:40 -05:00
|
|
|
return out
|
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
def update_metadata_set(source_set, target_set):
|
2014-10-19 10:04:40 -05:00
|
|
|
"""sync metadata from source key set to target key set
|
|
|
|
|
|
|
|
Keys not present in both sets are left intact."""
|
2017-05-24 08:42:23 -05:00
|
|
|
name = 'sync_metadata'
|
2014-10-19 10:04:40 -05:00
|
|
|
matching_keys = set(source_set.keys()).intersection(set(target_set.keys()))
|
2017-05-24 08:42:23 -05:00
|
|
|
logger.info("%s: keys in local HSM & LDAP: %s",
|
|
|
|
name, hex_set(matching_keys))
|
2014-10-19 10:04:40 -05:00
|
|
|
for key_id in matching_keys:
|
2017-05-24 08:42:23 -05:00
|
|
|
sync_pkcs11_metadata(name, source_set[key_id], target_set[key_id])
|
2014-10-19 10:04:40 -05:00
|
|
|
|
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
def find_unwrapping_key(localhsm, wrapping_key_uri):
|
2014-10-19 10:04:40 -05:00
|
|
|
wrap_keys = localhsm.find_keys(uri=wrapping_key_uri)
|
|
|
|
# find usable unwrapping key with matching ID
|
2016-09-26 07:08:17 -05:00
|
|
|
for key_id in wrap_keys.keys():
|
2014-10-19 10:04:40 -05:00
|
|
|
unwrap_keys = localhsm.find_keys(id=key_id, cka_unwrap=True)
|
|
|
|
if len(unwrap_keys) > 0:
|
|
|
|
return unwrap_keys.popitem()[1]
|
2019-04-20 06:01:42 -05:00
|
|
|
return None
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
def ldap2replica_master_keys_sync(ldapkeydb, localhsm):
|
2014-10-19 10:04:40 -05:00
|
|
|
## LDAP -> replica master key synchronization
|
|
|
|
# import new master keys from LDAP
|
|
|
|
new_keys = set(ldapkeydb.master_keys.keys()) \
|
|
|
|
- set(localhsm.master_keys.keys())
|
2017-05-24 08:42:23 -05:00
|
|
|
logger.debug("master keys in local HSM: %s",
|
|
|
|
hex_set(localhsm.master_keys.keys()))
|
|
|
|
logger.debug("master keys in LDAP HSM: %s",
|
|
|
|
hex_set(ldapkeydb.master_keys.keys()))
|
|
|
|
logger.debug("new master keys in LDAP HSM: %s",
|
|
|
|
hex_set(new_keys))
|
2014-10-19 10:04:40 -05:00
|
|
|
for mkey_id in new_keys:
|
|
|
|
mkey_ldap = ldapkeydb.master_keys[mkey_id]
|
2018-01-31 10:03:31 -06:00
|
|
|
if not mkey_ldap.wrapped_entries:
|
|
|
|
raise ValueError(
|
|
|
|
"Master key 0x%s in LDAP is missing key material "
|
|
|
|
"referenced by ipaSecretKeyRefObject attribute" %
|
|
|
|
str_hexlify(mkey_id)
|
|
|
|
)
|
2014-10-19 10:04:40 -05:00
|
|
|
for wrapped_ldap in mkey_ldap.wrapped_entries:
|
2017-05-24 08:42:23 -05:00
|
|
|
unwrapping_key = find_unwrapping_key(
|
|
|
|
localhsm, wrapped_ldap.single_value['ipaWrappingKey'])
|
2014-10-19 10:04:40 -05:00
|
|
|
if unwrapping_key:
|
|
|
|
break
|
|
|
|
|
|
|
|
# TODO: Could it happen in normal cases?
|
2018-01-31 10:03:31 -06:00
|
|
|
if unwrapping_key is None:
|
|
|
|
raise ValueError(
|
|
|
|
"Local HSM does not contain suitable unwrapping key "
|
|
|
|
"for master key 0x%s" % str_hexlify(mkey_id)
|
|
|
|
)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
|
|
|
params = ldap2p11helper_api_params(mkey_ldap)
|
|
|
|
params['data'] = wrapped_ldap.single_value['ipaSecretKey']
|
|
|
|
params['unwrapping_key'] = unwrapping_key.handle
|
|
|
|
params['wrapping_mech'] = wrappingmech_name2id[wrapped_ldap.single_value['ipaWrappingMech']]
|
2017-05-24 08:42:23 -05:00
|
|
|
logger.debug('Importing new master key: 0x%s %s',
|
2017-08-25 08:45:24 -05:00
|
|
|
str_hexlify(mkey_id), params)
|
2014-10-19 10:04:40 -05:00
|
|
|
localhsm.p11.import_wrapped_secret_key(**params)
|
|
|
|
|
|
|
|
# synchronize metadata about master keys in LDAP
|
2017-05-24 08:42:23 -05:00
|
|
|
update_metadata_set(ldapkeydb.master_keys, localhsm.master_keys)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
def ldap2replica_zone_keys_sync(ldapkeydb, localhsm):
|
2014-10-19 10:04:40 -05:00
|
|
|
## LDAP -> replica zone key synchronization
|
|
|
|
# import new zone keys from LDAP
|
|
|
|
new_keys = set(ldapkeydb.zone_keypairs.keys()) \
|
|
|
|
- set(localhsm.zone_privkeys.keys())
|
|
|
|
|
2017-05-24 08:42:23 -05:00
|
|
|
logger.debug("zone keys in local HSM: %s",
|
|
|
|
hex_set(localhsm.master_keys.keys()))
|
|
|
|
logger.debug("zone keys in LDAP HSM: %s",
|
|
|
|
hex_set(ldapkeydb.master_keys.keys()))
|
|
|
|
logger.debug("new zone keys in LDAP HSM: %s",
|
|
|
|
hex_set(new_keys))
|
2014-10-19 10:04:40 -05:00
|
|
|
for zkey_id in new_keys:
|
|
|
|
zkey_ldap = ldapkeydb.zone_keypairs[zkey_id]
|
2017-05-24 08:42:23 -05:00
|
|
|
logger.debug('Looking for unwrapping key "%s" for zone key 0x%s',
|
2017-08-25 08:45:24 -05:00
|
|
|
zkey_ldap['ipaWrappingKey'], str_hexlify(zkey_id))
|
2017-05-24 08:42:23 -05:00
|
|
|
unwrapping_key = find_unwrapping_key(
|
|
|
|
localhsm, zkey_ldap['ipaWrappingKey'])
|
2018-01-31 10:03:31 -06:00
|
|
|
if unwrapping_key is None:
|
|
|
|
raise ValueError(
|
|
|
|
"Local HSM does not contain suitable unwrapping key for "
|
|
|
|
"zone key 0x%s" % str_hexlify(zkey_id)
|
|
|
|
)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2017-08-25 08:45:24 -05:00
|
|
|
logger.debug('Importing zone key pair 0x%s', str_hexlify(zkey_id))
|
2014-10-19 10:04:40 -05:00
|
|
|
localhsm.import_private_key(zkey_ldap, zkey_ldap['ipaPrivateKey'],
|
|
|
|
unwrapping_key)
|
|
|
|
localhsm.import_public_key(zkey_ldap, zkey_ldap['ipaPublicKey'])
|
|
|
|
|
|
|
|
# synchronize metadata about zone keys in LDAP & local HSM
|
2017-05-24 08:42:23 -05:00
|
|
|
update_metadata_set(ldapkeydb.master_keys, localhsm.master_keys)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
|
|
|
# delete keys removed from LDAP
|
|
|
|
deleted_keys = set(localhsm.zone_privkeys.keys()) \
|
|
|
|
- set(ldapkeydb.zone_keypairs.keys())
|
|
|
|
|
|
|
|
for zkey_id in deleted_keys:
|
|
|
|
localhsm.p11.delete_key(localhsm.zone_pubkeys[zkey_id].handle)
|
|
|
|
localhsm.p11.delete_key(localhsm.zone_privkeys[zkey_id].handle)
|
|
|
|
|
|
|
|
|
|
|
|
# IPA framework initialization
|
2022-05-09 10:11:09 -05:00
|
|
|
debug = get_config_debug('dns')
|
|
|
|
standard_logging_setup(debug=debug, verbose=True)
|
|
|
|
if not debug:
|
|
|
|
logger.info("To increase debugging set debug=True in dns.conf "
|
|
|
|
"See default.conf(5) for details")
|
2017-06-21 02:28:22 -05:00
|
|
|
ipalib.api.bootstrap(context='dns', confdir=paths.ETC_IPA, in_server=True)
|
2014-10-19 10:04:40 -05:00
|
|
|
ipalib.api.finalize()
|
|
|
|
|
|
|
|
# Kerberos initialization
|
|
|
|
PRINCIPAL = str('%s/%s' % (DAEMONNAME, ipalib.api.env.host))
|
2017-05-24 09:35:07 -05:00
|
|
|
logger.debug('Kerberos principal: %s', PRINCIPAL)
|
2015-04-15 08:20:00 -05:00
|
|
|
ccache_filename = os.path.join(WORKDIR, 'ipa-dnskeysync-replica.ccache')
|
2015-08-18 11:33:37 -05:00
|
|
|
|
|
|
|
try:
|
2016-11-23 10:40:47 -06:00
|
|
|
kinit_keytab(PRINCIPAL, paths.IPA_DNSKEYSYNCD_KEYTAB, ccache_filename,
|
|
|
|
attempts=5)
|
2015-07-20 09:04:07 -05:00
|
|
|
except GSSError as e:
|
2017-05-24 09:35:07 -05:00
|
|
|
logger.critical('Kerberos authentication failed: %s', e)
|
2015-08-18 11:33:37 -05:00
|
|
|
sys.exit(1)
|
|
|
|
|
2015-03-16 10:43:10 -05:00
|
|
|
os.environ['KRB5CCNAME'] = ccache_filename
|
2017-05-24 09:35:07 -05:00
|
|
|
logger.debug('Got TGT')
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2019-04-09 09:58:35 -05:00
|
|
|
keys_dn = DN(
|
|
|
|
('cn', 'keys'), ('cn', 'sec'),
|
|
|
|
ipalib.api.env.container_dns,
|
|
|
|
ipalib.api.env.basedn
|
|
|
|
)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2019-04-09 09:58:35 -05:00
|
|
|
with open(paths.DNSSEC_SOFTHSM_PIN) as f:
|
|
|
|
localhsm = LocalHSM(
|
|
|
|
paths.LIBSOFTHSM2_SO,
|
|
|
|
SOFTHSM_DNSSEC_TOKEN_LABEL,
|
|
|
|
f.read()
|
|
|
|
)
|
2014-10-19 10:04:40 -05:00
|
|
|
|
2019-04-09 09:58:35 -05:00
|
|
|
try:
|
|
|
|
# LDAP initialization
|
|
|
|
ldap = ipaldap.LDAPClient(ipalib.api.env.ldap_uri)
|
|
|
|
logger.debug('Connecting to LDAP')
|
|
|
|
ldap.gssapi_bind()
|
|
|
|
logger.debug('Connected')
|
|
|
|
|
|
|
|
### DNSSEC master: key synchronization
|
|
|
|
ldapkeydb = LdapKeyDB(ldap, keys_dn)
|
|
|
|
ldap2replica_master_keys_sync(ldapkeydb, localhsm)
|
|
|
|
ldap2replica_zone_keys_sync(ldapkeydb, localhsm)
|
|
|
|
except (errors.NetworkError, errors.DatabaseError) as e:
|
|
|
|
# SERVER_DOWN, CONNECT_ERROR
|
|
|
|
logger.error("LDAP server is down: %s", e)
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
|
|
|
sys.exit(0)
|