1
+import os
2
+from typing import Optional
3
+
4
+from dotenv import load_dotenv
5
+from fastapi import HTTPException
6
+from loguru import logger
7
+
8
+from app.connectors.graylog.routes.monitoring import get_all_event_notifications
9
+from app.connectors.graylog.schema.management import UrlWhitelistEntryResponse
10
+from app.connectors.graylog.schema.monitoring import GraylogEventNotificationsResponse
11
+from app.connectors.graylog.services.collector import get_url_whitelist_entries
12
+from app.connectors.graylog.utils.universal import send_post_request
13
+from app.connectors.graylog.utils.universal import send_put_request
14
+from app.integrations.monitoring_alert.schema.provision import (
15
+ GraylogAlertProvisionConfig,
16
+)
17
+from app.integrations.monitoring_alert.schema.provision import (
18
+ GraylogAlertProvisionFieldSpecItem,
19
+)
20
+from app.integrations.monitoring_alert.schema.provision import (
21
+ GraylogAlertProvisionModel,
22
+)
23
+from app.integrations.monitoring_alert.schema.provision import (
24
+ GraylogAlertProvisionNotification,
25
+)
26
+from app.integrations.monitoring_alert.schema.provision import (
27
+ GraylogAlertProvisionNotificationSettings,
28
+)
29
+from app.integrations.monitoring_alert.schema.provision import (
30
+ GraylogAlertProvisionProvider,
31
+)
32
+from app.integrations.monitoring_alert.schema.provision import (
33
+ GraylogAlertWebhookNotificationModel,
34
+)
35
+from app.integrations.monitoring_alert.schema.provision import (
36
+ GraylogUrlWhitelistEntries,
37
+)
38
+from app.integrations.monitoring_alert.schema.provision import (
39
+ GraylogUrlWhitelistEntryConfig,
40
+)
41
+from app.integrations.monitoring_alert.schema.provision import (
42
+ ProvisionMonitoringAlertRequest,
43
+)
44
+from app.integrations.monitoring_alert.schema.provision import (
45
+ ProvisionWazuhMonitoringAlertResponse,
46
+)
47
+
48
+load_dotenv()
49
+import uuid
50
+
51
+
52
+async def convert_seconds_to_milliseconds(seconds: int) -> int:
53
+ """
54
+ Convert seconds to milliseconds.
55
+
56
+ Args:
57
+ seconds (int): The seconds to convert.
58
+
59
+ Returns:
60
+ int: The milliseconds.
61
+ """
62
+ return seconds * 1000
63
+
64
+
65
+async def generate_random_id() -> str:
66
+ """
67
+ Generate a random id.
68
+
69
+ Returns:
70
+ str: The random id.
71
+ """
72
+ return str(uuid.uuid4())
73
+
74
+
75
+async def check_if_url_whitelist_entry_exists(url: str) -> bool:
76
+ """
77
+ Check if the url whitelist entry exists.
78
+
79
+ Args:
80
+ url (str): The url to check.
81
+
82
+ Returns:
83
+ bool: True if the url whitelist entry exists, False otherwise.
84
+ """
85
+ url_whitelist_entries_response = await get_url_whitelist_entries()
86
+ if not url_whitelist_entries_response.success:
87
+ raise HTTPException(status_code=500, detail="Failed to collect url whitelist entries")
88
+ url_whitelist_entries_response = UrlWhitelistEntryResponse(**url_whitelist_entries_response.dict())
89
+ logger.info(f"Url whitelist entries collected: {url_whitelist_entries_response.url_whitelist_entries}")
90
+ if url in [url_whitelist_entry.value for url_whitelist_entry in url_whitelist_entries_response.url_whitelist_entries.entries]:
91
+ logger.info(f"Url whitelist entry {url} already exists")
92
+ return True
93
+ return False
94
+
95
+
96
+async def build_url_whitelisted_entries(whitelist_url_model: GraylogUrlWhitelistEntryConfig) -> GraylogUrlWhitelistEntries:
97
+ """
98
+ Builds the URL Whitelisted Entries model.
99
+
100
+ Returns:
101
+ GraylogUrlWhitelistEntries: The URL Whitelisted Entries model.
102
+ """
103
+ url_whitelist_entries_response = await get_url_whitelist_entries()
104
+ if not url_whitelist_entries_response.success:
105
+ raise HTTPException(status_code=500, detail="Failed to collect url whitelist entries")
106
+ url_whitelist_entries_response = UrlWhitelistEntryResponse(**url_whitelist_entries_response.dict())
107
+ logger.info(f"Url whitelist entries collected: {url_whitelist_entries_response}")
108
+ url_whitelist_entries = url_whitelist_entries_response.url_whitelist_entries.entries
109
+ url_whitelist_entries.append(whitelist_url_model)
110
+ return GraylogUrlWhitelistEntries(
111
+ entries=url_whitelist_entries,
112
+ disabled=False,
113
+ )
114
+
115
+
116
+async def provision_webhook_url_whitelist(whitelist_url_model: GraylogUrlWhitelistEntries) -> bool:
117
+ """
118
+ Provisions a webhook URL for Graylog.
119
+
120
+ Args:
121
+ whitelist_url_model (GraylogUrlWhitelistEntryConfig): The webhook URL model.
122
+
123
+ Returns:
124
+ bool: True if the webhook URL was provisioned successfully, False otherwise.
125
+ """
126
+ logger.info(f"Provisioning URL Whitelist: {whitelist_url_model.dict()}")
127
+ response = await send_put_request(endpoint="/api/system/urlwhitelist", data=whitelist_url_model.dict())
128
+ logger.info(f"URL Whitelist provisioned: {response}")
129
+ if response["success"]:
130
+ return True
131
+ raise HTTPException(status_code=500, detail="Failed to provision URL Whitelist")
132
+
133
+
134
+async def check_if_event_notification_exists(event_notification: str) -> bool:
135
+ """
136
+ Check if the event notification exists.
137
+
138
+ Args:
139
+ event_notification (str): The event notification to check.
140
+
141
+ Returns:
142
+ bool: True if the event notification exists, False otherwise.
143
+ """
144
+ event_notifications_response = await get_all_event_notifications()
145
+ if not event_notifications_response.success:
146
+ raise HTTPException(status_code=500, detail="Failed to collect event notifications")
147
+ event_notifications_response = GraylogEventNotificationsResponse(**event_notifications_response.dict())
148
+ logger.info(f"Event notifications collected: {event_notifications_response.event_notifications}")
149
+ if event_notification in [
150
+ event_notification.title for event_notification in event_notifications_response.event_notifications.notifications
151
+ ]:
152
+ return True
153
+ return False
154
+
155
+
156
+async def provision_webhook(webhook_model: GraylogAlertWebhookNotificationModel) -> Optional[str]:
157
+ """
158
+ Provisions a webhook for Graylog alerts.
159
+
160
+ Args:
161
+ webhook_model (GraylogAlertWebhookNotificationModel): The webhook model.
162
+
163
+ Returns:
164
+ bool: True if the webhook was provisioned successfully, False otherwise.
165
+ """
166
+ response = await send_post_request(endpoint="/api/events/notifications", data=webhook_model.dict())
167
+ if response["success"]:
168
+ logger.info(f"response: {response}")
169
+ return response["data"]["id"]
170
+ raise HTTPException(status_code=500, detail="Failed to provision webhook")
171
+
172
+
173
+async def provision_alert_definition(alert_definition_model: GraylogAlertProvisionModel) -> bool:
174
+ """
175
+ Provisions an alert definition for Graylog.
176
+
177
+ Args:
178
+ alert_definition_model (GraylogAlertProvisionModel): The alert definition model.
179
+
180
+ Returns:
181
+ bool: True if the alert definition was provisioned successfully, False otherwise.
182
+ """
183
+ response = await send_post_request(endpoint="/api/events/definitions", data=alert_definition_model.dict())
184
+ if response["success"]:
185
+ return True
186
+ raise HTTPException(status_code=500, detail="Failed to provision alert definition")
187
+
188
+
189
+async def provision_wazuh_monitoring_alert(request: ProvisionMonitoringAlertRequest) -> ProvisionWazuhMonitoringAlertResponse:
190
+ """
191
+ Provisions Wazuh monitoring alerts.
192
+
193
+ Returns:
194
+ ProvisionWazuhMonitoringAlertResponse: The response indicating the success of provisioning the monitoring alerts.
195
+ """
196
+ #
197
+ logger.info(f"Invoking provision_wazuh_monitoring_alert with request: {request.dict()}")
198
+ notification_exists = await check_if_event_notification_exists("SEND TO COPILOT")
199
+ if not notification_exists:
200
+ url_whitelisted = await check_if_url_whitelist_entry_exists(f"http://{os.getenv('SERVER_IP')}:5000/monitoring_alerts/create")
201
+ if not url_whitelisted:
202
+ logger.info("Provisioning URL Whitelist")
203
+ whitelisted_urls = await build_url_whitelisted_entries(
204
+ whitelist_url_model=GraylogUrlWhitelistEntryConfig(
205
+ id=await generate_random_id(),
206
+ value=f"http://{os.getenv('SERVER_IP')}:5000/monitoring_alerts/create",
207
+ title="SEND TO COPILOT",
208
+ type="literal",
209
+ ),
210
+ )
211
+ await provision_webhook_url_whitelist(whitelisted_urls)
212
+
213
+ logger.info("Provisioning SEND TO COPILOT Webhook")
214
+ notification_id = await provision_webhook(
215
+ GraylogAlertWebhookNotificationModel(
216
+ title="SEND TO COPILOT",
217
+ description="Send alert to Copilot",
218
+ config={"url": f"http://{os.getenv('SERVER_IP')}:5000/monitoring_alerts/create", "type": "http-notification-v1"},
219
+ ),
220
+ )
221
+ logger.info(f"SEND TO COPILOT Webhook provisioned with id: {notification_id}")
222
+ await provision_alert_definition(
223
+ GraylogAlertProvisionModel(
224
+ title="WAZUH SYSLOG LEVEL ALERT",
225
+ description="Alert on Wazuh syslog level equal to ALERT",
226
+ priority=2,
227
+ config=GraylogAlertProvisionConfig(
228
+ type="aggregation-v1",
229
+ query="syslog_level:ALERT AND syslog_type:wazuh",
230
+ query_parameters=[],
231
+ streams=[],
232
+ group_by=[],
233
+ series=[],
234
+ conditions={
235
+ "expression": None,
236
+ },
237
+ search_within_ms=await convert_seconds_to_milliseconds(request.search_within_last),
238
+ execute_every_ms=await convert_seconds_to_milliseconds(request.execute_every),
239
+ ),
240
+ field_spec={
241
+ "ALERT_ID": GraylogAlertProvisionFieldSpecItem(
242
+ data_type="string",
243
+ providers=[
244
+ GraylogAlertProvisionProvider(
245
+ type="template-v1",
246
+ template="${source._id}",
247
+ require_values=True,
248
+ ),
249
+ ],
250
+ ),
251
+ "CUSTOMER_CODE": GraylogAlertProvisionFieldSpecItem(
252
+ data_type="string",
253
+ providers=[
254
+ GraylogAlertProvisionProvider(
255
+ type="template-v1",
256
+ template="${source.agent_labels_customer}",
257
+ require_values=True,
258
+ ),
259
+ ],
260
+ ),
261
+ "ALERT_SOURCE": GraylogAlertProvisionFieldSpecItem(
262
+ data_type="string",
263
+ providers=[
264
+ GraylogAlertProvisionProvider(
265
+ type="template-v1",
266
+ template="WAZUH",
267
+ require_values=True,
268
+ ),
269
+ ],
270
+ ),
271
+ },
272
+ key_spec=[],
273
+ notification_settings=GraylogAlertProvisionNotificationSettings(
274
+ grace_period_ms=0,
275
+ backlog_size=None,
276
+ ),
277
+ notifications=[
278
+ GraylogAlertProvisionNotification(
279
+ notification_id=notification_id,
280
+ ),
281
+ ],
282
+ alert=True,
283
+ ),
284
+ )
285
+
286
+ return ProvisionWazuhMonitoringAlertResponse(success=True, message="Wazuh monitoring alerts provisioned successfully")