Skip to content

Commit 807f7fe

Browse files
authored
Merge pull request #14558 from FortiNDR-Integration/FortiNDR-Cloud-Sentinel-data-collection-api-retirement
Refactoring Post Data to use the new LogIngestion Client
2 parents 6a10edc + 8d21af3 commit 807f7fe

9 files changed

Lines changed: 119 additions & 137 deletions

File tree

Solutions/Fortinet FortiNDR Cloud/Data Connectors/FortinetFortiNdrCloud_API_AzureFunctionApp.json

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -107,43 +107,28 @@
107107
},
108108
{
109109
"title": "",
110-
"description": ">**(Optional Step)** Securely store **Azure Client Secret** and **API authorization key(s) or token(s)** in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App."
110+
"description": ">**(Optional Step)** Securely store **API authorization key(s) or token(s)** in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App."
111111
},
112112
{
113113
"title": "",
114114
"description": ">**NOTE:** This connector uses a parser based on a Kusto Function to normalize fields. [Follow these steps](https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Solutions/Fortinet%20FortiNDR%20Cloud/Parsers/Fortinet_FortiNDR_Cloud.md) to create the Kusto function alias **Fortinet_FortiNDR_Cloud**."
115115
},
116116
{
117-
"description": "**STEP 1 - Prerequisites & App Registration**\n\nEnsure you have registered an identity application in your Microsoft Entra / Azure Active Directory tenant and granted it the necessary REST API execution permissions. Generate the credentials (Client Secret). You will need the **Azure Tenant ID**, **Azure Client ID**, and a valid **Azure Client Secret** to complete the custom template deployment form."
118-
},
119-
{
120-
"description": "**STEP 2 - Deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the Fortinet FortiNDR Cloud connector, have the **Azure Tenant ID**, **Azure Client ID**, and **Azure Client Secret** from Step 1, as well as the values below, readily available.",
117+
"description": "**STEP 1 - Deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the Fortinet FortiNDR Cloud connector, have the value below, readily available.",
121118
"instructions": [
122119
{
123120
"parameters": {
124121
"fillWith": [
125-
"workspaceName"
126-
],
127-
"label": "Workspace Name"
128-
},
129-
"type": "CopyableLabel"
130-
},
131-
{
132-
"parameters": {
133-
"fillWith": [
134-
"TenantId"
122+
"WorkspaceId"
135123
],
136-
"label": "Azure Tenant ID"
124+
"label": "Workspace ID"
137125
},
138126
"type": "CopyableLabel"
139127
}
140128
]
141129
},
142130
{
143-
"description": "**STEP 3 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Fortinet FortiNDR Cloud connector.\n\n1. Click the **Deploy to Azure** button below.\n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-FortinetFortiNDR-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**.\n3. Enter the **workspace**, **AzureTenantID**, **AzureClientID**, and **AzureClientSecret**.\n4. Click **Create** to deploy."
144-
},
145-
{
146-
"description": "**STEP 4 - After the connector is deployed, go to the Data Collection Rule and search for the dcr-fortinetfortindrcloud. Select Access control (IAM) and assign the Monitoring Metrics Publisher role to the integration App registration (use the display name when looking for the members).**"
131+
"description": "**STEP 2 - Azure Resource Manager (ARM) Template**\n\nUse this method for automated deployment of the Fortinet FortiNDR Cloud connector.\n\n1. Click the **Deploy to Azure** button below.\n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinel-FortinetFortiNDR-azuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**.\n3. Click **Create** to deploy."
147132
}
148133
]
149134
}

Solutions/Fortinet FortiNDR Cloud/Data Connectors/azuredeploy_FortinetFortiNdrCloud_API_FunctionApp.json

