def test_release_session_on_dynamo(self): print('test_release_session_on_dynamo') sessions_table_lock_client = Mock() self.assertTrue( aws_services.release_session_on_dynamo('123', '222332', sessions_table_lock_client)) sessions_table_lock_client = MagicMock(Exception('AssertError')) self.assertFalse( aws_services.release_session_on_dynamo('123', '222332', sessions_table_lock_client))
def create_instance(instanceId, instanceDetails, storeParametersClass, logName, solutionAccountId, eventRegion, eventAccountId, instanceAccountPassword): if instanceDetails[ 'platform'] == "windows": # Windows machine return 'windows' all other return 'None' kp_processing.save_key_pair(instanceAccountPassword) instancePasswordData = get_instance_password_data( instanceId, solutionAccountId, eventRegion, eventAccountId) # decryptedPassword = convert_pem_to_password(instanceAccountPassword, instancePasswordData) rc, decryptedPassword = kp_processing.run_command_on_container([ "echo", str.strip(instancePasswordData), "|", "base64", "--decode", "|", "openssl", "rsautl", "-decrypt", "-inkey", "/tmp/pemValue.pem" ], True) AWSAccountName = 'AWS.{0}.Windows'.format(instanceId) instanceKey = decryptedPassword platform = WINDOWS_PLATFORM instanceUsername = ADMINISTRATOR safeName = storeParametersClass.windowsSafeName else: ppkKey = kp_processing.convert_pem_to_ppk(instanceAccountPassword) if not ppkKey: raise Exception("Error on key conversion") # ppkKey contains \r\n on each row end, adding escape char '\' trimmedPPKKey = str(ppkKey).replace("\n", "\\n") instanceKey = trimmedPPKKey.replace("\r", "\\r") AWSAccountName = 'AWS.{0}.Unix'.format(instanceId) platform = UNIX_PLATFORM safeName = storeParametersClass.unixSafeName instanceUsername = get_OS_distribution_user( instanceDetails['image_description']) # Check if account already exist - in case exist - just add it to DynamoDB pvwaConnectionnumber, sessionGuid = aws_services.get_available_session_from_dynamo( ) if not pvwaConnectionnumber: return sessionToken = pvwa_integration.logon_pvwa( storeParametersClass.vaultUsername, storeParametersClass.vaultPassword, storeParametersClass.pvwaURL, pvwaConnectionnumber) if not sessionToken: return searchAccountPattern = "{0},{1}".format(instanceDetails["address"], instanceUsername) existingInstanceAccountId = pvwa_api_calls.retrieve_accountId_from_account_name( sessionToken, searchAccountPattern, safeName, instanceId, storeParametersClass.pvwaURL) if existingInstanceAccountId: # account already exist and managed on vault, no need to create it again print("Account already exists in vault") aws_services.put_instance_to_dynamo_table(instanceId, instanceDetails['address'], OnBoardStatus.OnBoarded, "None", logName) return else: accountCreated, errorMessage = pvwa_api_calls.create_account_on_vault( sessionToken, AWSAccountName, instanceKey, storeParametersClass, platform, instanceDetails['address'], instanceId, instanceUsername, safeName) if accountCreated: # if account created, rotate the key immediately instanceAccountId = pvwa_api_calls.retrieve_accountId_from_account_name( sessionToken, searchAccountPattern, safeName, instanceId, storeParametersClass.pvwaURL) pvwa_api_calls.rotate_credentials_immediately( sessionToken, storeParametersClass.pvwaURL, instanceAccountId, instanceId) aws_services.put_instance_to_dynamo_table( instanceId, instanceDetails['address'], OnBoardStatus.OnBoarded, "None", logName) else: # on board failed, add the error to the table aws_services.put_instance_to_dynamo_table( instanceId, instanceDetails['address'], OnBoardStatus.OnBoarded_Failed, errorMessage, logName) pvwa_integration.logoff_pvwa(storeParametersClass.pvwaURL, sessionToken) aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid)
def elasticity_function(instance_id, action_type, event_account_id, event_region, solution_account_id, log_name): try: ec2_object = aws_services.get_account_details(solution_account_id, event_account_id, event_region) instance_details = aws_services.get_ec2_details( instance_id, ec2_object, event_account_id) instance_data = aws_services.get_instance_data_from_dynamo_table( instance_id) if action_type == 'terminated': if not instance_data: logger.info(f"Item {instance_id} does not exist on DB") return None instance_status = instance_data["Status"]["S"] if instance_status == OnBoardStatus.on_boarded_failed: logger.error( f"Item {instance_id} is in status OnBoard failed, removing from DynamoDB table" ) aws_services.remove_instance_from_dynamo_table(instance_id) return None elif action_type == 'running': if not instance_details[ "address"]: # In case querying AWS return empty address logger.error("Retrieving Instance Address from AWS failed.") return None if instance_data: instance_status = instance_data["Status"]["S"] if instance_status == OnBoardStatus.on_boarded: logger.info( f"Item {instance_id} already exists on DB, no need to add it to Vault" ) return None elif instance_status == OnBoardStatus.on_boarded_failed: logger.error( f"Item {instance_id} exists with status 'OnBoard failed', adding to Vault" ) else: logger.info( f"Item {instance_id} does not exist on DB, adding to Vault" ) else: logger.info('Unknown instance state') return store_parameters_class = aws_services.get_params_from_param_store() if not store_parameters_class: return if store_parameters_class.aob_mode == 'Production': # Save PVWA Verification key in /tmp folder logger.info('Saving verification key') crt = open("/tmp/server.crt", "w+") crt.write(store_parameters_class.pvwa_verification_key) crt.close() pvwa_connection_number, session_guid = aws_services.get_session_from_dynamo( ) if not pvwa_connection_number: return session_token = pvwa_integration_class.logon_pvwa( store_parameters_class.vault_username, store_parameters_class.vault_password, store_parameters_class.pvwa_url, pvwa_connection_number) if not session_token: return disconnect = False if action_type == 'terminated': logger.info(f'Detected termination of {instance_id}') instance_processing.delete_instance(instance_id, session_token, store_parameters_class, instance_data, instance_details) elif action_type == 'running': # get key pair logger.info('Retrieving account id where the key-pair is stored') # Retrieving the account id of the account where the instance keyPair is stored # AWS.<AWS Account>.<Event Region name>.<key pair name> key_pair_value_on_safe = f'AWS.{instance_details["aws_account_id"]}.{event_region}.{instance_details["key_name"]}' key_pair_account_id = pvwa_api_calls.check_if_kp_exists( session_token, key_pair_value_on_safe, store_parameters_class.key_pair_safe_name, instance_id, store_parameters_class.pvwa_url) if not key_pair_account_id: logger.error(f"Key Pair {key_pair_value_on_safe} does not exist in Safe " \ f"{store_parameters_class.key_pair_safe_name}") return instance_account_password = pvwa_api_calls.get_account_value( session_token, key_pair_account_id, instance_id, store_parameters_class.pvwa_url) if instance_account_password is False: return pvwa_integration_class.logoff_pvwa(store_parameters_class.pvwa_url, session_token) aws_services.release_session_on_dynamo(pvwa_connection_number, session_guid) disconnect = True instance_processing.create_instance(instance_id, instance_details, store_parameters_class, log_name, solution_account_id, event_region, event_account_id, instance_account_password) else: logger.error('Unknown instance state') return if not disconnect: pvwa_integration_class.logoff_pvwa(store_parameters_class.pvwa_url, session_token) aws_services.release_session_on_dynamo(pvwa_connection_number, session_guid) except Exception as e: logger.error(f"Unknown error occurred: {e}") if action_type == 'terminated': # put_instance_to_dynamo_table(instance_id, instance_details["address"]\ # , OnBoardStatus.delete_failed, str(e), log_name) aws_services.update_instances_table_status( instance_id, OnBoardStatus.delete_failed, str(e)) elif action_type == 'running': aws_services.put_instance_to_dynamo_table( instance_id, instance_details["address"], OnBoardStatus.on_boarded_failed, str(e), log_name) # TODO: Retry mechanism? aws_services.release_session_on_dynamo(pvwa_connection_number, session_guid) return
def lambda_handler(event, context): try: message = event["Records"][0]["Sns"]["Message"] data = json.loads(message) except Exception as e: print ("Error on retrieving Message Data from Event Message. Error: {0}".format(e)) try: instance_arn = data["resources"][0] except Exception as e: print ("Error on retrieving instance_arn from Event Message. Error: {0}".format(e)) try: instanceId = data["detail"]["instance-id"] except Exception as e: print ("Error on retrieving Instance Id from Event Message. Error: {0}".format(e)) try: actionType = data["detail"]["state"] except Exception as e: print ("Error on retrieving Action Type from Event Message. Error: {0}".format(e)) try: eventAccountId = data["account"] except Exception as e: print ("Error on retrieving Event Account Id from Event Message. Error: {0}".format(e)) try: eventRegion = data["region"] except Exception as e: print ("Error on retrieving Event Region from Event Message. Error: {0}".format(e)) logName = context.log_stream_name if context.log_stream_name else "None" try: solutionAccountId = context.invoked_function_arn.split(':')[4] instanceDetails = aws_services.get_ec2_details(instanceId, solutionAccountId, eventRegion, eventAccountId) instanceData = aws_services.get_instance_data_from_dynamo_table(instanceId) if actionType == 'terminated': if not instanceData: print('Item {0} does not exists on DB'.format(instanceId)) return None else: instanceStatus = instanceData["Status"]["S"] if instanceStatus == OnBoardStatus.OnBoarded_Failed: print("Item {0} is in status OnBoard failed, removing from DynamoDB table".format(instanceId)) aws_services.remove_instance_from_dynamo_table(instanceId) return None elif actionType == 'running': if not instanceDetails["address"]: # In case querying AWS return empty address print("Retrieving Instance address from AWS failed.") return None if instanceData: instanceStatus = instanceData["Status"]["S"] if instanceStatus == OnBoardStatus.OnBoarded: print('Item: {0}, exists on DB, no need to add it to vault'.format(instanceId)) return None elif instanceStatus == OnBoardStatus.OnBoarded_Failed: print("Item {0} exists with status 'OnBoard failed', adding to vault".format(instanceId)) else: print('Item {0} does not exists on DB, adding to vault'.format(instanceId)) else: print('Unknown instance state') return storeParametersClass = aws_services.get_params_from_param_store() if not storeParametersClass: return pvwaConnectionnumber, sessionGuid = aws_services.get_available_session_from_dynamo() if not pvwaConnectionnumber: return sessionToken = pvwa_integration.logon_pvwa(storeParametersClass.vaultUsername, storeParametersClass.vaultPassword, storeParametersClass.pvwaURL, pvwaConnectionnumber) if not sessionToken: return disconnect = False if actionType == 'terminated': instance_processing.delete_instance(instanceId, sessionToken, storeParametersClass, instanceData, instanceDetails) elif actionType == 'running': # get key pair # Retrieving the account id of the account where the instance keyPair is stored # AWS.<AWS Account>.<Event Region name>.<key pair name> keyPairValueOnSafe = "AWS.{0}.{1}.{2}".format(instanceDetails["aws_account_id"], eventRegion, instanceDetails["key_name"]) keyPairAccountId = pvwa_api_calls.check_if_kp_exists(sessionToken, keyPairValueOnSafe, storeParametersClass.keyPairSafeName, instanceId, storeParametersClass.pvwaURL) if not keyPairAccountId: print("Key Pair '{0}' does not exist in safe '{1}'".format(keyPairValueOnSafe, storeParametersClass.keyPairSafeName)) return instanceAccountPassword = pvwa_api_calls.get_account_value(sessionToken, keyPairAccountId, instanceId, storeParametersClass.pvwaURL) if instanceAccountPassword is False: return pvwa_integration.logoff_pvwa(storeParametersClass.pvwaURL, sessionToken) aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid) disconnect = True instance_processing.create_instance(instanceId, instanceDetails, storeParametersClass, logName, solutionAccountId, eventRegion, eventAccountId, instanceAccountPassword) else: print('Unknown instance state') return if not disconnect: pvwa_integration.logoff_pvwa(storeParametersClass.pvwaURL, sessionToken) aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid) except Exception as e: print("Unknown error occurred:{0}".format(e)) if actionType == 'terminated': # put_instance_to_dynamo_table(instanceId, instanceDetails["address"]\ # , OnBoardStatus.Delete_Failed, str(e), logName) aws_services.update_instances_table_status(instanceId, OnBoardStatus.Delete_Failed, str(e)) elif actionType == 'running': aws_services.put_instance_to_dynamo_table(instanceId, instanceDetails["address"], OnBoardStatus.OnBoarded_Failed, str(e), logName) # TODO: Retry mechanism? aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid) return
def create_instance(instance_id, instance_details, store_parameters_class, log_name, solution_account_id, event_region, event_account_id, instance_account_password): logger.trace(instance_id, instance_details, store_parameters_class, log_name, solution_account_id, event_region, event_account_id, caller_name='create_instance') logger.info(f'Adding {instance_id} to AOB') if instance_details[ 'platform'] == "windows": # Windows machine return 'windows' all other return 'None' logger.info('Windows platform detected') kp_processing.save_key_pair(instance_account_password) instance_password_data = get_instance_password_data( instance_id, solution_account_id, event_region, event_account_id) decrypted_password = kp_processing.decrypt_password( instance_password_data) aws_account_name = f'AWS.{instance_id}.Windows' instance_key = decrypted_password platform = WINDOWS_PLATFORM instance_username = ADMINISTRATOR safe_name = store_parameters_class.windows_safe_name else: logger.info('Linux\\Unix platform detected') ppk_key = kp_processing.convert_pem_to_ppk(instance_account_password) if not ppk_key: raise Exception("Error on key conversion") # ppk_key contains \r\n on each row end, adding escape char '\' trimmed_ppk_key = str(ppk_key).replace("\n", "\\n") instance_key = trimmed_ppk_key.replace("\r", "\\r") aws_account_name = f'AWS.{instance_id}.Unix' platform = UNIX_PLATFORM safe_name = store_parameters_class.unix_safe_name instance_username = get_os_distribution_user( instance_details['image_description']) # Check if account already exist - in case exist - just add it to DynamoDB print('pvwa_connection_number') pvwa_connection_number, session_guid = aws_services.get_session_from_dynamo( ) if not pvwa_connection_number: return False session_token = pvwa_integration_class.logon_pvwa( store_parameters_class.vault_username, store_parameters_class.vault_password, store_parameters_class.pvwa_url, pvwa_connection_number) print('session_token') if not session_token: return False search_account_pattern = f"{instance_details['address']},{instance_username}" print('retrieve_account_id_from_account_name') existing_instance_account_id = pvwa_api_calls.retrieve_account_id_from_account_name( session_token, search_account_pattern, safe_name, instance_id, store_parameters_class.pvwa_url) if existing_instance_account_id: # account already exist and managed on vault, no need to create it again logger.info("Account already exists in vault") aws_services.put_instance_to_dynamo_table(instance_id, instance_details['address'], OnBoardStatus.on_boarded, "None", log_name) return False else: account_created, error_message = pvwa_api_calls.create_account_on_vault( session_token, aws_account_name, instance_key, store_parameters_class, platform, instance_details['address'], instance_id, instance_username, safe_name) if account_created: # if account created, rotate the key immediately instance_account_id = pvwa_api_calls.retrieve_account_id_from_account_name( session_token, search_account_pattern, safe_name, instance_id, store_parameters_class.pvwa_url) pvwa_api_calls.rotate_credentials_immediately( session_token, store_parameters_class.pvwa_url, instance_account_id, instance_id) aws_services.put_instance_to_dynamo_table( instance_id, instance_details['address'], OnBoardStatus.on_boarded, "None", log_name) else: # on board failed, add the error to the table aws_services.put_instance_to_dynamo_table( instance_id, instance_details['address'], OnBoardStatus.on_boarded_failed, error_message, log_name) pvwa_integration_class.logoff_pvwa(store_parameters_class.pvwa_url, session_token) aws_services.release_session_on_dynamo(pvwa_connection_number, session_guid) return True
def lambda_handler(event, context): logName = context.log_stream_name if context.log_stream_name else "None" instanceId, actionType = event.split(";") try: instanceDetails = aws_services.get_ec2_details(instanceId, context) instanceData = aws_services.get_instance_data_from_dynamo_table( instanceId) if actionType == 'terminated': if not instanceData: print('Item {0} does not exists on DB'.format(instanceId)) return None else: instanceStatus = instanceData["Status"]["S"] if instanceStatus == OnBoardStatus.OnBoarded_Failed: print( "Item {0} is in status OnBoard failed, removing from DynamoDB table" .format(instanceId)) aws_services.remove_instance_from_dynamo_table(instanceId) return None elif actionType == 'running': if not instanceDetails[ "address"]: # In case querying AWS return empty address print("Retrieving Instance address from AWS failed.") return None if instanceData: instanceStatus = instanceData["Status"]["S"] if instanceStatus == OnBoardStatus.OnBoarded: print( 'Item: {0}, exists on DB, no need to add it to vault'. format(instanceId)) return None elif instanceStatus == OnBoardStatus.OnBoarded_Failed: print( "Item {0} exists with status 'OnBoard failed', adding to vault" .format(instanceId)) else: print('Item {0} does not exists on DB, adding to vault'. format(instanceId)) else: print('Unknown instance state') return storeParametersClass = aws_services.get_params_from_param_store() if not storeParametersClass: return pvwaConnectionnumber, sessionGuid = aws_services.get_available_session_from_dynamo( ) if not pvwaConnectionnumber: return sessionToken = pvwa_integration.logon_pvwa( storeParametersClass.vaultUsername, storeParametersClass.vaultPassword, storeParametersClass.pvwaURL, pvwaConnectionnumber) if not sessionToken: return if actionType == 'terminated': instance_processing.delete_instance(instanceId, sessionToken, storeParametersClass, instanceData, instanceDetails) elif actionType == 'running': instance_processing.create_instance(instanceId, sessionToken, instanceDetails, storeParametersClass, logName) else: print('Unknown instance state') return pvwa_integration.logoff_pvwa(storeParametersClass.pvwaURL, sessionToken) aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid) except Exception as e: print("Unknown error occurred:{0}".format(e)) if actionType == 'terminated': # put_instance_to_dynamo_table(instanceId, instanceDetails["address"]\ # , OnBoardStatus.Delete_Failed, str(e), logName) aws_services.update_instances_table_status( instanceId, OnBoardStatus.Delete_Failed, str(e)) elif actionType == 'running': aws_services.put_instance_to_dynamo_table( instanceId, instanceDetails["address"], OnBoardStatus.OnBoarded_Failed, str(e), logName) # TODO: Retry mechanism? aws_services.release_session_on_dynamo(pvwaConnectionnumber, sessionGuid) return