def get_input(log, msg, default, type="", options=None): # Interactive prompt display_default = default if isinstance(default, bool): if default: display_default = "yes" else: display_default = "no" while 1: if display_default != "": val = input(f'\n{msg} [{display_default}]: ') else: val = input(f'\n{msg}: ') if val != '': if type == "dn": if is_a_dn(val, allow_anon=False): return val else: log.info( f"\n ---> The value you entered \"{val}\" is not a valid DN" ) continue elif type == "int": if val.isdigit(): if int(val) < 1: log.info( "\n ---> You must enter number greater than 0") continue return int(val) else: log.info( f"\n ---> The number you entered \"{val}\" is not a number" ) continue elif type == "bool": if val.lower() == "y" or val.lower() == "yes": return True elif val.lower() == "n" or val.lower() == "no": return False else: log.info( f"\n ---> Invalid value ({val}), please enter \"yes\" or \"no\"." ) continue else: # Just a string, nothing to validate if options is not None: if val.lower() not in options: opt_str = ', '.join(options) log.info( f"\n ---> Invalid value ({val}), please enter one of the following types: {opt_str}" ) continue return val else: # User selected the default value return default
def convert(self, new_rdn): """Convert conflict entry to a valid entry, but we need to give the conflict entry a new rdn since we are not replacing the existing valid counterpart entry. """ if not is_a_dn(new_rdn): raise ValueError("The new RDN (" + new_rdn + ") is not a valid DN") # Get the conflict entry info conflict_value = self.get_attr_val_utf8('nsds5ReplConflict') entry_dn = conflict_value.split(' ', 2)[2] entry_rdn = ldap.explode_dn(entry_dn, 1)[0] rdn_attr = entry_dn.split('=', 1)[0] # Rename conflict entry self.rename(new_rdn, deloldrdn=False) # Cleanup entry self.remove(rdn_attr, entry_rdn) if self.present('objectclass', 'ldapsubentry'): self.remove('objectclass', 'ldapsubentry') self.remove_all('nsds5ReplConflict')
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 create_from_cli(self): # Ask questions to generate general, slapd, and backends print('Install Directory Server (interactive mode)') print('===========================================') # Set the defaults general = { 'config_version': 2, 'full_machine_name': socket.getfqdn(), 'strict_host_checking': False, 'selinux': True, 'systemd': ds_paths.with_systemd, 'defaults': '999999999', 'start': True } slapd = { 'self_sign_cert_valid_months': 24, 'group': ds_paths.group, 'root_dn': ds_paths.root_dn, 'initconfig_dir': ds_paths.initconfig_dir, 'self_sign_cert': True, 'root_password': '', 'port': 389, 'instance_name': 'localhost', 'user': ds_paths.user, 'secure_port': 636, 'prefix': ds_paths.prefix, 'bin_dir': ds_paths.bin_dir, 'sbin_dir': ds_paths.sbin_dir, 'sysconf_dir': ds_paths.sysconf_dir, 'data_dir': ds_paths.data_dir, 'local_state_dir': ds_paths.local_state_dir, 'ldapi': ds_paths.ldapi, 'lib_dir': ds_paths.lib_dir, 'run_dir': ds_paths.run_dir, 'tmp_dir': ds_paths.tmp_dir, 'cert_dir': ds_paths.cert_dir, 'config_dir': ds_paths.config_dir, 'inst_dir': ds_paths.inst_dir, 'backup_dir': ds_paths.backup_dir, 'db_dir': ds_paths.db_dir, 'db_home_dir': ds_paths.db_home_dir, 'db_lib': get_default_db_lib(), 'ldif_dir': ds_paths.ldif_dir, 'lock_dir': ds_paths.lock_dir, 'log_dir': ds_paths.log_dir, 'schema_dir': ds_paths.schema_dir } # Let them know about the selinux status if not selinux_present(): val = input( '\nSelinux support will be disabled, continue? [yes]: ') if val.strip().lower().startswith('n'): return # Start asking questions, beginning with the hostname... val = input('\nEnter system\'s hostname [{}]: '.format( general['full_machine_name'])).rstrip() if val != "": general['full_machine_name'] = val # Instance name - adjust defaults once set while 1: slapd['instance_name'] = general['full_machine_name'].split( '.', 1)[0] # Check if default server id is taken if self._server_id_taken(slapd['instance_name'], prefix=slapd['prefix']): slapd['instance_name'] = "" val = input('\nEnter the instance name [{}]: '.format( slapd['instance_name'])).rstrip() if val != "": if len(val) > 80: print( "Server identifier should not be longer than 80 symbols" ) continue if not all(ord(c) < 128 for c in val): print( "Server identifier can not contain non ascii characters" ) continue if ' ' in val: print("Server identifier can not contain a space") continue if val == 'admin': print( "Server identifier \"admin\" is reserved, please choose a different identifier" ) continue # Check that valid characters are used safe = re.compile(r'^[#%:\w@_-]+$').search if not bool(safe(val)): print( "Server identifier has invalid characters, please choose a different value" ) continue # Check if server id is taken if self._server_id_taken(val, prefix=slapd['prefix']): print( "Server identifier \"{}\" is already taken, please choose a new name" .format(val)) continue # instance name is good slapd['instance_name'] = val break elif slapd['instance_name'] == "": continue else: # Check if default server id is taken if self._server_id_taken(slapd['instance_name'], prefix=slapd['prefix']): print( "Server identifier \"{}\" is already taken, please choose a new name" .format(slapd['instance_name'])) continue break # Finally have a good server id, adjust the default paths for key, value in slapd.items(): if isinstance(value, str): slapd[key] = value.format(instance_name=slapd['instance_name']) # Non-Secure Port if not socket_check_open('::1', slapd['port']): port = get_port(slapd['port'], slapd['port']) else: # Port 389 is already taken, pick another port port = get_port(slapd['port'], "") slapd['port'] = port # Self-Signed Cert DB while 1: val = input('\nCreate self-signed certificate database [yes]: ' ).rstrip().lower() if val != "": if val == 'no' or val == "n": slapd['self_sign_cert'] = False break elif val == "yes" or val == "y": # Default value is already yes break else: print('Invalid value "{}", please use "yes" or "no"') continue else: # use default break # Secure Port (only if using self signed cert) if slapd['self_sign_cert']: if not socket_check_open('::1', slapd['secure_port']): port = get_port(slapd['secure_port'], slapd['secure_port'], secure=True) else: # Port 636 is already taken, pick another port port = get_port(slapd['secure_port'], "", secure=True) slapd['secure_port'] = port else: slapd['secure_port'] = False # Root DN while 1: val = input('\nEnter Directory Manager DN [{}]: '.format( slapd['root_dn'])).rstrip() if val != '': # Validate value is a DN if is_a_dn(val, allow_anon=False): slapd['root_dn'] = val break else: print('The value "{}" is not a valid DN'.format(val)) continue else: # use default break # Root DN Password while 1: rootpw1 = getpass.getpass( '\nEnter the Directory Manager password: '******'': print('Password can not be empty') continue if len(rootpw1) < 8: print('Password must be at least 8 characters long') continue rootpw2 = getpass.getpass( 'Confirm the Directory Manager Password: '******'Passwords do not match') continue # Passwords match, set it slapd['root_password'] = rootpw1 break # Backend [{'name': 'userroot', 'suffix': 'dc=example,dc=com'}] backend = {'name': 'userroot', 'suffix': ''} backends = [backend] suffix = '' domain_comps = general['full_machine_name'].split('.') for comp in domain_comps: if suffix == '': suffix = "dc=" + comp else: suffix += ",dc=" + comp while 1: val = input( "\nEnter the database suffix (or enter \"none\" to skip) [{}]: " .format(suffix)).rstrip() if val != '': if val.lower() == "none": # No database, no problem backends = [] break if is_a_dn(val, allow_anon=False): backend['suffix'] = val break else: print("The suffix \"{}\" is not a valid DN".format(val)) continue else: backend['suffix'] = suffix break # Add sample entries or root suffix entry? if len(backends) > 0: while 1: val = input("\nCreate sample entries in the suffix [no]: " ).rstrip().lower() if val != "": if val == "no" or val == "n": break if val == "yes" or val == "y": backend['sample_entries'] = INSTALL_LATEST_CONFIG break # Unknown value print( "Value \"{}\" is invalid, please use \"yes\" or \"no\"" .format(val)) continue else: break if 'sample_entries' not in backend: # Check if they want to create the root node entry instead while 1: val = input("\nCreate just the top suffix entry [no]: " ).rstrip().lower() if val != "": if val == "no" or val == "n": break if val == "yes" or val == "y": backend['create_suffix_entry'] = True break # Unknown value print( "Value \"{}\" is invalid, please use \"yes\" or \"no\"" .format(val)) continue else: break # Start the instance? while 1: val = input( '\nDo you want to start the instance after the installation? [yes]: ' ).rstrip().lower() if val == '' or val == 'yes' or val == 'y': # Default behaviour break elif val == "no" or val == 'n': general['start'] = False break else: print('Invalid value, please use \"yes\" or \"no\"') continue # Are you ready? while 1: val = input('\nAre you ready to install? [no]: ').rstrip().lower() if val == '' or val == "no" or val == 'n': print('Aborting installation...') sys.exit(0) elif val == 'yes' or val == 'y': # lets do it! break else: print('Invalid value, please use \"yes\" or \"no\"') continue self.create_from_args(general, slapd, backends, self.extra) return True
def _prepare_ds(self, general, slapd, backends): assert(general['defaults'] is not None) if self.verbose: self.log.info("PASSED: using config settings %s" % general['defaults']) # Validate our arguments. assert(slapd['user'] is not None) # check the user exists assert(pwd.getpwnam(slapd['user'])) slapd['user_uid'] = pwd.getpwnam(slapd['user']).pw_uid assert(slapd['group'] is not None) assert(grp.getgrnam(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(os.geteuid() == 0 or getpass.getuser() == slapd['user']) if self.verbose: self.log.info("PASSED: user / group checking") assert(general['full_machine_name'] is not None) assert(general['strict_host_checking'] is not None) if general['strict_host_checking'] is True: # Check it resolves with dns assert(socket.gethostbyname(general['full_machine_name'])) if self.verbose: self.log.info("PASSED: Hostname strict checking") assert(slapd['prefix'] is not None) if (slapd['prefix'] != ""): assert(os.path.exists(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(slapd['instance_name'] is not None) # 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(len(insts) == 0) if self.verbose: self.log.info("PASSED: instance checking") assert(slapd['root_dn'] is not None) # Assert this is a valid DN assert(is_a_dn(slapd['root_dn'])) assert(slapd['root_password'] is not None) # 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("PASSED: root user checking") assert(slapd['port'] is not None) assert(socket_check_open('::1', slapd['port']) is False) assert(slapd['secure_port'] is not None) assert(socket_check_open('::1', slapd['secure_port']) is False) if self.verbose: self.log.info("PASSED: network avaliability checking")
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")