main
py 403 lines 15.6 KB
Raw
1 import json
2 from datetime import datetime
3
4 from fastapi import HTTPException
5 from loguru import logger
6 from sqlalchemy.ext.asyncio import AsyncSession
7
8 from app.connectors.grafana.schema.dashboards import DashboardProvisionRequest
9 from app.connectors.grafana.schema.dashboards import FortinetDashboard
10 from app.connectors.grafana.services.dashboards import provision_dashboards
11 from app.connectors.grafana.utils.universal import create_grafana_client
12 from app.connectors.graylog.services.collector import (
13 get_content_pack_id_by_content_pack_name,
14 )
15 from app.connectors.graylog.services.collector import get_input_id_by_input_name
16 from app.connectors.graylog.services.collector import get_stream_id_by_stream_name
17 from app.connectors.graylog.services.streams import assign_stream_to_index
18 from app.connectors.graylog.utils.universal import send_post_request
19 from app.connectors.wazuh_indexer.services.monitoring import (
20 output_shard_number_to_be_set_based_on_nodes,
21 )
22 from app.customer_provisioning.schema.grafana import GrafanaDatasource
23 from app.customer_provisioning.schema.grafana import GrafanaDataSourceCreationResponse
24 from app.customer_provisioning.schema.graylog import GraylogIndexSetCreationResponse
25 from app.customer_provisioning.schema.graylog import StreamConnectionToPipelineRequest
26 from app.customer_provisioning.schema.graylog import TimeBasedIndexSet
27 from app.customer_provisioning.schema.provision import ProvisionNewCustomer
28 from app.customer_provisioning.services.grafana import create_grafana_folder
29 from app.customer_provisioning.services.grafana import get_opensearch_version
30 from app.customer_provisioning.services.graylog import connect_stream_to_pipeline
31 from app.customer_provisioning.services.graylog import get_pipeline_id
32 from app.customers.routes.customers import get_customer_meta
33 from app.network_connectors.models.network_connectors import (
34 CustomerNetworkConnectorsMeta,
35 )
36 from app.stack_provisioning.graylog.schema.fortinet import FortinetCustomerDetails
37 from app.stack_provisioning.graylog.schema.fortinet import ProvisionFortinetKeys
38 from app.stack_provisioning.graylog.schema.fortinet import ProvisionFortinetResponse
39 from app.stack_provisioning.graylog.schema.provision import ContentPackKeywords
40 from app.stack_provisioning.graylog.schema.provision import (
41 ProvisionNetworkContentPackRequest,
42 )
43 from app.stack_provisioning.graylog.services.provision import (
44 provision_content_pack_network_connector,
45 )
46 from app.stack_provisioning.graylog.services.utils import set_deployed_flag
47 from app.utils import get_connector_attribute
48 from app.utils import get_customer_meta_attribute
49
50
51 #### ! GRAYLOG ! ####
52 async def build_index_set_config(request: FortinetCustomerDetails) -> TimeBasedIndexSet:
53 """
54 Build the configuration for a time-based index set.
55
56 Args:
57 request (FortinetCustomerDetails): The request object containing customer information.
58
59 Returns:
60 TimeBasedIndexSet: The configured time-based index set.
61 """
62 return TimeBasedIndexSet(
63 title=f"{request.customer_name} - FORTINET EVENTS",
64 description=f"{request.customer_name} - FORTINET EVENTS",
65 index_prefix=f"fortinet-{request.customer_code}",
66 rotation_strategy_class="org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategy",
67 rotation_strategy={
68 "type": "org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategyConfig",
69 "rotation_period": "P1D",
70 "rotate_empty_index_set": False,
71 "max_rotation_period": None,
72 },
73 retention_strategy_class="org.graylog2.indexer.retention.strategies.DeletionRetentionStrategy",
74 retention_strategy={
75 "type": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategyConfig",
76 "max_number_of_indices": request.hot_data_retention,
77 },
78 creation_date=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
79 index_analyzer="standard",
80 shards=await output_shard_number_to_be_set_based_on_nodes(),
81 replicas=request.index_replicas,
82 index_optimization_max_num_segments=1,
83 index_optimization_disabled=False,
84 writable=True,
85 field_type_refresh_interval=5000,
86 )
87
88
89 # Function to send the POST request and handle the response
90 async def send_index_set_creation_request(
91 index_set: TimeBasedIndexSet,
92 ) -> GraylogIndexSetCreationResponse:
93 """
94 Sends a request to create an index set in Graylog.
95
96 Args:
97 index_set (TimeBasedIndexSet): The index set to be created.
98
99 Returns:
100 GraylogIndexSetCreationResponse: The response from Graylog after creating the index set.
101 """
102 json_index_set = json.dumps(index_set.model_dump())
103 logger.info(f"json_index_set set: {json_index_set}")
104 response_json = await send_post_request(
105 endpoint="/api/system/indices/index_sets",
106 data=index_set.model_dump(),
107 )
108 return GraylogIndexSetCreationResponse(**response_json)
109
110
111 # Refactored create_index_set function
112 async def create_index_set(
113 request: ProvisionNewCustomer,
114 ) -> GraylogIndexSetCreationResponse:
115 """
116 Creates an index set for a new customer.
117
118 Args:
119 request (ProvisionNewCustomer): The request object containing the customer information.
120
121 Returns:
122 GraylogIndexSetCreationResponse: The response object containing the result of the index set creation.
123 """
124 logger.info(f"Creating index set for customer {request.customer_name}")
125 index_set_config = await build_index_set_config(request)
126 return await send_index_set_creation_request(index_set_config)
127
128
129 async def provision_content_pack(customer_details):
130 """
131 Provisions a content pack for a customer.
132
133 Args:
134 customer_details (CustomerDetails): The details of the customer.
135
136 Returns:
137 ContentPack: The provisioned content pack.
138 """
139 return await provision_content_pack_network_connector(
140 content_pack_request=ProvisionNetworkContentPackRequest(
141 content_pack_name="FORTINET",
142 keywords=ContentPackKeywords(
143 customer_name=customer_details.customer_name,
144 customer_code=customer_details.customer_code,
145 protocol_type=customer_details.protocal_type,
146 syslog_port=customer_details.syslog_port,
147 ),
148 ),
149 )
150
151
152 async def get_stream_and_index_ids(customer_details):
153 """
154 Retrieves the stream ID and index ID for a given customer.
155
156 Args:
157 customer_details (CustomerDetails): The details of the customer.
158
159 Returns:
160 tuple: A tuple containing the stream ID and index ID.
161 """
162 stream_id = await get_stream_id_by_stream_name(stream_name=f"{customer_details.customer_name} - FORTINET LOGS AND EVENTS")
163 index_id = (await create_index_set(request=customer_details)).data.id
164 content_pack_stream_id = await get_content_pack_id_by_content_pack_name(
165 content_pack_name=f"{customer_details.customer_name}_FORTINET_STREAM",
166 )
167 if customer_details.protocal_type == "TCP":
168 content_pack_input_id = await get_content_pack_id_by_content_pack_name(
169 content_pack_name=f"{customer_details.customer_name}_FORTINET_INPUT_SYSLOG_TCP",
170 )
171 elif customer_details.protocal_type == "UDP":
172 content_pack_input_id = await get_content_pack_id_by_content_pack_name(
173 content_pack_name=f"{customer_details.customer_name}_FORTINET_INPUT_SYSLOG_UDP",
174 )
175 return stream_id, index_id, content_pack_stream_id, content_pack_input_id
176
177
178 #### ! GRAFANA ! ####
179 async def create_grafana_datasource(
180 customer_code: str,
181 session: AsyncSession,
182 ) -> GrafanaDataSourceCreationResponse:
183 """
184 Creates a Grafana datasource for the specified customer.
185
186 Args:
187 customer_code (str): The customer code.
188 session (AsyncSession): The async session.
189
190 Returns:
191 GrafanaDataSourceCreationResponse: The response containing the created datasource details.
192 """
193 logger.info("Creating Grafana datasource")
194 grafana_client = await create_grafana_client("Grafana")
195 # Switch to the newly created organization
196 grafana_client.user.switch_actual_user_organisation(
197 (await get_customer_meta(customer_code, session)).customer_meta.customer_meta_grafana_org_id,
198 )
199 datasource_payload = GrafanaDatasource(
200 name="FORTINET",
201 type="grafana-opensearch-datasource",
202 typeName="OpenSearch",
203 access="proxy",
204 url=await get_connector_attribute(
205 connector_id=1,
206 column_name="connector_url",
207 session=session,
208 ),
209 database=f"fortinet-{customer_code}*",
210 basicAuth=True,
211 basicAuthUser=await get_connector_attribute(
212 connector_id=1,
213 column_name="connector_username",
214 session=session,
215 ),
216 secureJsonData={
217 "basicAuthPassword": await get_connector_attribute(
218 connector_id=1,
219 column_name="connector_password",
220 session=session,
221 ),
222 },
223 isDefault=False,
224 jsonData={
225 "database": f"fortinet-{customer_code}*",
226 "flavor": "opensearch",
227 "includeFrozen": False,
228 "logLevelField": "severity",
229 "logMessageField": "summary",
230 "maxConcurrentShardRequests": 5,
231 "pplEnabled": True,
232 "timeField": "timestamp",
233 "tlsSkipVerify": True,
234 "version": await get_opensearch_version(),
235 },
236 readOnly=True,
237 )
238 results = grafana_client.datasource.create_datasource(
239 datasource=datasource_payload.model_dump(),
240 )
241 return GrafanaDataSourceCreationResponse(**results)
242
243
244 async def create_customer_network_connector_meta(
245 customer_details,
246 stream_id,
247 index_id,
248 content_pack_stream_id,
249 content_pack_input_id,
250 session,
251 ):
252 """
253 Create a CustomerNetworkConnectorsMeta object with the provided details.
254
255 Args:
256 customer_details (CustomerDetails): Details of the customer.
257 stream_id (int): ID of the Graylog stream.
258 index_id (int): ID of the Graylog index.
259 session (Session): Database session.
260
261 Returns:
262 CustomerNetworkConnectorsMeta: The created CustomerNetworkConnectorsMeta object.
263 """
264 return CustomerNetworkConnectorsMeta(
265 customer_code=customer_details.customer_code,
266 network_connector_name="FORTINET",
267 graylog_stream_id=stream_id,
268 graylog_input_id=(await get_input_id_by_input_name(input_name=f"{customer_details.customer_name} - FORTINET LOGS AND EVENTS")),
269 graylog_pipeline_id=((await get_pipeline_id(subscription="FORTINET"))[0]),
270 graylog_content_pack_input_id=content_pack_input_id,
271 graylog_content_pack_stream_id=content_pack_stream_id,
272 grafana_org_id=(
273 await get_customer_meta_attribute(
274 session=session,
275 customer_code=customer_details.customer_code,
276 column_name="customer_meta_grafana_org_id",
277 )
278 ),
279 graylog_index_id=index_id,
280 grafana_dashboard_folder_id=None,
281 grafana_datasource_uid=None,
282 )
283
284
285 async def validate_grafana_organization_id(customer_code, session):
286 """
287 Validate the Grafana organization ID for the customer.
288
289 Args:
290 customer_code (str): The customer code.
291 session (Session): Database session.
292
293 Returns:
294 int: The Grafana organization ID.
295 """
296 return await get_customer_meta_attribute(session=session, customer_code=customer_code, column_name="customer_meta_grafana_org_id")
297
298
299 async def provision_fortinet(
300 customer_details: FortinetCustomerDetails,
301 keys: ProvisionFortinetKeys,
302 session: AsyncSession,
303 ) -> ProvisionFortinetResponse:
304 """
305 Provisions a Fortinet customer by performing the following steps:
306 1. Provisions the content pack for the customer.
307 2. Retrieves the stream and index IDs for the customer.
308 3. Creates customer network connector metadata.
309 4. Assigns the stream to the index.
310 5. Retrieves the pipeline ID for the "FORTINET" subscription.
311 6. Connects the stream to the pipeline.
312 7. Inserts the customer network connector metadata into the database.
313
314 Args:
315 customer_details (FortinetCustomerDetails): The details of the Fortinet customer.
316 keys (ProvisionFortinetKeys): The keys required for provisioning.
317 session (AsyncSession): The database session.
318
319 Returns:
320 None
321 """
322 if await validate_grafana_organization_id(customer_details.customer_code, session) is None:
323 raise HTTPException(status_code=404, detail="Grafana organization ID not found. Please provision Grafana for the customer first.")
324 await provision_content_pack(customer_details)
325 stream_id, index_id, content_pack_stream_id, content_pack_input_id = await get_stream_and_index_ids(customer_details)
326 customer_network_connector_meta = await create_customer_network_connector_meta(
327 customer_details,
328 stream_id,
329 index_id,
330 content_pack_stream_id,
331 content_pack_input_id,
332 session,
333 )
334 await assign_stream_to_index(stream_id=stream_id, index_id=index_id)
335 pipeline_id = await get_pipeline_id(subscription="FORTINET")
336 await connect_stream_to_pipeline(stream_and_pipeline=StreamConnectionToPipelineRequest(stream_id=stream_id, pipeline_ids=pipeline_id))
337 # Grafana Deployment
338 customer_network_connector_meta.grafana_datasource_uid = (
339 await create_grafana_datasource(
340 customer_code=customer_details.customer_code,
341 session=session,
342 )
343 ).datasource.uid
344 grafana_folder = await create_grafana_folder(
345 organization_id=(
346 await get_customer_meta(
347 customer_details.customer_code,
348 session,
349 )
350 ).customer_meta.customer_meta_grafana_org_id,
351 folder_title="FORTINET",
352 )
353 await provision_dashboards(
354 DashboardProvisionRequest(
355 dashboards=[dashboard.name for dashboard in FortinetDashboard],
356 organizationId=(
357 await get_customer_meta(
358 customer_details.customer_code,
359 session,
360 )
361 ).customer_meta.customer_meta_grafana_org_id,
362 folderId=grafana_folder.id,
363 datasourceUid=customer_network_connector_meta.grafana_datasource_uid,
364 ),
365 )
366 customer_network_connector_meta.grafana_dashboard_folder_id = grafana_folder.uid
367 await insert_into_customer_network_connectors_meta_table(
368 customer_network_connectors_meta=customer_network_connector_meta,
369 session=session,
370 )
371
372 await set_deployed_flag(
373 customer_code=customer_details.customer_code,
374 network_connector_service_name="Fortinet",
375 flag=True,
376 session=session,
377 )
378
379 return ProvisionFortinetResponse(
380 message="Fortinet customer provisioned successfully",
381 success=True,
382 )
383
384
385 async def insert_into_customer_network_connectors_meta_table(
386 customer_network_connectors_meta: CustomerNetworkConnectorsMeta,
387 session: AsyncSession,
388 ) -> None:
389 """
390 Insert the customer network connectors meta into the database.
391
392 Args:
393 customer_network_connectors_meta (CustomerNetworkConnectorsMeta): The customer network connectors meta to insert.
394 session (AsyncSession): The async session object for database operations.
395
396 Returns:
397 None
398 """
399 logger.info("Inserting customer network connectors meta into the database")
400 session.add(customer_network_connectors_meta)
401 await session.commit()
402 logger.info("Customer network connectors meta inserted successfully")
403 return None