Lines changed: 87 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,6 @@
6161
],
6262
"type": "Int"
6363
},
64-
"AzureClientID": {
65-
"type": "string",
66-
"minLength": 1,
67-
"metadata": {
68-
"description": "Enter Azure Client ID that you have created during app registration"
69-
}
70-
},
71-
"AzureClientSecret": {
72-
"type": "securestring",
73-
"minLength": 1,
74-
"metadata": {
75-
"description": "Enter Azure Client Secret for your app registration"
76-
}
77-
},
78-
"AzureTenantID": {
79-
"type": "string",
80-
"minLength": 1,
81-
"metadata": {
82-
"description": "Enter Azure Tenant ID"
83-
}
84-
},
8564
"AwsAccessKeyId": {
8665
"type": "string",
8766
"defaultValue": "<AwsAccessKeyId>"
@@ -164,19 +143,36 @@
164143
},
165144
"resources": [
166145
{
167-
"type": "Microsoft.OperationalInsights/workspaces/tables",
168-
"apiVersion": "2022-10-01",
169-
"name": "FortinetFortiNdrCloudRaw_CL",
170-
"scope": "[tenantResourceId(subscription().subscriptionId, parameters('workspaceResourceGroup'), 'Microsoft.OperationalInsights/workspaces', parameters('workspace'))]",
146+
"type": "Microsoft.Resources/deployments",
147+
"apiVersion": "2022-09-01",
148+
"name": "deploy-fortinet-table",
149+
"resourceGroup": "[parameters('workspaceResourceGroup')]",
171150
"properties": {
172-
"retentionInDays": 30,
173-
"totalRetentionInDays": 30,
174-
"schema": {
175-
"name": "FortinetFortiNdrCloudRaw_CL",
176-
"columns": [
177-
{ "name": "TimeGenerated", "type": "datetime" },
178-
{ "name": "LogTypeSuffix", "type": "string" },
179-
{ "name": "RawData", "type": "string" }
151+
"mode": "Incremental",
152+
"expressionEvaluationOptions": {
153+
"scope": "outer"
154+
},
155+
"template": {
156+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
157+
"contentVersion": "1.0.0.0",
158+
"resources": [
159+
{
160+
"type": "Microsoft.OperationalInsights/workspaces/tables",
161+
"apiVersion": "2022-10-01",
162+
"name": "[concat(parameters('workspace'), '/FortinetFortiNdrCloudRaw_CL')]",
163+
"properties": {
164+
"retentionInDays": 30,
165+
"totalRetentionInDays": 30,
166+
"schema": {
167+
"name": "FortinetFortiNdrCloudRaw_CL",
168+
"columns": [
169+
{ "name": "TimeGenerated", "type": "datetime" },
170+
{ "name": "LogTypeSuffix", "type": "string" },
171+
{ "name": "RawData", "type": "string" }
172+
]
173+
}
174+
}
175+
}
180176
]
181177
}
182178
}
@@ -196,7 +192,7 @@
196192
"name": "dcr-FortinetFortiNdrCloud",
197193
"location": "[parameters('location')]",
198194
"dependsOn": [
199-
"[tenantResourceId(subscription().subscriptionId, parameters('workspaceResourceGroup'), 'Microsoft.OperationalInsights/workspaces/tables', parameters('workspace'), 'FortinetFortiNdrCloudRaw_CL')]",
195+
"[resourceId('Microsoft.Resources/deployments', 'deploy-fortinet-table')]",
200196
"[resourceId('Microsoft.Insights/dataCollectionEndpoints', 'dce-fortinet-fortindr-cloud')]"
201197
],
202198
"properties": {
@@ -330,7 +326,6 @@
330326
}
331327
},
332328
"siteConfig": {
333-
"packageUri": "https://aka.ms/sentinel-fortinetFortiNdrCloudDataConnector-functionapp",
334329
"appSettings": [
335330
{
336331
"name": "AzureWebJobsStorage__accountName",
@@ -368,18 +363,6 @@
368363
"name": "APPLICATIONINSIGHTS_CONNECTION_STRING",
369364
"value": "[reference(resourceId('microsoft.insights/components', variables('applicationInsightsName')), '2020-02-02').ConnectionString]"
370365
},
371-
{
372-
"name": "TENANT_ID",
373-
"value": "[parameters('AzureTenantID')]"
374-
},
375-
{
376-
"name": "CLIENT_ID",
377-
"value": "[parameters('AzureClientID')]"
378-
},
379-
{
380-
"name": "CLIENT_SECRET",
381-
"value": "[parameters('AzureClientSecret')]"
382-
},
383366
{
384367
"name": "DceUri",
385368
"value": "[reference(resourceId('Microsoft.Insights/dataCollectionEndpoints', 'dce-fortinet-fortindr-cloud'), '2022-06-01').logsIngestion.endpoint]"
@@ -513,6 +496,63 @@
513496
"roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
514497
"principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2016-08-01', 'Full').identity.principalId]"
515498
}
499+
},
500+
{
501+
"type": "Microsoft.Authorization/roleAssignments",
502+
"apiVersion": "2022-04-01",
503+
"name": "[guid(resourceId('Microsoft.Insights/dataCollectionRules', 'dcr-FortinetFortiNdrCloud'), 'monitoring-metrics-publisher')]",
504+
"scope": "[resourceId('Microsoft.Insights/dataCollectionRules', 'dcr-FortinetFortiNdrCloud')]",
505+
"dependsOn": [
506+
"[resourceId('Microsoft.Insights/dataCollectionRules', 'dcr-FortinetFortiNdrCloud')]",
507+
"[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
508+
],
509+
"properties": {
510+
"roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]",
511+
"principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2023-12-01', 'Full').identity.principalId]"
512+
}
513+
},
514+
{
515+
"type": "Microsoft.Resources/deploymentScripts",
516+
"apiVersion": "2020-10-01",
517+
"name": "WaitSection",
518+
"location": "[parameters('location')]",
519+
"dependsOn": [
520+
"[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
521+
],
522+
"kind": "AzurePowerShell",
523+
"properties": {
524+
"azPowerShellVersion": "7.0",
525+
"scriptContent": "start-sleep -Seconds 30",
526+
"cleanupPreference": "Always",
527+
"retentionInterval": "PT1H"
528+
}
529+
},
530+
{
531+
"type": "Microsoft.OperationalInsights/workspaces/savedSearches",
532+
"apiVersion": "2020-08-01",
533+
"name": "[format('{0}/Fortinet_FortiNDR_Cloud', parameters('workspace'))]",
534+
"location": "[parameters('location')]",
535+
"dependsOn": [
536+
"[resourceId('Microsoft.Resources/deployments', 'deploy-fortinet-table')]"
537+
],
538+
"properties": {
539+
"category": "Microsoft Sentinel Parser",
540+
"displayName": "Parser for Fortinet_FortiNDR_Cloud",
541+
"functionAlias": "Fortinet_FortiNDR_Cloud",
542+
"query": "let FortiNDR_Cloud_suricata_view = view () { FortinetFortiNdrCloudRaw_CL | where LogTypeSuffix == 'suricata' or column_ifexists('event_type', '') == 'suricata' | extend parsed = parse_json(column_ifexists('RawData', '{}')) | extend su_timestamp=coalesce(todatetime(tostring(parsed.timestamp)), TimeGenerated), su_uuid=tostring(parsed.uuid), su_event_type=coalesce(tostring(parsed.event_type), 'suricata'), su_customer_id=tostring(parsed.customer_id), su_sensor_id=tostring(parsed.sensor_id), su_source=tostring(parsed.source), su_src_ip=tostring(parsed.src_ip), su_src_port=todouble(parsed.src_port), su_dst_ip=tostring(parsed.dest_ip), su_dst_port=todouble(parsed.dest_port), su_proto=tostring(parsed.proto), su_sig_id=todouble(parsed.alert.signature_id), su_sig_rev=todouble(parsed.alert.rev), su_sig_name=tostring(parsed.alert.signature), su_sig_category=tostring(parsed.alert.category), su_sig_severity=todouble(parsed.alert.severity), Type='FncEventsSuricata_CL' | project TimeGenerated, su_timestamp, su_event_type, su_src_ip, su_src_port, su_dst_ip, su_dst_port, su_sig_name, su_sig_id, su_sig_rev, su_sig_category, su_sig_severity, su_source, su_proto, su_sensor_id, su_uuid, su_customer_id, Type }; let FortiNDR_Cloud_observation_view = view () { FortinetFortiNdrCloudRaw_CL | where LogTypeSuffix == 'observation' or column_ifexists('event_type', '') == 'observation' | extend parsed = parse_json(column_ifexists('RawData', '{}')) | extend ob_timestamp=coalesce(todatetime(tostring(parsed.timestamp)), TimeGenerated), ob_uuid=tostring(parsed.uuid), ob_event_type=coalesce(tostring(parsed.event_type), 'observation'), ob_customer_id=tostring(parsed.customer_id), ob_sensor_id=tostring(parsed.sensor_id), ob_source=tostring(parsed.source), ob_observation_title=tostring(parsed['title']), ob_confidence=tostring(parsed.confidence), ob_src_ip=tostring(parsed.src_ip), ob_dst_ip=tostring(parsed.dst_ip), Type='FncEventsObservation_CL' | project TimeGenerated, ob_timestamp, ob_observation_title, ob_confidence, ob_event_type, ob_src_ip, ob_dst_ip, ob_source, ob_sensor_id, ob_uuid, ob_customer_id, Type }; let FortiNDR_Cloud_detections_view = view () { FortinetFortiNdrCloudRaw_CL | where LogTypeSuffix == 'detections' or LogTypeSuffix == 'detection' or column_ifexists('event_type', '') == 'detection' | extend parsed = parse_json(column_ifexists('RawData', '{}')) | extend de_account_id=tostring(parsed.account_uuid), de_device_ip=tostring(parsed.device_ip), de_rule_name=tostring(parsed.rule_name), de_severity=tostring(parsed.rule_severity), de_confidence=tostring(parsed.rule_confidence), de_sensor_id=tostring(parsed.sensor_id), de_created=coalesce(todatetime(tostring(parsed.created)), TimeGenerated), de_uuid=tostring(parsed.uuid), de_status=tostring(parsed.status), de_category=tostring(parsed.rule_category), Type='FncEventsDetections_CL' | project TimeGenerated, de_device_ip, de_status, de_rule_name, de_severity, de_confidence, de_category, de_created, de_sensor_id, de_account_id, de_uuid, Type }; union isfuzzy=true FortiNDR_Cloud_suricata_view, FortiNDR_Cloud_observation_view, FortiNDR_Cloud_detections_view"
543+
}
544+
},
545+
{
546+
"type": "Microsoft.Web/sites/extensions",
547+
"apiVersion": "2022-09-01",
548+
"name": "[format('{0}/{1}', parameters('functionAppName'), 'onedeploy')]",
549+
"dependsOn": [
550+
"[resourceId('Microsoft.Resources/deploymentScripts', 'WaitSection')]"
551+
],
552+
"properties": {
553+
"packageUri": "https://aka.ms/sentinel-fortinetFortiNdrCloudDataConnector-functionapp",
554+
"remoteBuild": true
555+
}
516556
}
517557
]
518558
}
Binary file not shown.

Solutions/Fortinet FortiNDR Cloud/Data Connectors/fortinetFortiNdrCloudDataConn/OrchestratorWatchdog/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,11 @@
5757

5858

5959
def validate_credentials():
60-
tenant_id = (os.environ.get("TENANT_ID") or "").strip()
61-
client_id = (os.environ.get("CLIENT_ID") or "").strip()
62-
client_secret = (os.environ.get("CLIENT_SECRET") or "").strip()
6360
endpoint = (os.environ.get("DceUri") or "").strip()
6461

65-
if not tenant_id or not client_id or not client_secret or not endpoint:
62+
if not endpoint:
6663
raise InputError(
67-
"Sentinel Infrastructure Information is missing. Provide (Tenant ID, Client ID, Client Secret and Data Collection Endpoint).")
64+
"Sentinel Infrastructure Information is missing. Provide (Log Ingestion endpoint URI).")
6865

6966

7067
def validate_configuration():

Solutions/Fortinet FortiNDR Cloud/Data Connectors/fortinetFortiNdrCloudDataConn/sentinel/sentinel.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,15 @@
44
from datetime import datetime, timezone
55

66
from azure.core.exceptions import ClientAuthenticationError, HttpResponseError
7-
from azure.identity import ClientSecretCredential
7+
from azure.identity import ManagedIdentityCredential
88
from azure.monitor.ingestion import LogsIngestionClient
99

1010
try:
11-
AZURE_TENANT_ID = (os.environ.get("TENANT_ID") or "").strip()
12-
AZURE_CLIENT_ID = (os.environ.get("CLIENT_ID") or "").strip()
13-
AZURE_CLIENT_SECRET = (os.environ.get("CLIENT_SECRET") or "").strip()
1411
AZURE_ENDPOINT = (os.environ.get("DceUri") or "").strip()
1512

1613
logging.info("Initializing Azure Global Clients at startup.")
1714

18-
# (Assuming AZURE_CLIENT_ID, etc. are defined above or pulled from os.environ)
19-
GLOBAL_CREDS = ClientSecretCredential(
20-
client_id=AZURE_CLIENT_ID,
21-
client_secret=AZURE_CLIENT_SECRET,
22-
tenant_id=AZURE_TENANT_ID,
23-
)
15+
GLOBAL_CREDS = ManagedIdentityCredential()
2416

2517
GLOBAL_CLIENT = LogsIngestionClient(
2618
endpoint=AZURE_ENDPOINT,
@@ -78,9 +70,7 @@ def post_data(events: list[dict], log_type_suffix: str):
7870

7971
except ClientAuthenticationError as exc:
8072
logging.error(
81-
f"{log_type_suffix} : Authentication failed - verify CLIENT_ID, "
82-
f"CLIENT_SECRET, TENANT_ID, and that the App Registration has "
83-
f"'Monitoring Metrics Publisher' role assigned on the DCR. Error: {exc}"
73+
f"{log_type_suffix} : Authentication failed - verify the 'Monitoring Metrics Publisher' role assigned on the DCR. Error: {exc}"
8474
)
8575
raise
8676
except HttpResponseError as e:
Binary file not shown.
-408 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)