def read_ldap_credentials(self): """Read ldap credentials (rootdn, sgiam) from the openldap_config file.""" try: # Load the openldap config file index_id = 'openldap_config_file_read_index' Conf.load(index_id, f'yaml://{self.openldap_config_file}') # Read the cluster id from openldap_config file self.cluster_id = Conf.get(index_id, f'{self.cluster_id_key}') cipher_key = Cipher.generate_key( self.cluster_id, self.get_confkey('CONFSTORE_OPENLDAP_CONST_KEY')) # rootdn username/password self.ldap_root_user = Conf.get(index_id, f'{self.rootdn_user_key}') encrypted_rootdn_pass = Conf.get(index_id, f'{self.rootdn_pass_key}') if encrypted_rootdn_pass != None: self.rootdn_passwd = Cipher.decrypt( cipher_key, bytes(str(encrypted_rootdn_pass), 'utf-8')) except Exception as e: Log.error(f'read ldap credentials failed, error: {e}') raise e
def test_stonith_ok(self): """Check Stonith configuration.""" for node in self.node_list: bmc_ip = self.bmc_data[node]['ip'] bmc_user = self.bmc_data[node]['user'] secret = self.bmc_data[node]['secret'] key = Cipher.generate_key(self.cluster_id, 'cluster') bmc_passwd = Cipher.decrypt(key, secret.encode('ascii')).decode() BmcV().validate('stonith', [node, bmc_ip, bmc_user, bmc_passwd])
def test_accessibility_ok(self): """Check BMC accessibility for nodes in cluster.""" for node in self.node_list: bmc_ip = self.bmc_data[node]['ip'] bmc_user = self.bmc_data[node]['user'] secret = self.bmc_data[node]['secret'] key = Cipher.generate_key(self.cluster_id, 'cluster') bmc_passwd = Cipher.decrypt(key, secret.encode('ascii')).decode() BmcV().validate('accessible', [node, bmc_ip, bmc_user, bmc_passwd])
def test_accessibility_error_on_invalid_bmc_ip(self): """Check 'accessible' validation type for fake bmc_ip argument.""" node = self.node_list[0] bmc_ip = "10.256.256.10" user = self.bmc_data[node]['user'] secret = self.bmc_data[node]['secret'] key = Cipher.generate_key(self.cluster_id, 'cluster') passwd = Cipher.decrypt(key, secret.encode('ascii')).decode() self.assertRaises(VError, BmcV().validate, 'accessible', [node, bmc_ip, user, passwd])
def decrypt(component, secret): """ Decrypt secret. Args: secret: Secret to be decrypted. """ retval = None cluster_id = __grains__['cluster_id'] cipher_key = Cipher.generate_key(cluster_id, component) if secret: retval = (Cipher.decrypt(cipher_key, secret.encode("utf-8"))).decode("utf-8") return retval
def decrypt_passwd(self, encr_pass): """Returns decrypted password""" _component = "storage" try: cipher_key = Cipher.generate_key(enc_id_on_node, _component) return ( Cipher.decrypt( cipher_key, encr_pass.encode("utf-8") ) ).decode("utf-8") except Exception as exc: self.logger.error( "Could not decrypt the password stored in the configuration\n" ) raise exc
def decrypt(component, secret): """ Decrypt secret. Args: secret: Secret to be decrypted. """ from cortx.utils.security.cipher import Cipher, CipherInvalidToken retval = None cluster_id = __grains__['cluster_id'] cipher_key = Cipher.generate_key(cluster_id, component) if secret: retval = (Cipher.decrypt(cipher_key, secret.encode("utf-8"))).decode("utf-8") return retval
def decrypt(key, text, caller=None): '''Decrypt the <text>''' decrypt_text = text try: decrypt_text = Cipher.decrypt(key, text.encode("utf-8")) except CipherInvalidToken as e: logger.error("{0}:Password decryption failed requested by {1}.".format(e, caller)) return decrypt_text.decode("utf-8")
def decrypt(component, secret): """Decrypt secret. Args: secret: Secret to be decrypted. """ from cortx.utils.security.cipher import Cipher retval = None cluster_id = getattr(sys.modules[__name__], '__grains__')['cluster_id'] cipher_key = Cipher.generate_key(cluster_id, component) if secret: retval = (Cipher.decrypt(cipher_key, secret.encode("utf-8"))).decode("utf-8") return retval
def test_bmc_config(args): """Check if BMC configuration are valid. Testing BMC config with ipmitool is possible only when ipmi over lan is configured(out-band setup). It is taken care by test_bmc_is_accessible. So, validation on bmc onfiguration with bmc ip, user and secret value through ssh is fine at this time. """ bmc_ip = Conf.get(GLOBAL_CONF, BMC_IP_KEY) bmc_user = Conf.get(GLOBAL_CONF, BMC_USER_KEY) bmc_secret = Conf.get(GLOBAL_CONF, BMC_SECRET_KEY) bmc_key = Cipher.generate_key(MACHINE_ID, "server_node") bmc_passwd = Cipher.decrypt(bmc_key, bmc_secret.encode("utf-8")).decode("utf-8") # check BMC ip, user, password are valid session = SSHChannel(bmc_ip, bmc_user, bmc_passwd) session.disconnect()
def decrypt(key, text): '''Decrypt the <text>''' decrypt_text = text try: decrypt_text = Cipher.decrypt(key, text).decode() return decrypt_text except CipherInvalidToken as e: print("Password decryption failed requested by %s" % SECTION) return decrypt_text.decode()
def test_bmc_is_accessible(args): """Check if BMC is accessible through KCS or LAN.""" channel_interface = Conf.get(SSPL_CONF, "BMC_INTERFACE>default", 'system') if channel_interface == "system": # Check BMC is accessible through KCS cmd = "sudo ipmitool channel info" expected_channel = "KCS" channel_found = None res_op, res_err, res_rc = SimpleProcess(cmd).run() if res_rc == 0: res_op = res_op.decode() search_res = re.search( r"%s[\s]+:[\s]+(\w+)(.*)" % CHANNEL_PROTOCOL, res_op) if search_res: channel_found = search_res.groups()[0] if expected_channel != channel_found: print("UNEXPECTED BMC CHANNEL TYPE FOUND.") print("Expected: %s" % expected_channel) print("Found: %s" % channel_found) else: res_err = res_err.decode() kcs_errors = ("could not find inband device", "driver timeout") if not any(err for err in kcs_errors if err in res_err): raise Exception( "BMC is NOT accessible through KCS - ERROR: %s" % res_err) elif channel_interface == "lan": # Check BMC is accessible through LAN subcommand = "channel info" bmc_ip = Conf.get(GLOBAL_CONF, BMC_IP_KEY) bmc_user = Conf.get(GLOBAL_CONF, BMC_USER_KEY) bmc_secret = Conf.get(GLOBAL_CONF, BMC_SECRET_KEY) bmc_key = Cipher.generate_key(MACHINE_ID, "server_node") bmc_passwd = Cipher.decrypt(bmc_key, bmc_secret.encode("utf-8")).decode("utf-8") cmd = "sudo ipmitool -H %s -U %s -P %s -I lan %s" % ( bmc_ip, bmc_user, bmc_passwd, subcommand) res_op, res_err, res_rc = SimpleProcess(cmd).run() if res_rc != 0: raise Exception("BMC is NOT accessible over lan - ERROR: %s" % res_err.decode())
async def get(self, name: str) -> bytes: """ Gets bytes from the encrypted storage. Acquires the data from the storage and decrypts it with the default CORTX cipher Raises CipherInvalidToken if decryption fails. """ neb = await self._get_item(name) if neb is None: return None decrypted_bytes = Cipher.decrypt(self._key, neb.data.encode('ascii')) return decrypted_bytes
def get_key(self): try: key = Cipher.generate_key(self.cluster_id, self.const_key) except Exception as err: raise CipherInvalidToken( "Cipher generate key failed with error : {0}".format(err)) if (self.use_base64): key = base64.b64encode(key, str.encode("AZ")) if (len(key) < self.key_len): while (len(key) < self.key_len): key = key * 2 key = key[:self.key_len] elif (len(key) > self.key_len): key = key[:self.key_len] return key.decode("utf-8")
async def store(self, name: str, data: bytes, force=False) -> None: """ Saves the data to the encrypted storage. Data is AES encrypted with the default CORTX cipher and stored as Base64 encoded string with the provided name. Raises KeyError if an item with the provided name exists and "force" flag is not set. """ if not force: neb = await self._get_item(name) if neb is not None: raise KeyError(f'{name} already exists in the secure storage') encrypted_bytes = Cipher.encrypt(self._key, data) # Encrypted token is base64 encoded, thus there won't be a problem with storing it in String neb = NamedEncryptedBytes.instantiate(name, encrypted_bytes.decode('ascii')) await self._storage(NamedEncryptedBytes).store(neb)
def gen_key(unique_seed, root_node): '''Generate Cipher key based on unique seed and corresponding root_node''' # Generate key for decryption key = Cipher.generate_key(unique_seed, root_node) return key
def config_apply(solution_config_url: str, cortx_conf_url: str = None, force_override: bool = False): """ Description: Parses input config and store in CORTX config location Parameters: [IN] Solution Config URL [OUT] CORTX Config URL """ if Log.logger is None: CortxProvisionerLog.initialize(const.SERVICE_NAME, const.TMP_LOG_PATH) if cortx_conf_url is None: cortx_conf_url = CortxProvisioner._cortx_conf_url cortx_conf = MappedConf(CortxProvisioner._tmp_cortx_conf_url) # Load same config again if force_override is True try: cs_option = {"fail_reload": False} if force_override else {"skip_reload": True} Log.info('Applying config %s' % solution_config_url) Conf.load(CortxProvisioner._solution_index, solution_config_url, **cs_option) except ConfError as e: Log.error(f'Unable to load {solution_config_url} url, Error:{e}') # Secrets path from config file if cortx_conf.get('cortx>common>storage>local'): CortxProvisioner._secrets_path = cortx_conf.get('cortx>common>storage>local')+CortxProvisioner._rel_secret_path # source code for encrypting and storing secret key if Conf.get(CortxProvisioner._solution_index, 'cluster') is not None: CortxProvisioner.apply_cluster_config(cortx_conf, CortxProvisioner.cortx_release) if Conf.get(CortxProvisioner._solution_index, 'cortx') is not None: # generating cipher key cipher_key = None cluster_id = Conf.get(CortxProvisioner._solution_index, 'cluster>id') if cluster_id is None: cluster_id = cortx_conf.get('cluster>id') if cluster_id is None: raise CortxProvisionerError(errno.EINVAL, 'Cluster ID not specified') cipher_key = Cipher.gen_key(cluster_id, 'cortx') if cipher_key is None: raise CortxProvisionerError(errno.EINVAL, 'Cipher key not specified') for key in Conf.get_keys(CortxProvisioner._solution_index): # using path /etc/cortx/solution/secret to confirm secret if key.endswith('secret'): secret_val = Conf.get(CortxProvisioner._solution_index, key) val = None with open(os.path.join(CortxProvisioner._secrets_path, secret_val), 'rb') as secret: val = secret.read() if val is None: raise CortxProvisionerError(errno.EINVAL, f'Could not find the Secret in {CortxProvisioner._secrets_path}') val = Cipher.encrypt(cipher_key, val) # decoding the byte string in val variable Conf.set(CortxProvisioner._solution_index, key, val.decode('utf-8')) CortxProvisioner.apply_cortx_config(cortx_conf, CortxProvisioner.cortx_release) # Adding array count key in conf cortx_conf.add_num_keys() Conf.save(cortx_conf._conf_idx)
def decrypt(key, text, caller=None): """Decrypt the <text>.""" decrypt_text = Cipher.decrypt(key, text.encode("utf-8")).decode("utf-8") return decrypt_text
def encrypt(key, text): """Encrypt sensitive data. Ex: messaging credentials.""" # Before encrypting text we need to convert string to bytes using encode() # method return Cipher.encrypt(key, text.encode())
def process(self): """ Process config command. """ Log.info("Processing config command") # Read machine-id and using machine-id read minion name from confstore # This minion name will be used for adding the node to the cluster. node_name: str = self.get_node_name() nodelist: list = self.get_nodelist(fetch_from=ConfigCmd.PROV_CONFSTORE) # Read cluster name and cluster user machine_id = self.get_machine_id() cluster_id = Conf.get(self._index, f"server_node.{machine_id}.cluster_id") cluster_name = Conf.get(self._index, f"cluster.{cluster_id}.name") cluster_user = Conf.get( self._index, f"cortx.software.{const.HA_CLUSTER_SOFTWARE}.user") node_type = Conf.get(self._index, f"server_node.{machine_id}.type").strip() # Read cluster user password and decrypt the same cluster_secret = Conf.get( self._index, f"cortx.software.{const.HA_CLUSTER_SOFTWARE}.secret") key = Cipher.generate_key(cluster_id, const.HACLUSTER_KEY) cluster_secret = Cipher.decrypt( key, cluster_secret.encode('ascii')).decode() mgmt_info: dict = self._get_mgmt_vip(machine_id, cluster_id) s3_instances = ConfigCmd.get_s3_instance(machine_id) self._update_env(node_name, node_type, const.HA_CLUSTER_SOFTWARE) self._fetch_fids() self._update_cluster_manager_config() # Update cluster and resources self._cluster_manager = CortxClusterManager(default_log_enable=False) Log.info("Checking if cluster exists already") cluster_exists = bool( json.loads( self._cluster_manager.cluster_controller.cluster_exists()).get( "msg")) Log.info(f"Cluster exists? {cluster_exists}") if not cluster_exists: node_count: int = len( self.get_nodelist(fetch_from=ConfigCmd.HA_CONFSTORE)) if node_count == 0: Log.info( f"Creating cluster: {cluster_name} with node: {node_name}") # Create cluster try: self._create_cluster(cluster_name, cluster_user, cluster_secret, node_name) self._create_resource(s3_instances=s3_instances, mgmt_info=mgmt_info) self._confstore.set( f"{const.CLUSTER_CONFSTORE_NODES_KEY}/{node_name}") except Exception as e: Log.error( f"Cluster creation failed; destroying the cluster. Error: {e}" ) output = self._execute.run_cmd(const.PCS_CLUSTER_DESTROY) Log.error(f"Cluster destroyed. Output: {output}") # Delete the node from nodelist if it was added in the store if self._confstore.key_exists( f"{const.CLUSTER_CONFSTORE_NODES_KEY}/{node_name}" ): self._confstore.delete( f"{const.CLUSTER_CONFSTORE_NODES_KEY}/{node_name}") raise HaConfigException("Cluster creation failed") # Add Other Node for node in nodelist: if node != node_name: Log.info( f"Adding node {node} to Cluster {cluster_name}") self._add_node(node, cluster_user, cluster_secret) else: # Add node with SSH self._add_node_remotely(node_name, cluster_user, cluster_secret) else: for node in nodelist: if node != node_name: Log.info(f"Adding node {node} to Cluster {cluster_name}") self._add_node(node, cluster_user, cluster_secret) self._execute.run_cmd(const.PCS_CLEANUP) Log.info("config command is successful")
def gen_key(cluster_id, service_name): ''' Generate key for decryption ''' key = Cipher.generate_key(cluster_id, service_name) return key
def validate(self): """Check below requirements. 1. Validate input configs 2. Validate BMC connectivity 3. Validate storage controller connectivity 4. Validate network interface availability """ machine_id = Utility.get_machine_id() mgmt_interfaces = [] data_private_interfaces = [] data_public_interfaces = [] # Validate input/provisioner configs node_type = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>type" % machine_id) cluster_id = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>cluster_id" % machine_id) if node_type.lower() not in ["virtual", "vm"]: bmc_ip = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>bmc>ip" % machine_id) bmc_user = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>bmc>user" % machine_id) bmc_secret = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>bmc>secret" % machine_id) bmc_key = Cipher.generate_key(machine_id, ServiceTypes.SERVER_NODE.value) bmc_passwd = Cipher.decrypt( bmc_key, bmc_secret.encode("utf-8")).decode("utf-8") data_private_interfaces = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>data>private_interfaces" % machine_id) data_public_interfaces = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>data>public_interfaces" % machine_id) mgmt_public_fqdn = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>management>public_fqdn" % machine_id) mgmt_interfaces = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>management>interfaces" % machine_id) data_private_fqdn = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>data>private_fqdn" % machine_id) data_public_fqdn = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>network>data>public_fqdn" % machine_id) enclosure_id = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "server_node>%s>storage>enclosure_id" % machine_id) primary_ip = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "storage_enclosure>%s>controller>primary>ip" % enclosure_id) secondary_ip = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "storage_enclosure>%s>controller>secondary>ip" % enclosure_id) cntrlr_user = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "storage_enclosure>%s>controller>user" % enclosure_id) cntrlr_secret = Utility.get_config_value( PRVSNR_CONFIG_INDEX, "storage_enclosure>%s>controller>secret" % enclosure_id) cntrlr_key = Cipher.generate_key(enclosure_id, ServiceTypes.STORAGE_ENCLOSURE.value) cntrlr_passwd = Cipher.decrypt( cntrlr_key, cntrlr_secret.encode("utf-8")).decode("utf-8") # Validate BMC connectivity & storage controller accessibility if node_type.lower() not in ["virtual", "vm"]: NetworkV().validate("connectivity", [bmc_ip, primary_ip, secondary_ip]) BmcV().validate("accessible", [socket.getfqdn(), bmc_ip, bmc_user, bmc_passwd]) c_validator = ControllerV() c_validator.validate("accessible", [primary_ip, cntrlr_user, cntrlr_passwd]) c_validator.validate("accessible", [secondary_ip, cntrlr_user, cntrlr_passwd]) # Validate network fqdn reachability NetworkV().validate( "connectivity", [mgmt_public_fqdn, data_private_fqdn, data_public_fqdn]) # Validate network interface availability for i_list in [ mgmt_interfaces, data_private_interfaces, data_public_interfaces ]: self.validate_nw_cable_connection(i_list) self.validate_nw_interfaces(i_list)
def encrypt_secret(secret, component, key): key_cipher = Cipher.generate_key(key, component) return Cipher.encrypt(key_cipher, secret.encode("utf-8")).decode("utf-8")
def process(self): """ Process config command. """ Log.info("Processing config command") # Read machine-id and using machine-id read minion name from confstore # This minion name will be used for adding the node to the cluster. nodelist = [] command = "cat /etc/machine-id" machine_id, err, rc = self._execute.run_cmd(command, check_error=True) Log.info(f"Read machine-id. Output: {machine_id}, Err: {err}, RC: {rc}") minion_name = Conf.get(self._index, f"cluster.server_nodes.{machine_id.strip()}") nodelist.append(minion_name) # The config step will be called from primary node alwasys, # see how to get and use the node name then. # Read cluster name and cluster user cluster_name = Conf.get(self._index, 'corosync-pacemaker.cluster_name') cluster_user = Conf.get(self._index, 'corosync-pacemaker.user') # Read cluster user password and decrypt the same cluster_id = Conf.get(self._index, 'cluster.cluster_id') cluster_secret = Conf.get(self._index, 'corosync-pacemaker.secret') key = Cipher.generate_key(cluster_id, 'corosync-pacemaker') cluster_secret = Cipher.decrypt(key, cluster_secret.encode('ascii')).decode() # Get s3 instance count try: s3_instances = Conf.get(self._index, f"cluster.{minion_name}.s3_instances") if int(s3_instances) < 1: raise HaConfigException(f"Found {s3_instances} which is invalid s3 instance count.") except Exception as e: Log.error(f"Found {s3_instances} which is invalid s3 instance count. Error: {e}") raise HaConfigException(f"Found {s3_instances} which is invalid s3 instance count.") # Check if the cluster exists already, if yes skip creating the cluster. output, err, rc = self._execute.run_cmd(const.PCS_CLUSTER_STATUS, check_error=False) Log.info(f"Cluster status. Output: {output}, Err: {err}, RC: {rc}") if rc != 0: if(err.find("No such file or directory: 'pcs'") != -1): Log.error("Cluster config failed; pcs not installed") raise HaConfigException("Cluster config failed; pcs not installed") # If cluster is not created; create a cluster. elif(err.find("cluster is not currently running on this node") != -1): try: Log.info(f"Creating cluster: {cluster_name} with node: {minion_name}") cluster_auth(cluster_user, cluster_secret, nodelist) cluster_create(cluster_name, nodelist) Log.info(f"Created cluster: {cluster_name} successfully") Log.info("Creating pacemaker resources") create_all_resources(s3_instances=s3_instances) Log.info("Created pacemaker resources successfully") except Exception as e: Log.error(f"Cluster creation failed; destroying the cluster. Error: {e}") output = self._execute.run_cmd(const.PCS_CLUSTER_DESTROY, check_error=True) Log.info(f"Cluster destroyed. Output: {output}") raise HaConfigException("Cluster creation failed") else: pass # Nothing to do else: # Cluster exists already, check if it is a new node and add it to the existing cluster. Log.info("The cluster exists already, check and add new node") Log.info("config command is successful")
def encrypt(key, text): ''' Encrypt sensitive data. Ex: RabbitMQ credentials ''' # Before encrypting text we need to convert string to bytes using encode() # method return Cipher.encrypt(key, text.encode())
def encrypt(key: str, data: str): edata = Cipher.encrypt(bytes(key, 'utf-8'), bytes(data, 'utf-8')) return edata.decode("utf-8")
def decrypt(key: str, data: str): ddata = Cipher.decrypt(bytes(key, 'utf-8'), bytes(data, 'utf-8')) return ddata.decode("utf-8")
def gen_key(unique_seed, root_node): # Generate key for decryption key = Cipher.generate_key(unique_seed, root_node) return key
def decrypt(key, text): ''' Decrypt the <text> ''' return Cipher.decrypt(key, text).decode()
def decrypt(key, text): ''' Decrypt the <text> ''' return Cipher.decrypt(key, text.encode("utf-8")).decode("utf-8")