-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove old implementation with new one
- Loading branch information
1 parent
8ae7353
commit 3abd7f8
Showing
12 changed files
with
819 additions
and
1,065 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +0,0 @@ | ||
CONFIG_SCHEMA = { | ||
"name": {"required": True, "type": "string"}, | ||
"containers": {"required": True, "type": "list"}, | ||
"users": {"required": True, "type": "list"}, | ||
"identityFile": {"required": True, "type": "list"}, | ||
"hosts": {"required": True, "type": "list"}, | ||
"metrics": { | ||
"required": True, | ||
"type": "dict", | ||
"schema": { | ||
"percentage": { | ||
"required": True, | ||
"type": "dict", | ||
"schema": { | ||
"value": {"required": True, "type": "number", "min": 0, "max": 100}, | ||
"trend": { | ||
"type": "string", | ||
"nullable": True, | ||
"regex": "^(?i)(down|equal|up)$", | ||
}, | ||
}, | ||
} | ||
}, | ||
}, | ||
} | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
|
||
import os | ||
import sys | ||
from typing import List | ||
import yamale | ||
import click | ||
import pathlib | ||
|
||
from ipaddress import ip_network | ||
from yamale import YamaleError | ||
from yamale.validators import DefaultValidators | ||
|
||
sys.path.append(os.getcwd()) | ||
from src.host import Host | ||
from src.log_config import get_logger | ||
from src.utils import Path | ||
|
||
|
||
logger = get_logger("ctf_creator.ctf") | ||
|
||
class CTFCreator(): | ||
def __init__(self, config: str, save_path: str) -> None: | ||
self.config = self._get_config(config) | ||
logger.info(f"Containers: {self.config.get('containers')}") | ||
logger.info(f"Users: {self.config.get('users')}") | ||
logger.info(f"Key: {self.config.get('key')}") | ||
logger.info(f"Hosts: {self.config.get('hosts')}") | ||
logger.info( | ||
f"IP-Address Subnet-base: {self.config.get('subnet')} " | ||
) | ||
|
||
self.save_path = save_path | ||
self.subnet = ip_network(self.config.get("subnet")) | ||
|
||
def _get_config(self, config: dict) -> dict: | ||
try: | ||
validators = DefaultValidators.copy() # This is a dictionary | ||
validators[Path.tag] = Path | ||
schema = yamale.make_schema(f'{pathlib.Path(__file__).parent.resolve()}/schema.yaml', validators=validators) | ||
# Create a Data object | ||
data = yamale.make_data(content=config) | ||
# Validate data against the schema. Throws a ValueError if data is invalid. | ||
yamale.validate(schema, data) | ||
|
||
logger.info("YAML file loaded successfully.") | ||
|
||
return data[0][0] | ||
except YamaleError as e: | ||
logger.error('Validation failed!\n') | ||
for result in e.results: | ||
logger.error("Error validating data '%s' with '%s'\n\t" % (result.data, result.schema)) | ||
for error in result.errors: | ||
logger.error('\t%s' % error) | ||
exit(1) | ||
|
||
def _get_hosts(self) -> List: | ||
hosts = [] | ||
for host in self.config.get('hosts'): | ||
host_object = Host(host=host, save_path=self.save_path) | ||
hosts.append(host_object) | ||
host_object.clean_up() | ||
return hosts | ||
|
||
def _extract_ovpn_info(self, file_path): | ||
""" | ||
Extracts the host IP address, port number, and subnet from an OpenVPN configuration file. | ||
Args: | ||
file_path (str): Path to the OpenVPN configuration file. | ||
Returns: | ||
tuple: A tuple containing: | ||
- host_ip_address (str): The IP address found after the 'remote' keyword. | ||
- port_number (int): The port number found after the IP address on the 'remote' line. | ||
""" | ||
if not os.path.exists(file_path): | ||
logger.error(f"File {file_path} does not exist.") | ||
return None | ||
|
||
host_ip_address = None | ||
port_number = None | ||
|
||
with open(file_path, "r") as file: | ||
lines = file.readlines() | ||
|
||
for line in lines: | ||
if line.startswith("remote "): | ||
parts = line.split() | ||
if len(parts) == 3: | ||
host_ip_address = parts[1] | ||
port_number = int(parts[2]) | ||
|
||
if host_ip_address and port_number: | ||
return host_ip_address, port_number | ||
else: | ||
logger.error("Failed to extract all necessary information.") | ||
return None | ||
|
||
def create_challenge(self): | ||
logger.info("Set up hosts.") | ||
self.hosts = self._get_hosts() | ||
logger.info("Begin set up of challenge.") | ||
|
||
http_port = 40000 | ||
openvpn_port = 50000 | ||
challenge_counter = 1 | ||
next_network = self.subnet | ||
|
||
# TODO Handle issue, when address for OpenVPN or HTTP Port is already in use. | ||
for idx, user in enumerate(self.config.get("users")): | ||
if os.path.exists(f"{self.save_path}/data/{user}"): | ||
logger.info(f"OpenVPN data exists for the user: {user}") | ||
logger.info(f"Data for the user: {user} will NOT be changed. Starting OVPN Docker container with existing data.") | ||
ip, openvpn_port = self._extract_ovpn_info(f"{self.save_path}/data/{user}/client.ovpn") | ||
|
||
logger.info(f"Get host with IP {ip}") | ||
host: Host = [d for d in self.hosts if str(d.ip) == ip][0] | ||
|
||
host.send_and_extract_tar(user=user) | ||
host.start_openvpn(user, openvpn_port, http_port, next_network) | ||
else: | ||
logger.info( | ||
f"For the user: {user}, an OpenVPN configuration file will be generated!" | ||
) | ||
host: Host = self.hosts[idx % len(self.hosts)] | ||
host.start_openvpn(user, openvpn_port + challenge_counter, http_port + challenge_counter, next_network) | ||
|
||
# TODO handle start containers | ||
# host.start_containers(user, self.config.get("containers")) | ||
|
||
next_network = ip_network((int(next_network.network_address) + next_network.num_addresses), strict=False) | ||
challenge_counter += 1 | ||
|
||
|
||
@click.command() | ||
@click.option( | ||
"--config", | ||
required=True, | ||
help="The path to the .yaml configuration file for the CTF-Creator.", | ||
type=click.File('r', encoding='utf8'), | ||
) | ||
@click.option( | ||
"--save", | ||
required=True, | ||
help="The path where you want to save the user data for the CTF-Creator. E.g. /home/nick/ctf-creator", | ||
type=click.Path(writable=True), | ||
) | ||
def main(config, save): | ||
ctfcreator = CTFCreator(config=config.read(), save_path=save) | ||
ctfcreator.create_challenge() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.