From d3054a32772c391f6650b28be489b5cf7477812c Mon Sep 17 00:00:00 2001 From: foroogh shahab Date: Sun, 21 Apr 2024 15:27:05 +0200 Subject: [PATCH] Level 15 is added, this level has 4 nodes with 1 flag. --- emulation-system/envs/050/level_15/Makefile | 13 + emulation-system/envs/050/level_15/README.MD | 47 + emulation-system/envs/050/level_15/config.py | 1367 +++++++++++++++++ emulation-system/envs/050/level_15/env.png | Bin 0 -> 60580 bytes .../envs/050/level_15/test_config.py | 34 + emulation-system/envs/Makefile | 12 + 6 files changed, 1473 insertions(+) create mode 100644 emulation-system/envs/050/level_15/Makefile create mode 100644 emulation-system/envs/050/level_15/README.MD create mode 100644 emulation-system/envs/050/level_15/config.py create mode 100644 emulation-system/envs/050/level_15/env.png create mode 100644 emulation-system/envs/050/level_15/test_config.py diff --git a/emulation-system/envs/050/level_15/Makefile b/emulation-system/envs/050/level_15/Makefile new file mode 100644 index 000000000..4584488d6 --- /dev/null +++ b/emulation-system/envs/050/level_15/Makefile @@ -0,0 +1,13 @@ + +# Installs the configuration in the metastore +install: + python config.py --install + +# Uninstalls the configuration from the metastore +uninstall: + python config.py --uninstall + +# Cleans all configuration files +clean_config: + rm -rf ./config.json + rm -rf ./containers diff --git a/emulation-system/envs/050/level_15/README.MD b/emulation-system/envs/050/level_15/README.MD new file mode 100644 index 000000000..bb46ad6f1 --- /dev/null +++ b/emulation-system/envs/050/level_15/README.MD @@ -0,0 +1,47 @@ +# Capture the Flag - Level 15 + +An emulation environment with a set of nodes that run common networked services such as SSH, Kafka, +etc. Some of the services are vulnerable to simple dictionary attacks as they use weak passwords. +The task of an attacker agent is to identify the vulnerabilities and exploit them and discover hidden flags +on the nodes. Conversely, the task of the defender is to harden the defense of the nodes and to detect the +attacker. + +- Number of nodes: 4 +- IDS: Yes (Snort) +- Traffic generation: Yes +- Number of flags: 1 +- Vulnerabilities: SSH, FTP, Telnet servers that can be compromised using dictionary attacks + +## Architecture +

+ +

