summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorMartin Basti <[email protected]>2014-10-16 16:03:46 +0200
committerMartin Kosek <[email protected]>2014-10-21 12:18:55 +0200
commitabf4418c4612e5354e85c00a461bf537d8deff9c (patch)
tree7d86c5bfd2a75ed63989f09bca6c3dffc00f5295 /ipaserver
parent52acc54f9efbac863e06d130f7e2110f3ee71db9 (diff)
downloadfreeipa-abf4418c4612e5354e85c00a461bf537d8deff9c.tar.gz
freeipa-abf4418c4612e5354e85c00a461bf537d8deff9c.tar.xz
freeipa-abf4418c4612e5354e85c00a461bf537d8deff9c.zip
DNSSEC: opendnssec services
Tickets: https://fedorahosted.org/freeipa/ticket/3801 https://fedorahosted.org/freeipa/ticket/4417 Design: https://fedorahosted.org/bind-dyndb-ldap/wiki/BIND9/Design/DNSSEC Reviewed-By: Jan Cholasta <[email protected]> Reviewed-By: David Kupka <[email protected]>
Diffstat (limited to 'ipaserver')
-rw-r--r--ipaserver/install/odsexporterinstance.py179
-rw-r--r--ipaserver/install/opendnssecinstance.py299
2 files changed, 478 insertions, 0 deletions
diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py
new file mode 100644
index 000000000..57b1451c0
--- /dev/null
+++ b/ipaserver/install/odsexporterinstance.py
@@ -0,0 +1,179 @@
+#
+# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
+#
+
+import service
+import installutils
+import os
+import pwd
+import grp
+
+import ldap
+
+from ipapython.ipa_log_manager import *
+from ipapython.dn import DN
+from ipapython import sysrestore, ipautil, ipaldap
+from ipaplatform.paths import paths
+from ipaplatform import services
+from ipalib import errors
+
+
+class ODSExporterInstance(service.Service):
+ def __init__(self, fstore=None, dm_password=None):
+ service.Service.__init__(
+ self, "ipa-ods-exporter",
+ service_desc="IPA OpenDNSSEC exporter daemon",
+ dm_password=dm_password,
+ ldapi=False,
+ autobind=ipaldap.AUTOBIND_DISABLED
+ )
+ self.dm_password = dm_password
+ self.ods_uid = None
+ self.ods_gid = None
+ self.enable_if_exists = False
+
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore(paths.SYSRESTORE)
+
+ suffix = ipautil.dn_attribute_property('_suffix')
+
+ def create_instance(self, fqdn, realm_name):
+ self.backup_state("enabled", self.is_enabled())
+ self.backup_state("running", self.is_running())
+ self.fqdn = fqdn
+ self.realm = realm_name
+ self.suffix = ipautil.realm_to_suffix(self.realm)
+
+ try:
+ self.stop()
+ except:
+ pass
+
+ # get a connection to the DS
+ self.ldap_connect()
+ # checking status step must be first
+ self.step("checking status", self.__check_dnssec_status)
+ self.step("setting up DNS Key Exporter", self.__setup_key_exporter)
+ self.step("setting up kerberos principal", self.__setup_principal)
+ self.step("disabling default signer daemon", self.__disable_signerd)
+ self.step("starting DNS Key Exporter", self.__start)
+ self.step("configuring DNS Key Exporter to start on boot", self.__enable)
+ self.start_creation()
+
+ def __check_dnssec_status(self):
+ ods_enforcerd = services.knownservices.ods_enforcerd
+
+ try:
+ self.ods_uid = pwd.getpwnam(ods_enforcerd.get_user_name()).pw_uid
+ except KeyError:
+ raise RuntimeError("OpenDNSSEC UID not found")
+
+ try:
+ self.ods_gid = grp.getgrnam(ods_enforcerd.get_group_name()).gr_gid
+ except KeyError:
+ raise RuntimeError("OpenDNSSEC GID not found")
+
+ def __enable(self):
+
+ try:
+ self.ldap_enable('DNSKeyExporter', self.fqdn, self.dm_password,
+ self.suffix)
+ except errors.DuplicateEntry:
+ root_logger.error("DNSKeyExporter service already exists")
+ self.enable()
+
+ def __setup_key_exporter(self):
+ installutils.set_directive(paths.SYSOCNFIG_IPA_ODS_EXPORTER,
+ 'SOFTHSM2_CONF',
+ paths.DNSSEC_SOFTHSM2_CONF,
+ quotes=False, separator='=')
+
+ def __setup_principal(self):
+ assert self.ods_uid is not None
+ dns_exporter_principal = "ipa-ods-exporter/" + self.fqdn + "@" + self.realm
+ installutils.kadmin_addprinc(dns_exporter_principal)
+
+ # Store the keytab on disk
+ installutils.create_keytab(paths.IPA_ODS_EXPORTER_KEYTAB, dns_exporter_principal)
+ p = self.move_service(dns_exporter_principal)
+ if p is None:
+ # the service has already been moved, perhaps we're doing a DNS reinstall
+ dns_exporter_principal_dn = DN(
+ ('krbprincipalname', dns_exporter_principal),
+ ('cn', 'services'), ('cn', 'accounts'), self.suffix)
+ else:
+ dns_exporter_principal_dn = p
+
+ # Make sure access is strictly reserved to the ods user
+ os.chmod(paths.IPA_ODS_EXPORTER_KEYTAB, 0440)
+ os.chown(paths.IPA_ODS_EXPORTER_KEYTAB, 0, self.ods_gid)
+
+ dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'),
+ ('cn', 'pbac'), self.suffix)
+ mod = [(ldap.MOD_ADD, 'member', dns_exporter_principal_dn)]
+
+ try:
+ self.admin_conn.modify_s(dns_group, mod)
+ except ldap.TYPE_OR_VALUE_EXISTS:
+ pass
+ except Exception, e:
+ root_logger.critical("Could not modify principal's %s entry: %s"
+ % (dns_exporter_principal_dn, str(e)))
+ raise
+
+ # limit-free connection
+
+ mod = [(ldap.MOD_REPLACE, 'nsTimeLimit', '-1'),
+ (ldap.MOD_REPLACE, 'nsSizeLimit', '-1'),
+ (ldap.MOD_REPLACE, 'nsIdleTimeout', '-1'),
+ (ldap.MOD_REPLACE, 'nsLookThroughLimit', '-1')]
+ try:
+ self.admin_conn.modify_s(dns_exporter_principal_dn, mod)
+ except Exception, e:
+ root_logger.critical("Could not set principal's %s LDAP limits: %s"
+ % (dns_exporter_principal_dn, str(e)))
+ raise
+
+ def __disable_signerd(self):
+ signerd_service = services.knownservices.ods_signerd
+
+ self.backup_state("singerd_running", signerd_service.is_running())
+ self.backup_state("singerd_enabled", signerd_service.is_enabled())
+
+ # disable default opendnssec signer daemon
+ signerd_service.stop()
+ signerd_service.mask()
+
+ def __start(self):
+ self.start()
+
+ def uninstall(self):
+ if not self.is_configured():
+ return
+
+ self.print_msg("Unconfiguring %s" % self.service_name)
+
+ running = self.restore_state("running")
+ enabled = self.restore_state("enabled")
+
+ if enabled is not None and not enabled:
+ self.disable()
+
+ if running is not None and running:
+ self.start()
+
+ # restore state of dnssec default signer daemon
+ signerd_enabled = self.restore_state("singerd_enabled")
+ signerd_running = self.restore_state("singerd_runnning")
+ signerd_service = services.knownservices.ods_signerd
+
+ signerd_service.unmask()
+
+ # service was stopped and disabled by setup
+ if signerd_enabled:
+ signerd_service.enable()
+
+ if signerd_running:
+ signerd_service.start()
diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
new file mode 100644
index 000000000..0d2fb009e
--- /dev/null
+++ b/ipaserver/install/opendnssecinstance.py
@@ -0,0 +1,299 @@
+#
+# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
+#
+
+import random
+
+import service
+import os
+import pwd
+import grp
+import stat
+
+import _ipap11helper
+
+import installutils
+from ipapython.ipa_log_manager import *
+from ipapython.dn import DN
+from ipapython import sysrestore, ipautil, ipaldap, p11helper
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipalib import errors, api
+from ipaserver.install import dnskeysyncinstance
+
+KEYMASTER = u'dnssecKeyMaster'
+softhsm_slot = 0
+
+
+def get_dnssec_key_masters(conn):
+ """
+ :return: list of active dnssec key masters
+ """
+ assert conn is not None
+
+ dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+
+ filter_attrs = {
+ u'cn': u'DNSSEC',
+ u'objectclass': u'ipaConfigObject',
+ u'ipaConfigString': [KEYMASTER, u'enabledService'],
+ }
+ only_masters_f = conn.make_filter(filter_attrs, rules=conn.MATCH_ALL)
+
+ try:
+ entries = conn.find_entries(filter=only_masters_f, base_dn=dn)
+ except errors.NotFound:
+ return []
+
+ keymasters_list = []
+ for entry in entries[0]:
+ keymasters_list.append(str(entry.dn[1].value))
+
+ return keymasters_list
+
+
+def check_inst():
+ if not os.path.exists(paths.ODS_KSMUTIL):
+ print ("Please install the 'opendnssec' package and start "
+ "the installation again")
+ return False
+ return True
+
+
+class OpenDNSSECInstance(service.Service):
+ def __init__(self, fstore=None, dm_password=None):
+ service.Service.__init__(
+ self, "ods-enforcerd",
+ service_desc="OpenDNSSEC enforcer daemon",
+ dm_password=dm_password,
+ ldapi=False,
+ autobind=ipaldap.AUTOBIND_DISABLED
+ )
+ self.dm_password = dm_password
+ self.ods_uid = None
+ self.ods_gid = None
+ self.conf_file_dict = {
+ 'SOFTHSM_LIB': paths.LIBSOFTHSM2_SO,
+ 'TOKEN_LABEL': dnskeysyncinstance.softhsm_token_label,
+ 'KASP_DB': paths.OPENDNSSEC_KASP_DB,
+ }
+ self.kasp_file_dict = {}
+ self.extra_config = [KEYMASTER]
+
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore(paths.SYSRESTORE)
+
+ suffix = ipautil.dn_attribute_property('_suffix')
+
+ def get_masters(self):
+ if not self.admin_conn:
+ self.ldap_connect()
+ return get_dnssec_key_masters(self.admin_conn)
+
+ def create_instance(self, fqdn, realm_name, generate_master_key=True):
+ self.backup_state("enabled", self.is_enabled())
+ self.backup_state("running", self.is_running())
+ self.fqdn = fqdn
+ self.realm = realm_name
+ self.suffix = ipautil.realm_to_suffix(self.realm)
+
+ try:
+ self.stop()
+ except Exception:
+ pass
+
+ # get a connection to the DS
+ if not self.admin_conn:
+ self.ldap_connect()
+ # checking status must be first
+ self.step("checking status", self.__check_dnssec_status)
+ self.step("setting up configuration files", self.__setup_conf_files)
+ self.step("setting up ownership and file mode bits", self.__setup_ownership_file_modes)
+ if generate_master_key:
+ self.step("generating master key", self.__generate_master_key)
+ self.step("setting up OpenDNSSEC", self.__setup_dnssec)
+ self.step("setting up ipa-dnskeysyncd", self.__setup_dnskeysyncd)
+ self.step("starting OpenDNSSEC enforcer", self.__start)
+ self.step("configuring OpenDNSSEC enforcer to start on boot", self.__enable)
+ self.start_creation()
+
+ def __check_dnssec_status(self):
+ named = services.knownservices.named
+ ods_enforcerd = services.knownservices.ods_enforcerd
+
+ try:
+ self.named_uid = pwd.getpwnam(named.get_user_name()).pw_uid
+ except KeyError:
+ raise RuntimeError("Named UID not found")
+
+ try:
+ self.named_gid = grp.getgrnam(named.get_group_name()).gr_gid
+ except KeyError:
+ raise RuntimeError("Named GID not found")
+
+ try:
+ self.ods_uid = pwd.getpwnam(ods_enforcerd.get_user_name()).pw_uid
+ except KeyError:
+ raise RuntimeError("OpenDNSSEC UID not found")
+
+ try:
+ self.ods_gid = grp.getgrnam(ods_enforcerd.get_group_name()).gr_gid
+ except KeyError:
+ raise RuntimeError("OpenDNSSEC GID not found")
+
+ def __enable(self):
+ try:
+ self.ldap_enable('DNSSEC', self.fqdn, self.dm_password,
+ self.suffix, self.extra_config)
+ except errors.DuplicateEntry:
+ root_logger.error("DNSSEC service already exists")
+ self.enable()
+
+ def __setup_conf_files(self):
+ if not self.fstore.has_file(paths.OPENDNSSEC_CONF_FILE):
+ self.fstore.backup_file(paths.OPENDNSSEC_CONF_FILE)
+
+ if not self.fstore.has_file(paths.OPENDNSSEC_KASP_FILE):
+ self.fstore.backup_file(paths.OPENDNSSEC_KASP_FILE)
+
+ pin_fd = open(paths.DNSSEC_SOFTHSM_PIN, "r")
+ pin = pin_fd.read()
+ pin_fd.close()
+
+ # add pin to template
+ sub_conf_dict = self.conf_file_dict
+ sub_conf_dict['PIN'] = pin
+
+ ods_conf_txt = ipautil.template_file(
+ ipautil.SHARE_DIR + "opendnssec_conf.template", sub_conf_dict)
+ ods_conf_fd = open(paths.OPENDNSSEC_CONF_FILE, 'w')
+ ods_conf_fd.seek(0)
+ ods_conf_fd.truncate(0)
+ ods_conf_fd.write(ods_conf_txt)
+ ods_conf_fd.close()
+
+ ods_kasp_txt = ipautil.template_file(
+ ipautil.SHARE_DIR + "opendnssec_kasp.template", self.kasp_file_dict)
+ ods_kasp_fd = open(paths.OPENDNSSEC_KASP_FILE, 'w')
+ ods_kasp_fd.seek(0)
+ ods_kasp_fd.truncate(0)
+ ods_kasp_fd.write(ods_kasp_txt)
+ ods_kasp_fd.close()
+
+ if not self.fstore.has_file(paths.SYSCONFIG_ODS):
+ self.fstore.backup_file(paths.SYSCONFIG_ODS)
+
+ installutils.set_directive(paths.SYSCONFIG_ODS,
+ 'SOFTHSM2_CONF',
+ paths.DNSSEC_SOFTHSM2_CONF,
+ quotes=False, separator='=')
+
+ def __setup_ownership_file_modes(self):
+ assert self.ods_uid is not None
+ assert self.ods_gid is not None
+
+ # workarounds for packaging bugs in opendnssec-1.4.5-2.fc20.x86_64
+ # https://bugzilla.redhat.com/show_bug.cgi?id=1098188
+ for (root, dirs, files) in os.walk(paths.ETC_OPENDNSSEC_DIR):
+ for directory in dirs:
+ dir_path = os.path.join(root, directory)
+ os.chmod(dir_path, 0770)
+ # chown to root:ods
+ os.chown(dir_path, 0, self.ods_gid)
+ for filename in files:
+ file_path = os.path.join(root, filename)
+ os.chmod(file_path, 0660)
+ # chown to root:ods
+ os.chown(file_path, 0, self.ods_gid)
+
+ for (root, dirs, files) in os.walk(paths.VAR_OPENDNSSEC_DIR):
+ for directory in dirs:
+ dir_path = os.path.join(root, directory)
+ os.chmod(dir_path, 0770)
+ # chown to ods:ods
+ os.chown(dir_path, self.ods_uid, self.ods_gid)
+ for filename in files:
+ file_path = os.path.join(root, filename)
+ os.chmod(file_path, 0660)
+ # chown to ods:ods
+ os.chown(file_path, self.ods_uid, self.ods_gid)
+
+ def __generate_master_key(self):
+
+ with open(paths.DNSSEC_SOFTHSM_PIN, "r") as f:
+ pin = f.read()
+
+ os.environ["SOFTHSM2_CONF"] = paths.DNSSEC_SOFTHSM2_CONF
+ p11 = _ipap11helper.P11_Helper(softhsm_slot, pin, paths.LIBSOFTHSM2_SO)
+ try:
+ # generate master key
+ root_logger.debug("Creating master key")
+ p11helper.generate_master_key(p11)
+
+ # change tokens mod/owner
+ root_logger.debug("Changing ownership of token files")
+ for (root, dirs, files) in os.walk(paths.DNSSEC_TOKENS_DIR):
+ for directory in dirs:
+ dir_path = os.path.join(root, directory)
+ os.chmod(dir_path, 0770 | stat.S_ISGID)
+ os.chown(dir_path, self.ods_uid, self.named_gid) # chown to ods:named
+ for filename in files:
+ file_path = os.path.join(root, filename)
+ os.chmod(file_path, 0770 | stat.S_ISGID)
+ os.chown(file_path, self.ods_uid, self.named_gid) # chown to ods:named
+
+ finally:
+ p11.finalize()
+
+ def __setup_dnssec(self):
+ # run once only
+ if self.get_state("KASP_DB_configured"):
+ root_logger.debug("Already configured, skipping step")
+
+ self.backup_state("KASP_DB_configured", True)
+
+ if not self.fstore.has_file(paths.OPENDNSSEC_KASP_DB):
+ self.fstore.backup_file(paths.OPENDNSSEC_KASP_DB)
+
+ command = [
+ paths.ODS_KSMUTIL,
+ 'setup'
+ ]
+
+ ods_enforcerd = services.knownservices.ods_enforcerd
+ ipautil.run(command, stdin="y", runas=ods_enforcerd.get_user_name())
+
+ def __setup_dnskeysyncd(self):
+ # set up dnskeysyncd this is DNSSEC master
+ installutils.set_directive(paths.SYSCONFIG_IPA_DNSKEYSYNCD,
+ 'ISMASTER',
+ '1',
+ quotes=False, separator='=')
+
+ def __start(self):
+ self.restart() # needed to reload conf files
+
+ def uninstall(self):
+ if not self.is_configured():
+ return
+
+ self.print_msg("Unconfiguring %s" % self.service_name)
+
+ running = self.restore_state("running")
+ enabled = self.restore_state("enabled")
+
+ for f in [paths.OPENDNSSEC_CONF_FILE, paths.OPENDNSSEC_KASP_FILE,
+ paths.OPENDNSSEC_KASP_DB, paths.SYSCONFIG_ODS]:
+ try:
+ self.fstore.restore_file(f)
+ except ValueError, error:
+ root_logger.debug(error)
+ pass
+
+ if enabled is not None and not enabled:
+ self.disable()
+
+ if running is not None and running:
+ self.start()
OSZAR »