Loading proto/l3_centralizedattackdetector.proto +10 −25 Original line number Diff line number Diff line Loading @@ -17,17 +17,15 @@ syntax = "proto3"; import "context.proto"; service L3Centralizedattackdetector { // Sends a greeting // Sends single input to the ML model in the CAD component rpc SendInput (L3CentralizedattackdetectorMetrics) returns (Empty) {} // Sends another greeting rpc GetOutput (Empty) returns (L3CentralizedattackdetectorModelOutput) {} // Sends a batch of inputs to the ML model in the CAD component rpc SendInputBatch (L3CentralizedattackdetectorModelInput) returns (Empty) {} } message L3CentralizedattackdetectorMetrics { /* Model input sent to the Inferencer by the client There are currently 9 values and */ // Input sent by the DAD compoenent to the ML model integrated in the CAD component. // Machine learning model features float c_pkts_all = 1; Loading @@ -54,22 +52,13 @@ message L3CentralizedattackdetectorMetrics { float time_end = 20; } message Empty { string message = 1; // Collection (batcb) of model inputs that will be sent to the model message L3CentralizedattackdetectorModelInput { repeated L3CentralizedattackdetectorMetrics metrics = 1; } message L3CentralizedattackdetectorModelOutput { float confidence = 1; string timestamp = 2; string ip_o = 3; string tag_name = 4; int32 tag = 5; string flow_id = 6; string protocol = 7; string port_d = 8; string ml_id = 9; float time_start = 10; float time_end = 11; message Empty { string message = 1; } // Collections or streams? Loading @@ -77,8 +66,4 @@ message L3CentralizedattackdetectorModelOutput { message InputCollection { repeated model_input = 1; } message OutputCollection { repeated model_output = 1; } */ src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py +89 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import numpy as np import onnxruntime as rt import logging from time import sleep import time from common.proto.l3_centralizedattackdetector_pb2 import Empty from common.proto.l3_centralizedattackdetector_pb2_grpc import L3CentralizedattackdetectorServicer Loading Loading @@ -118,6 +119,13 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto self.time_interval_start = None self.time_interval_end = None # CAD evaluation tests self.cad_inference_times = [] self.cad_num_inference_measurements = 100 # AM evaluation tests self.am_notification_times = [] """ Create a monitored KPI for a specific service and add it to the Monitoring Client -input: Loading Loading @@ -479,8 +487,56 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto ] ) # Print input data shape LOGGER.debug("x_data.shape: {}".format(x_data.shape)) # Get batch size batch_size = x_data.shape[0] # Print batch size LOGGER.debug("batch_size: {}".format(batch_size)) # TEST: Remove later test_batch_size = 1024 # duplicate x_data to test_batch_size x_data = np.repeat(x_data, test_batch_size, axis=0) LOGGER.debug("x_data.shape: {}".format(x_data.shape)) inference_time_start = time.perf_counter() # Perform inference predictions = self.model.run([self.prob_name], {self.input_name: x_data.astype(np.float32)})[0] inference_time_end = time.perf_counter() # Measure inference time inference_time = inference_time_end - inference_time_start self.cad_inference_times.append(inference_time) if len(self.cad_inference_times) > self.cad_num_inference_measurements: inference_times_np_array = np.array(self.cad_inference_times) np.save(f"inference_times_{test_batch_size}.npy", inference_times_np_array) avg_inference_time = np.mean(inference_times_np_array) max_inference_time = np.max(inference_times_np_array) min_inference_time = np.min(inference_times_np_array) std_inference_time = np.std(inference_times_np_array) median_inference_time = np.median(inference_times_np_array) LOGGER.debug("Average inference time: {}".format(avg_inference_time)) LOGGER.debug("Max inference time: {}".format(max_inference_time)) LOGGER.debug("Min inference time: {}".format(min_inference_time)) LOGGER.debug("Standard deviation inference time: {}".format(std_inference_time)) LOGGER.debug("Median inference time: {}".format(median_inference_time)) with open(f"inference_times_stats_{batch_size}.txt", "w") as f: f.write("Average inference time: {}\n".format(avg_inference_time)) f.write("Max inference time: {}\n".format(max_inference_time)) f.write("Min inference time: {}\n".format(min_inference_time)) f.write("Standard deviation inference time: {}\n".format(std_inference_time)) f.write("Median inference time: {}\n".format(median_inference_time)) # Gather the predicted class, the probability of that class and other relevant information required to block the attack output_message = { "confidence": None, Loading Loading @@ -537,8 +593,12 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto self.monitor_kpis() LOGGER.debug("cryptomining_detector_output: {}".format(cryptomining_detector_output)) # Only notify Attack Mitigator when a cryptomining connection has been detected if cryptomining_detector_output["tag_name"] == "Crypto": notification_time_start = time.perf_counter() logging.info("Crypto attack detected") # Notify the Attack Mitigator component about the attack Loading @@ -550,6 +610,35 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto logging.info("Sending the connection information to the Attack Mitigator component...") message = L3AttackmitigatorOutput(**cryptomining_detector_output) response = self.attackmitigator_client.SendOutput(message) notification_time_end = time.perf_counter() self.am_notification_times.append(notification_time_end - notification_time_start) LOGGER.debug(f"am_notification_times length: {len(self.am_notification_times)}") if len(self.am_notification_times) > 100: am_notification_times_np_array = np.array(self.am_notification_times) np.save("am_notification_times.npy", am_notification_times_np_array) avg_notification_time = np.mean(am_notification_times_np_array) max_notification_time = np.max(am_notification_times_np_array) min_notification_time = np.min(am_notification_times_np_array) std_notification_time = np.std(am_notification_times_np_array) median_notification_time = np.median(am_notification_times_np_array) LOGGER.debug("Average notification time: {}".format(avg_notification_time)) LOGGER.debug("Max notification time: {}".format(max_notification_time)) LOGGER.debug("Min notification time: {}".format(min_notification_time)) LOGGER.debug("Std notification time: {}".format(std_notification_time)) LOGGER.debug("Median notification time: {}".format(median_notification_time)) with open("am_notification_times_stats.txt", "w") as f: f.write("Average notification time: {}\n".format(avg_notification_time)) f.write("Max notification time: {}\n".format(max_notification_time)) f.write("Min notification time: {}\n".format(min_notification_time)) f.write("Std notification time: {}\n".format(std_notification_time)) f.write("Median notification time: {}\n".format(median_notification_time)) # logging.info("Attack Mitigator notified and received response: ", response.message) # FIX No message received logging.info("Attack Mitigator notified") Loading webui.sh 0 → 100755 +80 −0 Original line number Diff line number Diff line echo "Configuring WebUI DataStores and Dashboards..." sleep 3 # INFLUXDB_HOST="monitoringservice" # INFLUXDB_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service/monitoringservice -o jsonpath='{.spec.ports[?(@.name=="influxdb")].port}') # INFLUXDB_URL="http://${INFLUXDB_HOST}:${INFLUXDB_PORT}" # INFLUXDB_USER=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_USER}' | base64 --decode) # INFLUXDB_PASSWORD=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_PASSWORD}' | base64 --decode) # INFLUXDB_DATABASE=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_DB}' | base64 --decode) # Exposed through the ingress controller "tfs-ingress" GRAFANA_HOSTNAME="127.0.0.1" GRAFANA_PORT="80" GRAFANA_BASEURL="/grafana" # Default Grafana credentials GRAFANA_USERNAME="admin" GRAFANA_PASSWORD="admin" # Default Grafana API URL GRAFANA_URL_DEFAULT="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" # Updated Grafana API URL GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT echo "Connecting to grafana at URL: ${GRAFANA_URL_DEFAULT}..." # Configure Grafana Admin Password # Ref: https://grafana.com/docs/grafana/latest/http_api/user/#change-password curl -X PUT -H "Content-Type: application/json" -d '{ "oldPassword": "'${GRAFANA_PASSWORD}'", "newPassword": "'${TFS_GRAFANA_PASSWORD}'", "confirmNew" : "'${TFS_GRAFANA_PASSWORD}'" }' ${GRAFANA_URL_DEFAULT}/api/user/password echo # Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/ # TODO: replace user, password and database by variables to be saved echo "Creating a datasource..." curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{ "access" : "proxy", "type" : "postgres", "name" : "monitoringdb", "url" : "monitoringservice:8812", "database" : "monitoring", "user" : "admin", "basicAuth": false, "isDefault": true, "jsonData" : { "sslmode" : "disable", "postgresVersion" : 1100, "maxOpenConns" : 0, "maxIdleConns" : 2, "connMaxLifetime" : 14400, "tlsAuth" : false, "tlsAuthWithCACert" : false, "timescaledb" : false, "tlsConfigurationMethod": "file-path", "tlsSkipVerify" : true }, "secureJsonData": { "password": "quest" } }' ${GRAFANA_URL_UPDATED}/api/datasources echo # Create Monitoring Dashboard # Ref: https://grafana.com/docs/grafana/latest/http_api/dashboard/ curl -X POST -H "Content-Type: application/json" \ -d '@src/webui/grafana_dashboard_psql.json' \ ${GRAFANA_URL_UPDATED}/api/dashboards/db echo DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tf-l3-monit" DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id') curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID} printf "\n\n" No newline at end of file Loading
proto/l3_centralizedattackdetector.proto +10 −25 Original line number Diff line number Diff line Loading @@ -17,17 +17,15 @@ syntax = "proto3"; import "context.proto"; service L3Centralizedattackdetector { // Sends a greeting // Sends single input to the ML model in the CAD component rpc SendInput (L3CentralizedattackdetectorMetrics) returns (Empty) {} // Sends another greeting rpc GetOutput (Empty) returns (L3CentralizedattackdetectorModelOutput) {} // Sends a batch of inputs to the ML model in the CAD component rpc SendInputBatch (L3CentralizedattackdetectorModelInput) returns (Empty) {} } message L3CentralizedattackdetectorMetrics { /* Model input sent to the Inferencer by the client There are currently 9 values and */ // Input sent by the DAD compoenent to the ML model integrated in the CAD component. // Machine learning model features float c_pkts_all = 1; Loading @@ -54,22 +52,13 @@ message L3CentralizedattackdetectorMetrics { float time_end = 20; } message Empty { string message = 1; // Collection (batcb) of model inputs that will be sent to the model message L3CentralizedattackdetectorModelInput { repeated L3CentralizedattackdetectorMetrics metrics = 1; } message L3CentralizedattackdetectorModelOutput { float confidence = 1; string timestamp = 2; string ip_o = 3; string tag_name = 4; int32 tag = 5; string flow_id = 6; string protocol = 7; string port_d = 8; string ml_id = 9; float time_start = 10; float time_end = 11; message Empty { string message = 1; } // Collections or streams? Loading @@ -77,8 +66,4 @@ message L3CentralizedattackdetectorModelOutput { message InputCollection { repeated model_input = 1; } message OutputCollection { repeated model_output = 1; } */
src/l3_centralizedattackdetector/service/l3_centralizedattackdetectorServiceServicerImpl.py +89 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import numpy as np import onnxruntime as rt import logging from time import sleep import time from common.proto.l3_centralizedattackdetector_pb2 import Empty from common.proto.l3_centralizedattackdetector_pb2_grpc import L3CentralizedattackdetectorServicer Loading Loading @@ -118,6 +119,13 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto self.time_interval_start = None self.time_interval_end = None # CAD evaluation tests self.cad_inference_times = [] self.cad_num_inference_measurements = 100 # AM evaluation tests self.am_notification_times = [] """ Create a monitored KPI for a specific service and add it to the Monitoring Client -input: Loading Loading @@ -479,8 +487,56 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto ] ) # Print input data shape LOGGER.debug("x_data.shape: {}".format(x_data.shape)) # Get batch size batch_size = x_data.shape[0] # Print batch size LOGGER.debug("batch_size: {}".format(batch_size)) # TEST: Remove later test_batch_size = 1024 # duplicate x_data to test_batch_size x_data = np.repeat(x_data, test_batch_size, axis=0) LOGGER.debug("x_data.shape: {}".format(x_data.shape)) inference_time_start = time.perf_counter() # Perform inference predictions = self.model.run([self.prob_name], {self.input_name: x_data.astype(np.float32)})[0] inference_time_end = time.perf_counter() # Measure inference time inference_time = inference_time_end - inference_time_start self.cad_inference_times.append(inference_time) if len(self.cad_inference_times) > self.cad_num_inference_measurements: inference_times_np_array = np.array(self.cad_inference_times) np.save(f"inference_times_{test_batch_size}.npy", inference_times_np_array) avg_inference_time = np.mean(inference_times_np_array) max_inference_time = np.max(inference_times_np_array) min_inference_time = np.min(inference_times_np_array) std_inference_time = np.std(inference_times_np_array) median_inference_time = np.median(inference_times_np_array) LOGGER.debug("Average inference time: {}".format(avg_inference_time)) LOGGER.debug("Max inference time: {}".format(max_inference_time)) LOGGER.debug("Min inference time: {}".format(min_inference_time)) LOGGER.debug("Standard deviation inference time: {}".format(std_inference_time)) LOGGER.debug("Median inference time: {}".format(median_inference_time)) with open(f"inference_times_stats_{batch_size}.txt", "w") as f: f.write("Average inference time: {}\n".format(avg_inference_time)) f.write("Max inference time: {}\n".format(max_inference_time)) f.write("Min inference time: {}\n".format(min_inference_time)) f.write("Standard deviation inference time: {}\n".format(std_inference_time)) f.write("Median inference time: {}\n".format(median_inference_time)) # Gather the predicted class, the probability of that class and other relevant information required to block the attack output_message = { "confidence": None, Loading Loading @@ -537,8 +593,12 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto self.monitor_kpis() LOGGER.debug("cryptomining_detector_output: {}".format(cryptomining_detector_output)) # Only notify Attack Mitigator when a cryptomining connection has been detected if cryptomining_detector_output["tag_name"] == "Crypto": notification_time_start = time.perf_counter() logging.info("Crypto attack detected") # Notify the Attack Mitigator component about the attack Loading @@ -550,6 +610,35 @@ class l3_centralizedattackdetectorServiceServicerImpl(L3Centralizedattackdetecto logging.info("Sending the connection information to the Attack Mitigator component...") message = L3AttackmitigatorOutput(**cryptomining_detector_output) response = self.attackmitigator_client.SendOutput(message) notification_time_end = time.perf_counter() self.am_notification_times.append(notification_time_end - notification_time_start) LOGGER.debug(f"am_notification_times length: {len(self.am_notification_times)}") if len(self.am_notification_times) > 100: am_notification_times_np_array = np.array(self.am_notification_times) np.save("am_notification_times.npy", am_notification_times_np_array) avg_notification_time = np.mean(am_notification_times_np_array) max_notification_time = np.max(am_notification_times_np_array) min_notification_time = np.min(am_notification_times_np_array) std_notification_time = np.std(am_notification_times_np_array) median_notification_time = np.median(am_notification_times_np_array) LOGGER.debug("Average notification time: {}".format(avg_notification_time)) LOGGER.debug("Max notification time: {}".format(max_notification_time)) LOGGER.debug("Min notification time: {}".format(min_notification_time)) LOGGER.debug("Std notification time: {}".format(std_notification_time)) LOGGER.debug("Median notification time: {}".format(median_notification_time)) with open("am_notification_times_stats.txt", "w") as f: f.write("Average notification time: {}\n".format(avg_notification_time)) f.write("Max notification time: {}\n".format(max_notification_time)) f.write("Min notification time: {}\n".format(min_notification_time)) f.write("Std notification time: {}\n".format(std_notification_time)) f.write("Median notification time: {}\n".format(median_notification_time)) # logging.info("Attack Mitigator notified and received response: ", response.message) # FIX No message received logging.info("Attack Mitigator notified") Loading
webui.sh 0 → 100755 +80 −0 Original line number Diff line number Diff line echo "Configuring WebUI DataStores and Dashboards..." sleep 3 # INFLUXDB_HOST="monitoringservice" # INFLUXDB_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service/monitoringservice -o jsonpath='{.spec.ports[?(@.name=="influxdb")].port}') # INFLUXDB_URL="http://${INFLUXDB_HOST}:${INFLUXDB_PORT}" # INFLUXDB_USER=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_USER}' | base64 --decode) # INFLUXDB_PASSWORD=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_PASSWORD}' | base64 --decode) # INFLUXDB_DATABASE=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_DB}' | base64 --decode) # Exposed through the ingress controller "tfs-ingress" GRAFANA_HOSTNAME="127.0.0.1" GRAFANA_PORT="80" GRAFANA_BASEURL="/grafana" # Default Grafana credentials GRAFANA_USERNAME="admin" GRAFANA_PASSWORD="admin" # Default Grafana API URL GRAFANA_URL_DEFAULT="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" # Updated Grafana API URL GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT echo "Connecting to grafana at URL: ${GRAFANA_URL_DEFAULT}..." # Configure Grafana Admin Password # Ref: https://grafana.com/docs/grafana/latest/http_api/user/#change-password curl -X PUT -H "Content-Type: application/json" -d '{ "oldPassword": "'${GRAFANA_PASSWORD}'", "newPassword": "'${TFS_GRAFANA_PASSWORD}'", "confirmNew" : "'${TFS_GRAFANA_PASSWORD}'" }' ${GRAFANA_URL_DEFAULT}/api/user/password echo # Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/ # TODO: replace user, password and database by variables to be saved echo "Creating a datasource..." curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{ "access" : "proxy", "type" : "postgres", "name" : "monitoringdb", "url" : "monitoringservice:8812", "database" : "monitoring", "user" : "admin", "basicAuth": false, "isDefault": true, "jsonData" : { "sslmode" : "disable", "postgresVersion" : 1100, "maxOpenConns" : 0, "maxIdleConns" : 2, "connMaxLifetime" : 14400, "tlsAuth" : false, "tlsAuthWithCACert" : false, "timescaledb" : false, "tlsConfigurationMethod": "file-path", "tlsSkipVerify" : true }, "secureJsonData": { "password": "quest" } }' ${GRAFANA_URL_UPDATED}/api/datasources echo # Create Monitoring Dashboard # Ref: https://grafana.com/docs/grafana/latest/http_api/dashboard/ curl -X POST -H "Content-Type: application/json" \ -d '@src/webui/grafana_dashboard_psql.json' \ ${GRAFANA_URL_UPDATED}/api/dashboards/db echo DASHBOARD_URL="${GRAFANA_URL_UPDATED}/api/dashboards/uid/tf-l3-monit" DASHBOARD_ID=$(curl -s "${DASHBOARD_URL}" | jq '.dashboard.id') curl -X POST ${GRAFANA_URL_UPDATED}/api/user/stars/dashboard/${DASHBOARD_ID} printf "\n\n" No newline at end of file