+ + +## Useful commands + +```bash +make install # Install the emulation in the metastore +make uninstall # Uninstall the emulation from the metastore +make clean_config # Clean config files +docker container ls --all # list all running containers +docker image ls --all # list all images +docker system prune # remove unused images and containers +docker container prune # remove stopped containers +sudo useradd -rm -d /home/csle_admin -s /bin/bash -g root -G sudo -p "$(openssl passwd -1 'csle@admin-pw_191')" csle_admin +docker run --name=iperf3 -d --restart=unless-stopped -p 5201:5201/tcp -p 5201:5201/udp mlabbe/iperf3 # Start the iperf server on the host +iperf3 -R -c # network performance, where is the IP where the iperf server is running e.g. the host 172.31.212.92 +``` + +## Author & Maintainer + +Kim Hammar +Forough Shahab + +## Copyright and license + +[LICENSE](../../../../../LICENSE.md) + +Creative Commons + +(C) 2020-2024, Kim Hammar \ No newline at end of file diff --git a/emulation-system/envs/050/level_15/config.py b/emulation-system/envs/050/level_15/config.py new file mode 100644 index 000000000..1fa01ac8b --- /dev/null +++ b/emulation-system/envs/050/level_15/config.py @@ -0,0 +1,1367 @@ +from typing import Dict, List, Union +import argparse +import os +import multiprocessing +import csle_common.constants.constants as constants +import csle_collector.constants.constants as collector_constants +from csle_collector.client_manager.dao.constant_arrival_config import ConstantArrivalConfig +from csle_collector.client_manager.dao.workflows_config import WorkflowsConfig +from csle_collector.client_manager.dao.workflow_service import WorkflowService +from csle_collector.client_manager.dao.workflow_markov_chain import WorkflowMarkovChain +from csle_collector.client_manager.dao.client import Client +from csle_common.dao.emulation_config.topology_config import TopologyConfig +from csle_common.dao.emulation_config.node_firewall_config import NodeFirewallConfig +from csle_common.dao.emulation_config.default_network_firewall_config import DefaultNetworkFirewallConfig +from csle_common.dao.emulation_config.containers_config import ContainersConfig +from csle_common.dao.emulation_config.node_container_config import NodeContainerConfig +from csle_common.dao.emulation_config.container_network import ContainerNetwork +from csle_common.dao.emulation_config.flags_config import FlagsConfig +from csle_common.dao.emulation_config.node_flags_config import NodeFlagsConfig +from csle_common.dao.emulation_config.resources_config import ResourcesConfig +from csle_common.dao.emulation_config.node_resources_config import NodeResourcesConfig +from csle_common.dao.emulation_config.node_network_config import NodeNetworkConfig +from csle_common.dao.emulation_config.packet_loss_type import PacketLossType +from csle_common.dao.emulation_config.packet_delay_distribution_type import PacketDelayDistributionType +from csle_common.dao.emulation_config.traffic_config import TrafficConfig +from csle_common.dao.emulation_config.node_traffic_config import NodeTrafficConfig +from csle_common.dao.emulation_config.users_config import UsersConfig +from csle_common.dao.emulation_config.node_users_config import NodeUsersConfig +from csle_common.dao.emulation_config.vulnerabilities_config import VulnerabilitiesConfig +from csle_common.dao.emulation_config.emulation_env_config import EmulationEnvConfig +from csle_common.controllers.emulation_env_controller import EmulationEnvController +from csle_common.dao.emulation_config.client_population_config import ClientPopulationConfig +from csle_common.dao.emulation_config.kafka_config import KafkaConfig +from csle_common.dao.emulation_config.kafka_topic import KafkaTopic +from csle_common.util.experiment_util import ExperimentUtil +from csle_common.dao.emulation_config.flag import Flag +from csle_common.dao.emulation_config.node_vulnerability_config import NodeVulnerabilityConfig +from csle_common.dao.emulation_config.credential import Credential +from csle_common.dao.emulation_config.vulnerability_type import VulnType +from csle_common.dao.emulation_config.transport_protocol import TransportProtocol +from csle_common.dao.emulation_config.node_services_config import NodeServicesConfig +from csle_common.dao.emulation_config.services_config import ServicesConfig +from csle_common.dao.emulation_config.network_service import NetworkService +from csle_common.dao.emulation_config.ovs_config import OVSConfig +from csle_common.dao.emulation_config.sdn_controller_config import SDNControllerConfig +from csle_common.dao.emulation_config.user import User +from csle_common.dao.emulation_action.attacker.emulation_attacker_action import EmulationAttackerAction +from csle_common.dao.emulation_config.host_manager_config import HostManagerConfig +from csle_common.dao.emulation_config.snort_ids_manager_config import SnortIDSManagerConfig +from csle_common.dao.emulation_config.ossec_ids_manager_config import OSSECIDSManagerConfig +from csle_common.dao.emulation_config.docker_stats_manager_config import DockerStatsManagerConfig +from csle_common.dao.emulation_config.elk_config import ElkConfig +from csle_common.dao.emulation_config.beats_config import BeatsConfig +from csle_common.dao.emulation_config.node_beats_config import NodeBeatsConfig + + +def default_config(name: str, network_id: int = 15, level: int = 15, version: str = "0.5.0", + time_step_len_seconds: int = 15) -> EmulationEnvConfig: + """ + Returns the default configuration of the emulation environment + + :param name: the name of the emulation + :param network_id: the network id of the emulation + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the emulation environment configuration + """ + containers_cfg = default_containers_config(network_id=network_id, level=level, version=version) + flags_cfg = default_flags_config(network_id=network_id) + resources_cfg = default_resource_constraints_config(network_id=network_id, level=level) + topology_cfg = default_topology_config(network_id=network_id) + traffic_cfg = default_traffic_config(network_id=network_id, time_step_len_seconds=time_step_len_seconds) + users_cfg = default_users_config(network_id=network_id) + vuln_cfg = default_vulns_config(network_id=network_id) + kafka_cfg = default_kafka_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + services_cfg = default_services_config(network_id=network_id) + descr = "An emulation environment with a set of nodes that run common networked services " \ + "such as SSH, FTP, Telnet, IRC, Kafka," \ + " etc. Some of the services are vulnerable to simple dictionary attacks as " \ + "they use weak passwords." \ + "The task of an attacker agent is to identify the vulnerabilities and exploit them and " \ + "discover hidden flags" \ + "on the nodes. Conversely, the task of the defender is to harden the defense of the nodes " \ + "and to detect the" \ + "attacker." + static_attackers_cfg = default_static_attacker_sequences(topology_cfg.subnetwork_masks) + ovs_cfg = default_ovs_config(network_id=network_id, level=level, version=version) + sdn_controller_cfg = default_sdn_controller_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + host_manager_cfg = default_host_manager_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + snort_ids_manager_cfg = default_snort_ids_manager_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + ossec_ids_manager_cfg = default_ossec_ids_manager_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + docker_stats_manager_cfg = default_docker_stats_manager_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + elk_cfg = default_elk_config(network_id=network_id, level=level, version=version, + time_step_len_seconds=time_step_len_seconds) + beats_cfg = default_beats_config(network_id=network_id) + emulation_env_cfg = EmulationEnvConfig( + name=name, containers_config=containers_cfg, users_config=users_cfg, flags_config=flags_cfg, + vuln_config=vuln_cfg, topology_config=topology_cfg, traffic_config=traffic_cfg, resources_config=resources_cfg, + kafka_config=kafka_cfg, services_config=services_cfg, + descr=descr, static_attacker_sequences=static_attackers_cfg, ovs_config=ovs_cfg, + sdn_controller_config=sdn_controller_cfg, host_manager_config=host_manager_cfg, + snort_ids_manager_config=snort_ids_manager_cfg, ossec_ids_manager_config=ossec_ids_manager_cfg, + docker_stats_manager_config=docker_stats_manager_cfg, elk_config=elk_cfg, + level=level, execution_id=-1, version=version, beats_config=beats_cfg + ) + return emulation_env_cfg + + +def default_containers_config(network_id: int, level: int, version: str) -> ContainersConfig: + """ + Generates default containers config + + :param version: the version of the containers to use + :param level: the level parameter of the emulation + :param network_id: the network id + :return: the ContainersConfig of the emulation + """ + containers = [ + NodeContainerConfig( + name=f"{constants.CONTAINER_IMAGES.CLIENT_1}", + os=constants.CONTAINER_OS.CLIENT_1_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH0, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.254", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH2, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )) + ], + version=version, level=str(level), restart_policy=constants.DOCKER.ON_FAILURE_3, suffix="_1"), + NodeContainerConfig(name=f"{constants.CONTAINER_IMAGES.HACKER_KALI_1}", + os=constants.CONTAINER_OS.HACKER_KALI_1_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH0, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.191", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH2, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )) + ], + version=version, level=str(level), + restart_policy=constants.DOCKER.ON_FAILURE_3, + suffix="_1"), + NodeContainerConfig(name=f"{constants.CONTAINER_IMAGES.ROUTER_2}", + os=constants.CONTAINER_OS.ROUTER_2_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH0, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + ( + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.10", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH2, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.10", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH3, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )) + ], + version=version, level=str(level), + restart_policy=constants.DOCKER.ON_FAILURE_3, + suffix="_1"), + NodeContainerConfig(name=f"{constants.CONTAINER_IMAGES.SSH_1}", + os=constants.CONTAINER_OS.SSH_1_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH0, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.78", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + interface=constants.NETWORKING.ETH2, + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )) + ], + version=version, level=str(level), + restart_policy=constants.DOCKER.ON_FAILURE_3, + suffix="_1") + ] + containers_cfg = ContainersConfig( + containers=containers, + agent_ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + router_ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + ids_enabled=True, + vulnerable_nodes=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78" + ], + agent_reachable_nodes=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78" + ], + networks=[ + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ), + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ), + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ] + ) + return containers_cfg + + +def default_flags_config(network_id: int) -> FlagsConfig: + """ + Generates default flags config + + :param network_id: the network id + :return: The flags confguration + """ + flags = [ + NodeFlagsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + flags=[Flag( + name=f"{constants.COMMON.FLAG_FILENAME_PREFIX}2", + path=f"/{constants.COMMANDS.TMP_DIR}/{constants.COMMON.FLAG_FILENAME_PREFIX}2" + f"{constants.FILE_PATTERNS.TXT_FILE_SUFFIX}", + dir=f"/{constants.COMMANDS.TMP_DIR}/", + id=2, requires_root=True, score=1 + )]) + ] + flags_config = FlagsConfig(node_flag_configs=flags) + return flags_config + + +def default_resource_constraints_config(network_id: int, level: int) -> ResourcesConfig: + """ + Generates default resource constraints config + + :param level: the level parameter of the emulation + :param network_id: the network id + :return: generates the ResourcesConfig + """ + node_resources_configurations = [ + NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.HACKER_KALI_1}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=1, available_memory_gb=4, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + NodeNetworkConfig( + interface=constants.NETWORKING.ETH0, + limit_packets_queue=30000, packet_delay_ms=2, + packet_delay_jitter_ms=0.5, packet_delay_correlation_percentage=25, + packet_delay_distribution=PacketDelayDistributionType.PARETO, + packet_loss_type=PacketLossType.GEMODEL, + loss_gemodel_p=0.02, loss_gemodel_r=0.97, + loss_gemodel_k=0.98, loss_gemodel_h=0.0001, packet_corrupt_percentage=0.02, + packet_corrupt_correlation_percentage=25, packet_duplicate_percentage=0.00001, + packet_duplicate_correlation_percentage=25, packet_reorder_percentage=2, + packet_reorder_correlation_percentage=25, packet_reorder_gap=5, + rate_limit_mbit=100, packet_overhead_bytes=0, + cell_overhead_bytes=0 + ))]), + NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.CLIENT_1}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=min(16, multiprocessing.cpu_count()), available_memory_gb=4, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + NodeNetworkConfig( + interface=constants.NETWORKING.ETH0, + limit_packets_queue=30000, packet_delay_ms=2, + packet_delay_jitter_ms=0.5, packet_delay_correlation_percentage=25, + packet_delay_distribution=PacketDelayDistributionType.PARETO, + packet_loss_type=PacketLossType.GEMODEL, + loss_gemodel_p=0.02, loss_gemodel_r=0.97, + loss_gemodel_k=0.98, loss_gemodel_h=0.0001, packet_corrupt_percentage=0.02, + packet_corrupt_correlation_percentage=25, packet_duplicate_percentage=0.00001, + packet_duplicate_correlation_percentage=25, packet_reorder_percentage=2, + packet_reorder_correlation_percentage=25, packet_reorder_gap=5, + rate_limit_mbit=10000, packet_overhead_bytes=0, + cell_overhead_bytes=0 + ))]), + NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.ROUTER_2}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=1, available_memory_gb=4, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + NodeNetworkConfig( + interface=constants.NETWORKING.ETH0, + limit_packets_queue=30000, packet_delay_ms=0.1, + packet_delay_jitter_ms=0.025, packet_delay_correlation_percentage=25, + packet_delay_distribution=PacketDelayDistributionType.PARETO, + packet_loss_type=PacketLossType.GEMODEL, + loss_gemodel_p=0.0001, loss_gemodel_r=0.999, + loss_gemodel_k=0.9999, loss_gemodel_h=0.0001, packet_corrupt_percentage=0.00001, + packet_corrupt_correlation_percentage=25, packet_duplicate_percentage=0.00001, + packet_duplicate_correlation_percentage=25, packet_reorder_percentage=0.0025, + packet_reorder_correlation_percentage=25, packet_reorder_gap=5, + rate_limit_mbit=1000, packet_overhead_bytes=0, + cell_overhead_bytes=0 + )), + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f""f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.10", + NodeNetworkConfig( + interface=constants.NETWORKING.ETH2, + limit_packets_queue=30000, packet_delay_ms=2, + packet_delay_jitter_ms=0.5, packet_delay_correlation_percentage=25, + packet_delay_distribution=PacketDelayDistributionType.PARETO, + packet_loss_type=PacketLossType.GEMODEL, + loss_gemodel_p=0.02, loss_gemodel_r=0.97, + loss_gemodel_k=0.98, loss_gemodel_h=0.0001, packet_corrupt_percentage=0.02, + packet_corrupt_correlation_percentage=25, packet_duplicate_percentage=0.00001, + packet_duplicate_correlation_percentage=25, packet_reorder_percentage=2, + packet_reorder_correlation_percentage=25, packet_reorder_gap=5, + rate_limit_mbit=100, packet_overhead_bytes=0, + cell_overhead_bytes=0 + ))]), + NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.SSH_1}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=1, available_memory_gb=4, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + NodeNetworkConfig( + interface=constants.NETWORKING.ETH0, + limit_packets_queue=30000, packet_delay_ms=0.1, + packet_delay_jitter_ms=0.025, packet_delay_correlation_percentage=25, + packet_delay_distribution=PacketDelayDistributionType.PARETO, + packet_loss_type=PacketLossType.GEMODEL, + loss_gemodel_p=0.0001, loss_gemodel_r=0.999, + loss_gemodel_k=0.9999, loss_gemodel_h=0.0001, packet_corrupt_percentage=0.00001, + packet_corrupt_correlation_percentage=25, packet_duplicate_percentage=0.00001, + packet_duplicate_correlation_percentage=25, packet_reorder_percentage=0.0025, + packet_reorder_correlation_percentage=25, packet_reorder_gap=5, + rate_limit_mbit=1000, packet_overhead_bytes=0, + cell_overhead_bytes=0 + ))]) + ] + resources_config = ResourcesConfig(node_resources_configurations=node_resources_configurations) + return resources_config + + +def default_topology_config(network_id: int) -> TopologyConfig: + """ + Generates default topology config + + :param network_id: the network id + :return: the Topology configuration + """ + node_1 = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.ROUTER_2}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.ACCEPT, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.10", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.ACCEPT, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.10", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set([]), + output_drop=set(), input_drop=set(), forward_drop=set(), routes=set()) + node_2 = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.SSH_1}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=None, + default_gw=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.78", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set(), output_drop=set(), input_drop=set(), routes=set(), forward_drop=set() + ) + + node_6 = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.HACKER_KALI_1}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=None, + default_gw=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.10", + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.191", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set(), output_drop=set(), input_drop=set(), forward_drop=set(), + routes=set()) + node_7 = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.CLIENT_1}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=None, + default_gw=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}" + f".{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.10", + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_1", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ), + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.254", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.DROP, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set(), output_drop=set(), input_drop=set(), forward_drop=set(), + routes=set()) + node_configs = [node_1, node_2, node_6, node_7] + topology = TopologyConfig(node_configs=node_configs, + subnetwork_masks=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.1{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}" + ]) + return topology + + +def default_traffic_config(network_id: int, time_step_len_seconds: int) -> TrafficConfig: + """ + Generates default traffic config + + :param network_id: the network id + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the traffic configuration + """ + traffic_generators = [ + NodeTrafficConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + commands=(constants.TRAFFIC_COMMANDS.DEFAULT_COMMANDS[constants.CONTAINER_IMAGES.ROUTER_2] + + constants.TRAFFIC_COMMANDS.DEFAULT_COMMANDS[ + constants.TRAFFIC_COMMANDS.GENERIC_COMMANDS]), + traffic_manager_port=collector_constants.MANAGER_PORTS.TRAFFIC_MANAGER_DEFAULT_PORT, + traffic_manager_log_file=collector_constants.LOG_FILES.TRAFFIC_MANAGER_LOG_FILE, + traffic_manager_log_dir=collector_constants.LOG_FILES.TRAFFIC_MANAGER_LOG_DIR, + traffic_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS), + NodeTrafficConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + commands=(constants.TRAFFIC_COMMANDS.DEFAULT_COMMANDS[constants.CONTAINER_IMAGES.SSH_1] + + constants.TRAFFIC_COMMANDS.DEFAULT_COMMANDS[ + constants.TRAFFIC_COMMANDS.GENERIC_COMMANDS]), + traffic_manager_port=collector_constants.MANAGER_PORTS.TRAFFIC_MANAGER_DEFAULT_PORT, + traffic_manager_log_file=collector_constants.LOG_FILES.TRAFFIC_MANAGER_LOG_FILE, + traffic_manager_log_dir=collector_constants.LOG_FILES.TRAFFIC_MANAGER_LOG_DIR, + traffic_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + ] + all_ips_and_commands = [] + for i in range(len(traffic_generators)): + all_ips_and_commands.append((traffic_generators[i].ip, traffic_generators[i].commands)) + workflows_config = WorkflowsConfig( + workflow_services=[ + WorkflowService(id=0, ips_and_commands=all_ips_and_commands) + ], + workflow_markov_chains=[ + WorkflowMarkovChain( + transition_matrix=[ + [0.8, 0.2], + [0, 1] + ], + initial_state=0, + id=0 + ) + ] + ) + client_population_config = ClientPopulationConfig( + networks=[ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_2", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.2{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )], + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + client_manager_port=collector_constants.MANAGER_PORTS.CLIENT_MANAGER_DEFAULT_PORT, + client_time_step_len_seconds=time_step_len_seconds, + client_manager_log_dir=collector_constants.LOG_FILES.CLIENT_MANAGER_LOG_DIR, + client_manager_log_file=collector_constants.LOG_FILES.CLIENT_MANAGER_LOG_FILE, + client_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS, + clients=[ + Client(id=0, workflow_distribution=[1], + arrival_config=ConstantArrivalConfig(lamb=20), mu=4, exponential_service_time=True) + ], + workflows_config=workflows_config) + traffic_conf = TrafficConfig(node_traffic_configs=traffic_generators, + client_population_config=client_population_config) + return traffic_conf + + +def default_beats_config(network_id: int) -> BeatsConfig: + """ + Generates default beats config + + :param network_id: the network id + :return: the beats configuration + """ + node_beats_configs = [ + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE, + collector_constants.FILEBEAT.SNORT_MODULE], + kafka_input=False, start_filebeat_automatically=False, + start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}" + ]), + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE], + kafka_input=False, start_filebeat_automatically=False, + start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}" + ]), + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE, + collector_constants.FILEBEAT.KAFKA_MODULE], + kafka_input=True, start_filebeat_automatically=False, + start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE, + collector_constants.FILEBEAT.KAFKA_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.254", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.191", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.78" + ]), + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE, + collector_constants.FILEBEAT.ELASTICSEARCH_MODULE, + collector_constants.FILEBEAT.KIBANA_MODULE, + collector_constants.FILEBEAT.LOGSTASH_MODULE], kafka_input=False, + start_filebeat_automatically=False, start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE, + collector_constants.FILEBEAT.ELASTICSEARCH_MODULE, + collector_constants.FILEBEAT.KIBANA_MODULE, + collector_constants.FILEBEAT.LOGSTASH_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.254", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.191", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}.78" + ]), + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE], + kafka_input=False, start_filebeat_automatically=False, + start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}" + ]), + NodeBeatsConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + log_files_paths=collector_constants.LOG_FILES.DEFAULT_LOG_FILE_PATHS, + filebeat_modules=[collector_constants.FILEBEAT.SYSTEM_MODULE], + kafka_input=False, start_filebeat_automatically=False, + start_packetbeat_automatically=False, + metricbeat_modules=[collector_constants.METRICBEAT.SYSTEM_MODULE, + collector_constants.METRICBEAT.LINUX_MODULE], + start_metricbeat_automatically=False, + start_heartbeat_automatically=False, + heartbeat_hosts_to_monitor=[ + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}" + ]) + ] + beats_conf = BeatsConfig(node_beats_configs=node_beats_configs, num_elastic_shards=1, reload_enabled=False) + return beats_conf + + +def default_kafka_config(network_id: int, level: int, version: str, time_step_len_seconds: int) -> KafkaConfig: + """ + Generates the default kafka configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the kafka configuration + """ + container = NodeContainerConfig( + name=f"{constants.CONTAINER_IMAGES.KAFKA_1}", + os=constants.CONTAINER_OS.KAFKA_1_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + ], + version=version, level=str(level), + restart_policy=constants.DOCKER.ON_FAILURE_3, suffix=collector_constants.KAFKA_CONFIG.SUFFIX) + + resources = NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.KAFKA_1}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=1, available_memory_gb=4, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + None)]) + + firewall_config = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.KAFKA_1}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_FOURTH_OCTET}", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.ACCEPT, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.KAFKA_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set([]), + output_drop=set(), input_drop=set(), forward_drop=set(), routes=set()) + + topics = [ + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.CLIENT_POPULATION_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.CLIENT_POPULATION_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.SNORT_IDS_LOG_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.SNORT_IDS_LOG_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.OSSEC_IDS_LOG_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.OSSEC_IDS_LOG_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.HOST_METRICS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.HOST_METRICS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.DOCKER_STATS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.DOCKER_STATS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.ATTACKER_ACTIONS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.ATTACKER_ACTIONS_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.DEFENDER_ACTIONS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.DEFENDER_ACTIONS_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.DOCKER_HOST_STATS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.DOCKER_STATS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.OPENFLOW_FLOW_STATS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.OPENFLOW_FLOW_STATS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.OPENFLOW_PORT_STATS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.OPENFLOW_PORT_STATS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.AVERAGE_OPENFLOW_FLOW_STATS_PER_SWITCH_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.AVERAGE_OPENFLOW_FLOW_STATS_PER_SWITCH_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.AVERAGE_OPENFLOW_PORT_STATS_PER_SWITCH_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.AVERAGE_OPENFLOW_PORT_STATS_PER_SWITCH_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.OPENFLOW_AGG_FLOW_STATS_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.OPENFLOW_AGG_FLOW_STATS_TOPIC_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.SNORT_IDS_RULE_LOG_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.SNORT_IDS_RULE_LOG_ATTRIBUTES + ), + KafkaTopic( + name=collector_constants.KAFKA_CONFIG.SNORT_IDS_IP_LOG_TOPIC_NAME, + num_replicas=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_REPLICAS, + num_partitions=collector_constants.KAFKA_CONFIG.DEFAULT_NUM_PARTITIONS, + retention_time_hours=collector_constants.KAFKA_CONFIG.DEFAULT_RETENTION_TIME_HOURS, + attributes=collector_constants.KAFKA_CONFIG.SNORT_IDS_IP_LOG_ATTRIBUTES + ) + ] + + config = KafkaConfig(container=container, resources=resources, topics=topics, firewall_config=firewall_config, + version=version, + kafka_port=collector_constants.KAFKA.PORT, + kafka_port_external=collector_constants.KAFKA.EXTERNAL_PORT, + kafka_manager_port=collector_constants.MANAGER_PORTS.KAFKA_MANAGER_DEFAULT_PORT, + time_step_len_seconds=time_step_len_seconds, + kafka_manager_log_file=collector_constants.LOG_FILES.KAFKA_MANAGER_LOG_FILE, + kafka_manager_log_dir=collector_constants.LOG_FILES.KAFKA_MANAGER_LOG_DIR, + kafka_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +def default_users_config(network_id: int) -> UsersConfig: + """ + Generates default users config + + :param network_id: the network id + :return: generates the UsersConfig + """ + users = [ + NodeUsersConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + users=[User(username="agent", pw="agent", root=True)]), + NodeUsersConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", users=[ + User(username="admin", pw="admin1235912", root=True), + User(username="jessica", pw="water", root=False) + ]), + NodeUsersConfig(ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", users=[ + User(username="admin", pw="test32121", root=True), + User(username="user1", pw="123123", root=True) + ]) + ] + users_conf = UsersConfig(users_configs=users) + return users_conf + + +def default_vulns_config(network_id: int) -> VulnerabilitiesConfig: + """ + Generates default vulnerabilities config + + :param network_id: the network id + :return: the vulnerability config + """ + vulns = [ + NodeVulnerabilityConfig( + name=constants.EXPLOIT_VULNERABILITES.SSH_DICT_SAME_USER_PASS, + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + vuln_type=VulnType.WEAK_PW, + credentials=[Credential(username="puppet", pw="puppet", root=True, + service=constants.SSH.SERVICE_NAME, + protocol=TransportProtocol.TCP, + port=constants.SSH.DEFAULT_PORT)], + cvss=constants.EXPLOIT_VULNERABILITES.WEAK_PASSWORD_CVSS, + cve=None, + root=True, port=constants.SSH.DEFAULT_PORT, protocol=TransportProtocol.TCP, + service=constants.SSH.SERVICE_NAME) + ] + vulns_config = VulnerabilitiesConfig(node_vulnerability_configs=vulns) + return vulns_config + + +def default_services_config(network_id: int) -> ServicesConfig: + """ + Generates default services config + + :param network_id: the network id + :return: The services configuration + """ + services_configs = [ + NodeServicesConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.254", + services=[ + NetworkService(protocol=TransportProtocol.TCP, port=constants.SSH.DEFAULT_PORT, + name=constants.SSH.SERVICE_NAME, credentials=[]) + ] + ), + NodeServicesConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.EXTERNAL_NETWORK.NETWORK_ID_THIRD_OCTET}.191", + services=[ + NetworkService(protocol=TransportProtocol.TCP, port=constants.SSH.DEFAULT_PORT, + name=constants.SSH.SERVICE_NAME, credentials=[]) + ] + ), + NodeServicesConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.10", + services=[ + NetworkService(protocol=TransportProtocol.TCP, port=constants.SSH.DEFAULT_PORT, + name=constants.SSH.SERVICE_NAME, credentials=[]) + ] + ), + NodeServicesConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}.2.78", + services=[ + NetworkService(protocol=TransportProtocol.TCP, port=constants.SSH.DEFAULT_PORT, + name=constants.SSH.SERVICE_NAME, credentials=[]), + NetworkService(protocol=TransportProtocol.TCP, port=constants.DNS.DEFAULT_PORT, + name=constants.DNS.SERVICE_NAME, credentials=[]), + NetworkService(protocol=TransportProtocol.TCP, port=constants.HTTP.DEFAULT_PORT, + name=constants.HTTP.SERVICE_NAME, credentials=[]) + ] + ) + ] + service_cfg = ServicesConfig( + services_configs=services_configs + ) + return service_cfg + + +def default_static_attacker_sequences(subnet_masks: List[str]) -> Dict[str, List[EmulationAttackerAction]]: + """ + Generates default attacker sequences config + + :param subnetmasks: list of subnet masks for the emulation + :return: the default static attacker sequences configuration + """ + return {} + + +def default_ovs_config(network_id: int, level: int, version: str) -> OVSConfig: + """ + Generates default OVS config + + :param network_id: the network id of the emulation + :param level: the level of the emulation + :param version: the version of the emulation + :return: the default OVS config + """ + ovs_config = OVSConfig(switch_configs=[]) + return ovs_config + + +def default_sdn_controller_config(network_id: int, level: int, version: str, time_step_len_seconds: int) \ + -> Union[None, SDNControllerConfig]: + """ + Generates the default SDN controller config + + :param network_id: the network id of the emulation + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the default SDN Controller config + """ + return None + + +def default_host_manager_config(network_id: int, level: int, version: str, time_step_len_seconds: int) \ + -> HostManagerConfig: + """ + Generates the default host manager configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the host manager configuration + """ + config = HostManagerConfig(version=version, time_step_len_seconds=time_step_len_seconds, + host_manager_port=collector_constants.MANAGER_PORTS.HOST_MANAGER_DEFAULT_PORT, + host_manager_log_file=collector_constants.LOG_FILES.HOST_MANAGER_LOG_FILE, + host_manager_log_dir=collector_constants.LOG_FILES.HOST_MANAGER_LOG_DIR, + host_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +def default_snort_ids_manager_config(network_id: int, level: int, version: str, time_step_len_seconds: int) \ + -> SnortIDSManagerConfig: + """ + Generates the default Snort IDS manager configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the Snort IDS manager configuration + """ + config = SnortIDSManagerConfig( + version=version, time_step_len_seconds=time_step_len_seconds, + snort_ids_manager_port=collector_constants.MANAGER_PORTS.SNORT_IDS_MANAGER_DEFAULT_PORT, + snort_ids_manager_log_dir=collector_constants.LOG_FILES.SNORT_IDS_MANAGER_LOG_DIR, + snort_ids_manager_log_file=collector_constants.LOG_FILES.SNORT_IDS_MANAGER_LOG_FILE, + snort_ids_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +def default_ossec_ids_manager_config(network_id: int, level: int, version: str, time_step_len_seconds: int) \ + -> OSSECIDSManagerConfig: + """ + Generates the default OSSEC IDS manager configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the OSSEC IDS manager configuration + """ + config = OSSECIDSManagerConfig( + version=version, time_step_len_seconds=time_step_len_seconds, + ossec_ids_manager_port=collector_constants.MANAGER_PORTS.OSSEC_IDS_MANAGER_DEFAULT_PORT, + ossec_ids_manager_log_file=collector_constants.LOG_FILES.OSSEC_IDS_MANAGER_LOG_FILE, + ossec_ids_manager_log_dir=collector_constants.LOG_FILES.OSSEC_IDS_MANAGER_LOG_DIR, + ossec_ids_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +def default_docker_stats_manager_config(network_id: int, level: int, version: str, time_step_len_seconds: int) \ + -> DockerStatsManagerConfig: + """ + Generates the default docker stats manager configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the docker stats manager configuration + """ + config = DockerStatsManagerConfig( + version=version, time_step_len_seconds=time_step_len_seconds, + docker_stats_manager_port=collector_constants.MANAGER_PORTS.DOCKER_STATS_MANAGER_DEFAULT_PORT, + docker_stats_manager_log_file=collector_constants.LOG_FILES.DOCKER_STATS_MANAGER_LOG_FILE, + docker_stats_manager_log_dir=collector_constants.LOG_FILES.DOCKER_STATS_MANAGER_LOG_DIR, + docker_stats_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +def default_elk_config(network_id: int, level: int, version: str, time_step_len_seconds: int) -> ElkConfig: + """ + Generates the default ELK configuration + + :param network_id: the id of the emulation network + :param level: the level of the emulation + :param version: the version of the emulation + :param time_step_len_seconds: default length of a time-step in the emulation + :return: the ELK configuration + """ + container = NodeContainerConfig( + name=f"{constants.CONTAINER_IMAGES.ELK_1}", + os=constants.CONTAINER_OS.ELK_1_OS, + ips_and_networks=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}", + ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + )), + ], + version=version, level=str(level), + restart_policy=constants.DOCKER.ON_FAILURE_3, suffix=collector_constants.ELK_CONFIG.SUFFIX) + + resources = NodeResourcesConfig( + container_name=f"{constants.CSLE.NAME}-" + f"{constants.CONTAINER_IMAGES.ELK_1}_1-{constants.CSLE.LEVEL}{level}", + num_cpus=2, available_memory_gb=16, + ips_and_network_configs=[ + (f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}", + None)]) + + firewall_config = NodeFirewallConfig( + hostname=f"{constants.CONTAINER_IMAGES.ELK_1}_1", + ips_gw_default_policy_networks=[ + DefaultNetworkFirewallConfig( + ip=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}." + f"{collector_constants.ELK_CONFIG.NETWORK_ID_FOURTH_OCTET}", + default_gw=None, + default_input=constants.FIREWALL.ACCEPT, + default_output=constants.FIREWALL.ACCEPT, + default_forward=constants.FIREWALL.ACCEPT, + network=ContainerNetwork( + name=f"{constants.CSLE.CSLE_NETWORK_PREFIX}{network_id}_" + f"{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}", + subnet_mask=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}" + f"{network_id}.{collector_constants.ELK_CONFIG.NETWORK_ID_THIRD_OCTET}" + f"{constants.CSLE.CSLE_EDGE_SUBNETMASK_SUFFIX}", + subnet_prefix=f"{constants.CSLE.CSLE_SUBNETMASK_PREFIX}{network_id}", + bitmask=constants.CSLE.CSLE_EDGE_BITMASK + ) + ) + ], + output_accept=set([]), + input_accept=set([]), + forward_accept=set([]), + output_drop=set(), input_drop=set(), forward_drop=set(), routes=set()) + + config = ElkConfig(version=version, time_step_len_seconds=time_step_len_seconds, + elastic_port=collector_constants.ELK.ELASTIC_PORT, + kibana_port=collector_constants.ELK.KIBANA_PORT, + logstash_port=collector_constants.ELK.LOGSTASH_PORT, + elk_manager_port=collector_constants.MANAGER_PORTS.ELK_MANAGER_DEFAULT_PORT, + container=container, + resources=resources, firewall_config=firewall_config, + elk_manager_log_file=collector_constants.LOG_FILES.ELK_MANAGER_LOG_FILE, + elk_manager_log_dir=collector_constants.LOG_FILES.ELK_MANAGER_LOG_DIR, + elk_manager_max_workers=collector_constants.GRPC_WORKERS.DEFAULT_MAX_NUM_WORKERS) + return config + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--install", help="Boolean parameter, if true, install config", + action="store_true") + parser.add_argument("-u", "--uninstall", help="Boolean parameter, if true, uninstall config", + action="store_true") + args = parser.parse_args() + config = default_config(name="csle-level15-050", network_id=15, level=15, version="0.5.0", time_step_len_seconds=30) + ExperimentUtil.write_emulation_config_file(config, ExperimentUtil.default_emulation_config_path()) + + if args.install: + EmulationEnvController.install_emulation(config=config) + img_path = ExperimentUtil.default_emulation_picture_path() + if os.path.exists(img_path): + encoded_image_str = ExperimentUtil.read_env_picture(img_path) + EmulationEnvController.save_emulation_image(img=encoded_image_str, emulation_name=config.name) + if args.uninstall: + EmulationEnvController.uninstall_emulation(config=config) \ No newline at end of file diff --git a/emulation-system/envs/050/level_15/env.png b/emulation-system/envs/050/level_15/env.png new file mode 100644 index 0000000000000000000000000000000000000000..6e90abd42aa02b31518a019af6ce67ef82c5ec8d GIT binary patch literal 60580 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

