working on it

This commit is contained in:
n0mad1k
2025-04-11 09:56:44 -04:00
parent 4e08f52d77
commit a7490bd655
9 changed files with 983 additions and 233 deletions
+78 -125
View File
@@ -436,12 +436,22 @@ def select_random_region(config):
if provider == "aws":
region_choices = config.get("aws_region_choices", [])
elif provider == "linode":
# Look for multiple possible key names in the Linode config
region_choices = config.get("region_choices", [])
if not region_choices:
region_choices = config.get("linode_region_choices", [])
elif provider == "flokinet":
region_choices = config.get("flokinet_region_choices", [])
if not region_choices:
logging.warning(f"No region choices found for {provider}")
# Fallback regions by provider if none found in config
if provider == "linode":
region_choices = ["us-east", "us-central", "eu-west", "ap-south"]
logging.info(f"Using fallback regions for Linode: {region_choices}")
if not region_choices:
return None
# Select random region
@@ -492,24 +502,9 @@ def create_inventory_file(config, deployment_type):
def run_ansible_playbook(playbook, inventory, config, debug=False):
"""Run an Ansible playbook with the given inventory and config"""
# Check if playbook exists
if not os.path.exists(playbook):
logging.error(f"Playbook {playbook} not found")
return False, "", f"ERROR! the playbook: {playbook} could not be found"
# Convert config dict to JSON for extra-vars
# Filter out None values and complex objects
extra_vars = {k: v for k, v in config.items() if v is not None and not isinstance(v, (dict, list, tuple))}
# Ensure ssh_user is defined to avoid template recursion
if 'ssh_user' not in extra_vars:
provider = config.get('provider')
if provider and provider in DEFAULT_SSH_USER:
extra_vars['ssh_user'] = DEFAULT_SSH_USER[provider]
else:
extra_vars['ssh_user'] = "root"
# Convert to JSON
extra_vars_json = json.dumps(extra_vars)
# Build command
@@ -522,18 +517,16 @@ def run_ansible_playbook(playbook, inventory, config, debug=False):
# Add verbosity if debug mode is enabled
if debug:
cmd.append("-vvvv")
cmd.append("-vvv")
else:
# Add some verbosity even in non-debug mode to capture errors
# Always add some verbosity for basic output
cmd.append("-v")
# Add environment variables to avoid hangs on confirmations
cmd_env = os.environ.copy()
cmd_env["ANSIBLE_HOST_KEY_CHECKING"] = "False"
cmd_env["ANSIBLE_NOCOLOR"] = "True"
# Log the command
logging.debug(f"Running Ansible command: {' '.join(cmd)}")
if debug:
logging.debug(f"Running Ansible command: {' '.join(cmd)}")
else:
logging.info(f"Running Ansible playbook: {playbook}")
# Run the command
try:
@@ -542,28 +535,20 @@ def run_ansible_playbook(playbook, inventory, config, debug=False):
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
env=cmd_env
text=True
)
logging.info(f"Playbook {playbook} executed successfully")
# Log the stdout/stderr, but truncate if too long
if len(result.stdout) > 0:
logging.debug(f"Command stdout: {result.stdout}")
if len(result.stderr) > 0:
logging.debug(f"Command stderr: {result.stderr}")
return True, result.stdout, result.stderr
except subprocess.CalledProcessError as e:
# More detailed error logging
logging.error(f"Playbook {playbook} failed with exit code {e.returncode}")
logging.error(f"Command stdout: {e.stdout}")
logging.error(f"Command stderr: {e.stderr}")
if debug:
logging.error(f"Command stdout: {e.stdout}")
logging.error(f"Command stderr: {e.stderr}")
return False, e.stdout, e.stderr
def deploy_infrastructure(config):
"""Deploy infrastructure based on provider and configuration"""
provider = config['provider']
provider_dir = PROVIDER_DIRS.get(provider, provider.upper())
logging.info(f"Deploying {provider} infrastructure...")
# Set provider-specific environment variables
@@ -577,58 +562,68 @@ def deploy_infrastructure(config):
os.environ['LINODE_TOKEN'] = config['linode_token']
# Select random region if not specified
if provider == "aws" and not config.get('aws_region'):
config['aws_region'] = select_random_region(config)
elif provider == "linode" and not config.get('linode_region'):
config['linode_region'] = select_random_region(config)
if not config.get('region'):
config['region'] = select_random_region(config)
# Determine playbook path based on deployment mode
if config.get('deploy_tracker'):
playbook = f"{provider_dir}/tracker.yml"
deployment_type = "tracker"
elif config.get('redirector_only'):
# Determine which components to deploy
success = True
# Use the correct case for provider directory
provider_dir = provider.capitalize() if provider != "aws" else provider.upper()
# Redirector deployment
if not config.get('c2_only'):
playbook = f"{provider_dir}/redirector.yml"
deployment_type = "redirector"
elif config.get('c2_only'):
playbook = f"{provider_dir}/c2.yml"
deployment_type = "c2"
else:
# Full deployment
if provider == "flokinet":
# FlokiNET requires separate deployments for redirector and C2
redirector_success = deploy_flokinet_redirector(config)
if not redirector_success:
return False
# If redirector deployed successfully, deploy C2
return deploy_flokinet_c2(config)
else:
# AWS and Linode use a single playbook for full deployment
playbook = f"{provider_dir}/c2-deploy.yaml"
deployment_type = "local"
# Create inventory file
inventory_path = create_inventory_file(config, deployment_type)
# Run the playbook
try:
success, stdout, stderr = run_ansible_playbook(
inventory_path = create_inventory_file(config, "local")
logging.info(f"Deploying redirector using {playbook}")
redirector_success, stdout, stderr = run_ansible_playbook(
playbook, inventory_path, config, config.get('debug', False)
)
# Clean up inventory file
# Log Ansible output
if config.get('debug', False):
logging.debug(f"Ansible stdout: {stdout}")
if stderr:
logging.debug(f"Ansible stderr: {stderr}")
else:
# Always log at least a summary even without debug mode
logging.info(f"Redirector deployment {'succeeded' if redirector_success else 'failed'}")
if os.path.exists(inventory_path):
os.unlink(inventory_path)
if not redirector_success:
logging.error("Redirector deployment failed")
return False
# C2 deployment
if not config.get('redirector_only'):
playbook = f"{provider_dir}/c2.yml"
inventory_path = create_inventory_file(config, "local")
return success
except Exception as e:
logging.error(f"Deployment failed: {e}")
logging.info(f"Deploying C2 server using {playbook}")
c2_success, stdout, stderr = run_ansible_playbook(
playbook, inventory_path, config, config.get('debug', False)
)
# Log Ansible output
if config.get('debug', False):
logging.debug(f"Ansible stdout: {stdout}")
if stderr:
logging.debug(f"Ansible stderr: {stderr}")
else:
# Always log at least a summary even without debug mode
logging.info(f"C2 server deployment {'succeeded' if c2_success else 'failed'}")
# Clean up inventory file
if os.path.exists(inventory_path):
os.unlink(inventory_path)
return False
if not c2_success:
logging.error("C2 server deployment failed")
return False
return True
def deploy_flokinet_redirector(config):
"""Deploy FlokiNET redirector separately"""
@@ -744,6 +739,8 @@ def run_tests(config):
else:
logging.warning(f"No tests playbook found at {playbook}")
def ssh_to_instance(config):
"""SSH into the deployed instance"""
logging.info("Connecting to instance via SSH...")
@@ -815,62 +812,18 @@ def ssh_to_instance(config):
def cleanup_resources(config):
"""Clean up resources if deployment fails"""
provider = config.get('provider')
if not provider:
logging.warning("No provider specified for cleanup")
return
provider_dir = PROVIDER_DIRS.get(provider, provider.upper())
logging.info(f"Cleaning up {provider} resources...")
# Use the correct case for provider directory
provider_dir = provider.capitalize() if provider != "aws" else provider.upper()
# Use Ansible for cleanup
playbook = f"{provider_dir}/cleanup.yml"
if os.path.exists(playbook):
logging.info(f"Running cleanup playbook: {playbook}")
# Create a new config with string values to avoid template recursion issues
safe_config = config.copy()
# Set confirm_cleanup to "false" as a string value
safe_config['confirm_cleanup'] = "false"
# Set ssh_user explicitly if it's missing to avoid template recursion
if 'ssh_user' not in safe_config:
if provider in DEFAULT_SSH_USER:
safe_config['ssh_user'] = DEFAULT_SSH_USER[provider]
else:
safe_config['ssh_user'] = "root"
inventory_path = create_inventory_file(safe_config, "local")
inventory_path = create_inventory_file(config, "local")
try:
# Run with non-interactive mode to bypass confirmation prompts
cmd_env = os.environ.copy()
cmd_env["ANSIBLE_HOST_KEY_CHECKING"] = "False"
cmd_env["ANSIBLE_NOCOLOR"] = "True"
# Build custom command with additional environment variables
playbook_cmd = [
"ansible-playbook",
"-i", inventory_path,
playbook,
"-e", json.dumps(safe_config),
"--extra-vars", "confirm_cleanup=false",
"-v"
]
# Run command with environment variables
result = subprocess.run(
playbook_cmd,
env=cmd_env,
check=False, # Don't check return code to allow for partial cleanup
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Log output regardless of success/failure
if result.stdout:
logging.info(f"Cleanup playbook output: {result.stdout}")
if result.stderr:
logging.warning(f"Cleanup playbook errors: {result.stderr}")
run_ansible_playbook(playbook, inventory_path, config)
except Exception as e:
logging.error(f"Cleanup playbook failed: {e}")
finally: