Skip to content

Commit

Permalink
Merge pull request #9201 from aka0/zoom_validation
Browse files Browse the repository at this point in the history
Update Zoom data connector to pass Zoom webhook endpoint validation
  • Loading branch information
v-atulyadav authored Nov 7, 2023
2 parents 423db83 + 77302c1 commit 314083b
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 131 deletions.
232 changes: 129 additions & 103 deletions DataConnectors/Zoom/ZoomLogs/run.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function Write-OMSLogfile {
Given a value pair hash table, this function will write the data to an OMS Log Analytics workspace.
Certain variables, such as Customer ID and Shared Key are specific to the OMS workspace data is being written to.
This function will not write to multiple OMS workspaces. Build-signature and post-analytics function from Microsoft documentation
at https://docs.microsoft.com/en-us/azure/log-analytics/log-analytics-data-collector-api
at https://docs.microsoft.com/azure/log-analytics/log-analytics-data-collector-api
.PARAMETER DateTime
date and time for the log. DateTime value
.PARAMETER Type
Expand Down Expand Up @@ -40,119 +40,145 @@ function Write-OMSLogfile {
$returnCode = Write-OMSLogfile $dateTime $type $data -Verbose
write-output $returnCode
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[datetime]$dateTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$type,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$logdata,
[Parameter(Mandatory = $true, Position = 3)]
[string]$CustomerID,
[Parameter(Mandatory = $true, Position = 4)]
[string]$SharedKey
)
Write-Verbose -Message "DateTime: $dateTime"
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
Write-Verbose -Message "Type: $type"
write-Verbose -Message "LogData: $logdata"

#region Workspace ID and Key
# Workspace ID for the workspace
#$CustomerID = 'ENTER WORKSPACE ID HERE'

# Shared key needs to be set for environment
# Below uses an encrypted variable from Azure Automation
# Uncomment the next two lines if using Azure Automation Variable and comment the last
# $automationVarName = 'Enter Variable Name Here'
# $sharedKey = Get-AutomationVariable -name $automationVarName
# Key Vault is another secure option for storing the value
# Less secure option is to put the key in the code
#$SharedKey = 'ENTER WORKSPACE KEY HERE'

#endregion

# Supporting Functions
# Function to create the auth signature
function Build-signature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
$xheaders = 'x-ms-date:' + $Date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($SharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.key = $keyBytes
$calculateHash = $sha256.ComputeHash($bytesToHash)
$encodeHash = [convert]::ToBase64String($calculateHash)
$authorization = 'SharedKey {0}:{1}' -f $CustomerID,$encodeHash
return $authorization
}
# Function to create and post the request
Function Post-LogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
$method = "POST"
$ContentType = 'application/json'
$resource = '/api/logs'
$rfc1123date = ($dateTime).ToString('r')
$ContentLength = $Body.Length
$signature = Build-signature `
-customerId $CustomerID `
-sharedKey $SharedKey `
-date $rfc1123date `
-contentLength $ContentLength `
-method $method `
-contentType $ContentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @{
"Authorization" = $signature;
"Log-Type" = $type;
"x-ms-date" = $rfc1123date
"time-generated-field" = $dateTime
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $ContentType -Headers $headers -Body $body -UseBasicParsing
Write-Verbose -message ('Post Function Return Code ' + $response.statuscode)
return $response.statuscode
}
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[datetime]$dateTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$type,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$logdata,
[Parameter(Mandatory = $true, Position = 3)]
[string]$CustomerID,
[Parameter(Mandatory = $true, Position = 4)]
[string]$SharedKey
)
Write-Verbose -Message "DateTime: $dateTime"
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
Write-Verbose -Message "Type: $type"
write-Verbose -Message "LogData: $logdata"

# Check if time is UTC, Convert to UTC if not.
# $dateTime = (Get-Date)
if ($dateTime.kind.tostring() -ne 'Utc'){
$dateTime = $dateTime.ToUniversalTime()
Write-Verbose -Message $dateTime
}
#region Workspace ID and Key
# Workspace ID for the workspace
#$CustomerID = 'ENTER WORKSPACE ID HERE'

# Add DateTime to hashtable
#$logdata.add("DateTime", $dateTime)
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime
# Shared key needs to be set for environment
# Below uses an encrypted variable from Azure Automation
# Uncomment the next two lines if using Azure Automation Variable and comment the last
# $automationVarName = 'Enter Variable Name Here'
# $sharedKey = Get-AutomationVariable -name $automationVarName
# Key Vault is another secure option for storing the value
# Less secure option is to put the key in the code
#$SharedKey = 'ENTER WORKSPACE KEY HERE'

#Build the JSON file
$logMessage = ConvertTo-Json $logdata -Depth 20
Write-Verbose -Message $logMessage
#endregion

# Supporting Functions
# Function to create the auth signature
function Build-signature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
$xheaders = 'x-ms-date:' + $Date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($SharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.key = $keyBytes
$calculateHash = $sha256.ComputeHash($bytesToHash)
$encodeHash = [convert]::ToBase64String($calculateHash)
$authorization = 'SharedKey {0}:{1}' -f $CustomerID, $encodeHash
return $authorization
}
# Function to create and post the request
Function Post-LogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
$method = "POST"
$ContentType = 'application/json'
$resource = '/api/logs'
$rfc1123date = ($dateTime).ToString('r')
$ContentLength = $Body.Length
$signature = Build-signature `
-customerId $CustomerID `
-sharedKey $SharedKey `
-date $rfc1123date `
-contentLength $ContentLength `
-method $method `
-contentType $ContentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @{
"Authorization" = $signature;
"Log-Type" = $type;
"x-ms-date" = $rfc1123date
"time-generated-field" = $dateTime
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $ContentType -Headers $headers -Body $body -UseBasicParsing
Write-Verbose -message ('Post Function Return Code ' + $response.statuscode)
return $response.statuscode
}

#Submit the data
$returnCode = Post-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($logMessage)) -Type $type
Write-Verbose -Message "Post Statement Return Code $returnCode"
return $returnCode
# Check if time is UTC, Convert to UTC if not.
# $dateTime = (Get-Date)
if ($dateTime.kind.tostring() -ne 'Utc') {
$dateTime = $dateTime.ToUniversalTime()
Write-Verbose -Message $dateTime
}

# Add DateTime to hashtable
#$logdata.add("DateTime", $dateTime)
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime

#Build the JSON file
$logMessage = ConvertTo-Json $logdata -Depth 20
Write-Verbose -Message $logMessage

#Submit the data
$returnCode = Post-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($logMessage)) -Type $type
Write-Verbose -Message "Post Statement Return Code $returnCode"
return $returnCode
}

Write-Host "PowerShell HTTP trigger function processed a request."
$currentUTCtime = (Get-Date).ToUniversalTime()
$headers = $Request.Headers

# If Verification key is set to None skip verification
If ($env:ZoomVerification -eq "None") {Write-Host "Not Verifying"}
If ($env:ZoomVerification -eq "None") { Write-Host "Not Verifying" }

# If authorization key is not valid
If ($headers["authorization"] -eq $env:ZoomVerification -Or $env:ZoomVerification -eq "None") {
# Get request body
$event = $Request.Body

# Write logs to OMS Workspace
$writeResult = Write-OMSLogfile $currentUTCtime $env:customLogName $event $env:workspaceId $env:workspaceKey

# Return Write-OMSLogfile response to output binding
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $writeResult
Body = ""
})
}{Write-Host "Invalid Source"}
# Get request body
$event = $Request.Body

if ($null -ne $event) {
# Handles Zoom webhook validation. See: https://developers.zoom.us/docs/api/rest/webhook-reference/#validate-your-webhook-endpoint
$ZoomSecretToken = $env:ZoomSecretToken
if ( $ZoomSecretToken -ne "None" -And $event.event -eq "endpoint.url_validation" ) {
Write-Host "Processing Zoom validation request"
$key = [Text.Encoding]::UTF8.GetBytes($ZoomSecretToken)
$hasher = New-Object System.Security.Cryptography.HMACSHA256
$hasher.key = $key
$hash = $hasher.ComputeHash([Text.Encoding]::UTF8.GetBytes($event.payload.plainToken))
$hexString = [System.BitConverter]::ToString($hash).Replace("-", "")

$validationResponse = @{
"plainToken" = $event.payload.plainToken
"encryptedToken" = $hexString.ToLower()
} | ConvertTo-Json

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = 200
Body = $validationResponse
})

}
else {

# Write logs to OMS Workspace
$writeResult = Write-OMSLogfile $currentUTCtime $env:customLogName $event $env:workspaceId $env:workspaceKey

# Return Write-OMSLogfile response to output binding
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $writeResult
Body = ""
})
}
}
} { Write-Host "Invalid Source" }
35 changes: 17 additions & 18 deletions DataConnectors/Zoom/azuredeploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"customLogName": {
"type": "string",
"defaultValue": "Zoom"
},
},
"workspaceID": {
"type": "string",
"defaultValue": "<workspaceID>"
Expand All @@ -21,10 +21,13 @@
"ZoomVerification": {
"type": "string",
"defaultValue": "None"
},
"ZoomSecretToken": {
"type": "string",
"defaultValue": "None"
}
},
"variables": {
},
"variables": {},
"resources": [
{
"type": "Microsoft.Insights/components",
Expand Down Expand Up @@ -52,8 +55,7 @@
"enabledForDiskEncryption": false,
"enabledForTemplateDeployment": true,
"enableSoftDelete": true,
"accessPolicies": [
]
"accessPolicies": []
}
},
{
Expand Down Expand Up @@ -84,10 +86,8 @@
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [
],
"ipRules": [
],
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
Expand Down Expand Up @@ -136,8 +136,7 @@
},
"properties": {
"cors": {
"corsRules": [
]
"corsRules": []
},
"deleteRetentionPolicy": {
"enabled": false
Expand All @@ -157,8 +156,7 @@
},
"properties": {
"cors": {
"corsRules": [
]
"corsRules": []
}
}
},
Expand Down Expand Up @@ -204,10 +202,11 @@
"WEBSITE_CONTENTSHARE": "[toLower(parameters('FunctionName'))]",
"customLogName": "[parameters('customLogName')]",
"workspaceID": "[parameters('workspaceID')]",
"workspaceKey": "[concat('@Microsoft.KeyVault(SecretUri=', reference(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('FunctionName'), 'workspaceKey')).SecretUriWithVersion, ')')]",
"WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-zoom-functionapp",
"ZoomVerification": "[parameters('ZoomVerification')]"
}
"workspaceKey": "[concat('@Microsoft.KeyVault(SecretUri=', reference(resourceId('Microsoft.KeyVault/vaults/secrets', parameters('FunctionName'), 'workspaceKey')).SecretUriWithVersion, ')')]",
"WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-zoom-functionappV2",
"ZoomVerification": "[parameters('ZoomVerification')]",
"ZoomSecretToken": "[parameters('ZoomSecretToken')]"
}
}
]
},
Expand Down Expand Up @@ -261,4 +260,4 @@
}
}
]
}
}
20 changes: 10 additions & 10 deletions DataConnectors/Zoom/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The easiest way is via the provided ARM templates:

