def _validate_ds_2_config(self, config): assert_c(config.has_section('slapd'), "Missing configuration section [slapd]") # Extract them in a way that create can understand. general_options = General2Base(self.log) general_options.parse_inf_config(config) general_options.verify() general = general_options.collect() if self.verbose: self.log.info("Configuration general %s" % general) slapd_options = Slapd2Base(self.log) slapd_options.parse_inf_config(config) slapd_options.verify() slapd = slapd_options.collect() if self.verbose: self.log.info("Configuration slapd %s" % slapd) backends = [] for section in config.sections(): if section.startswith('backend-'): be = {} # TODO: Add the other BACKEND_ types be[BACKEND_NAME] = section.replace('backend-', '') be[BACKEND_SUFFIX] = config.get(section, 'suffix') be[BACKEND_SAMPLE_ENTRIES] = config.get(section, 'sample_entries') backends.append(be) if self.verbose: self.log.info("Configuration backends %s" % backends) return (general, slapd, backends)
def _validate_ds_config(self, config): # This will move to lib389 later. # Check we have all the sections. # Make sure we have needed keys. assert_c(config.has_section('general'), "Missing configuration section [general]") if config.get('general', 'config_version', fallback='2') == '2': # Call our child api to validate itself from the inf. self._validate_config_2(config) return self._validate_ds_2_config(config) else: assert_c(False, "Unsupported config_version in section [general]")
def _validate_ds_2_config(self, config): assert_c(config.has_section('slapd'), "Missing configuration section [slapd]") # Extract them in a way that create can understand. general_options = General2Base(self.log) general_options.parse_inf_config(config) general_options.verify() general = general_options.collect() self.log.debug("Configuration general %s", general) slapd_options = Slapd2Base(self.log) slapd_options.parse_inf_config(config) slapd_options.verify() slapd = slapd_options.collect() self.log.debug("Configuration slapd %s", slapd) backends = [] for section in config.sections(): if section.startswith('backend-'): backend_options = Backend2Base(self.log, section) backend_options.parse_inf_config(config) suffix = config.get(section, 'suffix', fallback='') if suffix != '': # Suffix be = {} be[BACKEND_NAME] = section.replace('backend-', '') be[BACKEND_SUFFIX] = suffix be['create_suffix_entry'] = config.get(section, 'create_suffix_entry', fallback=False) # Sample entries sample_entries = config.get(section, 'sample_entries', fallback='no') if sample_entries.lower() != 'no': if sample_entries.lower() == 'yes': be[BACKEND_SAMPLE_ENTRIES] = INSTALL_LATEST_CONFIG elif (sample_entries != '001003006' and sample_entries != '001004000'): # invalid value raise ValueError('Invalid value for sample_entries ({}), you must use "yes", "no", "001003006", or "001004000"'.format(sample_entries)) else: be[BACKEND_SAMPLE_ENTRIES] = sample_entries # Require index req_idx = config.getboolean(section, 'require_index', fallback=False) if req_idx: be[BACKEND_REQ_INDEX] = "on" # Add this backend to the list backends.append(be) self.log.debug("Configuration backends %s", backends) return (general, slapd, backends)
def _get_arg(args, msg=None, hidden=False, confirm=False): if args is not None and len(args) > 0: if type(args) is list: return args[0] else: return args else: if hidden: if confirm: x = getpass("%s : " % msg) y = getpass("CONFIRM - %s : " % msg) assert_c(x == y, "inputs do not match, aborting.") return y else: return getpass("%s : " % msg) else: return input("%s : " % msg)
def remove_ds_instance(dirsrv, force=False): """ This will delete the instance as it is define. This must be a local instance. This is designed to raise exceptions quickly and often if *any* error is hit. However, this can be run repeatedly, and only when the instance is truely removed, will this program fail to run further. :param dirsrv: A directory server instance :type dirsrv: DirSrv :param force: A psycological aid, for people who think force means do something, harder. Does literally nothing in this program because state machines are a thing. :type force: bool """ _log = dirsrv.log.getChild('remove_ds') _log.debug("Removing instance %s" % dirsrv.serverid) # Copy all the paths we are about to tamper with remove_paths = {} remove_paths['backup_dir'] = dirsrv.ds_paths.backup_dir remove_paths['cert_dir'] = dirsrv.ds_paths.cert_dir remove_paths['config_dir'] = dirsrv.ds_paths.config_dir remove_paths['db_dir'] = dirsrv.ds_paths.db_dir remove_paths['db_dir_parent'] = dirsrv.ds_paths.db_dir + "/../" ### WARNING: The changelogdb isn't removed. we assume it's in: # db_dir ../changelogdb. So remove that too! # abspath will resolve the ".." down. remove_paths['changelogdb_dir'] = dirsrv.get_changelog_dir() remove_paths['ldif_dir'] = dirsrv.ds_paths.ldif_dir remove_paths['lock_dir'] = dirsrv.ds_paths.lock_dir remove_paths['log_dir'] = dirsrv.ds_paths.log_dir # remove_paths['run_dir'] = dirsrv.ds_paths.run_dir remove_paths['inst_dir'] = dirsrv.ds_paths.inst_dir remove_paths['etc_sysconfig'] = "%s/sysconfig/dirsrv-%s" % ( dirsrv.ds_paths.sysconf_dir, dirsrv.serverid) tmpfiles_d_path = dirsrv.ds_paths.tmpfiles_d + "/dirsrv-" + dirsrv.serverid + ".conf" # These are handled in a special way. dse_ldif_path = os.path.join(dirsrv.ds_paths.config_dir, 'dse.ldif') # Check the marker exists. If it *does not* warn about this, and say that to # force removal you should touch this file. _log.debug("Checking for instance marker at %s" % dse_ldif_path) if not os.path.exists(dse_ldif_path): _log.info("Instance configuration not found, no action will be taken") _log.info("If you want us to cleanup anyway, recreate '%s'" % dse_ldif_path) return _log.debug("Found instance marker at %s! Proceeding to remove ..." % dse_ldif_path) # Stop the instance (if running) and now we know it really does exist # and hopefully have permission to access it ... _log.debug("Stopping instance %s" % dirsrv.serverid) dirsrv.stop() _log.debug("Found instance marker at %s! Proceeding to remove ..." % dse_ldif_path) # Stop the instance (if running) and now we know it really does exist # and hopefully have permission to access it ... _log.debug("Stopping instance %s" % dirsrv.serverid) dirsrv.stop() ### ANY NEW REMOVAL ACTION MUST BE BELOW THIS LINE!!! # Remove these paths: # for path in ('backup_dir', 'cert_dir', 'config_dir', 'db_dir', # 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir'): for path_k in remove_paths: _log.debug("Removing %s" % remove_paths[path_k]) shutil.rmtree(remove_paths[path_k], ignore_errors=True) # Remove parent (/var/lib/dirsrv/slapd-INST) shutil.rmtree(remove_paths['db_dir'].replace('db', ''), ignore_errors=True) # We can not assume we have systemd ... if dirsrv.ds_paths.with_systemd: # Remove the systemd symlink _log.debug("Removing the systemd symlink") subprocess.check_call( ["systemctl", "disable", "dirsrv@{}".format(dirsrv.serverid)]) _log.debug("Removing %s" % tmpfiles_d_path) shutil.rmtree(tmpfiles_d_path, ignore_errors=True) # Nor can we assume we have selinux. Try docker sometime ;) if dirsrv.ds_paths.with_selinux: # Remove selinux port label _log.debug("Removing the port labels") selinux_label_port(dirsrv.port, remove_label=True) # This is a compatability with ancient installs, all modern install have tls port if dirsrv.sslport is not None: selinux_label_port(dirsrv.sslport, remove_label=True) # If this was the last instance, remove the ssca instance insts = dirsrv.list(all=True) if len(insts) == 0: ssca = NssSsl(dbpath=dirsrv.get_ssca_dir()) ssca.remove_db() ### ANY NEW REMOVAL ACTIONS MUST BE ABOVE THIS LINE!!! # Finally means FINALLY, the last thing, the LAST LAST thing. By doing this absolutely # last, it means that we can have any failure above, and continue to re-run until resolved # because this instance marker (dse.ldif) continues to exist! # Move the config_dir to config_dir.removed config_dir = dirsrv.ds_paths.config_dir config_dir_rm = "{}.removed".format(config_dir) if os.path.exists(config_dir_rm): _log.debug("Removing previously existed %s" % config_dir_rm) shutil.rmtree(config_dir_rm) assert_c(not os.path.exists(config_dir_rm)) # That's it, everything before this MUST have suceeded, so now we can move the # config dir (containing dse.ldif, the marker) out of the way. _log.debug("Moving %s to %s" % (config_dir, config_dir_rm)) try: shutil.move(config_dir, config_dir_rm) except FileNotFoundError: pass # DO NOT PUT ANY CODE BELOW THIS COMMENT BECAUSE THAT WOULD VIOLATE THE ASSERTIONS OF THE # ABOVE CODE. # Done! _log.debug("Complete")
def _install_ds(self, general, slapd, backends): """ Actually install the Ds from the dicts provided. You should never call this directly, as it bypasses assertions. """ ######################## WARNING ############################# # DO NOT CHANGE THIS FUNCTION OR ITS CONTENTS WITHOUT READING # ALL OF THE COMMENTS FIRST. THERE ARE VERY DELICATE # AND DETAILED INTERACTIONS OF COMPONENTS IN THIS FUNCTION. # # IF IN DOUBT CONTACT WILLIAM BROWN <*****@*****.**> ### This first section is about creating the *minimal* required paths and config to get # directory server to start: After this, we then perform all configuration as online # changes from after this point. # Create dse.ldif with a temporary root password. # This is done first, because instances are found for removal and listing by detecting # the present of their dse.ldif!!!! # The template is in slapd['data_dir']/dirsrv/data/template-dse.ldif # Variables are done with %KEY%. self.log.debug("ACTION: Creating dse.ldif") try: os.umask( 0o007 ) # For parent dirs that get created -> sets 770 for perms os.makedirs(slapd['config_dir'], mode=0o770) except OSError: pass # Get suffix for some plugin defaults (if possible) # annoyingly for legacy compat backend takes TWO key types # and we have to now deal with that .... # # Create ds_suffix here else it won't be in scope .... ds_suffix = '' if len(backends) > 0: ds_suffix = normalizeDN(backends[0]['nsslapd-suffix']) dse = "" with open( os.path.join(slapd['data_dir'], 'dirsrv', 'data', 'template-dse.ldif')) as template_dse: for line in template_dse.readlines(): dse += line.replace('%', '{', 1).replace('%', '}', 1) # Check if we are in a container, if so don't use /dev/shm for the db home dir # as containers typically don't allocate enough space for dev/shm and we don't # want to unexpectedly break the server after an upgrade # # If we know we are are in a container, we don't need to re-detect on systemd. # It actually turns out if you add systemd-detect-virt, that pulls in system # which subsequently breaks containers starting as instance.start then believes # it COULD check the ds status. The times we need to check for systemd are mainly # in other environments that use systemd natively in their containers. container_result = 1 if not self.containerised: container_result = subprocess.run(["systemd-detect-virt", "-c"], stdout=subprocess.PIPE) if self.containerised or container_result.returncode == 0: # In a container, set the db_home_dir to the db path self.log.debug( "Container detected setting db home directory to db directory." ) slapd['db_home_dir'] = slapd['db_dir'] with open(os.path.join(slapd['config_dir'], 'dse.ldif'), 'w') as file_dse: dse_fmt = dse.format( schema_dir=slapd['schema_dir'], lock_dir=slapd['lock_dir'], tmp_dir=slapd['tmp_dir'], cert_dir=slapd['cert_dir'], ldif_dir=slapd['ldif_dir'], bak_dir=slapd['backup_dir'], run_dir=slapd['run_dir'], inst_dir=slapd['inst_dir'], log_dir=slapd['log_dir'], fqdn=general['full_machine_name'], ds_port=slapd['port'], ds_user=slapd['user'], rootdn=slapd['root_dn'], instance_name=slapd['instance_name'], ds_passwd=self. _secure_password, # We set our own password here, so we can connect and mod. # This is because we never know the users input root password as they can validly give # us a *hashed* input. ds_suffix=ds_suffix, config_dir=slapd['config_dir'], db_dir=slapd['db_dir'], db_home_dir=slapd['db_home_dir'], db_lib=slapd['db_lib'], ldapi_enabled="on", ldapi=slapd['ldapi'], ldapi_autobind="on", ) file_dse.write(dse_fmt) self.log.info("Create file system structures ...") # Create all the needed paths # we should only need to make bak_dir, cert_dir, config_dir, db_dir, ldif_dir, lock_dir, log_dir, run_dir? for path in ('backup_dir', 'cert_dir', 'db_dir', 'db_home_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir'): self.log.debug("ACTION: creating %s", slapd[path]) try: os.umask( 0o007 ) # For parent dirs that get created -> sets 770 for perms os.makedirs(slapd[path], mode=0o770) except OSError: pass os.chown(slapd[path], slapd['user_uid'], slapd['group_gid']) # /var/lock/dirsrv needs special attention... parentdir = os.path.abspath(os.path.join(slapd['lock_dir'], os.pardir)) os.chown(parentdir, slapd['user_uid'], slapd['group_gid']) ### Warning! We need to down the directory under db too for .restore to work. # During a restore, the db dir is deleted and recreated, which is why we need # to own it for a restore. # # However, in a container, we can't always guarantee this due to how the volumes # work and are mounted. Specifically, if we have an anonymous volume we will # NEVER be able to own it, but in a true deployment it is reasonable to expect # we DO own it. Thus why we skip it in this specific context if not self.containerised: db_parent = os.path.join(slapd['db_dir'], '..') os.chown(db_parent, slapd['user_uid'], slapd['group_gid']) # Copy correct data to the paths. # Copy in the schema # This is a little fragile, make it better. # It won't matter when we move schema to usr anyway ... _ds_shutil_copytree( os.path.join(slapd['sysconf_dir'], 'dirsrv/schema'), slapd['schema_dir']) os.chown(slapd['schema_dir'], slapd['user_uid'], slapd['group_gid']) os.chmod(slapd['schema_dir'], 0o770) # Copy in the collation srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/slapd-collations.conf') dstfile = os.path.join(slapd['config_dir'], 'slapd-collations.conf') shutil.copy(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) os.chmod(dstfile, 0o440) # Copy in the certmap configuration srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/certmap.conf') dstfile = os.path.join(slapd['config_dir'], 'certmap.conf') shutil.copy(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) os.chmod(dstfile, 0o440) # If we are on the correct platform settings, systemd if general['systemd']: # Should create the symlink we need, but without starting it. result = subprocess.run( ["systemctl", "enable", "dirsrv@%s" % slapd['instance_name']], stdout=subprocess.PIPE, stderr=subprocess.PIPE) args = ' '.join(ensure_list_str(result.args)) stdout = ensure_str(result.stdout) stderr = ensure_str(result.stderr) # Systemd encodes some odd charecters into it's symlink output on newer versions which # can trip up the logger. self.log.debug( f"CMD: {args} ; STDOUT: {stdout} ; STDERR: {stderr}".encode( "utf-8")) # Setup tmpfiles_d tmpfile_d = ds_paths.tmpfiles_d + "/dirsrv-" + slapd[ 'instance_name'] + ".conf" with open(tmpfile_d, "w") as TMPFILE_D: TMPFILE_D.write("d {} 0770 {} {}\n".format( slapd['run_dir'], slapd['user'], slapd['group'])) TMPFILE_D.write("d {} 0770 {} {}\n".format( slapd['lock_dir'].replace( "slapd-" + slapd['instance_name'], ""), slapd['user'], slapd['group'])) TMPFILE_D.write("d {} 0770 {} {}\n".format( slapd['lock_dir'], slapd['user'], slapd['group'])) # Else we need to detect other init scripts? # WB: No, we just install and assume that docker will start us ... # Bind sockets to our type? # Create certdb in sysconfidir self.log.debug("ACTION: Creating certificate database is %s", slapd['cert_dir']) # BELOW THIS LINE - all actions are now ONLINE changes to the directory server. # if it all possible, ALWAYS ADD NEW INSTALLER CHANGES AS ONLINE ACTIONS. # Should I move this import? I think this prevents some recursion from lib389 import DirSrv ds_instance = DirSrv(self.verbose, containerised=self.containerised) if self.containerised: ds_instance.systemd_override = general['systemd'] # By default SUSE does something extremely silly - it creates a hostname # that CANT be resolved by DNS. As a result this causes all installs to # fail. We need to guarantee that we only connect to localhost here, as # it's the only stable and guaranteed way to connect to the instance # at this point. # # Use ldapi which would prevent the need # to configure a temp root pw in the setup phase. args = { SER_HOST: "localhost", SER_PORT: slapd['port'], SER_SERVERID_PROP: slapd['instance_name'], SER_ROOT_DN: slapd['root_dn'], SER_ROOT_PW: self._raw_secure_password, SER_DEPLOYED_DIR: slapd['prefix'], SER_LDAPI_ENABLED: 'on', SER_LDAPI_SOCKET: slapd['ldapi'], SER_LDAPI_AUTOBIND: 'on' } ds_instance.allocate(args) # Does this work? assert_c(ds_instance.exists(), "Instance failed to install, does not exist when expected") # Create a certificate database. tlsdb = NssSsl(dirsrv=ds_instance, dbpath=slapd['cert_dir']) if not tlsdb._db_exists(): tlsdb.reinit() if slapd['self_sign_cert']: self.log.info("Create self-signed certificate database ...") etc_dirsrv_path = os.path.join(slapd['sysconf_dir'], 'dirsrv/') ssca_path = os.path.join(etc_dirsrv_path, 'ssca/') ssca = NssSsl(dbpath=ssca_path) # If it doesn't exist, create a CA DB if not ssca._db_exists(): ssca.reinit() ssca.create_rsa_ca(months=slapd['self_sign_cert_valid_months']) # If CA is expired or will expire soon, # Reissue it and resign the existing certs that were signed by the cert previously elif ssca.rsa_ca_needs_renew(): ca = ssca.renew_rsa_ca( months=slapd['self_sign_cert_valid_months']) # Import CA to the existing instances except the one we install now (we import it later) for dir in os.listdir(etc_dirsrv_path): if dir.startswith("slapd-") and dir != slapd['cert_dir']: tlsdb_inst = NssSsl( dbpath=os.path.join(etc_dirsrv_path, dir)) tlsdb_inst.import_rsa_crt(ca) csr = tlsdb.create_rsa_key_and_csr( alt_names=[general['full_machine_name']]) (ca, crt) = ssca.rsa_ca_sign_csr(csr) tlsdb.import_rsa_crt(ca, crt) if general['selinux']: # Set selinux port label selinux_label_port(slapd['secure_port']) # Do selinux fixups if general['selinux']: self.log.info("Perform SELinux labeling ...") selinux_paths = ('backup_dir', 'cert_dir', 'config_dir', 'db_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'db_home_dir', 'run_dir', 'schema_dir', 'tmp_dir') for path in selinux_paths: selinux_restorecon(slapd[path]) selinux_label_port(slapd['port']) # Start the server # Make changes using the temp root self.log.debug(f"asan_enabled={ds_instance.has_asan()}") self.log.debug( f"libfaketime installed ={'libfaketime' in sys.modules}") assert_c( not ds_instance.has_asan() or 'libfaketime' not in sys.modules, "libfaketime python module is incompatible with ASAN build.") ds_instance.start(timeout=60) ds_instance.open() # In some cases we may want to change log settings # ds_instance.config.enable_log('audit') # Create the configs related to this version. base_config = get_config(general['defaults']) base_config_inst = base_config(ds_instance) base_config_inst.apply_config(install=True) # Setup TLS with the instance. # We *ALWAYS* set secure port, even if security is off, because it breaks # tests with standalone.enable_tls if we do not. It's only when security; on # that we actually start listening on it. if not slapd['secure_port']: slapd['secure_port'] = "636" ds_instance.config.set('nsslapd-secureport', '%s' % slapd['secure_port']) if slapd['self_sign_cert']: ds_instance.config.set('nsslapd-security', 'on') # Before we create any backends, create any extra default indexes that may be # dynamically provisioned, rather than from template-dse.ldif. Looking at you # entryUUID (requires rust enabled). # # Indexes defaults to default_index_dn indexes = Indexes(ds_instance) if ds_instance.ds_paths.rust_enabled: indexes.create( properties={ 'cn': 'entryUUID', 'nsSystemIndex': 'false', 'nsIndexType': ['eq', 'pres'], }) # Create the backends as listed # Load example data if needed. for backend in backends: self.log.info( f"Create database backend: {backend['nsslapd-suffix']} ...") is_sample_entries_in_props = "sample_entries" in backend create_suffix_entry_in_props = backend.pop('create_suffix_entry', False) ds_instance.backends.create(properties=backend) if not is_sample_entries_in_props and create_suffix_entry_in_props: # Set basic ACIs c_aci = '(targetattr="c || description || objectClass")(targetfilter="(objectClass=country)")(version 3.0; acl "Enable anyone c read"; allow (read, search, compare)(userdn="ldap:///anyone");)' o_aci = '(targetattr="o || description || objectClass")(targetfilter="(objectClass=organization)")(version 3.0; acl "Enable anyone o read"; allow (read, search, compare)(userdn="ldap:///anyone");)' dc_aci = '(targetattr="dc || description || objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search, compare)(userdn="ldap:///anyone");)' ou_aci = '(targetattr="ou || description || objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compare)(userdn="ldap:///anyone");)' cn_aci = '(targetattr="cn || description || objectClass")(targetfilter="(objectClass=nscontainer)")(version 3.0; acl "Enable anyone cn read"; allow (read, search, compare)(userdn="ldap:///anyone");)' suffix_rdn_attr = backend['nsslapd-suffix'].split( '=')[0].lower() if suffix_rdn_attr == 'dc': domain = create_base_domain(ds_instance, backend['nsslapd-suffix']) domain.add('aci', dc_aci) elif suffix_rdn_attr == 'o': org = create_base_org(ds_instance, backend['nsslapd-suffix']) org.add('aci', o_aci) elif suffix_rdn_attr == 'ou': orgunit = create_base_orgunit(ds_instance, backend['nsslapd-suffix']) orgunit.add('aci', ou_aci) elif suffix_rdn_attr == 'cn': cn = create_base_cn(ds_instance, backend['nsslapd-suffix']) cn.add('aci', cn_aci) elif suffix_rdn_attr == 'c': c = create_base_c(ds_instance, backend['nsslapd-suffix']) c.add('aci', c_aci) else: # Unsupported rdn raise ValueError( "Suffix RDN '{}' in '{}' is not supported. Supported RDN's are: 'c', 'cn', 'dc', 'o', and 'ou'" .format(suffix_rdn_attr, backend['nsslapd-suffix'])) # Create all required sasl maps: if we have a single backend ... # our default maps are really really bad, and we should feel bad. # they basically only work with a single backend, and they'll break # GSSAPI in some cases too :( if len(backends) > 0: self.log.debug("Adding sasl maps for suffix %s" % backend['nsslapd-suffix']) backend = backends[0] saslmappings = SaslMappings(ds_instance) saslmappings.create( properties={ 'cn': 'rfc 2829 u syntax', 'nsSaslMapRegexString': '^u:\\(.*\\)', 'nsSaslMapBaseDNTemplate': backend['nsslapd-suffix'], 'nsSaslMapFilterTemplate': '(uid=\\1)' }) # I think this is for LDAPI saslmappings.create( properties={ 'cn': 'uid mapping', 'nsSaslMapRegexString': '^[^:@]+$', 'nsSaslMapBaseDNTemplate': backend['nsslapd-suffix'], 'nsSaslMapFilterTemplate': '(uid=&)' }) else: self.log.debug("Skipping default SASL maps - no backend found!") self.log.info("Perform post-installation tasks ...") # Change the root password finally ds_instance.config.set('nsslapd-rootpw', slapd['root_password']) # We need to log the password when containerised if self.containerised: self.log.debug("Root DN password: {}".format( slapd['root_password'])) # Complete. if general['start']: # Restart for changes to take effect - this could be removed later ds_instance.restart(post_open=False) else: # Just stop the instance now. ds_instance.stop() self.log.debug(" 🎉 Instance setup complete")
def _prepare_ds(self, general, slapd, backends): self.log.info("Validate installation settings ...") assert_c(general['defaults'] is not None, "Configuration defaults in section [general] not found") self.log.debug("PASSED: using config settings %s" % general['defaults']) # Validate our arguments. assert_c(slapd['user'] is not None, "Configuration user in section [slapd] not found") # check the user exists assert_c(pwd.getpwnam(slapd['user']), "user %s not found on system" % slapd['user']) slapd['user_uid'] = pwd.getpwnam(slapd['user']).pw_uid assert_c(slapd['group'] is not None, "Configuration group in section [slapd] not found") assert_c(grp.getgrnam(slapd['group']), "group %s not found on system" % slapd['group']) slapd['group_gid'] = grp.getgrnam(slapd['group']).gr_gid # check this group exists # Check that we are running as this user / group, or that we are root. assert_c( os.geteuid() == 0 or getpass.getuser() == slapd['user'], "Not running as user root or %s, may not have permission to continue" % slapd['user']) self.log.debug("PASSED: user / group checking") assert_c( general['full_machine_name'] is not None, "Configuration full_machine_name in section [general] not found") assert_c( general['strict_host_checking'] is not None, "Configuration strict_host_checking in section [general] not found" ) if general['strict_host_checking'] is True: # Check it resolves with dns assert_c( socket.gethostbyname(general['full_machine_name']), "Strict hostname check failed. Check your DNS records for %s" % general['full_machine_name']) self.log.debug("PASSED: Hostname strict checking") assert_c(slapd['prefix'] is not None, "Configuration prefix in section [slapd] not found") if (slapd['prefix'] != ""): assert_c(os.path.exists(slapd['prefix']), "Prefix location '%s' not found" % slapd['prefix']) self.log.debug("PASSED: prefix checking") # We need to know the prefix before we can do the instance checks assert_c(slapd['instance_name'] is not None, "Configuration instance_name in section [slapd] not found") assert_c( len(slapd['instance_name']) <= 80, "Server identifier should not be longer than 80 symbols") assert_c(all(ord(c) < 128 for c in slapd['instance_name']), "Server identifier can not contain non ascii characters") assert_c(' ' not in slapd['instance_name'], "Server identifier can not contain a space") assert_c( slapd['instance_name'] != 'admin', "Server identifier \"admin\" is reserved, please choose a different identifier" ) # Check that valid characters are used safe = re.compile(r'^[:\w@_-]+$').search assert_c( bool(safe(slapd['instance_name'])), "Server identifier has invalid characters, please choose a different value" ) # Check if the instance exists or not. # Should I move this import? I think this prevents some recursion from lib389 import DirSrv ds = DirSrv(verbose=self.verbose) ds.containerised = self.containerised ds.prefix = slapd['prefix'] insts = ds.list(serverid=slapd['instance_name']) assert_c( len(insts) == 0, "Another instance named '%s' may already exist" % slapd['instance_name']) self.log.debug("PASSED: instance checking") assert_c(slapd['root_dn'] is not None, "Configuration root_dn in section [slapd] not found") # Assert this is a valid DN assert_c(is_a_dn(slapd['root_dn']), "root_dn in section [slapd] is not a well formed LDAP DN") assert_c( slapd['root_password'] is not None and slapd['root_password'] != '', "Configuration attribute 'root_password' in section [slapd] not found" ) if len(slapd['root_password']) < 8: raise ValueError( "root_password must be at least 8 characters long") # Check if pre-hashed or not. # !!!!!!!!!!!!!! # Right now, the way that rootpw works on ns-slapd works, it force hashes the pw # see https://fedorahosted.org/389/ticket/48859 if not re.match('^([A-Z0-9]+).*$', slapd['root_password']): # We need to hash it. Call pwdhash-bin. # slapd['root_password'] = password_hash(slapd['root_password'], prefix=slapd['prefix']) pass else: pass # Create a random string # Hash it. # This will be our temporary rootdn password so that we can do # live mods and setup rather than static ldif manipulations. self._raw_secure_password = password_generate() self._secure_password = password_hash(self._raw_secure_password, bin_dir=slapd['bin_dir']) self.log.debug("INFO: temp root password set to %s" % self._raw_secure_password) self.log.debug("PASSED: root user checking") assert_c(slapd['port'] is not None, "Configuration port in section [slapd] not found") if self.containerised: if slapd['port'] <= 1024: self.log.warning( "WARNING: slapd port %s may not work without NET_BIND_SERVICE in containers" % slapd['port']) if slapd['secure_port'] <= 1024: self.log.warning( "WARNING: slapd secure_port %s may not work without NET_BIND_SERVICE in containers" % slapd['secure_port']) assert_c( socket_check_open('::1', slapd['port']) is False, "port %s is already in use, or missing NET_BIND_SERVICE" % slapd['port']) # We enable secure port by default. assert_c(slapd['secure_port'] is not None, "Configuration secure_port in section [slapd] not found") assert_c( socket_check_open('::1', slapd['secure_port']) is False, "secure_port %s is already in use, or missing NET_BIND_SERVICE" % slapd['secure_port']) self.log.debug("PASSED: network avaliability checking") # Make assertions of the paths? # Make assertions of the backends? # First fix some compat shenanigans. I hate legacy ... for be in backends: for k in BACKEND_PROPNAME_TO_ATTRNAME: if k in be: be[BACKEND_PROPNAME_TO_ATTRNAME[k]] = be[k] del (be[k]) for be in backends: assert_c('nsslapd-suffix' in be) assert_c('cn' in be)
def _install_ds(self, general, slapd, backends): """ Actually install the Ds from the dicts provided. You should never call this directly, as it bypasses assertions. """ ######################## WARNING ############################# # DO NOT CHANGE THIS FUNCTION OR ITS CONTENTS WITHOUT READING # ALL OF THE COMMENTS FIRST. THERE ARE VERY DELICATE # AND DETAILED INTERACTIONS OF COMPONENTS IN THIS FUNCTION. # # IF IN DOUBT CONTACT WILLIAM BROWN <*****@*****.**> ### This first section is about creating the *minimal* required paths and config to get # directory server to start: After this, we then perform all configuration as online # changes from after this point. # Create dse.ldif with a temporary root password. # This is done first, because instances are found for removal and listing by detecting # the present of their dse.ldif!!!! # The template is in slapd['data_dir']/dirsrv/data/template-dse.ldif # Variables are done with %KEY%. self.log.debug("ACTION: Creating dse.ldif") try: os.umask(0o007) # For parent dirs that get created -> sets 770 for perms os.makedirs(slapd['config_dir'], mode=0o770) except OSError: pass # Get suffix for some plugin defaults (if possible) # annoyingly for legacy compat backend takes TWO key types # and we have to now deal with that .... # # Create ds_suffix here else it won't be in scope .... ds_suffix = '' if len(backends) > 0: ds_suffix = normalizeDN(backends[0]['nsslapd-suffix']) dse = "" with open(os.path.join(slapd['data_dir'], 'dirsrv', 'data', 'template-dse.ldif')) as template_dse: for line in template_dse.readlines(): dse += line.replace('%', '{', 1).replace('%', '}', 1) with open(os.path.join(slapd['config_dir'], 'dse.ldif'), 'w') as file_dse: file_dse.write(dse.format( schema_dir=slapd['schema_dir'], lock_dir=slapd['lock_dir'], tmp_dir=slapd['tmp_dir'], cert_dir=slapd['cert_dir'], ldif_dir=slapd['ldif_dir'], bak_dir=slapd['backup_dir'], run_dir=slapd['run_dir'], inst_dir=slapd['inst_dir'], log_dir=slapd['log_dir'], fqdn=general['full_machine_name'], ds_port=slapd['port'], ds_user=slapd['user'], rootdn=slapd['root_dn'], ds_passwd=self._secure_password, # We set our own password here, so we can connect and mod. # This is because we never know the users input root password as they can validily give # us a *hashed* input. ds_suffix=ds_suffix, config_dir=slapd['config_dir'], db_dir=slapd['db_dir'], )) # Create all the needed paths # we should only need to make bak_dir, cert_dir, config_dir, db_dir, ldif_dir, lock_dir, log_dir, run_dir? for path in ('backup_dir', 'cert_dir', 'db_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir'): self.log.debug("ACTION: creating %s", slapd[path]) try: os.umask(0o007) # For parent dirs that get created -> sets 770 for perms os.makedirs(slapd[path], mode=0o770) except OSError: pass os.chown(slapd[path], slapd['user_uid'], slapd['group_gid']) # /var/lock/dirsrv needs special attention... parentdir = os.path.abspath(os.path.join(slapd['lock_dir'], os.pardir)) os.chown(parentdir, slapd['user_uid'], slapd['group_gid']) ### Warning! We need to down the directory under db too for .restore to work. # See dblayer.c for more! db_parent = os.path.join(slapd['db_dir'], '..') os.chown(db_parent, slapd['user_uid'], slapd['group_gid']) # Copy correct data to the paths. # Copy in the schema # This is a little fragile, make it better. # It won't matter when we move schema to usr anyway ... _ds_shutil_copytree(os.path.join(slapd['sysconf_dir'], 'dirsrv/schema'), slapd['schema_dir']) os.chown(slapd['schema_dir'], slapd['user_uid'], slapd['group_gid']) os.chmod(slapd['schema_dir'], 0o770) # Copy in the collation srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/slapd-collations.conf') dstfile = os.path.join(slapd['config_dir'], 'slapd-collations.conf') shutil.copy2(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) os.chmod(dstfile, 0o440) # Copy in the certmap configuration srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/certmap.conf') dstfile = os.path.join(slapd['config_dir'], 'certmap.conf') shutil.copy2(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) os.chmod(dstfile, 0o440) # If we are on the correct platform settings, systemd if general['systemd']: # Should create the symlink we need, but without starting it. subprocess.check_call(["systemctl", "enable", "dirsrv@%s" % slapd['instance_name']]) # Setup tmpfiles_d tmpfile_d = ds_paths.tmpfiles_d + "/dirsrv-" + slapd['instance_name'] + ".conf" with open(tmpfile_d, "w") as TMPFILE_D: TMPFILE_D.write("d {} 0770 {} {}\n".format(slapd['run_dir'], slapd['user'], slapd['group'])) TMPFILE_D.write("d {} 0770 {} {}\n".format(slapd['lock_dir'].replace("slapd-" + slapd['instance_name'], ""), slapd['user'], slapd['group'])) TMPFILE_D.write("d {} 0770 {} {}\n".format(slapd['lock_dir'], slapd['user'], slapd['group'])) # Else we need to detect other init scripts? # WB: No, we just install and assume that docker will start us ... # Bind sockets to our type? # Create certdb in sysconfidir self.log.debug("ACTION: Creating certificate database is %s", slapd['cert_dir']) # BELOWE THIS LINE - all actions are now ONLINE changes to the directory server. # if it all possible, ALWAYS ADD NEW INSTALLER CHANGES AS ONLINE ACTIONS. # Should I move this import? I think this prevents some recursion from lib389 import DirSrv ds_instance = DirSrv(self.verbose) if self.containerised: ds_instance.systemd = general['systemd'] args = { SER_PORT: slapd['port'], SER_SERVERID_PROP: slapd['instance_name'], SER_ROOT_DN: slapd['root_dn'], SER_ROOT_PW: self._raw_secure_password, SER_DEPLOYED_DIR: slapd['prefix'] } ds_instance.allocate(args) # Does this work? assert_c(ds_instance.exists(), "Instance failed to install, does not exist when expected") # Create a certificate database. tlsdb = NssSsl(dbpath=slapd['cert_dir']) if not tlsdb._db_exists(): tlsdb.reinit() if slapd['self_sign_cert']: etc_dirsrv_path = os.path.join(slapd['sysconf_dir'], 'dirsrv/') ssca_path = os.path.join(etc_dirsrv_path, 'ssca/') ssca = NssSsl(dbpath=ssca_path) # If it doesn't exist, create a CA DB if not ssca._db_exists(): ssca.reinit() ssca.create_rsa_ca(months=slapd['self_sign_cert_valid_months']) # If CA is expired or will expire soon, # Reissue it and resign the existing certs that were signed by the cert previously elif ssca.rsa_ca_needs_renew(): ca = ssca.renew_rsa_ca(months=slapd['self_sign_cert_valid_months']) # Import CA to the existing instances except the one we install now (we import it later) for dir in os.listdir(etc_dirsrv_path): if dir.startswith("slapd-") and dir != slapd['cert_dir']: tlsdb_inst = NssSsl(dbpath=os.path.join(etc_dirsrv_path, dir)) tlsdb_inst.import_rsa_crt(ca) csr = tlsdb.create_rsa_key_and_csr() (ca, crt) = ssca.rsa_ca_sign_csr(csr) tlsdb.import_rsa_crt(ca, crt) if general['selinux']: # Set selinux port label selinux_label_port(slapd['secure_port']) # Do selinux fixups if general['selinux']: selinux_paths = ('backup_dir', 'cert_dir', 'config_dir', 'db_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir', 'schema_dir', 'tmp_dir') for path in selinux_paths: selinux_restorecon(slapd[path]) selinux_label_port(slapd['port']) # Start the server # Make changes using the temp root ds_instance.start(timeout=60) ds_instance.open() # In some cases we may want to change log settings # ds_instance.config.enable_log('audit') # Create the configs related to this version. base_config = get_config(general['defaults']) base_config_inst = base_config(ds_instance) base_config_inst.apply_config(install=True) # Setup TLS with the instance. # We *ALWAYS* set secure port, even if security is off, because it breaks # tests with standalone.enable_tls if we do not. It's only when security; on # that we actually start listening on it. if not slapd['secure_port']: slapd['secure_port'] = "636" ds_instance.config.set('nsslapd-secureport', '%s' % slapd['secure_port']) if slapd['self_sign_cert']: ds_instance.config.set('nsslapd-security', 'on') # Create the backends as listed # Load example data if needed. for backend in backends: is_sample_entries_in_props = "sample_entries" in backend create_suffix_entry_in_props = backend.pop('create_suffix_entry', False) ds_instance.backends.create(properties=backend) if not is_sample_entries_in_props and create_suffix_entry_in_props: domain = create_base_domain(ds_instance, backend['nsslapd-suffix']) # Set basic ACI domain.add('aci', [ # Allow reading the base domain object '(targetattr="dc || description || objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search, compare)(userdn="ldap:///anyone");)', # Allow reading the ou '(targetattr="ou || objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compare)(userdn="ldap:///anyone");)' ]) # Initialise ldapi socket information. IPA expects this .... ldapi_path = os.path.join(slapd['local_state_dir'], "run/slapd-%s.socket" % slapd['instance_name']) ds_instance.config.set('nsslapd-ldapifilepath', ldapi_path) ds_instance.config.set('nsslapd-ldapilisten', 'on') ds_instance.config.set('nsslapd-ldapiautobind', 'on') ds_instance.config.set('nsslapd-ldapimaprootdn', slapd['root_dn']) # Create all required sasl maps: if we have a single backend ... # our default maps are really really bad, and we should feel bad. # they basically only work with a single backend, and they'll break # GSSAPI in some cases too :( if len(backends) > 0: self.log.debug("Adding sasl maps for suffix %s" % backend['nsslapd-suffix']) backend = backends[0] saslmappings = SaslMappings(ds_instance) saslmappings.create(properties={ 'cn': 'rfc 2829 u syntax', 'nsSaslMapRegexString': '^u:\\(.*\\)', 'nsSaslMapBaseDNTemplate': backend['nsslapd-suffix'], 'nsSaslMapFilterTemplate': '(uid=\\1)' }) # I think this is for LDAPI saslmappings.create(properties={ 'cn': 'uid mapping', 'nsSaslMapRegexString': '^[^:@]+$', 'nsSaslMapBaseDNTemplate': backend['nsslapd-suffix'], 'nsSaslMapFilterTemplate': '(uid=&)' }) else: self.log.debug("Skipping default SASL maps - no backend found!") # Change the root password finally ds_instance.config.set('nsslapd-rootpw', slapd['root_password']) # We need to log the password when containerised if self.containerised: self.log.debug("Root DN password: {}".format(slapd['root_password'])) # Complete. if general['start']: # Restart for changes to take effect - this could be removed later ds_instance.restart(post_open=False) else: # Just stop the instance now. ds_instance.stop()
def __init__(self, instance, dn="cn=config,cn=ldbm database,cn=plugins,cn=config"): super(DatabaseConfig, self).__init__(instance, dn) self._rdn_attribute = 'cn' self._must_attributes = ['cn'] self._global_attrs = [ 'nsslapd-lookthroughlimit', 'nsslapd-mode', 'nsslapd-idlistscanlimit', 'nsslapd-directory', 'nsslapd-import-cachesize', 'nsslapd-idl-switch', 'nsslapd-search-bypass-filter-test', 'nsslapd-search-use-vlv-index', 'nsslapd-exclude-from-export', 'nsslapd-serial-lock', 'nsslapd-subtree-rename-switch', 'nsslapd-pagedlookthroughlimit', 'nsslapd-pagedidlistscanlimit', 'nsslapd-rangelookthroughlimit', 'nsslapd-backend-opt-level', 'nsslapd-backend-implement', ] self._db_attrs = { 'bdb': [ 'nsslapd-dbcachesize', 'nsslapd-db-logdirectory', 'nsslapd-db-home-directory', 'nsslapd-db-durable-transaction', 'nsslapd-db-transaction-wait', 'nsslapd-db-checkpoint-interval', 'nsslapd-db-compactdb-interval', 'nsslapd-db-page-size', 'nsslapd-db-transaction-batch-val', 'nsslapd-db-transaction-batch-min-wait', 'nsslapd-db-transaction-batch-max-wait', 'nsslapd-db-logbuf-size', 'nsslapd-db-locks', 'nsslapd-db-private-import-mem', 'nsslapd-import-cache-autosize', 'nsslapd-cache-autosize', 'nsslapd-cache-autosize-split', 'nsslapd-import-cachesize', 'nsslapd-search-bypass-filter-test', 'nsslapd-serial-lock', 'nsslapd-db-deadlock-policy', ], 'lmdb': [] } self._create_objectclasses = ['top', 'extensibleObject'] self._protected = True # This could be "bdb" or "lmdb", use what we have configured in the global config self._db_lib = self.get_attr_val_utf8_l('nsslapd-backend-implement') self._dn = "cn=config,cn=ldbm database,cn=plugins,cn=config" self._db_dn = f"cn={self._db_lib},cn=config,cn=ldbm database,cn=plugins,cn=config" self._globalObj = DSLdapObject(self._instance, dn=self._dn) self._dbObj = DSLdapObject(self._instance, dn=self._db_dn) # Assert there is no overlap in different config sets assert_c( len( set(self._global_attrs).intersection( set(self._db_attrs['bdb']), set(self._db_attrs['lmdb']))) == 0)
def _install_ds(self, general, slapd, backends): """ Actually install the Ds from the dicts provided. You should never call this directly, as it bypasses assert_cions. """ # register the instance to /etc/sysconfig # We do this first so that we can trick remove-ds.pl if needed. # There may be a way to create this from template like the dse.ldif ... initconfig = "" with open("%s/dirsrv/config/template-initconfig" % slapd['sysconf_dir']) as template_init: for line in template_init.readlines(): initconfig += line.replace('{{', '{', 1).replace('}}', '}', 1).replace('-', '_') try: os.makedirs("%s/sysconfig" % slapd['sysconf_dir'], mode=0o775) except FileExistsError: pass with open("%s/sysconfig/dirsrv-%s" % (slapd['sysconf_dir'], slapd['instance_name']), 'w') as f: f.write(initconfig.format( SERVER_DIR=slapd['lib_dir'], SERVERBIN_DIR=slapd['sbin_dir'], CONFIG_DIR=slapd['config_dir'], INST_DIR=slapd['inst_dir'], RUN_DIR=slapd['run_dir'], DS_ROOT='', PRODUCT_NAME='slapd', )) # Create all the needed paths # we should only need to make bak_dir, cert_dir, config_dir, db_dir, ldif_dir, lock_dir, log_dir, run_dir? schema_dir, for path in ('backup_dir', 'cert_dir', 'config_dir', 'db_dir', 'ldif_dir', 'lock_dir', 'log_dir', 'run_dir'): if self.verbose: self.log.info("ACTION: creating %s" % slapd[path]) try: os.makedirs(slapd[path], mode=0o775) except OSError: pass os.chown(slapd[path], slapd['user_uid'], slapd['group_gid']) ### Warning! We need to down the directory under db too for .restore to work. # See dblayer.c for more! db_parent = os.path.join(slapd['db_dir'], '..') os.chown(db_parent, slapd['user_uid'], slapd['group_gid']) # Copy correct data to the paths. # Copy in the schema # This is a little fragile, make it better. # It won't matter when we move schema to usr anyway ... _ds_shutil_copytree(os.path.join(slapd['sysconf_dir'], 'dirsrv/schema'), slapd['schema_dir']) os.chown(slapd['schema_dir'], slapd['user_uid'], slapd['group_gid']) # Copy in the collation srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/slapd-collations.conf') dstfile = os.path.join(slapd['config_dir'], 'slapd-collations.conf') shutil.copy2(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) # Copy in the certmap configuration srcfile = os.path.join(slapd['sysconf_dir'], 'dirsrv/config/certmap.conf') dstfile = os.path.join(slapd['config_dir'], 'certmap.conf') shutil.copy2(srcfile, dstfile) os.chown(dstfile, slapd['user_uid'], slapd['group_gid']) # If we are on the correct platform settings, systemd if general['systemd'] and not self.containerised: # Should create the symlink we need, but without starting it. subprocess.check_call(["/usr/bin/systemctl", "enable", "dirsrv@%s" % slapd['instance_name']]) # Else we need to detect other init scripts? # Bind sockets to our type? # Create certdb in sysconfidir if self.verbose: self.log.info("ACTION: Creating certificate database is %s" % slapd['cert_dir']) # Create dse.ldif with a temporary root password. # The template is in slapd['data_dir']/dirsrv/data/template-dse.ldif # Variables are done with %KEY%. # You could cheat and read it in, do a replace of % to { and } then use format? if self.verbose: self.log.info("ACTION: Creating dse.ldif") dse = "" with open(os.path.join(slapd['data_dir'], 'dirsrv', 'data', 'template-dse.ldif')) as template_dse: for line in template_dse.readlines(): dse += line.replace('%', '{', 1).replace('%', '}', 1) with open(os.path.join(slapd['config_dir'], 'dse.ldif'), 'w') as file_dse: file_dse.write(dse.format( schema_dir=slapd['schema_dir'], lock_dir=slapd['lock_dir'], tmp_dir=slapd['tmp_dir'], cert_dir=slapd['cert_dir'], ldif_dir=slapd['ldif_dir'], bak_dir=slapd['backup_dir'], run_dir=slapd['run_dir'], inst_dir="", log_dir=slapd['log_dir'], fqdn=general['full_machine_name'], ds_port=slapd['port'], ds_user=slapd['user'], rootdn=slapd['root_dn'], # ds_passwd=slapd['root_password'], ds_passwd=self._secure_password, # We set our own password here, so we can connect and mod. ds_suffix='', config_dir=slapd['config_dir'], db_dir=slapd['db_dir'], )) # open the connection to the instance. # Should I move this import? I think this prevents some recursion from lib389 import DirSrv ds_instance = DirSrv(self.verbose) ds_instance.containerised = self.containerised args = { SER_PORT: slapd['port'], SER_SERVERID_PROP: slapd['instance_name'], SER_ROOT_DN: slapd['root_dn'], SER_ROOT_PW: self._raw_secure_password, SER_DEPLOYED_DIR: slapd['prefix'] } ds_instance.allocate(args) # Does this work? assert_c(ds_instance.exists(), "Instance failed to install, does not exist when expected") # Create a certificate database. tlsdb = NssSsl(dbpath=slapd['cert_dir']) if not tlsdb._db_exists(): tlsdb.reinit() if slapd['self_sign_cert']: # If it doesn't exist, create a cadb. ssca_path = os.path.join(slapd['sysconf_dir'], 'dirsrv/ssca/') ssca = NssSsl(dbpath=ssca_path) if not ssca._db_exists(): ssca.reinit() ssca.create_rsa_ca() csr = tlsdb.create_rsa_key_and_csr() (ca, crt) = ssca.rsa_ca_sign_csr(csr) tlsdb.import_rsa_crt(ca, crt) ## LAST CHANCE, FIX PERMISSIONS. # Selinux fixups? # Restorecon of paths? # Start the server ds_instance.start(timeout=60) ds_instance.open() # In some cases we may want to change log settings # ds_instance.config.enable_log('audit') # Create the configs related to this version. base_config = get_config(general['defaults']) base_config_inst = base_config(ds_instance) base_config_inst.apply_config(install=True) # Setup TLS with the instance. ds_instance.config.set('nsslapd-secureport', '%s' % slapd['secure_port']) if slapd['self_sign_cert']: ds_instance.config.set('nsslapd-security', 'on') # Create the backends as listed # Load example data if needed. for backend in backends: ds_instance.backends.create(properties=backend) # Make changes using the temp root # Change the root password finally # Initialise ldapi socket information. IPA expects this .... ds_instance.config.set('nsslapd-ldapifilepath', ds_instance.get_ldapi_path()) ds_instance.config.set('nsslapd-ldapilisten', 'on') # Complete. ds_instance.config.set('nsslapd-rootpw', ensure_str(slapd['root_password'])) if self.containerised: # In a container build we need to stop DirSrv at the end ds_instance.stop() else: # Restart for changes to take effect - this could be removed later ds_instance.restart(post_open=False)
def _prepare_ds(self, general, slapd, backends): assert_c(general['defaults'] is not None, "Configuration defaults in section [general] not found") if self.verbose: self.log.info("PASSED: using config settings %s" % general['defaults']) # Validate our arguments. assert_c(slapd['user'] is not None, "Configuration user in section [slapd] not found") # check the user exists assert_c(pwd.getpwnam(slapd['user']), "user %s not found on system" % slapd['user']) slapd['user_uid'] = pwd.getpwnam(slapd['user']).pw_uid assert_c(slapd['group'] is not None, "Configuration group in section [slapd] not found") assert_c(grp.getgrnam(slapd['group']), "group %s not found on system" % slapd['group']) slapd['group_gid'] = grp.getgrnam(slapd['group']).gr_gid # check this group exists # Check that we are running as this user / group, or that we are root. assert_c(os.geteuid() == 0 or getpass.getuser() == slapd['user'], "Not running as user root or %s, may not have permission to continue" % slapd['user']) if self.verbose: self.log.info("PASSED: user / group checking") assert_c(general['full_machine_name'] is not None, "Configuration full_machine_name in section [general] not found") assert_c(general['strict_host_checking'] is not None, "Configuration strict_host_checking in section [general] not found") if general['strict_host_checking'] is True: # Check it resolves with dns assert_c(socket.gethostbyname(general['full_machine_name']), "Strict hostname check failed. Check your DNS records for %s" % general['full_machine_name']) if self.verbose: self.log.info("PASSED: Hostname strict checking") assert_c(slapd['prefix'] is not None, "Configuration prefix in section [slapd] not found") if (slapd['prefix'] != ""): assert_c(os.path.exists(slapd['prefix']), "Prefix location '%s' not found" % slapd['prefix']) if self.verbose: self.log.info("PASSED: prefix checking") # We need to know the prefix before we can do the instance checks assert_c(slapd['instance_name'] is not None, "Configuration instance_name in section [slapd] not found") # Check if the instance exists or not. # Should I move this import? I think this prevents some recursion from lib389 import DirSrv ds = DirSrv(verbose=self.verbose) ds.containerised = self.containerised ds.prefix = slapd['prefix'] insts = ds.list(serverid=slapd['instance_name']) assert_c(len(insts) == 0, "Another instance named '%s' may already exist" % slapd['instance_name']) if self.verbose: self.log.info("PASSED: instance checking") assert_c(slapd['root_dn'] is not None, "Configuration root_dn in section [slapd] not found") # Assert this is a valid DN assert_c(is_a_dn(slapd['root_dn']), "root_dn in section [slapd] is not a well formed LDAP DN") assert_c(slapd['root_password'] is not None, "Configuration root_password in section [slapd] not found") # Check if pre-hashed or not. # !!!!!!!!!!!!!! # Right now, the way that rootpw works on ns-slapd works, it force hashes the pw # see https://fedorahosted.org/389/ticket/48859 if not re.match('^\{[A-Z0-9]+\}.*$', slapd['root_password']): # We need to hash it. Call pwdhash-bin. # slapd['root_password'] = password_hash(slapd['root_password'], prefix=slapd['prefix']) pass else: pass # Create a random string # Hash it. # This will be our temporary rootdn password so that we can do # live mods and setup rather than static ldif manipulations. self._raw_secure_password = password_generate() self._secure_password = password_hash(self._raw_secure_password, bin_dir=slapd['bin_dir']) if self.verbose: self.log.info("INFO: temp root password set to %s" % self._raw_secure_password) self.log.info("PASSED: root user checking") assert_c(slapd['port'] is not None, "Configuration port in section [slapd] not found") assert_c(socket_check_open('::1', slapd['port']) is False, "port %s is already in use" % slapd['port']) # We enable secure port by default. assert_c(slapd['secure_port'] is not None, "Configuration secure_port in section [slapd] not found") assert_c(socket_check_open('::1', slapd['secure_port']) is False, "secure_port %s is already in use" % slapd['secure_port']) if self.verbose: self.log.info("PASSED: network avaliability checking")