@cryptotaxi247 / CoPilot / commits / d492c8ec

Healthyagents by customer (#74)

* wazuh velo and full healthchecks done by customer determines the agents that belong to the given customer and endpoints to run wazuh, velo, and full healthchecks on the customer's agents * precommit fixes

taylor_socfortress committed Jul 26, 2023 at 12:26 UTC d492c8ec894c66a260372963b2480896a0946432
3 files changed +249 -7
backend/app/routes/customers.py
+103
@@ -9,6 +9,7 @@ from app.models.agents import AgentMetadata
9 from app.models.agents import agent_metadatas_schema
10 from app.services.Customers.universal import UniversalCustomers
11 from app.services.Customers.universal import UniversalCustomersMeta
12 +from app.services.Healthchecks.agents import HealthcheckAgentsService
13
14 bp = Blueprint("customers", __name__)
15
@@ -309,3 +310,105 @@ def read_customer_agents(id: int):
310 200,
311 )
312 return jsonify({"message": "No agents found for this customer.", "success": False}), 404
313 +
314 +
315 +@bp.route("/customers/healthchecks/agents/<int:id>/wazuh", methods=["GET"])
316 +def read_customer_agents_healthchecks_wazuh(id: int):
317 + """
318 + Endpoint to fetch the `customerCode` from the `customers_meta` table for the given customer id.
319 + Then, it uses the customer code to fetch all agents from the `agent_metadata` table where the `customerCode`
320 + is a wildcard match on the `label` column within the `agent_metadata` table.
321 +
322 + Returns:
323 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
324 + containing the customer code and the list of agents, and the second element is
325 + the HTTP status code.
326 + """
327 + logger.info(f"Received request to get customer agents with id {id}")
328 + customer_meta = UniversalCustomersMeta.read_by_id(id)
329 + if customer_meta:
330 + customer_code = customer_meta["customerCode"]
331 + agents = AgentMetadata.query.filter(AgentMetadata.label.like(f"%{customer_code}%")).all()
332 + if agents:
333 + agents_list = agent_metadatas_schema.dump(agents)
334 + agent_health = HealthcheckAgentsService().perform_healthcheck_wazuh(agents=agents_list)
335 + return (
336 + jsonify(
337 + {
338 + "customerCode": customer_code,
339 + "agents": agent_health,
340 + "message": "Customer Wazuh agents health fetched.",
341 + "success": True,
342 + },
343 + ),
344 + 200,
345 + )
346 + return jsonify({"message": "No wazuh agents found for this customer.", "success": False}), 404
347 +
348 +
349 +@bp.route("/customers/healthchecks/agents/<int:id>/velociraptor", methods=["GET"])
350 +def read_customer_agents_healthchecks_velociraptor(id: int):
351 + """
352 + Endpoint to fetch the `customerCode` from the `customers_meta` table for the given customer id.
353 + Then, it uses the customer code to fetch all agents from the `agent_metadata` table where the `customerCode`
354 + is a wildcard match on the `label` column within the `agent_metadata` table.
355 +
356 + Returns:
357 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
358 + containing the customer code and the list of agents, and the second element is
359 + the HTTP status code.
360 + """
361 + logger.info(f"Received request to get customer agents with id {id}")
362 + customer_meta = UniversalCustomersMeta.read_by_id(id)
363 + if customer_meta:
364 + customer_code = customer_meta["customerCode"]
365 + agents = AgentMetadata.query.filter(AgentMetadata.label.like(f"%{customer_code}%")).all()
366 + if agents:
367 + agents_list = agent_metadatas_schema.dump(agents)
368 + agent_health = HealthcheckAgentsService().perform_healthcheck_velociraptor(agents=agents_list)
369 + return (
370 + jsonify(
371 + {
372 + "customerCode": customer_code,
373 + "agents": agent_health,
374 + "message": "Customer Velociraptor agents health fetched.",
375 + "success": True,
376 + },
377 + ),
378 + 200,
379 + )
380 + return jsonify({"message": "No velociraptor agents found for this customer.", "success": False}), 404
381 +
382 +
383 +@bp.route("/customers/healthchecks/agents/<int:id>/full", methods=["GET"])
384 +def read_customer_agents_healthchecks_full(id: int):
385 + """
386 + Endpoint to fetch the `customerCode` from the `customers_meta` table for the given customer id.
387 + Then, it uses the customer code to fetch all agents from the `agent_metadata` table where the `customerCode`
388 + is a wildcard match on the `label` column within the `agent_metadata` table.
389 +
390 + Returns:
391 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
392 + containing the customer code and the list of agents, and the second element is
393 + the HTTP status code.
394 + """
395 + logger.info(f"Received request to get customer agents with id {id}")
396 + customer_meta = UniversalCustomersMeta.read_by_id(id)
397 + if customer_meta:
398 + customer_code = customer_meta["customerCode"]
399 + agents = AgentMetadata.query.filter(AgentMetadata.label.like(f"%{customer_code}%")).all()
400 + if agents:
401 + agents_list = agent_metadatas_schema.dump(agents)
402 + agent_health = HealthcheckAgentsService().perform_healthcheck_full(agents=agents_list, check_logs=True)
403 + return (
404 + jsonify(
405 + {
406 + "customerCode": customer_code,
407 + "agents": agent_health,
408 + "message": "Customer full agents health fetched.",
409 + "success": True,
410 + },
411 + ),
412 + 200,
413 + )
414 + return jsonify({"message": "No agents found for this customer.", "success": False}), 404
backend/app/services/Healthchecks/agents.py
+2 -7
@@ -92,13 +92,8 @@ class HealthcheckAgentsService:
92 continue
93 response = self.universal_service.run_query(query, index, size=1)
94 if response["query_results"]["hits"]["total"]["value"] > 0:
95 - return True
96 - # Check the next interval only if the previous interval didn't return results
97 - if interval == 1:
98 - break
99 - # Proceed to next agent if logs are found
100 - break
101 - return False
95 + return True # Logs found, stop checking.
96 + return False # No logs found after checking all intervals and indices.
97
98 @staticmethod
99 def _generate_recent_logs_query(agent_hostname: str, minutes: int) -> Dict:
backend/app/static/swagger.json
+144
@@ -880,6 +880,150 @@
880 }
881 }
882 },
883 + "/customers/healthchecks/agents/{id}/wazuh": {
884 + "get": {
885 + "tags": ["Customers"],
886 + "summary": "Get a customer",
887 + "description": "Endpoint to retrieve a customer details from the `customers` and `customers_meta` tables.",
888 + "parameters": [
889 + {
890 + "name": "id",
891 + "in": "path",
892 + "description": "ID of the customer to retrieve",
893 + "required": true,
894 + "type": "integer"
895 + }
896 + ],
897 + "responses": {
898 + "200": {
899 + "description": "Successful operation",
900 + "content": {
901 + "application/json": {
902 + "schema": {
903 + "type": "object",
904 + "properties": {
905 + "message": { "type": "string", "example": "Customer retrieved successfully." },
906 + "success": { "type": "boolean", "example": true },
907 + "customer": { "$ref": "#/definitions/Customer" },
908 + "customerMeta": { "$ref": "#/definitions/CustomerMeta" }
909 + }
910 + }
911 + }
912 + }
913 + },
914 + "404": {
915 + "description": "Customer not found",
916 + "content": {
917 + "application/json": {
918 + "schema": {
919 + "type": "object",
920 + "properties": {
921 + "message": { "type": "string", "example": "Customer not found." },
922 + "success": { "type": "boolean", "example": false }
923 + }
924 + }
925 + }
926 + }
927 + }
928 + }
929 + }
930 + },
931 + "/customers/healthchecks/agents/{id}/velociraptor": {
932 + "get": {
933 + "tags": ["Customers"],
934 + "summary": "Get a customer",
935 + "description": "Endpoint to retrieve a customer details from the `customers` and `customers_meta` tables.",
936 + "parameters": [
937 + {
938 + "name": "id",
939 + "in": "path",
940 + "description": "ID of the customer to retrieve",
941 + "required": true,
942 + "type": "integer"
943 + }
944 + ],
945 + "responses": {
946 + "200": {
947 + "description": "Successful operation",
948 + "content": {
949 + "application/json": {
950 + "schema": {
951 + "type": "object",
952 + "properties": {
953 + "message": { "type": "string", "example": "Customer retrieved successfully." },
954 + "success": { "type": "boolean", "example": true },
955 + "customer": { "$ref": "#/definitions/Customer" },
956 + "customerMeta": { "$ref": "#/definitions/CustomerMeta" }
957 + }
958 + }
959 + }
960 + }
961 + },
962 + "404": {
963 + "description": "Customer not found",
964 + "content": {
965 + "application/json": {
966 + "schema": {
967 + "type": "object",
968 + "properties": {
969 + "message": { "type": "string", "example": "Customer not found." },
970 + "success": { "type": "boolean", "example": false }
971 + }
972 + }
973 + }
974 + }
975 + }
976 + }
977 + }
978 + },
979 + "/customers/healthchecks/agents/{id}/full": {
980 + "get": {
981 + "tags": ["Customers"],
982 + "summary": "Get a customer",
983 + "description": "Endpoint to retrieve a customer details from the `customers` and `customers_meta` tables.",
984 + "parameters": [
985 + {
986 + "name": "id",
987 + "in": "path",
988 + "description": "ID of the customer to retrieve",
989 + "required": true,
990 + "type": "integer"
991 + }
992 + ],
993 + "responses": {
994 + "200": {
995 + "description": "Successful operation",
996 + "content": {
997 + "application/json": {
998 + "schema": {
999 + "type": "object",
1000 + "properties": {
1001 + "message": { "type": "string", "example": "Customer retrieved successfully." },
1002 + "success": { "type": "boolean", "example": true },
1003 + "customer": { "$ref": "#/definitions/Customer" },
1004 + "customerMeta": { "$ref": "#/definitions/CustomerMeta" }
1005 + }
1006 + }
1007 + }
1008 + }
1009 + },
1010 + "404": {
1011 + "description": "Customer not found",
1012 + "content": {
1013 + "application/json": {
1014 + "schema": {
1015 + "type": "object",
1016 + "properties": {
1017 + "message": { "type": "string", "example": "Customer not found." },
1018 + "success": { "type": "boolean", "example": false }
1019 + }
1020 + }
1021 + }
1022 + }
1023 + }
1024 + }
1025 + }
1026 + },
1027 "/agents": {
1028 "get": {
1029 "tags": ["Agents"],