|
|
@ -2,10 +2,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
""" Base class for classes to control any kind of machine: vm, bare metal, cloudified """
|
|
|
|
""" Base class for classes to control any kind of machine: vm, bare metal, cloudified """
|
|
|
|
|
|
|
|
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
# from typing import Optional
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
from typing import Optional, Any
|
|
|
|
|
|
|
|
|
|
|
|
from app.config import MachineConfig
|
|
|
|
from app.config import MachineConfig
|
|
|
|
|
|
|
|
from app.exceptions import ConfigurationError
|
|
|
|
from app.interface_sfx import CommandlineColors
|
|
|
|
from app.interface_sfx import CommandlineColors
|
|
|
|
from plugins.base.plugin_base import BasePlugin
|
|
|
|
from plugins.base.plugin_base import BasePlugin
|
|
|
|
|
|
|
|
|
|
|
@ -34,31 +36,31 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
###############
|
|
|
|
###############
|
|
|
|
# This is stuff you might want to implement
|
|
|
|
# This is stuff you might want to implement
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
def __init__(self) -> None:
|
|
|
|
super().__init__()
|
|
|
|
super().__init__()
|
|
|
|
self.connection = None # Connection
|
|
|
|
self.connection = None # Connection
|
|
|
|
self.config = None
|
|
|
|
self.config: Optional[MachineConfig] = None
|
|
|
|
|
|
|
|
|
|
|
|
def create(self, reboot: bool = True):
|
|
|
|
def create(self, reboot: bool = True) -> None:
|
|
|
|
""" Create a machine
|
|
|
|
""" Create a machine
|
|
|
|
|
|
|
|
|
|
|
|
@param reboot: Reboot the machine after creation
|
|
|
|
@param reboot: Reboot the machine after creation
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def up(self): # pylint: disable=invalid-name
|
|
|
|
def up(self) -> None: # pylint: disable=invalid-name
|
|
|
|
""" Start a machine, create it if it does not exist """
|
|
|
|
""" Start a machine, create it if it does not exist """
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def halt(self):
|
|
|
|
def halt(self) -> None:
|
|
|
|
""" Halt a machine """
|
|
|
|
""" Halt a machine """
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def destroy(self):
|
|
|
|
def destroy(self) -> None:
|
|
|
|
""" Destroy a machine """
|
|
|
|
""" Destroy a machine """
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def connect(self):
|
|
|
|
def connect(self) -> Any:
|
|
|
|
""" Connect to a machine
|
|
|
|
""" Connect to a machine
|
|
|
|
|
|
|
|
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
@ -66,18 +68,19 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def remote_run(self, cmd: str, disown: bool = False):
|
|
|
|
def remote_run(self, cmd: str, disown: bool = False) -> str:
|
|
|
|
""" Connects to the machine and runs a command there
|
|
|
|
""" Connects to the machine and runs a command there
|
|
|
|
|
|
|
|
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
|
|
|
|
|
|
|
|
:param cmd: command to run int he machine's shell
|
|
|
|
:param cmd: command to run int he machine's shell
|
|
|
|
:param disown: Send the connection into background
|
|
|
|
:param disown: Send the connection into background
|
|
|
|
|
|
|
|
:returns: the results as string
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
def disconnect(self) -> None:
|
|
|
|
""" Disconnect from a machine
|
|
|
|
""" Disconnect from a machine
|
|
|
|
|
|
|
|
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
@ -85,7 +88,7 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def put(self, src: str, dst: str):
|
|
|
|
def put(self, src: str, dst: str) -> Any:
|
|
|
|
""" Send a file to a machine
|
|
|
|
""" Send a file to a machine
|
|
|
|
|
|
|
|
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
@ -95,7 +98,7 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def get(self, src: str, dst: str):
|
|
|
|
def get(self, src: str, dst: str) -> Any:
|
|
|
|
""" Get a file to a machine
|
|
|
|
""" Get a file to a machine
|
|
|
|
|
|
|
|
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
|
If you want to use SSH, check out the class SSHFeatures, it is already implemented there
|
|
|
@ -105,7 +108,7 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def is_running(self):
|
|
|
|
def is_running(self) -> bool:
|
|
|
|
""" Returns if the machine is running """
|
|
|
|
""" Returns if the machine is running """
|
|
|
|
return self.get_state() == MachineStates.RUNNING
|
|
|
|
return self.get_state() == MachineStates.RUNNING
|
|
|
|
|
|
|
|
|
|
|
@ -121,21 +124,37 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def get_paw(self):
|
|
|
|
def get_paw(self) -> str:
|
|
|
|
""" Returns the paw of the current machine """
|
|
|
|
""" Returns the paw of the current machine """
|
|
|
|
return self.config.caldera_paw()
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
def get_group(self):
|
|
|
|
paw = self.config.caldera_paw()
|
|
|
|
|
|
|
|
if paw is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
return paw
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_group(self) -> str:
|
|
|
|
""" Returns the group of the current machine """
|
|
|
|
""" Returns the group of the current machine """
|
|
|
|
return self.config.caldera_group()
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
def get_os(self):
|
|
|
|
group = self.config.caldera_group()
|
|
|
|
|
|
|
|
if group is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
return group
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_os(self) -> str:
|
|
|
|
""" Returns the OS of the machine """
|
|
|
|
""" Returns the OS of the machine """
|
|
|
|
|
|
|
|
if self.config is None:
|
|
|
|
return self.config.os()
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
the_os = self.config.os()
|
|
|
|
def get_playground(self):
|
|
|
|
if the_os is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
return the_os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_playground(self) -> Optional[str]:
|
|
|
|
""" Path on the machine where all the attack tools will be copied to. """
|
|
|
|
""" Path on the machine where all the attack tools will be copied to. """
|
|
|
|
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
|
|
|
|
return self.config.get_playground()
|
|
|
|
return self.config.get_playground()
|
|
|
|
|
|
|
|
|
|
|
@ -145,36 +164,46 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
@returns: the machine name
|
|
|
|
@returns: the machine name
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
|
|
|
|
return self.config.vmname()
|
|
|
|
return self.config.vmname()
|
|
|
|
|
|
|
|
|
|
|
|
def get_machine_path_internal(self):
|
|
|
|
def get_machine_path_internal(self) -> str:
|
|
|
|
""" The vm internal path for all the data """
|
|
|
|
""" The vm internal path for all the data """
|
|
|
|
|
|
|
|
|
|
|
|
# Maybe we do not need that ! playground should replace it
|
|
|
|
# Maybe we do not need that ! playground should replace it
|
|
|
|
|
|
|
|
|
|
|
|
raise NotImplementedError
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
def get_machine_path_external(self):
|
|
|
|
def get_machine_path_external(self) -> str:
|
|
|
|
""" The path on the controlling host where vm specific data is stored """
|
|
|
|
""" The path on the controlling host where vm specific data is stored """
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
|
|
|
|
return os.path.join(self.config.vagrantfilepath(), self.config.machinepath())
|
|
|
|
return os.path.join(self.config.vagrantfilepath(), self.config.machinepath())
|
|
|
|
|
|
|
|
|
|
|
|
###############
|
|
|
|
###############
|
|
|
|
# This is the interface from the main code to the plugin system. Do not touch
|
|
|
|
# This is the interface from the main code to the plugin system. Do not touch
|
|
|
|
def __call_halt__(self):
|
|
|
|
def __call_halt__(self) -> None:
|
|
|
|
""" Wrapper around halt """
|
|
|
|
""" Wrapper around halt """
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.config is None:
|
|
|
|
|
|
|
|
raise ConfigurationError
|
|
|
|
|
|
|
|
|
|
|
|
self.vprint(f"{CommandlineColors.OKBLUE}Stopping machine: {self.config.vmname()} {CommandlineColors.ENDC}", 1)
|
|
|
|
self.vprint(f"{CommandlineColors.OKBLUE}Stopping machine: {self.config.vmname()} {CommandlineColors.ENDC}", 1)
|
|
|
|
self.halt()
|
|
|
|
self.halt()
|
|
|
|
self.vprint(f"{CommandlineColors.OKGREEN}Machine stopped: {self.config.vmname()}{CommandlineColors.ENDC}", 1)
|
|
|
|
self.vprint(f"{CommandlineColors.OKGREEN}Machine stopped: {self.config.vmname()}{CommandlineColors.ENDC}", 1)
|
|
|
|
|
|
|
|
|
|
|
|
def __call_process_config__(self, config: MachineConfig):
|
|
|
|
def __call_process_config__(self, config: MachineConfig) -> None:
|
|
|
|
""" Wrapper around process_config """
|
|
|
|
""" Wrapper around process_config """
|
|
|
|
|
|
|
|
|
|
|
|
# print("===========> Processing config")
|
|
|
|
# print("===========> Processing config")
|
|
|
|
self.config = config
|
|
|
|
self.config = config
|
|
|
|
self.process_config(config.raw_config.__dict__)
|
|
|
|
self.process_config(config.raw_config.__dict__)
|
|
|
|
|
|
|
|
|
|
|
|
def __call_remote_run__(self, cmd: str, disown: bool = False):
|
|
|
|
def __call_remote_run__(self, cmd: str, disown: bool = False) -> str:
|
|
|
|
""" Simplifies connect and run
|
|
|
|
""" Simplifies connect and run
|
|
|
|
|
|
|
|
|
|
|
|
@param cmd: Command to run as shell command
|
|
|
|
@param cmd: Command to run as shell command
|
|
|
@ -183,22 +212,22 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
|
|
|
|
|
|
|
|
return self.remote_run(cmd, disown)
|
|
|
|
return self.remote_run(cmd, disown)
|
|
|
|
|
|
|
|
|
|
|
|
def __call_disconnect__(self):
|
|
|
|
def __call_disconnect__(self) -> None:
|
|
|
|
""" Command connection dis-connect """
|
|
|
|
""" Command connection dis-connect """
|
|
|
|
|
|
|
|
|
|
|
|
self.disconnect()
|
|
|
|
self.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
def __call_connect__(self):
|
|
|
|
def __call_connect__(self) -> None:
|
|
|
|
""" command connection. establish it """
|
|
|
|
""" command connection. establish it """
|
|
|
|
|
|
|
|
|
|
|
|
return self.connect()
|
|
|
|
return self.connect()
|
|
|
|
|
|
|
|
|
|
|
|
def __call_up__(self):
|
|
|
|
def __call_up__(self) -> None:
|
|
|
|
""" Starts a VM. Creates it if not already created """
|
|
|
|
""" Starts a VM. Creates it if not already created """
|
|
|
|
|
|
|
|
|
|
|
|
self.up()
|
|
|
|
self.up()
|
|
|
|
|
|
|
|
|
|
|
|
def __call_create__(self, reboot: bool = True):
|
|
|
|
def __call_create__(self, reboot: bool = True) -> None:
|
|
|
|
""" Create a VM
|
|
|
|
""" Create a VM
|
|
|
|
|
|
|
|
|
|
|
|
@param reboot: Reboot the VM during installation. Required if you want to install software
|
|
|
|
@param reboot: Reboot the VM during installation. Required if you want to install software
|
|
|
@ -206,7 +235,7 @@ class MachineryPlugin(BasePlugin):
|
|
|
|
|
|
|
|
|
|
|
|
self.create(reboot)
|
|
|
|
self.create(reboot)
|
|
|
|
|
|
|
|
|
|
|
|
def __call_destroy__(self):
|
|
|
|
def __call_destroy__(self) -> None:
|
|
|
|
""" Destroys the current machine """
|
|
|
|
""" Destroys the current machine """
|
|
|
|
|
|
|
|
|
|
|
|
self.destroy()
|
|
|
|
self.destroy()
|
|
|
|