UbBAHx*zE`+d+r195-JWbq0>;5j!N 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 JB~Oe4{y#K_w{8Fc literal 0 HcmV?d00001 diff --git a/emulation-system/envs/050/level_15/test_config.py b/emulation-system/envs/050/level_15/test_config.py new file mode 100644 index 000000000..aff7dd754 --- /dev/null +++ b/emulation-system/envs/050/level_15/test_config.py @@ -0,0 +1,34 @@ +from config import default_config + + +class TestEmulationConfigSuite: + """ + Test suite for the emulation configuration for 'level-4' + """ + + def test_create_config(self) -> None: + """ + Tests creation of the emulation configuration + + :return: None + """ + config = default_config(name="csle-level15-050", network_id=15, level=15, version="0.5.0", + time_step_len_seconds=15) + assert config.vuln_config is not None + assert config.containers_config is not None + assert config.flags_config is not None + assert config.resources_config is not None + assert config.topology_config is not None + assert config.traffic_config is not None + assert config.users_config is not None + assert config.vuln_config is not None + assert config.kafka_config is not None + assert config.services_config is not None + assert config.static_attacker_sequences is not None + assert config.ovs_config is not None + assert config.host_manager_config is not None + assert config.snort_ids_manager_config is not None + assert config.ossec_ids_manager_config is not None + assert config.docker_stats_manager_config is not None + assert config.elk_config is not None + assert config.beats_config is not None diff --git a/emulation-system/envs/Makefile b/emulation-system/envs/Makefile index 56be4b15f..f1ba89280 100644 --- a/emulation-system/envs/Makefile +++ b/emulation-system/envs/Makefile @@ -41,6 +41,9 @@ install_level_13: install_level_14: cd 050/level_14/ && $(MAKE) install +install_level_15: + cd 050/level_15/ && $(MAKE) install + # Installs all emulations install: cd 050/level_1/ && $(MAKE) install @@ -57,6 +60,7 @@ install: cd 050/level_12/ && $(MAKE) install cd 050/level_13/ && $(MAKE) install cd 050/level_14/ && $(MAKE) install + cd 050/level_15/ && $(MAKE) install # Targets for uninstalling each individual env uninstall_level_1: @@ -101,6 +105,9 @@ uninstall_level_13: uninstall_level_14: cd 050/level_14/ && $(MAKE) uninstall +uninstall_level_15: + cd 050/level_15/ && $(MAKE) uninstall + # Uninstalls all emulations uninstall: cd 050/level_1/ && $(MAKE) uninstall @@ -117,6 +124,7 @@ uninstall: cd 050/level_12/ && $(MAKE) uninstall cd 050/level_13/ && $(MAKE) uninstall cd 050/level_14/ && $(MAKE) uninstall + cd 050/level_15/ && $(MAKE) uninstall # Targets for cleaning the config each individual env clean_config_level_1: @@ -161,6 +169,9 @@ clean_config_level_13: clean_config_level_14: cd 050/level_14/ && $(MAKE) clean_config +clean_config_level_15: + cd 050/level_15/ && $(MAKE) clean_config + # Cleans the materialized configuration of each emulation clean_config: cd 050/level_1/ && $(MAKE) clean_config @@ -177,3 +188,4 @@ clean_config: cd 050/level_12/ && $(MAKE) clean_config cd 050/level_13/ && $(MAKE) clean_config cd 050/level_14/ && $(MAKE) clean_config + cd 050/level_15/ && $(MAKE) clean_config