Description: Canary supports outgoing Webhooks from your Console to an endpoint of your choice. This event-driven approach means you get sent alerts as they happen!
In this guide, we’ll send data to you Microsoft Sentinel instance, using an Azure logic app deployed with ARM templates.
Note: This guide assumes you have Sentinel setup as well as a Log Analytics Workspace. If not please follow the guide here.
Please also ensure all the below ARM templates are run in the same region and subscription as your workspace.
- Jump to setup webhook alert logging click here.
- If you already receiving logs and would like a Sentinel Analytics Rule to create incidents from alerts, click here.
- For an example incident playbook to automatically ignore source IP's click here.
Configuring Canary Webhooks to Sentinel.
Step 1: Deploy a log analytics API connection.
First we'll need to prepare the authentication connection the logic app and your workspace.
Head over to "Deploy a custom template" in your Azure portal and select Build your own template in the editor.
Paste the following schema into the editor and click "save".
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "thinkstcanary_azureloganalyticsdatacollector",
"location": "[resourceGroup().location]",
"kind": "V1",
"properties": {
"api": {
"name": "thinkstcanary_azureloganalyticsdatacollector",
"displayName": "Azure Log Analytics Data Collector",
"description": "Azure Log Analytics Data Collector will send data to any Azure Log Analytics workspace.",
"iconUri": "https://connectoricons-prod.azureedge.net/releases/v1.0.1683/1.0.1683.3680/azureloganalyticsdatacollector/icon.png",
"brandColor": "#0072C6",
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureloganalyticsdatacollector')]",
"type": "Microsoft.Web/locations/managedApis"
}
}
}
]
}
Finally enter your preferred resource group name, and select "Review + create" when done.
After the validation has completed, and the resource is created, select the "Go to resource" button.
Step 2: Configure the log analytics API connection.
You'll now have landed on your newly created API connection. From here we want to authenticate the connection with your Log Analytics Workspace ID and Workspace Key. Once entered, hit Save to finish the setup.
If you're unsure of where to find your ID and key, head over to your Log Analytics Workspaces blade, then find them under the Agents UI as shown below.
Step 3: Deploy the webhook Logic App.
With your API connection all setup, let's head back over to "Deploy a custom template" in your Azure portal and select Build your own template in the editor once more.
Paste the following schema into the editor and click "save".
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"canaryConsoleDomain": {
"type": "String",
"defaultValue": "ABC123.canary.tools",
"metadata": {
"description": "The domain of your Canary console."
}
},
"canaryConsoleAnalystKey": {
"type": "SecureString",
"defaultValue": "ABC123",
"metadata": {
"description": "The analyst API key for your Canary Console."
}
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Logic/workflows",
"apiVersion": "2017-07-01",
"name": "Thinkst-Canary-Webhooks",
"location": "[resourceGroup().location]",
"properties": {
"state": "Enabled",
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"Canary_Console_Domain": {
"defaultValue": "[parameters('canaryConsoleDomain')]",
"type": "String"
},
"Canary_Console_Analyst_Key": {
"defaultValue": "[parameters('canaryConsoleAnalystKey')]",
"type": "String"
},
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"Canary_Console_Webhook": {
"type": "Request",
"kind": "Http",
"inputs": {
"method": "POST"
}
}
},
"actions": {
"Parse_Webhook_Data": {
"runAfter": {},
"type": "ParseJson",
"inputs": {
"content": "@triggerBody()",
"schema": {
"type": "object",
"properties": {
"AdditionalDetails": {
"type": "array"
},
"AlertType": {
"type": "string"
},
"CanaryID": {
"type": "string"
},
"CanaryIP": {
"type": "string"
},
"CanaryLocation": {
"type": "string"
},
"CanaryName": {
"type": "string"
},
"CanaryNote": {
"type": "string"
},
"CanaryPort": {
"type": "integer"
},
"CanaryPublicIP": {
"type": "integer"
},
"Description": {
"type": "string"
},
"Flock": {
"type": "string"
},
"IncidentHash": {
"type": "string"
},
"IncidentKey": {
"type": "string"
},
"Intro": {
"type": "string"
},
"MatchedAnnotations": {
"type": "string"
},
"NumberOfDevices": {
"type": "integer"
},
"Reminder": {
"type": "string"
},
"ReverseDNS": {
"type": "string"
},
"SourceIP": {
"type": "string"
},
"Timestamp": {
"type": "string"
},
"TimestampGlobalTZ": {
"type": "string"
},
"Token": {
"type": "string"
},
"Triggered": {
"type": "string"
}
}
}
}
},
"Check_if_the_alert_is_a_test": {
"actions": {},
"runAfter": {
"Parse_Webhook_Data": [
"Succeeded"
]
},
"else": {
"actions": {
"Acknowledge_Incident_on_Console": {
"runAfter": {
"Send_Alert_to_Sentinel": [
"Succeeded"
]
},
"type": "Http",
"inputs": {
"uri": "@concat('https://', parameters('Canary_Console_Domain'), '/api/v1/incident/acknowledge')",
"method": "POST",
"headers": {
"X-Canary-Auth-Token": "@{parameters('Canary_Console_Analyst_Key')}"
},
"queries": {
"incident": "@{body('Parse_Webhook_Data')?['IncidentKey']}"
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
},
"Send_acknowledge_failure_to_Sentinel": {
"runAfter": {
"Acknowledge_Incident_on_Console": [
"TimedOut",
"Skipped",
"Failed"
]
},
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['azureloganalyticsdatacollector']['connectionId']"
}
},
"method": "post",
"body": "[[\n {\n \"Description\": \"Canary Logic App Failure.\",\n \"Intro\": \"Your Canary Webhook Logic App has experienced a failure, please check recent runs for any failures or contact support@canary.tools.\",\n \"AdditionalDetails\": \"Acknowledge API Operation Error Code: @{outputs('Acknowledge_Incident_on_Console')?['statusCode']}\",\n \"IncidentKey\": \"@{body('Parse_Webhook_Data')?['IncidentKey']}\"\n }\n ]",
"headers": {
"Log-Type": "Thinkst_Canary",
"time-generated-field": "@body('Parse_Webhook_Data')?['Timestamp']"
},
"path": "/api/logs"
}
},
"Send_Alert_to_Sentinel": {
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['azureloganalyticsdatacollector']['connectionId']"
}
},
"method": "post",
"body": "@triggerBody()",
"headers": {
"Log-Type": "Thinkst_Canary_Webhooks"
},
"path": "/api/logs"
}
}
}
},
"expression": {
"and": [
{
"equals": [
"@body('Parse_Webhook_Data')?['Flock']",
"flock:dummyincidentflock"
]
}
]
},
"type": "If"
}
},
"outputs": {}
},
"parameters": {
"$connections": {
"value": {
"azureloganalyticsdatacollector": {
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureloganalyticsdatacollector')]",
"connectionId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/connections/thinkstcanary_azureloganalyticsdatacollector')]",
"connectionName": "thinkstcanary_azureloganalyticsdatacollector"
}
}
}
}
}
}
]
}
Select the same resource group as before, and enter your Console domain and Analyst API Key —We'll use this to automatically acknowledge alerts later on. To find your domain and API Key, follow our guide here.
Select "Review + create" when done.
Note: If you'd prefer not to acknowledge incidents, you can leave these as their defaults.
After the validation has completed, and the resource is created, select the "Go to resource" button.
Step 4: (Optional) Disable Alert Acknowledgement.
If you prefer to not automatically acknowledge alerts in your Console, simply head over to the logic app designer screen and delete the 2 steps shown below.
Step 5: Configure your Console.
Head over to your new Logic App's overview screen and grab a copy of the webhook URL.
Head over to your Console's webhooks settings at the URL and add the webhook:
https://EXAMPLE.canary.tools/nest/settings/webhooks
You're done! ;)
Analytics Rule to create incidents from alerts.
With Canary alert data now flowing into your Sentinel instance, you may want to generate incidents from new alerts, this is entirely optional and below we'll create an analytics rule to automatically make that happen.
Step 1: Import the Analytics Rule
Head over to your Azure Sentinel blade here. From here select your Sentinel instance, and import a predefined Analytics Rule.
Note: You'll need at least 1 Canary (Not CanaryToken) alert sent to sentinel before this for the rule to successfully import. Simply generate one by interacting with one of your Birds services.
The below schema can be saved as a file locally and uploaded using the Import option.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/06360572-94a7-42a4-add7-58fb933b2353')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/06360572-94a7-42a4-add7-58fb933b2353')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "NRT",
"apiVersion": "2023-12-01-preview",
"properties": {
"displayName": "Canary Alerts To Incidents",
"description": "Creates Sentinel Incidents from Thinkst Canary Alerts",
"severity": "High",
"enabled": true,
"query": "Thinkst_Canary_Webhooks_CL\n| where Description_s <> \"Canary Disconnected\"\n| where Description_s <> \"Canary Reconnected\"\n| where Description_s <> \"Canary Settings Changed\"\n| where Description_s <> \"Fake Location\"\n| where Description_s <> \"Network Settings Roll-back\"",
"suppressionDuration": "PT5H",
"suppressionEnabled": false,
"tactics": [
"LateralMovement",
"Exfiltration"
],
"techniques": [],
"subTechniques": [],
"alertRuleTemplateName": null,
"incidentConfiguration": {
"createIncident": true,
"groupingConfiguration": {
"enabled": false,
"reopenClosedIncident": false,
"lookbackDuration": "PT5H",
"matchingMethod": "AllEntities",
"groupByEntities": [],
"groupByAlertDetails": [],
"groupByCustomDetails": []
}
},
"eventGroupingSettings": {
"aggregationKind": "AlertPerResult"
},
"alertDetailsOverride": {
"alertDisplayNameFormat": "{{{Description_s}}",
"alertDescriptionFormat": "{{Intro_s}}",
"alertDynamicProperties": []
},
"customDetails": {
"Events": "AdditionalDetails_s"
},
"entityMappings": [
{
"entityType": "IP",
"fieldMappings": [
{
"identifier": "Address",
"columnName": "SourceIP"
}
]
},
{
"entityType": "Host",
"fieldMappings": [
{
"identifier": "HostName",
"columnName": "ReverseDNS_s"
}
]
}
],
"sentinelEntitiesMappings": null,
"templateVersion": null
}
}
]
}
You'll now find the rule has been added.
Incidents will now be automatically created on new Canary alerts.
You're done! ;)
Sentinel Incident Playbooks.
You can also automate certain actions from incidents using other logic apps.
Below we'll run through an example playbook which will ignore the source IP of an alert in your Canary Console.
Step 1: Deploy a Sentinel API connection.
First we'll need to prepare an authenticated connection between the logic app and your Sentinel instance.
Head over to "Deploy a custom template" in your Azure portal and select Build your own template in the editor.
Paste the following schema into the editor and click "save".
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "thinkstcanary_azuresentinel",
"location": "[resourceGroup().location]",
"kind": "V1",
"properties": {
"api": {
"name": "thinkst_canary_azuresentinel",
"displayName": "Microsoft Sentinel",
"description": "Cloud-native SIEM with a built-in AI so you can focus on what matters most",
"iconUri": "https://connectoricons-prod.azureedge.net/releases/v1.0.1715/1.0.1715.3906/azuresentinel/icon.png",
"brandColor": "#0072C6",
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuresentinel')]",
"type": "Microsoft.Web/locations/managedApis"
}
}
}
]
}
Finally enter your preferred resource group name, and select "Review + create" when done.
After the validation has completed, and the resource is created, select the "Go to resource" button.
Step 2: Configure the Sentinel API connection.
You'll now have landed on your Sentinel API connection. Head over to Edit API connection and authorize the connection using your currently signed in account. Remember to select Save when you're done.
Step 3: Deploy the Sentinel Playbook.
With your API connection all setup, let's head back over to "Deploy a custom template" in your Azure portal and select Build your own template in the editor once more.
Paste the following schema into the editor and click "save".
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"canaryConsoleDomain": {
"type": "String",
"defaultValue": "ABC123.canary.tools",
"metadata": {
"description": "The domain of your Canary console."
}
},
"canaryConsoleGlobalKey": {
"type": "SecureString",
"defaultValue": "ABC123",
"metadata": {
"description": "The Global API key for your Canary Console."
}
}
},
"resources": [
{
"type": "Microsoft.Logic/workflows",
"apiVersion": "2017-07-01",
"name": "Thinkst-Canary-IgnoreIP",
"location": "[resourceGroup().location]",
"properties": {
"state": "Enabled",
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"Canary_Console_Global_Key": {
"defaultValue": "[parameters('canaryConsoleGlobalKey')]",
"type": "String"
},
"Canary_Console_Domain": {
"defaultValue": "[parameters('canaryConsoleDomain')]",
"type": "String"
},
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"Microsoft_Sentinel_incident": {
"type": "ApiConnectionWebhook",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['azuresentinel']['connectionId']"
}
},
"body": {
"callback_url": "@listCallbackUrl()"
},
"path": "/incident-creation"
}
}
},
"actions": {
"Entities_-_Get_IPs": {
"runAfter": {},
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['azuresentinel']['connectionId']"
}
},
"method": "post",
"body": "@triggerBody()?['object']?['properties']?['relatedEntities']",
"path": "/entities/ip"
}
},
"For_each": {
"foreach": "@body('Entities_-_Get_IPs')?['IPs']",
"actions": {
"Enable_Ignore_List": {
"type": "Http",
"inputs": {
"uri": "@concat('https://', parameters('Canary_Console_Domain'), '/api/v1/settings/whitelisting/enable')",
"method": "POST",
"headers": {
"X-Canary-Auth-Token": "@{parameters('Canary_Console_Global_Key')}"
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
},
"Ignore_IP": {
"runAfter": {
"Enable_Ignore_List": [
"Succeeded"
]
},
"type": "Http",
"inputs": {
"uri": "@concat('https://', parameters('Canary_Console_Domain'), '/api/v1/settings/whitelist_ip_port')",
"method": "POST",
"headers": {
"X-Canary-Auth-Token": "@{parameters('Canary_Console_Global_Key')}"
},
"queries": {
"src_ip": "@{item()?['Address']}"
}
},
"runtimeConfiguration": {
"contentTransfer": {
"transferMode": "Chunked"
}
}
}
},
"runAfter": {
"Entities_-_Get_IPs": [
"Succeeded"
]
},
"type": "Foreach"
}
},
"outputs": {}
},
"parameters": {
"$connections": {
"value": {
"azuresentinel": {
"id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azuresentinel')]",
"connectionId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/connections/thinkstcanary_azuresentinel')]",
"connectionName": "thinkstcanary_azuresentinel"
}
}
}
}
}
}
]
}
Select your prefered resource group, and enter your Console domain, however this time we'll need a Global API Key in order to make changes to our Console. Follow our guide here, if you're not familiar with our key types.
Select "Review + create" when done.
Step 4: Try out your Sentinel Playbook.
With the playbook deployed, head over to your Azure Sentinel blade here, and navigate to your incidents. A playbook will now be available to run against incidents as shown below.
This particular playbook will add the alerts source IP to your Console's global ignore list here.
https://EXAMPLE.canary.tools/nest/settings/ignorelist