def add_cert_to_service(self): """ Add a certificate to a service This server cert should be in DER format. """ # add_cert_to_service() is relatively rare operation # we actually call it twice during ipa-server-install, for different # instances: ds and cs. Unfortunately, it may happen that admin # connection was created well before add_cert_to_service() is called # If there are other operations in between, it will become stale and # since we are using SimpleLDAPObject, not ReconnectLDAPObject, the # action will fail. Thus, explicitly disconnect and connect again. # Using ReconnectLDAPObject instead of SimpleLDAPObject was considered # but consequences for other parts of the framework are largely # unknown. if self.admin_conn: self.ldap_disconnect() self.ldap_connect() dn = DN(('krbprincipalname', self.principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) entry = self.admin_conn.get_entry(dn) entry.setdefault('userCertificate', []).append(self.dercert) try: self.admin_conn.update_entry(entry) except Exception as e: root_logger.critical("Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
def add_cert_to_service(self): """ Add a certificate to a service This server cert should be in DER format. """ # add_cert_to_service() is relatively rare operation # we actually call it twice during ipa-server-install, for different # instances: ds and cs. Unfortunately, it may happen that admin # connection was created well before add_cert_to_service() is called # If there are other operations in between, it will become stale and # since we are using SimpleLDAPObject, not ReconnectLDAPObject, the # action will fail. Thus, explicitly disconnect and connect again. # Using ReconnectLDAPObject instead of SimpleLDAPObject was considered # but consequences for other parts of the framework are largely # unknown. if self.admin_conn: self.ldap_disconnect() self.ldap_connect() dn = DN(('krbprincipalname', self.principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) entry = self.admin_conn.get_entry(dn) entry.setdefault('userCertificate', []).append(self.dercert) try: self.admin_conn.update_entry(entry) except Exception as e: root_logger.critical("Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
def create_keytab(path, principal): try: if ipautil.file_exists(path): os.remove(path) except os.error: root_logger.critical("Failed to remove %s." % path) kadmin("ktadd -k " + path + " " + principal)
def create_keytab(path, principal): try: if ipautil.file_exists(path): os.remove(path) except os.error: root_logger.critical("Failed to remove %s." % path) kadmin("ktadd -k " + path + " " + principal)
def __add_rid_bases(self): """ Add RID bases to the range object for the local ID range. TODO: handle missing or multiple ranges more gracefully. """ try: # Get the ranges ranges = api.Backend.ldap2.get_entries( DN(api.env.container_ranges, self.suffix), ldap.SCOPE_ONELEVEL, "(objectclass=ipaDomainIDRange)") # Filter out ranges where RID base is already set no_rid_base_set = lambda r: not any( (r.single_value.get('ipaBaseRID'), r.single_value.get('ipaSecondaryBaseRID'))) ranges_with_no_rid_base = [r for r in ranges if no_rid_base_set(r)] # Return if no range is without RID base if len(ranges_with_no_rid_base) == 0: self.print_msg("RID bases already set, nothing to do") return # Abort if RID base needs to be added to more than one range if len(ranges_with_no_rid_base) != 1: root_logger.critical("Found more than one local domain ID " "range with no RID base set.") raise RuntimeError("Too many ID ranges\n") # Abort if RID bases are too close local_range = ranges_with_no_rid_base[0] size = local_range.single_value.get('ipaIDRangeSize') if abs(self.rid_base - self.secondary_rid_base) > size: self.print_msg("Primary and secondary RID base are too close. " "They have to differ at least by %d." % size) raise RuntimeError("RID bases too close.\n") # Modify the range # If the RID bases would cause overlap with some other range, # this will be detected by ipa-range-check DS plugin try: api.Backend.ldap2.modify_s( local_range.dn, [(ldap.MOD_ADD, "ipaBaseRID", str(self.rid_base)), (ldap.MOD_ADD, "ipaSecondaryBaseRID", str(self.secondary_rid_base))]) except ldap.CONSTRAINT_VIOLATION as e: self.print_msg("Failed to add RID bases to the local range " "object:\n %s" % e[0]['info']) raise RuntimeError("Constraint violation.\n") except errors.NotFound as e: root_logger.critical("ID range of the local domain not found, " "define it and run again.") raise e
def __add_rid_bases(self): """ Add RID bases to the range object for the local ID range. TODO: handle missing or multiple ranges more gracefully. """ try: # Get the ranges ranges = self.admin_conn.get_entries( DN(api.env.container_ranges, self.suffix), ldap.SCOPE_ONELEVEL, "(objectclass=ipaDomainIDRange)") # Filter out ranges where RID base is already set no_rid_base_set = lambda r: not any(( r.single_value.get('ipaBaseRID'), r.single_value.get('ipaSecondaryBaseRID'))) ranges_with_no_rid_base = [r for r in ranges if no_rid_base_set(r)] # Return if no range is without RID base if len(ranges_with_no_rid_base) == 0: self.print_msg("RID bases already set, nothing to do") return # Abort if RID base needs to be added to more than one range if len(ranges_with_no_rid_base) != 1: root_logger.critical("Found more than one local domain ID " "range with no RID base set.") raise RuntimeError("Too many ID ranges\n") # Abort if RID bases are too close local_range = ranges_with_no_rid_base[0] size = local_range.single_value.get('ipaIDRangeSize') if abs(self.rid_base - self.secondary_rid_base) > size: self.print_msg("Primary and secondary RID base are too close. " "They have to differ at least by %d." % size) raise RuntimeError("RID bases too close.\n") # Modify the range # If the RID bases would cause overlap with some other range, # this will be detected by ipa-range-check DS plugin try: self.admin_conn.modify_s(local_range.dn, [(ldap.MOD_ADD, "ipaBaseRID", str(self.rid_base)), (ldap.MOD_ADD, "ipaSecondaryBaseRID", str(self.secondary_rid_base))]) except ldap.CONSTRAINT_VIOLATION as e: self.print_msg("Failed to add RID bases to the local range " "object:\n %s" % e[0]['info']) raise RuntimeError("Constraint violation.\n") except errors.NotFound as e: root_logger.critical("ID range of the local domain not found, " "define it and run again.") raise e
def execute(self, **options): ldap = self.api.Backend.ldap2 old_style_plugin_search_filter = ( "(&" "(objectclass=nsSlapdPlugin)" "(nsslapd-pluginId=NSUniqueAttr)" "(nsslapd-pluginPath=libattr-unique-plugin)" "(nsslapd-pluginarg0=*)" # only entries with old configuration ")") try: entries, _truncated = ldap.find_entries( filter=old_style_plugin_search_filter, base_dn=self.plugins_dn, ) except errors.NotFound: root_logger.debug("No uniqueness plugin entries with old style " "configuration found") return False, [] update_list = [] new_attributes = [ 'uniqueness-subtree-entries-oc', 'uniqueness-top-entry-oc', 'uniqueness-attribute-name', 'uniqueness-subtrees', 'uniqueness-across-all-subtrees', ] for entry in entries: # test for mixed configuration if any(attr in entry for attr in new_attributes): root_logger.critical( "Mixed old and new style configuration " "for plugin %s. Plugin will not work. " "Skipping plugin migration, please fix it " "manually", entry.dn) continue root_logger.debug( "Configuration of plugin %s will be migrated " "to new style", entry.dn) try: # detect which configuration was used arg0 = entry.get('nsslapd-pluginarg0') if '=' in arg0: update = self.__objectclass_style(entry) else: update = self.__subtree_style(entry) except ValueError as e: root_logger.error( "Unable to migrate configuration of " "plugin %s (%s)", entry.dn, e) else: update_list.append(update) return False, update_list
def clean_samba_keytab(self): if os.path.exists(self.samba_keytab): try: ipautil.run(["ipa-rmkeytab", "--principal", self.cifs_principal, "-k", self.samba_keytab]) except ipautil.CalledProcessError as e: if e.returncode != 5: root_logger.critical("Failed to remove old key for %s" % self.cifs_principal)
def execute(self, **options): ldap = self.api.Backend.ldap2 old_style_plugin_search_filter = ( "(&" "(objectclass=nsSlapdPlugin)" "(nsslapd-pluginId=NSUniqueAttr)" "(nsslapd-pluginPath=libattr-unique-plugin)" "(nsslapd-pluginarg0=*)" # only entries with old configuration ")" ) try: entries, truncated = ldap.find_entries( filter=old_style_plugin_search_filter, base_dn=self.plugins_dn, ) except errors.NotFound: root_logger.debug("No uniqueness plugin entries with old style " "configuration found") return False, [] update_list = [] new_attributes = [ 'uniqueness-subtree-entries-oc', 'uniqueness-top-entry-oc', 'uniqueness-attribute-name', 'uniqueness-subtrees', 'uniqueness-across-all-subtrees', ] for entry in entries: # test for mixed configuration if any(attr in entry for attr in new_attributes): root_logger.critical("Mixed old and new style configuration " "for plugin %s. Plugin will not work. " "Skipping plugin migration, please fix it " "manually", entry.dn) continue root_logger.debug("Configuration of plugin %s will be migrated " "to new style", entry.dn) try: # detect which configuration was used arg0 = entry.get('nsslapd-pluginarg0') if '=' in arg0: update = self.__objectclass_style(entry) else: update = self.__subtree_style(entry) except ValueError as e: root_logger.error("Unable to migrate configuration of " "plugin %s (%s)", entry.dn, e) else: update_list.append(update) return False, update_list
def _ldap_mod(self, ldif, sub_dict=None, raise_on_err=True, ldap_uri=None, dm_password=None): pw_name = None fd = None path = os.path.join(paths.USR_SHARE_IPA_DIR, ldif) nologlist = [] if sub_dict is not None: txt = ipautil.template_file(path, sub_dict) fd = ipautil.write_tmp_file(txt) path = fd.name # do not log passwords if 'PASSWORD' in sub_dict: nologlist.append(sub_dict['PASSWORD']) if 'RANDOM_PASSWORD' in sub_dict: nologlist.append(sub_dict['RANDOM_PASSWORD']) args = [paths.LDAPMODIFY, "-v", "-f", path] # As we always connect to the local host, # use URI of admin connection if not ldap_uri: ldap_uri = api.Backend.ldap2.ldap_uri args += ["-H", ldap_uri] if dm_password: with tempfile.NamedTemporaryFile(mode='w', delete=False) as pw_file: pw_file.write(dm_password) pw_name = pw_file.name auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name] # Use GSSAPI auth when not using DM password or not being root elif os.getegid() != 0: auth_parms = ["-Y", "GSSAPI"] # Default to EXTERNAL auth mechanism else: auth_parms = ["-Y", "EXTERNAL"] args += auth_parms try: try: ipautil.run(args, nolog=nologlist) except ipautil.CalledProcessError as e: root_logger.critical("Failed to load %s: %s" % (ldif, str(e))) if raise_on_err: raise finally: if pw_name: os.remove(pw_name)
def __setup_principal(self): assert self.ods_uid is not None for f in [ paths.IPA_ODS_EXPORTER_CCACHE, paths.IPA_ODS_EXPORTER_KEYTAB ]: try: os.remove(f) except OSError: pass 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, 0o440) 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 as 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 as e: root_logger.critical( "Could not set principal's %s LDAP limits: %s" % (dns_exporter_principal_dn, str(e))) raise
def enable_and_start_oddjobd(self): oddjobd = services.service('oddjobd') self.sstore.backup_state('oddjobd', 'running', oddjobd.is_running()) self.sstore.backup_state('oddjobd', 'enabled', oddjobd.is_enabled()) try: oddjobd.enable() oddjobd.start() except Exception as e: root_logger.critical("Unable to start oddjobd: {0}".format(str(e)))
def enable_and_start_oddjobd(self): oddjobd = services.service('oddjobd') self.sstore.backup_state('oddjobd', 'running', oddjobd.is_running()) self.sstore.backup_state('oddjobd', 'enabled', oddjobd.is_enabled()) try: oddjobd.enable() oddjobd.start() except Exception as e: root_logger.critical("Unable to start oddjobd: {0}".format(str(e)))
def setup_pkinit(self): if self.pkcs12_info: certs.install_pem_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_CERT) certs.install_key_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_KEY) else: subject = str(DN(('cn', self.fqdn), self.subject_base)) krbtgt = "krbtgt/" + self.realm + "@" + self.realm certpath = (paths.KDC_CERT, paths.KDC_KEY) try: prev_helper = None if self.master_fqdn is None: ca_args = [ paths.CERTMONGER_DOGTAG_SUBMIT, '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, '--certfile', paths.RA_AGENT_PEM, '--keyfile', paths.RA_AGENT_KEY, '--cafile', paths.IPA_CA_CRT, '--agent-submit' ] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper('IPA', helper) else: self._wait_for_replica_kdc_entry() certmonger.request_and_wait_for_cert( certpath, subject, krbtgt, dns=self.fqdn, storage='FILE', profile='KDCs_PKINIT_Certs') except dbus.DBusException as e: # if the certificate is already tracked, ignore the error name = e.get_dbus_name() if name != 'org.fedorahosted.certmonger.duplicate': root_logger.error("Failed to initiate the request: %s", e) return finally: if prev_helper is not None: certmonger.modify_ca_helper('IPA', prev_helper) # Finally copy the cacert in the krb directory so we don't # have any selinux issues with the file context shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) try: self.restart() except Exception: root_logger.critical("krb5kdc service failed to restart") raise
def clean_samba_keytab(self): if os.path.exists(self.keytab): try: ipautil.run([ "ipa-rmkeytab", "--principal", self.principal, "-k", self.keytab ]) except ipautil.CalledProcessError as e: if e.returncode != 5: root_logger.critical("Failed to remove old key for %s" % self.principal)
def _ldap_mod(self, ldif, sub_dict=None, raise_on_err=True): pw_name = None fd = None path = ipautil.SHARE_DIR + ldif nologlist = [] if sub_dict is not None: txt = ipautil.template_file(path, sub_dict) fd = ipautil.write_tmp_file(txt) path = fd.name # do not log passwords if 'PASSWORD' in sub_dict: nologlist.append(sub_dict['PASSWORD']) if 'RANDOM_PASSWORD' in sub_dict: nologlist.append(sub_dict['RANDOM_PASSWORD']) args = [paths.LDAPMODIFY, "-v", "-f", path] # As we always connect to the local host, # use URI of admin connection if not self.admin_conn: self.ldap_connect() args += ["-H", self.admin_conn.ldap_uri] # If DM password is available, use it if self.dm_password: [pw_fd, pw_name] = tempfile.mkstemp() os.write(pw_fd, self.dm_password) os.close(pw_fd) auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name] # Use GSSAPI auth when not using DM password or not being root elif os.getegid() != 0: auth_parms = ["-Y", "GSSAPI"] # Default to EXTERNAL auth mechanism else: auth_parms = ["-Y", "EXTERNAL"] args += auth_parms try: try: ipautil.run(args, nolog=nologlist) except ipautil.CalledProcessError as e: root_logger.critical("Failed to load %s: %s" % (ldif, str(e))) if raise_on_err: raise finally: if pw_name: os.remove(pw_name) if fd is not None: fd.close()
def _ldap_mod(self, ldif, sub_dict=None, raise_on_err=False): pw_name = None fd = None path = ipautil.SHARE_DIR + ldif nologlist = [] if sub_dict is not None: txt = ipautil.template_file(path, sub_dict) fd = ipautil.write_tmp_file(txt) path = fd.name # do not log passwords if 'PASSWORD' in sub_dict: nologlist.append(sub_dict['PASSWORD']) if 'RANDOM_PASSWORD' in sub_dict: nologlist.append(sub_dict['RANDOM_PASSWORD']) args = [paths.LDAPMODIFY, "-v", "-f", path] # As we always connect to the local host, # use URI of admin connection if not self.admin_conn: self.ldap_connect() args += ["-H", self.admin_conn.ldap_uri] # If DM password is available, use it if self.dm_password: [pw_fd, pw_name] = tempfile.mkstemp() os.write(pw_fd, self.dm_password) os.close(pw_fd) auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name] # Use GSSAPI auth when not using DM password or not being root elif os.getegid() != 0: auth_parms = ["-Y", "GSSAPI"] # Default to EXTERNAL auth mechanism else: auth_parms = ["-Y", "EXTERNAL"] args += auth_parms try: try: ipautil.run(args, nolog=nologlist) except ipautil.CalledProcessError as e: if raise_on_err: raise root_logger.critical("Failed to load %s: %s" % (ldif, str(e))) finally: if pw_name: os.remove(pw_name) if fd is not None: fd.close()
def _ldap_mod(self, ldif, sub_dict=None, raise_on_err=True, ldap_uri=None, dm_password=None): pw_name = None fd = None path = os.path.join(paths.USR_SHARE_IPA_DIR, ldif) nologlist = [] if sub_dict is not None: txt = ipautil.template_file(path, sub_dict) fd = ipautil.write_tmp_file(txt) path = fd.name # do not log passwords if 'PASSWORD' in sub_dict: nologlist.append(sub_dict['PASSWORD']) if 'RANDOM_PASSWORD' in sub_dict: nologlist.append(sub_dict['RANDOM_PASSWORD']) args = [paths.LDAPMODIFY, "-v", "-f", path] # As we always connect to the local host, # use URI of admin connection if not ldap_uri: ldap_uri = api.Backend.ldap2.ldap_uri args += ["-H", ldap_uri] if dm_password: with tempfile.NamedTemporaryFile( mode='w', delete=False) as pw_file: pw_file.write(dm_password) pw_name = pw_file.name auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name] # Use GSSAPI auth when not using DM password or not being root elif os.getegid() != 0: auth_parms = ["-Y", "GSSAPI"] # Default to EXTERNAL auth mechanism else: auth_parms = ["-Y", "EXTERNAL"] args += auth_parms try: try: ipautil.run(args, nolog=nologlist) except ipautil.CalledProcessError as e: root_logger.critical("Failed to load %s: %s" % (ldif, str(e))) if raise_on_err: raise finally: if pw_name: os.remove(pw_name)
def __setup_principal(self): assert self.ods_uid is not None for f in [paths.IPA_ODS_EXPORTER_CCACHE, paths.IPA_ODS_EXPORTER_KEYTAB]: try: os.remove(f) except OSError: pass 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, 0o440) 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 as 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 as e: root_logger.critical("Could not set principal's %s LDAP limits: %s" % (dns_exporter_principal_dn, str(e))) raise
def set_subject_in_config(realm_name, dm_password, suffix, subject_base): ldapuri = 'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % ( installutils.realm_to_serverid(realm_name) ) try: conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn=suffix) conn.connect(bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password) except errors.ExecutionError, e: root_logger.critical("Could not connect to the Directory Server " "on %s" % realm_name) raise e
def __enable_compat_tree(self): try: compat_plugin_dn = DN("cn=Schema Compatibility,cn=plugins,cn=config") lookup_nsswitch_name = "schema-compat-lookup-nsswitch" for config in (("cn=users", "user"), ("cn=groups", "group")): entry_dn = DN(config[0], compat_plugin_dn) current = self.admin_conn.get_entry(entry_dn) lookup_nsswitch = current.get(lookup_nsswitch_name, []) if not(config[1] in lookup_nsswitch): current[lookup_nsswitch_name] = [config[1]] self.admin_conn.update_entry(current) except Exception as e: root_logger.critical("Enabling nsswitch support in slapi-nis failed with error '%s'" % e)
def restart(self, instance=''): api.Backend.ldap2.disconnect() try: super(DsInstance, self).restart(instance) if not is_ds_running(instance): root_logger.critical("Failed to restart the directory server. See the installation log for details.") raise ScriptError() except SystemExit as e: raise e except Exception as e: # TODO: roll back here? root_logger.critical("Failed to restart the directory server (%s). See the installation log for details." % e) api.Backend.ldap2.connect()
def __setup_principal(self): dns_principal = "DNS/" + self.fqdn + "@" + self.realm installutils.kadmin_addprinc(dns_principal) # Store the keytab on disk self.fstore.backup_file(paths.NAMED_KEYTAB) installutils.create_keytab(paths.NAMED_KEYTAB, dns_principal) p = self.move_service(dns_principal) if p is None: # the service has already been moved, perhaps we're doing a DNS reinstall dns_principal = DN(('krbprincipalname', dns_principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) else: dns_principal = p # Make sure access is strictly reserved to the named user pent = pwd.getpwnam(self.named_user) os.chown(paths.NAMED_KEYTAB, pent.pw_uid, pent.pw_gid) os.chmod(paths.NAMED_KEYTAB, 0o400) # modify the principal so that it is marked as an ipa service so that # it can host the memberof attribute, then also add it to the # dnsserver role group, this way the DNS is allowed to perform # DNS Updates dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'), ('cn', 'pbac'), self.suffix) mod = [(ldap.MOD_ADD, 'member', dns_principal)] try: self.admin_conn.modify_s(dns_group, mod) except ldap.TYPE_OR_VALUE_EXISTS: pass except Exception as e: root_logger.critical("Could not modify principal's %s entry: %s" \ % (dns_principal, str(e))) raise # bind-dyndb-ldap persistent search feature requires both size and time # 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_principal, mod) except Exception as e: root_logger.critical("Could not set principal's %s LDAP limits: %s" \ % (dns_principal, str(e))) raise
def add_cert_to_service(self): """ Add a certificate to a service This server cert should be in DER format. """ dn = DN(('krbprincipalname', self.principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) entry = api.Backend.ldap2.get_entry(dn) entry.setdefault('userCertificate', []).append(self.dercert) try: api.Backend.ldap2.update_entry(entry) except Exception as e: root_logger.critical("Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
def restart(self, instance=""): api.Backend.ldap2.disconnect() try: super(DsInstance, self).restart(instance) if not is_ds_running(instance): root_logger.critical("Failed to restart the directory server. See the installation log for details.") raise ScriptError() except SystemExit as e: raise e except Exception as e: # TODO: roll back here? root_logger.critical( "Failed to restart the directory server (%s). See the installation log for details." % e ) api.Backend.ldap2.connect()
def __setup_principal(self): dns_principal = "DNS/" + self.fqdn + "@" + self.realm installutils.kadmin_addprinc(dns_principal) # Store the keytab on disk self.fstore.backup_file(paths.NAMED_KEYTAB) installutils.create_keytab(paths.NAMED_KEYTAB, dns_principal) p = self.move_service(dns_principal) if p is None: # the service has already been moved, perhaps we're doing a DNS reinstall dns_principal = DN(('krbprincipalname', dns_principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) else: dns_principal = p # Make sure access is strictly reserved to the named user pent = pwd.getpwnam(self.named_user) os.chown(paths.NAMED_KEYTAB, pent.pw_uid, pent.pw_gid) os.chmod(paths.NAMED_KEYTAB, 0o400) # modify the principal so that it is marked as an ipa service so that # it can host the memberof attribute, then also add it to the # dnsserver role group, this way the DNS is allowed to perform # DNS Updates dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'), ('cn', 'pbac'), self.suffix) mod = [(ldap.MOD_ADD, 'member', dns_principal)] try: self.admin_conn.modify_s(dns_group, mod) except ldap.TYPE_OR_VALUE_EXISTS: pass except Exception as e: root_logger.critical("Could not modify principal's %s entry: %s" \ % (dns_principal, str(e))) raise # bind-dyndb-ldap persistent search feature requires both size and time # 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_principal, mod) except Exception as e: root_logger.critical("Could not set principal's %s LDAP limits: %s" \ % (dns_principal, str(e))) raise
def set_subject_in_config(realm_name, dm_password, suffix, subject_base): ldapuri = 'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % ( installutils.realm_to_serverid(realm_name)) try: conn = ldap2(api, ldap_uri=ldapuri) conn.connect(bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password) except errors.ExecutionError as e: root_logger.critical("Could not connect to the Directory Server " "on %s" % realm_name) raise e entry_attrs = conn.get_ipa_config() if 'ipacertificatesubjectbase' not in entry_attrs: entry_attrs['ipacertificatesubjectbase'] = [str(subject_base)] conn.update_entry(entry_attrs) conn.disconnect()
def __enable_compat_tree(self): try: compat_plugin_dn = DN( "cn=Schema Compatibility,cn=plugins,cn=config") lookup_nsswitch_name = "schema-compat-lookup-nsswitch" for config in (("cn=users", "user"), ("cn=groups", "group")): entry_dn = DN(config[0], compat_plugin_dn) current = api.Backend.ldap2.get_entry(entry_dn) lookup_nsswitch = current.get(lookup_nsswitch_name, []) if not (config[1] in lookup_nsswitch): current[lookup_nsswitch_name] = [config[1]] api.Backend.ldap2.update_entry(current) except Exception as e: root_logger.critical( "Enabling nsswitch support in slapi-nis failed with error '%s'" % e)
def add_cert_to_service(self): """ Add a certificate to a service This server cert should be in DER format. """ dn = DN(('krbprincipalname', self.principal), ('cn', 'services'), ('cn', 'accounts'), self.suffix) entry = api.Backend.ldap2.get_entry(dn) entry.setdefault('userCertificate', []).append(self.dercert) try: api.Backend.ldap2.update_entry(entry) except Exception as e: root_logger.critical( "Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
def set_subject_in_config(realm_name, dm_password, suffix, subject_base): ldapuri = 'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % ( installutils.realm_to_serverid(realm_name) ) try: conn = ldap2(api, ldap_uri=ldapuri) conn.connect(bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password) except errors.ExecutionError as e: root_logger.critical("Could not connect to the Directory Server " "on %s" % realm_name) raise e entry_attrs = conn.get_ipa_config() if 'ipacertificatesubjectbase' not in entry_attrs: entry_attrs['ipacertificatesubjectbase'] = [str(subject_base)] conn.update_entry(entry_attrs) conn.disconnect()
def __configure_sasl_mappings(self): # we need to remove any existing SASL mappings in the directory as otherwise they # they may conflict. try: res = api.Backend.ldap2.get_entries( DN(('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')), api.Backend.ldap2.SCOPE_ONELEVEL, "(objectclass=nsSaslMapping)") for r in res: try: api.Backend.ldap2.delete_entry(r) except Exception as e: root_logger.critical( "Error during SASL mapping removal: %s", e) raise except Exception as e: root_logger.critical("Error while enumerating SASL mappings %s", e) raise entry = api.Backend.ldap2.make_entry( DN( ('cn', 'Full Principal'), ('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')), objectclass=["top", "nsSaslMapping"], cn=["Full Principal"], nsSaslMapRegexString=['\(.*\)@\(.*\)'], nsSaslMapBaseDNTemplate=[self.suffix], nsSaslMapFilterTemplate=['(krbPrincipalName=\\1@\\2)'], nsSaslMapPriority=['10'], ) api.Backend.ldap2.add_entry(entry) entry = api.Backend.ldap2.make_entry( DN( ('cn', 'Name Only'), ('cn', 'mapping'), ('cn', 'sasl'), ('cn', 'config')), objectclass=["top", "nsSaslMapping"], cn=["Name Only"], nsSaslMapRegexString=['^[^:@]+$'], nsSaslMapBaseDNTemplate=[self.suffix], nsSaslMapFilterTemplate=['(krbPrincipalName=&@%s)' % self.realm], nsSaslMapPriority=['10'], ) api.Backend.ldap2.add_entry(entry)
def __setup_principal(self): try: api.Command.service_add(unicode(self.cifs_principal)) except errors.DuplicateEntry: # CIFS principal already exists, it is not the first time # adtrustinstance is managed # That's fine, we we'll re-extract the key again. pass except Exception as e: self.print_msg("Cannot add CIFS service: %s" % e) self.clean_samba_keytab() try: ipautil.run(["ipa-getkeytab", "--server", self.fqdn, "--principal", self.cifs_principal, "-k", self.samba_keytab]) except ipautil.CalledProcessError: root_logger.critical("Failed to add key for %s" % self.cifs_principal)
def __configure_sasl_mappings(self): # we need to remove any existing SASL mappings in the directory as otherwise they # they may conflict. try: res = api.Backend.ldap2.get_entries( DN(("cn", "mapping"), ("cn", "sasl"), ("cn", "config")), api.Backend.ldap2.SCOPE_ONELEVEL, "(objectclass=nsSaslMapping)", ) for r in res: try: api.Backend.ldap2.delete_entry(r) except Exception as e: root_logger.critical("Error during SASL mapping removal: %s", e) raise except Exception as e: root_logger.critical("Error while enumerating SASL mappings %s", e) raise entry = api.Backend.ldap2.make_entry( DN(("cn", "Full Principal"), ("cn", "mapping"), ("cn", "sasl"), ("cn", "config")), objectclass=["top", "nsSaslMapping"], cn=["Full Principal"], nsSaslMapRegexString=["\(.*\)@\(.*\)"], nsSaslMapBaseDNTemplate=[self.suffix], nsSaslMapFilterTemplate=["(krbPrincipalName=\\1@\\2)"], nsSaslMapPriority=["10"], ) api.Backend.ldap2.add_entry(entry) entry = api.Backend.ldap2.make_entry( DN(("cn", "Name Only"), ("cn", "mapping"), ("cn", "sasl"), ("cn", "config")), objectclass=["top", "nsSaslMapping"], cn=["Name Only"], nsSaslMapRegexString=["^[^:@]+$"], nsSaslMapBaseDNTemplate=[self.suffix], nsSaslMapFilterTemplate=["(krbPrincipalName=&@%s)" % self.realm], nsSaslMapPriority=["10"], ) api.Backend.ldap2.add_entry(entry)
def __check_replica(self): try: cifs_services = DN(api.env.container_service, self.suffix) # Search for cifs services which also belong to adtrust agents, these are our DCs res = self.admin_conn.get_entries(cifs_services, ldap.SCOPE_ONELEVEL, "(&(krbprincipalname=cifs/*@%s)(memberof=%s))" % (self.realm, str(self.smb_dn))) if len(res) > 1: # there are other CIFS services defined, we are not alone for entry in res: managedBy = entry.single_value.get('managedBy') if managedBy: fqdn = DN(managedBy)['fqdn'] if fqdn != unicode(self.fqdn): # this is CIFS service of a different host in our # REALM, we need to remember it to announce via # SRV records for _msdcs self.cifs_hosts.append(normalize_zone(fqdn)) except Exception as e: root_logger.critical("Checking replicas for cifs principals failed with error '%s'" % e)
def __setup_principal(self): try: api.Command.service_add(unicode(self.principal)) except errors.DuplicateEntry: # CIFS principal already exists, it is not the first time # adtrustinstance is managed # That's fine, we we'll re-extract the key again. pass except Exception as e: self.print_msg("Cannot add CIFS service: %s" % e) self.clean_samba_keytab() installutils.remove_ccache(paths.KRB5CC_SAMBA) try: ipautil.run([ "ipa-getkeytab", "--server", self.fqdn, "--principal", self.principal, "-k", self.keytab ]) except ipautil.CalledProcessError: root_logger.critical("Failed to add key for %s" % self.principal)
def enable_ssl(self): """ generate PKINIT certificate for KDC. If `--no-pkinit` was specified, only configure local self-signed KDC certificate for use as a FAST channel generator for WebUI. Do not advertise the installation steps in this case. """ if self.master_fqdn is not None: self._wait_for_replica_kdc_entry() if self.config_pkinit: self.steps = [] self.step("installing X509 Certificate for PKINIT", self.setup_pkinit) self.start_creation() else: self.issue_selfsigned_pkinit_certs() try: self.restart() except Exception: root_logger.critical("krb5kdc service failed to restart") raise
def enable_ssl(self): """ generate PKINIT certificate for KDC. If `--no-pkinit` was specified, only configure local self-signed KDC certificate for use as a FAST channel generator for WebUI. Do not advertise the installation steps in this case. """ if self.master_fqdn is not None: self._wait_for_replica_kdc_entry() if self.config_pkinit: self.steps = [] self.step("installing X509 Certificate for PKINIT", self.setup_pkinit) self.start_creation() else: self.issue_selfsigned_pkinit_certs() try: self.restart() except Exception: root_logger.critical("krb5kdc service failed to restart") raise
def __check_replica(self): try: cifs_services = DN(api.env.container_service, self.suffix) # Search for cifs services which also belong to adtrust agents, these are our DCs res = api.Backend.ldap2.get_entries( cifs_services, ldap.SCOPE_ONELEVEL, "(&(krbprincipalname=cifs/*@%s)(memberof=%s))" % (self.realm, str(self.smb_dn))) if len(res) > 1: # there are other CIFS services defined, we are not alone for entry in res: managedBy = entry.single_value.get('managedBy') if managedBy: fqdn = DN(managedBy)['fqdn'] if fqdn != unicode(self.fqdn): # this is CIFS service of a different host in our # REALM, we need to remember it to announce via # SRV records for _msdcs self.cifs_hosts.append(normalize_zone(fqdn)) except Exception as e: root_logger.critical( "Checking replicas for cifs principals failed with error '%s'" % e)
def add_ca_cert(self, cacert_fname, cacert_name=''): """Add a CA certificate to the directory server cert db. We first have to shut down the directory server in case it has opened the cert db read-only. Then we use the CertDB class to add the CA cert. We have to provide a nickname, and we do not use 'IPA CA' since that's the default, so we use 'Imported CA' if none specified. Then we restart the server.""" # first make sure we have a valid cacert_fname try: if not os.access(cacert_fname, os.R_OK): root_logger.critical( "The given CA cert file named [%s] could not be read" % cacert_fname) return False except OSError as e: root_logger.critical( "The given CA cert file named [%s] could not be read: %s" % (cacert_fname, str(e))) return False # ok - ca cert file can be read # shutdown the server self.stop() dirname = config_dirname(installutils.realm_to_serverid(self.realm)) certdb = certs.CertDB( self.realm, nssdir=dirname, subject_base=self.subject_base, ca_subject=self.ca_subject, ) if not cacert_name or len(cacert_name) == 0: cacert_name = "Imported CA" # we can't pass in the nickname, so we set the instance variable certdb.cacert_name = cacert_name status = True try: certdb.load_cacert(cacert_fname, 'C,,') except ipautil.CalledProcessError as e: root_logger.critical( "Error importing CA cert file named [%s]: %s" % (cacert_fname, str(e))) status = False # restart the directory server self.start() return status
def add_ca_cert(self, cacert_fname, cacert_name=""): """Add a CA certificate to the directory server cert db. We first have to shut down the directory server in case it has opened the cert db read-only. Then we use the CertDB class to add the CA cert. We have to provide a nickname, and we do not use 'IPA CA' since that's the default, so we use 'Imported CA' if none specified. Then we restart the server.""" # first make sure we have a valid cacert_fname try: if not os.access(cacert_fname, os.R_OK): root_logger.critical("The given CA cert file named [%s] could not be read" % cacert_fname) return False except OSError as e: root_logger.critical("The given CA cert file named [%s] could not be read: %s" % (cacert_fname, str(e))) return False # ok - ca cert file can be read # shutdown the server self.stop() dirname = config_dirname(installutils.realm_to_serverid(self.realm)) certdb = certs.CertDB(self.realm, nssdir=dirname, subject_base=self.subject_base) if not cacert_name or len(cacert_name) == 0: cacert_name = "Imported CA" # we can't pass in the nickname, so we set the instance variable certdb.cacert_name = cacert_name status = True try: certdb.load_cacert(cacert_fname, "C,,") except ipautil.CalledProcessError as e: root_logger.critical("Error importing CA cert file named [%s]: %s" % (cacert_fname, str(e))) status = False # restart the directory server self.start() return status
self.smb_dn, objectclass=["top", "GroupOfNames"], cn=[self.smb_dn['cn']], member=[self.cifs_agent], ) self.admin_conn.add_entry(entry) self.clean_samba_keytab() try: ipautil.run([ "ipa-getkeytab", "--server", self.fqdn, "--principal", self.cifs_principal, "-k", self.samba_keytab ]) except ipautil.CalledProcessError: root_logger.critical("Failed to add key for %s" % self.cifs_principal) def clean_samba_keytab(self): if os.path.exists(self.samba_keytab): try: ipautil.run([ "ipa-rmkeytab", "--principal", self.cifs_principal, "-k", self.samba_keytab ]) except ipautil.CalledProcessError, e: if e.returncode != 5: root_logger.critical("Failed to remove old key for %s" % self.cifs_principal) def srv_rec(self, host, port, prio): return "%(prio)d 100 %(port)d %(host)s" % dict(
def __start(self): try: self.start() services.service('winbind').start() except Exception: root_logger.critical("CIFS services failed to start")
entry = self.admin_conn.make_entry( self.smb_dn, objectclass=["top", "GroupOfNames"], cn=[self.smb_dn['cn']], member=[self.cifs_agent], ) self.admin_conn.add_entry(entry) self.clean_samba_keytab() try: ipautil.run(["ipa-getkeytab", "--server", self.fqdn, "--principal", self.cifs_principal, "-k", self.samba_keytab]) except ipautil.CalledProcessError: root_logger.critical("Failed to add key for %s" % self.cifs_principal) def clean_samba_keytab(self): if os.path.exists(self.samba_keytab): try: ipautil.run(["ipa-rmkeytab", "--principal", self.cifs_principal, "-k", self.samba_keytab]) except ipautil.CalledProcessError, e: if e.returncode != 5: root_logger.critical("Failed to remove old key for %s" % self.cifs_principal) def srv_rec(self, host, port, prio): return "%(prio)d 100 %(port)d %(host)s" % dict(host=host,prio=prio,port=port) def __add_dns_service_records(self):
def __start_instance(self): try: self.start() except Exception: root_logger.critical("krb5kdc service failed to start")
def __start(self): try: self.start() services.service('winbind', api).start() except Exception: root_logger.critical("CIFS services failed to start")
def __start_instance(self): try: self.start() except Exception: root_logger.critical("krb5kdc service failed to start")