Alternatively you can deploy the elements manually.
#### 2: Deploy via VS Code
Note: You will need to prepare VS code for Azure function development. See https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-powershell#prerequisites
Note: You will need to prepare VS code for Azure function development. See https://docs.microsoft.com/azure/azure-functions/functions-create-first-function-powershell#prerequisites
1. Download the [Zip](https://github.com/Azure/Azure-Sentinel/blob/master/DataConnectors/Zoom/zoom_logs_template.zip?raw=true) file of the Azure Funciton app from Github.
2. Extract to a location on your local host.
3. Open VS Code.
Expand Down Expand Up @@ -80,15 +80,15 @@ Note: You will need to prepare VS code for Azure function development. See http

## Configure your Zoom API app.
You also need to configure your Zoom account to sent events to your Function App. To do this go to https://marketplace.zoom.us/ and log in with a user who has admin access to your Zoom account.
1. Select ‘Develop’ in the top right hand corner and click ‘Build App’.
2. Select ‘Webhook Only’ as your app type.
1. Select ‘Develop’ in the top right hand corner and click ‘Build App’.
2. Select ‘Webhook Only’ as your app type.
3. Give your app a name.
4. Fill out the required Basic Information and click continue.
5. Under the Feature Tab enable the ‘Event Subscriptions’ toggle and click ‘Add new event subscription’.
6. Set a subscription name and in the Event notification endpoint URL enter your Function App URL. This will be in the format of https://<FunctionAppName>.azurewebsites.net/api/<FunctionName>. You can find this you app URL in the Azure Portal.
7. Click ‘Add Events’ and select the events you want to receive in Azure Sentinel. Then click done.
8. Copy your feature Verification token for your event subscription and save it.
9. Click ‘Save’ and ‘Continue’.
4. Fill out the required Basic Information and click continue.
5. Under the Feature Tab enable the ‘Event Subscriptions’ toggle and click ‘Add new event subscription’.
6. Set a subscription name and in the Event notification endpoint URL enter your Function App URL. This will be in the format of https://<FunctionAppName>.azurewebsites.net/api/<FunctionName>. You can find this you app URL in the Azure Portal.
7. Click ‘Add Events’ and select the events you want to receive in Azure Sentinel. Then click done.
8. Copy your feature Verification and Secret token for your event subscription and save them.
9. Click ‘Save’ and ‘Continue’.

Once you have done this you need to add your verification code to your previously deployed Function App. To do this go to the Function App in the Azure Portal.
1. Click Platform Features Tab.
Expand All @@ -98,4 +98,4 @@ Once you have done this you need to add your verification code to your previousl
5. Restart your function app.

If sucessfully deployed you should start to see events appear in your Azure Sentinel workpsace as soon as they are generated.
If you run into issues there are a number of options for [monitoring](https://docs.microsoft.com/en-us/azure/azure-functions/functions-monitoring?tabs=cmd) and [deugging](https://docs.microsoft.com/en-us/azure/azure-functions/functions-debug-powershell-local) your Function App.
If you run into issues there are a number of options for [monitoring](https://docs.microsoft.com/azure/azure-functions/functions-monitoring?tabs=cmd) and [deugging](https://docs.microsoft.com/azure/azure-functions/functions-debug-powershell-local) your Function App.
Binary file added DataConnectors/Zoom/zoom_logs_templateV2.zip
Binary file not shown.

0 comments on commit 314083b

Please sign in to comment.