init Varonis SaaS solution
This commit is contained in:
Родитель
401d041a81
Коммит
57a9753ba4
Двоичный файл не отображается.
|
@ -0,0 +1,30 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33627.172
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Varonis.Sentinel.Functions", "Varonis.Sentinel.Functions\Varonis.Sentinel.Functions.csproj", "{3DB744ED-E321-456E-8B54-C1090223B5D7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1AD1ECC3-1E56-4CEF-9EE8-B0C1CE3E0DEE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
NuGet.config = NuGet.config
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3DB744ED-E321-456E-8B54-C1090223B5D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3DB744ED-E321-456E-8B54-C1090223B5D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3DB744ED-E321-456E-8B54-C1090223B5D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3DB744ED-E321-456E-8B54-C1090223B5D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5E78338E-0AA0-4B43-AC5E-96C4C9EE41E6}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<clear />
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
<config>
|
||||
<clear />
|
||||
<add key="defaultPushSource" value="https://artifactory.varonis.com/artifactory/api/nuget/nuget-local-8x" />
|
||||
<add key="globalPackagesFolder" value="globalpackages" />
|
||||
</config>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget-local-8x" value="https://artifactory.varonis.com/artifactory/api/nuget/nuget-local-8x" />
|
||||
<add key="nuget-local-external" value="https://artifactory.varonis.com/artifactory/api/nuget/nuget-local-external" />
|
||||
<add key="nuget-webda-infra" value="https://artifactory.varonis.com/artifactory/api/nuget/nuget-webda-infra" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -0,0 +1,175 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Varonis.Sentinel.Functions.Helpers;
|
||||
using Varonis.Sentinel.Functions.Search;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.DatAlert
|
||||
{
|
||||
internal class DatAlertClient : IDatAlertClient
|
||||
{
|
||||
private readonly Uri _baseUri;
|
||||
private readonly string _apikey;
|
||||
private readonly ILogger _log;
|
||||
|
||||
public DatAlertClient(Uri baseUri, string apikey, ILogger log)
|
||||
{
|
||||
_baseUri = baseUri;
|
||||
_apikey = apikey;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<AlertItem>> GetDataAsync(DatAlertParams parameters)
|
||||
{
|
||||
var tokenJson = await GetAccessTokenAsync(_baseUri, _apikey).ConfigureAwait(false);
|
||||
var tokenInfo = CustomParser.ParseTokenInfo(tokenJson);
|
||||
|
||||
if (tokenInfo is null)
|
||||
{
|
||||
throw new Exception("Token object is not valid.");
|
||||
}
|
||||
|
||||
var token = tokenInfo.Value.token;
|
||||
|
||||
_log.LogInformation($"Access token was received: {token.Substring(0, 5)}...");
|
||||
|
||||
using var client = new HttpClient();
|
||||
client.BaseAddress = _baseUri;
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
|
||||
|
||||
var severities = CustomParser.ParseArrayFromCSV(parameters.Severities);
|
||||
var ruleIds = await GetRuleIdsAsync(client, parameters.ThreatModel)
|
||||
.ConfigureAwait(false);
|
||||
var statuses = CustomParser.ParseArrayFromCSV(parameters.Status);
|
||||
|
||||
var searchQuery = new AlertSearchQueryBuilder()
|
||||
.WithDateRange(parameters.Start, parameters.End, AlertAttributes.IngestTime)
|
||||
.WithSeverity(severities)
|
||||
.WithRules(ruleIds)
|
||||
.WithStatuses(statuses)
|
||||
.WithAggregations()
|
||||
.Build();
|
||||
|
||||
var payload = new SearchRequestBuilder(searchQuery, AlertAttributes.Columns)
|
||||
.WithOrdering(AlertAttributes.IngestTime, true)
|
||||
.Build();
|
||||
|
||||
var rowLink = await GetSearchResultPath(client, payload)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var result = await GetAlertItemsAsync(client, $"app/dataquery/api/search/{rowLink}")
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<string> GetAccessTokenAsync(Uri baseUri, string apikey)
|
||||
{
|
||||
using var client = new HttpClient { BaseAddress = baseUri };
|
||||
var payload = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
{ "grant_type", "varonis_custom" },
|
||||
//{ "x-api-key", apikey }
|
||||
});
|
||||
var content = await payload.ReadAsByteArrayAsync().ConfigureAwait(false);
|
||||
client.DefaultRequestHeaders.Add("x-api-key", apikey);
|
||||
client.DefaultRequestHeaders.Host = baseUri.Host;
|
||||
using var response = await client.PostAsync("api/authentication/api_keys/token", payload)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new WebException($"Receive {response.StatusCode} while trying to get an access token.");
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<Rule>> GetRulesAsync(HttpClient client)
|
||||
{
|
||||
const int threatModelEnumID = 5821;
|
||||
using var response = await client.GetAsync($"/api/entitymodel/enum/{threatModelEnumID}")
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new WebException($"Receive {response.StatusCode} while trying to get a list of rules.");
|
||||
}
|
||||
|
||||
var rules = await response.Content
|
||||
.ReadFromJsonAsync<IEnumerable<Rule>>()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
private static async Task<int[]> GetRuleIdsAsync(HttpClient client, string threatModels)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(threatModels))
|
||||
return Array.Empty<int>();
|
||||
|
||||
var allRules = await GetRulesAsync(client).ConfigureAwait(false);
|
||||
var threatModelNames = CustomParser.ParseArrayFromCSV(threatModels);
|
||||
|
||||
var rules = from r in allRules
|
||||
join tmn in threatModelNames on r.RuleName equals tmn
|
||||
select r.RuleID;
|
||||
|
||||
return rules.ToArray();
|
||||
}
|
||||
|
||||
private static async Task<string> GetSearchResultPath(HttpClient client, SearchRequest payload)
|
||||
{
|
||||
using var searchRespone = await client.PostAsJsonAsync("app/dataquery/api/search/v2/search", payload)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!searchRespone.IsSuccessStatusCode)
|
||||
{
|
||||
throw new WebException($"Receive {searchRespone.StatusCode} while trying to start searching.");
|
||||
}
|
||||
|
||||
var searchLinks = await searchRespone.Content.ReadFromJsonAsync<SearchResponseLink[]>()
|
||||
.ConfigureAwait(false);
|
||||
var rowLink = searchLinks.First(x => x.DataType == SearchResultType.Rows).Location;
|
||||
|
||||
return rowLink;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<AlertItem>> GetAlertItemsAsync(HttpClient client, string path)
|
||||
{
|
||||
var tryings = 30;
|
||||
while (--tryings > 0)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
|
||||
using var dataResponse = await client.GetAsync(path)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (dataResponse.StatusCode == HttpStatusCode.NotModified
|
||||
|| dataResponse.StatusCode == HttpStatusCode.PartialContent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var rows = await dataResponse.Content.ReadFromJsonAsync<SearchRowsResponse>()
|
||||
.ConfigureAwait(false);
|
||||
var rawData = SearchConverter.ConvertResponseToDictionary(rows);
|
||||
var mapper = new SearchAlertObjectMapper(_log);
|
||||
|
||||
var result = mapper.MapRowsToObject(rawData);
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new WebException($"Search operation was not completed after {tryings} sec. {path}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Net.Http.Json;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.DatAlert
|
||||
{
|
||||
internal class DatAlertClientFake : IDatAlertClient
|
||||
{
|
||||
private readonly Uri _baseUri;
|
||||
private readonly string _apikey;
|
||||
private readonly ILogger _log;
|
||||
|
||||
private string _dateFormat;
|
||||
|
||||
public DatAlertClientFake(Uri baseUri, string apikey, ILogger log, string dateFormat = "yyyy-MM-ddTHH:mm:ss")
|
||||
{
|
||||
_baseUri = baseUri;
|
||||
_apikey = apikey;
|
||||
_log = log;
|
||||
_dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<AlertItem>> GetDataAsync(DatAlertParams parameters)
|
||||
{
|
||||
var token = await GetAccessTokenAsync(_baseUri, _apikey).ConfigureAwait(false);
|
||||
|
||||
_log.LogInformation($"Access token was received: {token.Substring(0, 10)}");
|
||||
|
||||
using var client = new HttpClient();
|
||||
client.BaseAddress = _baseUri;
|
||||
var payload = new
|
||||
{
|
||||
StartDate = parameters.Start.ToUniversalTime().ToString(_dateFormat),
|
||||
EndDate = parameters.End.ToUniversalTime().ToString(_dateFormat)
|
||||
};
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
var response = await client.PostAsJsonAsync("/Alert/alerts-post", payload)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var data = await response.Content.ReadFromJsonAsync<AlertItem[]>()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static async Task<string> GetAccessTokenAsync(Uri baseUri, string apikey)
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
client.BaseAddress = baseUri;
|
||||
var payload = new Dictionary<string, string>
|
||||
{
|
||||
{ "grant_type", "varonis-custom" },
|
||||
{ "x-api-key", apikey }
|
||||
};
|
||||
var response = await client.PostAsync("/Auth", new FormUrlEncodedContent(payload))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return await response.Content.ReadAsStringAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.DatAlert
|
||||
{
|
||||
internal record DatAlertParams
|
||||
(
|
||||
DateTime Start,
|
||||
DateTime End,
|
||||
string Severities,
|
||||
string ThreatModel,
|
||||
string Status
|
||||
);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.DatAlert
|
||||
{
|
||||
internal interface IDatAlertClient
|
||||
{
|
||||
Task<IReadOnlyCollection<AlertItem>> GetDataAsync(DatAlertParams parameters);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Tasks;
|
||||
using Varonis.Sentinel.Functions.LogAnalytics;
|
||||
using Varonis.Sentinel.Functions.DatAlert;
|
||||
using Varonis.Sentinel.Functions.Helpers;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Varonis.Sentinel.Functions
|
||||
{
|
||||
public class FetchDataFunction
|
||||
{
|
||||
[FunctionName("VaronisSaaS")]
|
||||
public async Task Run([TimerTrigger("0 * * * * *")]TimerInfo timer, ILogger log)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hostname = Environment.GetEnvironmentVariable("DatAlertHostName");
|
||||
var datalertApiKey = Environment.GetEnvironmentVariable("DatAlertApiKey");
|
||||
var logAnalyticsKey = Environment.GetEnvironmentVariable("LogAnalyticsKey");
|
||||
var logAnalyticsWorkspace = Environment.GetEnvironmentVariable("LogAnalyticsWorkspace");
|
||||
|
||||
var firstFetchTimeStr = Environment.GetEnvironmentVariable("FirstFetchTime");
|
||||
var severities = Environment.GetEnvironmentVariable("Severities");
|
||||
var threatModelName = Environment.GetEnvironmentVariable("ThreatModelNameList");
|
||||
var status = Environment.GetEnvironmentVariable("Statuses");
|
||||
|
||||
var baseUri = new Uri($"https://{hostname}");
|
||||
|
||||
var client = new DatAlertClient(baseUri, datalertApiKey, log);
|
||||
var storage = new LogAnalyticsCollector(logAnalyticsKey, logAnalyticsWorkspace, log);
|
||||
|
||||
if (timer.IsPastDue)
|
||||
{
|
||||
log.LogInformation("Timer is running late!");
|
||||
}
|
||||
|
||||
var minDate = DateTime.MinValue.ToUniversalTime();
|
||||
|
||||
var last = timer.ScheduleStatus.Last.ToUniversalTime();
|
||||
var lastUpdated = timer.ScheduleStatus.LastUpdated.ToUniversalTime();
|
||||
var next = timer.ScheduleStatus.Next.ToUniversalTime().AddSeconds(-1);
|
||||
|
||||
log.LogInformation($"Schedule status: {last}, {lastUpdated}, {next}");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(firstFetchTimeStr))
|
||||
{
|
||||
var firstDate = CustomParser.ParseDate(firstFetchTimeStr);
|
||||
|
||||
if (last <= firstDate)
|
||||
{
|
||||
lastUpdated = firstDate;
|
||||
next = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
log.LogInformation($"DatAlert host name: {hostname}; LogAnalytics Key: {logAnalyticsKey.Substring(0, 5)}...;" +
|
||||
$" LogAnalytics Workspace: {logAnalyticsWorkspace}; Time: {DateTime.Now}");
|
||||
|
||||
var interval = timer.ScheduleStatus.Next - timer.ScheduleStatus.Last;
|
||||
|
||||
var parameters = new DatAlertParams(lastUpdated, next, severities, threatModelName, status);
|
||||
var alerts = await client.GetDataAsync(parameters);
|
||||
|
||||
if (!alerts.Any())
|
||||
{
|
||||
log.LogInformation("Request was successful, but data is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
var data = JsonSerializer.Serialize(alerts);
|
||||
|
||||
log.LogInformation($"Data was received successfully: {data.Substring(0, data.Length > 15 ? 15 : data.Length)}...");
|
||||
await storage.PublishAsync(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.LogError($"{ex.Message} {ex.InnerException?.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
public static class AlertExtensions
|
||||
{
|
||||
public static Dictionary<string, int> StatusesMap { get; } =
|
||||
new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
["New"] = 1,
|
||||
["Under Investigation"] = 2,
|
||||
["Closed"] = 3,
|
||||
["Action Required"] = 4,
|
||||
["Auto-Resolved"] = 5
|
||||
};
|
||||
|
||||
public static Dictionary<string, int> SeverityMap { get; } =
|
||||
new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
["High"] = 0,
|
||||
["Medium"] = 1,
|
||||
["Low"] = 2
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
internal abstract class BaseMapper<T>
|
||||
{
|
||||
public IReadOnlyCollection<T> MapRowsToObject(IEnumerable<IDictionary<string, string>> rowsData)
|
||||
{
|
||||
return rowsData.Select(Map).Where(ev => ev != null).ToArray();
|
||||
}
|
||||
|
||||
protected abstract T Map(IDictionary<string, string> row);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
internal static class CustomParser
|
||||
{
|
||||
public static DateTime ParseDate(string dateRange)
|
||||
{
|
||||
if (DateTime.TryParse(dateRange, out var date))
|
||||
{
|
||||
return date;
|
||||
}
|
||||
|
||||
var rangeArr = dateRange.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (rangeArr.Length != 2 || !int.TryParse(rangeArr[0], out var number))
|
||||
{
|
||||
goto FormatException;
|
||||
}
|
||||
|
||||
var sufix = rangeArr[1];
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
if (sufix.StartsWith("sec")) return now.AddSeconds(-number);
|
||||
if (sufix.StartsWith("min")) return now.AddMinutes(-number);
|
||||
if (sufix.StartsWith("hour")) return now.AddHours(-number);
|
||||
if (sufix.StartsWith("day")) return now.AddDays(-number);
|
||||
if (sufix.StartsWith("week")) return now.AddDays(-number*7);
|
||||
if (sufix.StartsWith("month")) return now.AddMonths(-number);
|
||||
if (sufix.StartsWith("year")) return now.AddYears(-number);
|
||||
|
||||
FormatException:
|
||||
throw new FormatException($"{dateRange} is not valid date.");
|
||||
}
|
||||
|
||||
public static string[] ParseArrayFromCSV(string propValue)
|
||||
{
|
||||
return propValue
|
||||
?.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
?.Select(x => x.Trim())
|
||||
?.ToArray() ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
public static (string token, string token_type, int expiresIn)? ParseTokenInfo(string json)
|
||||
{
|
||||
var jelement = JsonSerializer.Deserialize<JsonElement>(json);
|
||||
|
||||
return jelement.TryGetProperty("access_token", out var token)
|
||||
&& jelement.TryGetProperty("token_type", out var token_type)
|
||||
&& jelement.TryGetProperty("expires_in", out var expiresIn)
|
||||
? (token.GetString(), token_type.GetString(), expiresIn.GetInt32())
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
public static class ParametersToValuesConverter
|
||||
{
|
||||
public static List<dynamic> CreateMappedFilterValuesList(IEnumerable<string> values, IDictionary<string, int> valuesMapping)
|
||||
{
|
||||
var filterValues = values.Select(value =>
|
||||
new
|
||||
{
|
||||
Value = valuesMapping[value].ToString(),
|
||||
DisplayValue = value
|
||||
} as dynamic).ToList();
|
||||
|
||||
return filterValues;
|
||||
}
|
||||
|
||||
public static List<dynamic> CreateValuesListFromList<T>(IEnumerable<T> values)
|
||||
{
|
||||
var filterValues = values.Select(value =>
|
||||
new
|
||||
{
|
||||
Value = value.ToString()
|
||||
} as dynamic).ToList();
|
||||
|
||||
return filterValues;
|
||||
}
|
||||
|
||||
public static List<dynamic> CreateValuesListFromParameter<T>(T value)
|
||||
{
|
||||
var filterValues = new List<dynamic>
|
||||
{
|
||||
new
|
||||
{
|
||||
Value = value.ToString(),
|
||||
}
|
||||
};
|
||||
|
||||
return filterValues;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
internal class SearchAlertObjectMapper : BaseMapper<AlertItem>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SearchAlertObjectMapper(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override AlertItem Map(IDictionary<string, string> row)
|
||||
{
|
||||
var alertItem = new AlertItem();
|
||||
try
|
||||
{
|
||||
alertItem.ID = Guid.Parse(row[AlertAttributes.Id]);
|
||||
alertItem.Name = row[AlertAttributes.RuleName];
|
||||
alertItem.Time = DateTime.Parse(row[AlertAttributes.Time]);
|
||||
alertItem.Severity = row[AlertAttributes.RuleSeverityName];
|
||||
alertItem.SeverityId = int.Parse(row[AlertAttributes.RuleSeverityId]);
|
||||
alertItem.Category = row[AlertAttributes.RuleCategoryName];
|
||||
alertItem.Country = MultiValueToStringArray(row[AlertAttributes.LocationCountryName]);
|
||||
alertItem.State = MultiValueToStringArray(row[AlertAttributes.LocationSubdivisionName]);
|
||||
alertItem.Status = row[AlertAttributes.StatusName];
|
||||
alertItem.StatusId = int.Parse(row[AlertAttributes.StatusId]);
|
||||
alertItem.CloseReason = row[AlertAttributes.CloseReasonName];
|
||||
alertItem.BlacklistLocation = GetBoolValue(row, AlertAttributes.LocationBlacklistedLocation);
|
||||
alertItem.AbnormalLocation = MultiValueToStringArray(row[AlertAttributes.LocationAbnormalLocation]);
|
||||
alertItem.NumOfAlertedEvents = int.Parse(row[AlertAttributes.EventsCount]);
|
||||
alertItem.UserName = MultiValueToStringArray(row[AlertAttributes.UserName]);
|
||||
alertItem.SamAccountName = MultiValueToStringArray(row[AlertAttributes.UserSamAccountName]);
|
||||
alertItem.PrivilegedAccountType = MultiValueToStringArray(row[AlertAttributes.UserAccountTypeName]);
|
||||
alertItem.ContainMaliciousExternalIP = GetBoolValue(row, AlertAttributes.DeviceIsMaliciousExternalIp);
|
||||
alertItem.IPThreatTypes = MultiValueToStringArray(row[AlertAttributes.DeviceExternalIpThreatTypesName]);
|
||||
alertItem.Asset = MultiValueToStringArray(row[AlertAttributes.AssetPath]);
|
||||
alertItem.AssetContainsFlaggedData = MultiValueToBooleanArray(row[AlertAttributes.DataIsFlagged]);
|
||||
alertItem.AssetContainsSensitiveData = MultiValueToBooleanArray(row[AlertAttributes.DataIsSensitive]);
|
||||
alertItem.Platform = MultiValueToStringArray(row[AlertAttributes.FilerPlatformName]);
|
||||
alertItem.FileServerOrDomain = MultiValueToStringArray(row[AlertAttributes.FilerName]);
|
||||
alertItem.DeviceName = MultiValueToStringArray(row[AlertAttributes.DeviceHostname]);
|
||||
alertItem.IngestTime = DateTime.Parse(row[AlertAttributes.IngestTime]);
|
||||
alertItem.EventUTC = GetDateValue(row, AlertAttributes.InitialEventUtcTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError("Failed to map search Alert row, skipping alert.", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return alertItem;
|
||||
}
|
||||
|
||||
private static string[] MultiValueToStringArray(string multiValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(multiValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var valuesArray = multiValue.Split(',')
|
||||
.Select(v => v.Trim())
|
||||
.ToArray();
|
||||
|
||||
return valuesArray;
|
||||
}
|
||||
|
||||
private bool?[] MultiValueToBooleanArray(string multiValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(multiValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var valuesArray = multiValue.Split(',')
|
||||
.Select(ConvertToBoolean)
|
||||
.ToArray();
|
||||
|
||||
return valuesArray;
|
||||
}
|
||||
|
||||
private bool? GetBoolValue(IDictionary<string, string> row, string name)
|
||||
{
|
||||
return ConvertToBoolean(row[name]);
|
||||
}
|
||||
|
||||
private bool? ConvertToBoolean(string boolStr)
|
||||
{
|
||||
var value = boolStr?.ToLower().Trim();
|
||||
|
||||
if (value == "yes" || value == "1") return true;
|
||||
if (value == "no" || value == "0") return false;
|
||||
|
||||
return bool.TryParse(value, out var boolValue) ? (bool?)boolValue : null;
|
||||
}
|
||||
|
||||
private DateTime? GetDateValue(IDictionary<string, string> row, string name)
|
||||
{
|
||||
return DateTime.TryParse(row[name], out var dateTimeValue) ? (DateTime?)dateTimeValue : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Helpers
|
||||
{
|
||||
internal class SearchConverter
|
||||
{
|
||||
public static IEnumerable<IDictionary<string, string>> ConvertResponseToDictionary(SearchRowsResponse searchResponse)
|
||||
{
|
||||
var response = new List<Dictionary<string, string>>();
|
||||
|
||||
if (searchResponse.Rows.Any())
|
||||
{
|
||||
foreach (var row in searchResponse.Rows)
|
||||
{
|
||||
var rowsList = row.ToList();
|
||||
var rowDictionary = new Dictionary<string, string>();
|
||||
for (int columnId = 0; columnId < searchResponse.Columns.Length; ++columnId)
|
||||
{
|
||||
string column = searchResponse.Columns[columnId];
|
||||
string value = rowsList[columnId];
|
||||
|
||||
rowDictionary[column] = value;
|
||||
}
|
||||
response.Add(rowDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.LogAnalytics
|
||||
{
|
||||
internal interface ILogAnalyticsStorage
|
||||
{
|
||||
Task PublishAsync(string data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using HTTPDataCollectorAPI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.LogAnalytics
|
||||
{
|
||||
internal class LogAnalyticsCollector : ILogAnalyticsStorage
|
||||
{
|
||||
const string datalertTableName = "VaronisAlerts";
|
||||
private readonly string _logAnalyticsKey;
|
||||
private readonly string _logAnalyticsWorkspace;
|
||||
private readonly ILogger _log;
|
||||
|
||||
public LogAnalyticsCollector(string logAnalyticsKey, string logAnalyticsWorkspace, ILogger log)
|
||||
{
|
||||
_logAnalyticsKey = logAnalyticsKey;
|
||||
_logAnalyticsWorkspace = logAnalyticsWorkspace;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public async Task PublishAsync(string data)
|
||||
{
|
||||
var collector = new Collector(_logAnalyticsWorkspace, _logAnalyticsKey);
|
||||
await collector.Collect(datalertTableName, data).ConfigureAwait(false);
|
||||
_log.LogInformation("Data was sent to log analytics.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.LogAnalytics
|
||||
{
|
||||
internal class LogAnalyticsFake : ILogAnalyticsStorage
|
||||
{
|
||||
private const string FileName = "LogAnalyticsFakes.log";
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public LogAnalyticsFake(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
var fi = new FileInfo(FileName);
|
||||
_logger.LogInformation(fi.FullName);
|
||||
}
|
||||
|
||||
public async Task PublishAsync(string data)
|
||||
{
|
||||
using var writer = new StreamWriter(FileName, true);
|
||||
|
||||
await writer.WriteAsync(data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.LogAnalytics
|
||||
{
|
||||
internal class LogAnalyticsMonitor : ILogAnalyticsStorage
|
||||
{
|
||||
public Task PublishAsync(string data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Varonis.Sentinel.Functions.Helpers;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search
|
||||
{
|
||||
internal class AlertSearchQueryBuilder
|
||||
{
|
||||
private readonly List<Filter> _searchQueryFilters = new();
|
||||
|
||||
public SearchQuery Build()
|
||||
{
|
||||
return CreateAlertQuery();
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithSeverity(IReadOnlyCollection<string> severities)
|
||||
{
|
||||
if (severities != null && severities.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.RuleSeverityId,
|
||||
Values = ParametersToValuesConverter.CreateMappedFilterValuesList(severities, AlertExtensions.SeverityMap)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithDateRange(DateTime? from, DateTime? to, string pathName)
|
||||
{
|
||||
if (from is not null && to is not null)
|
||||
{
|
||||
const string datetimeFormat = "yyyy-MM-ddTHH:mm:ss";
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.Between,
|
||||
Path = pathName,
|
||||
Values = new object[]
|
||||
{
|
||||
new
|
||||
{
|
||||
StartDate = from.Value.ToUniversalTime().ToString(datetimeFormat),
|
||||
EndDate = to.Value.ToUniversalTime().ToString(datetimeFormat)
|
||||
}
|
||||
}
|
||||
}); ;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithRules(IReadOnlyCollection<int> rules)
|
||||
{
|
||||
if (rules != null && rules.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.RuleId,
|
||||
Values = ParametersToValuesConverter.CreateValuesListFromList(rules)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithStatuses(IReadOnlyCollection<string> statuses)
|
||||
{
|
||||
if (statuses != null && statuses.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.StatusId,
|
||||
Values = ParametersToValuesConverter.CreateMappedFilterValuesList(statuses, AlertExtensions.StatusesMap)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithAlertIds(IReadOnlyCollection<Guid> ids)
|
||||
{
|
||||
if (ids != null && ids.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.Id,
|
||||
Values = ParametersToValuesConverter.CreateValuesListFromList(ids)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithLastDays(int? lastDays)
|
||||
{
|
||||
if (lastDays is not null)
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.LastDays,
|
||||
Path = AlertAttributes.Time,
|
||||
Values = ParametersToValuesConverter.CreateValuesListFromParameter(lastDays)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithSidIds(IList<int> sidIds)
|
||||
{
|
||||
if (sidIds != null && sidIds.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.SidId,
|
||||
Values = ParametersToValuesConverter.CreateValuesListFromList(sidIds)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithDeviceName(IList<string> deviceNames)
|
||||
{
|
||||
if (deviceNames != null && deviceNames.Any())
|
||||
{
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.In,
|
||||
Path = AlertAttributes.DeviceHostname,
|
||||
Values = ParametersToValuesConverter.CreateValuesListFromList(deviceNames)
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertSearchQueryBuilder WithAggregations()
|
||||
{
|
||||
const string value = "1";
|
||||
|
||||
_searchQueryFilters.Add(new Filter
|
||||
{
|
||||
Operator = EmOperator.Equals,
|
||||
Path = AlertAttributes.Aggregate,
|
||||
Values = new List<dynamic>
|
||||
{
|
||||
new
|
||||
{
|
||||
DisplayValue = AlertAttributes.Aggregate,
|
||||
Value = value
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private SearchQuery CreateAlertQuery()
|
||||
{
|
||||
return new SearchQuery
|
||||
{
|
||||
EntityName = "Alert",
|
||||
Filter = new FilterGroup
|
||||
{
|
||||
FilterOperator = FilterOperator.And,
|
||||
Filters = _searchQueryFilters
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
public static class AlertAttributes
|
||||
{
|
||||
public const string Id = "Alert.ID";
|
||||
public const string RuleName = "Alert.Rule.Name";
|
||||
public const string RuleId = "Alert.Rule.ID";
|
||||
public const string Time = "Alert.TimeUTC";
|
||||
public const string RuleSeverityName = "Alert.Rule.Severity.Name";
|
||||
public const string RuleSeverityId = "Alert.Rule.Severity.ID";
|
||||
public const string RuleCategoryName = "Alert.Rule.Category.Name";
|
||||
public const string LocationCountryName = "Alert.Location.CountryName";
|
||||
public const string LocationSubdivisionName = "Alert.Location.SubdivisionName";
|
||||
public const string StatusName = "Alert.Status.Name";
|
||||
public const string StatusId = "Alert.Status.ID";
|
||||
public const string EventsCount = "Alert.EventsCount";
|
||||
public const string InitialEventUtcTime = "Alert.Initial.Event.TimeUTC";
|
||||
public const string UserName = "Alert.User.Name";
|
||||
public const string UserSamAccountName = "Alert.User.SamAccountName";
|
||||
public const string UserAccountTypeName = "Alert.User.AccountType.Name";
|
||||
public const string DeviceHostname = "Alert.Device.HostName";
|
||||
public const string DeviceIsMaliciousExternalIp = "Alert.Device.IsMaliciousExternalIP";
|
||||
public const string DeviceExternalIpThreatTypesName = "Alert.Device.ExternalIPThreatTypesName";
|
||||
public const string DataIsFlagged = "Alert.Data.IsFlagged";
|
||||
public const string DataIsSensitive = "Alert.Data.IsSensitive";
|
||||
public const string FilerPlatformName = "Alert.Filer.Platform.Name";
|
||||
public const string AssetPath = "Alert.Asset.Path";
|
||||
public const string FilerName = "Alert.Filer.Name";
|
||||
public const string CloseReasonName = "Alert.CloseReason.Name";
|
||||
public const string LocationBlacklistedLocation = "Alert.Location.BlacklistedLocation";
|
||||
public const string LocationAbnormalLocation = "Alert.Location.AbnormalLocation";
|
||||
public const string SidId = "Alert.User.SidID";
|
||||
public const string Aggregate = "Alert.AggregationFilter";
|
||||
public const string IngestTime = "Alert.IngestTime";
|
||||
|
||||
public static string[] Columns { get; } =
|
||||
new string[]
|
||||
{
|
||||
Id,
|
||||
RuleName,
|
||||
RuleId,
|
||||
Time,
|
||||
RuleSeverityName,
|
||||
RuleSeverityId,
|
||||
RuleCategoryName,
|
||||
LocationCountryName,
|
||||
LocationSubdivisionName,
|
||||
StatusName,
|
||||
StatusId,
|
||||
EventsCount,
|
||||
InitialEventUtcTime,
|
||||
UserName,
|
||||
UserSamAccountName,
|
||||
UserAccountTypeName,
|
||||
DeviceHostname,
|
||||
DeviceIsMaliciousExternalIp,
|
||||
DeviceExternalIpThreatTypesName,
|
||||
DataIsFlagged,
|
||||
DataIsSensitive,
|
||||
FilerPlatformName,
|
||||
AssetPath,
|
||||
FilerName,
|
||||
CloseReasonName,
|
||||
LocationBlacklistedLocation,
|
||||
LocationAbnormalLocation,
|
||||
SidId,
|
||||
IngestTime
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class AlertItem
|
||||
{
|
||||
public Guid ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
public string Severity { get; set; }
|
||||
public int SeverityId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string[] Country { get; set; }
|
||||
public string[] State { get; set; }
|
||||
public string Status { get; set; }
|
||||
public int StatusId { get; set; }
|
||||
public string CloseReason { get; set; }
|
||||
public bool? BlacklistLocation { get; set; }
|
||||
public string[] AbnormalLocation { get; set; }
|
||||
public int NumOfAlertedEvents { get; set; }
|
||||
public string[] UserName { get; set; }
|
||||
public string[] SamAccountName { get; set; }
|
||||
public string[] PrivilegedAccountType { get; set; }
|
||||
public bool? ContainMaliciousExternalIP { get; set; }
|
||||
public string[] IPThreatTypes { get; set; }
|
||||
public string[] Asset { get; set; }
|
||||
public bool?[] AssetContainsFlaggedData { get; set; }
|
||||
public bool?[] AssetContainsSensitiveData { get; set; }
|
||||
public string[] Platform { get; set; }
|
||||
public string[] FileServerOrDomain { get; set; }
|
||||
public DateTime? EventUTC { get; set; }
|
||||
public string[] DeviceName { get; set; }
|
||||
public DateTime IngestTime { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal enum EmOperator
|
||||
{
|
||||
In = 1,
|
||||
NotIn = 2,
|
||||
Between = 3,
|
||||
Equals = 4,
|
||||
NotEquals = 5,
|
||||
Contains = 6,
|
||||
NotContains = 7,
|
||||
LastDays = 10,
|
||||
IncludesAny = 11,
|
||||
IncludesAll = 12,
|
||||
ExcludesAll = 13,
|
||||
GreaterThan = 14,
|
||||
LessThan = 0xF,
|
||||
QueryId = 0x10,
|
||||
NotInQueryId = 17,
|
||||
IsEmpty = 20,
|
||||
InNestedSearch = 21,
|
||||
NotInNestedSearch = 22,
|
||||
HasValue = 23
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Newtonsoft.Json.Converters;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class Filter
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public EmOperator Operator { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public IReadOnlyCollection<object> Values { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Newtonsoft.Json.Converters;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class FilterGroup
|
||||
{
|
||||
public List<Filter> Filters { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public FilterOperator FilterOperator { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal enum FilterOperator
|
||||
{
|
||||
And,
|
||||
Or
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class RowDataRequest
|
||||
{
|
||||
public IReadOnlyCollection<string> Columns { get; set; }
|
||||
public IReadOnlyCollection<object> Ordering { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class Rule
|
||||
{
|
||||
public int RuleID { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class SearchQuery
|
||||
{
|
||||
public string EntityName { get; set; }
|
||||
|
||||
public FilterGroup Filter { get; set; }
|
||||
|
||||
public string TimeZone { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class SearchRequest
|
||||
{
|
||||
public SearchQuery Query { get; set; }
|
||||
|
||||
public RowDataRequest Rows { get; set; }
|
||||
|
||||
public List<object> Facets { get; set; }
|
||||
|
||||
public object RequestParams { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using Newtonsoft.Json.Converters;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class SearchResponseLink
|
||||
{
|
||||
public string Location { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchResultType DataType { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal enum SearchResultType
|
||||
{
|
||||
Rows,
|
||||
Facets,
|
||||
Terminate,
|
||||
ExportRows,
|
||||
ExportFacets,
|
||||
RowsV3,
|
||||
TerminateV3,
|
||||
ExportV3,
|
||||
SearchId,
|
||||
UnKnown
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Varonis.Sentinel.Functions.Search.Model
|
||||
{
|
||||
internal class SearchRowsResponse
|
||||
{
|
||||
public string[][] Rows { get; set; }
|
||||
public string[] Columns { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System.Collections.Generic;
|
||||
using Varonis.Sentinel.Functions.Search.Model;
|
||||
|
||||
namespace Varonis.Sentinel.Functions.Search
|
||||
{
|
||||
internal class SearchRequestBuilder
|
||||
{
|
||||
private readonly SearchRequest _searchRequest;
|
||||
public SearchRequestBuilder(SearchQuery query, IReadOnlyCollection<string> attributePaths)
|
||||
{
|
||||
_searchRequest = new SearchRequest
|
||||
{
|
||||
Query = query,
|
||||
Rows = new RowDataRequest
|
||||
{
|
||||
Columns = attributePaths
|
||||
},
|
||||
RequestParams = new
|
||||
{
|
||||
IgnoreCache = true,
|
||||
SearchSource = 1,
|
||||
SearchSourceName = "Alert"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public SearchRequestBuilder WithOrdering(string column, bool? desc = false)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(column))
|
||||
{
|
||||
_searchRequest.Rows.Ordering =
|
||||
new object[]
|
||||
{
|
||||
new
|
||||
{
|
||||
Path = column,
|
||||
SortOrder = desc ?? false ? "Desc" : "Asc"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public SearchRequest Build()
|
||||
{
|
||||
return _searchRequest;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HTTPDataCollectorAPI" Version="1.0.5" />
|
||||
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="host.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="local.settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Update="fake.local.settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true,
|
||||
"excludedTypes": "Request"
|
||||
},
|
||||
"enableLiveMetricsFilters": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
|
||||
"DatAlertHostName": "",
|
||||
"DatAlertApiKey": "",
|
||||
"LogAnalyticsKey": "",
|
||||
"LogAnalyticsWorkspace": "",
|
||||
"FirstFetchTime": "2 weeks",
|
||||
"Severities": "Low, Medium, High",
|
||||
"ThreatModelNameList": "",
|
||||
"Statuses": "New, Under Investigation"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appService_VaronisSentinelFunctions20230922173301_name": {
|
||||
"value": "VaronisSentinelFunctions20230922173301"
|
||||
},
|
||||
"storageaccount_varonissentinelfunctions": {
|
||||
"value": "varonissentinelfunctions"
|
||||
},
|
||||
"appInsights_VaronisSentinelFunctions": {
|
||||
"value": "VaronisSentinelFunctions"
|
||||
},
|
||||
"varonissentinelfunctionsStorageAccount_location": {
|
||||
"value": "Australia Central"
|
||||
},
|
||||
"varonissentinelfunctionsStorageAccount": {
|
||||
"value": "varonissentinelfunctions"
|
||||
},
|
||||
"varonissentinelfunctionsStorageAccountType": {
|
||||
"value": "Standard_LRS"
|
||||
},
|
||||
"appInsightsComponent_VaronisSentinelFunctions_name": {
|
||||
"value": "VaronisSentinelFunctions"
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_locationId": {
|
||||
"value": "northeurope"
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_type": {
|
||||
"value": "web"
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_hockeyAppToken": {
|
||||
"value": ""
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_requestSource": {
|
||||
"value": "WebTools16.0"
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_logAnalyticsName": {
|
||||
"value": "vrns-datalert-logs-ws"
|
||||
},
|
||||
"appInsightsComponentName_VaronisSentinelFunctions_logAnalyticsResourceGroup": {
|
||||
"value": "vrns-datalert-rg"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
{
|
||||
"id": "VaronisSaaS",
|
||||
"title": "Varonis SaaS",
|
||||
"publisher": "Varonis",
|
||||
"descriptionMarkdown": "Varonis SaaS provides the capability to ingest [Varonis Alerts](https://varonisdatalertservicemockwebapi20230907161659.azurewebsites.net/Alert/alerts) into Microsoft Sentinel.",
|
||||
"graphQueriesTableName": "VaronisAlerts_CL",
|
||||
"graphQueries": [
|
||||
{
|
||||
"baseQuery": "{{graphQueriesTableName}}",
|
||||
"legend": "Varonis alerts",
|
||||
"metricName": "Total data recieved"
|
||||
},
|
||||
{
|
||||
"baseQuery": "{{graphQueriesTableName}}\n| where severity_s == \"High\"",
|
||||
"legend": "High severity events",
|
||||
"metricName": "High severity events"
|
||||
},
|
||||
{
|
||||
"baseQuery": "{{graphQueriesTableName}}\n| where severity_s == \"Medium\"",
|
||||
"legend": "Medium severity events",
|
||||
"metricName": "Medium severity events"
|
||||
},
|
||||
{
|
||||
"baseQuery": "{{graphQueriesTableName}}\n| where severity_s == \"Low\"",
|
||||
"legend": "Low severity events",
|
||||
"metricName": "Low severity events"
|
||||
}
|
||||
],
|
||||
"sampleQueries": [
|
||||
{
|
||||
"description": "List all Varonis Alerts",
|
||||
"query": "{{graphQueriesTableName}}\n| sort by TimeGenerated desc"
|
||||
}
|
||||
],
|
||||
"dataTypes": [
|
||||
{
|
||||
"lastDataReceivedQuery": "{{graphQueriesTableName}}\n| summarize Time = max(TimeGenerated)\n| where isnotempty(Time)",
|
||||
"name": "{{graphQueriesTableName}}"
|
||||
}
|
||||
],
|
||||
"connectivityCriterias": [
|
||||
{
|
||||
"type": "IsConnectedQuery",
|
||||
"value": [
|
||||
"{{graphQueriesTableName}}\n| summarize LastLogReceived = max(TimeGenerated)\n| project IsConnected = LastLogReceived > ago(3d)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"availability": {
|
||||
"status": 1,
|
||||
"isPreview": true
|
||||
},
|
||||
"permissions": {
|
||||
"resourceProvider": [
|
||||
{
|
||||
"permissionsDisplayText": "Read and Write permissions on the Log Analytics workspace are required to enable the data connector",
|
||||
"provider": "Microsoft.OperationalInsights/workspaces",
|
||||
"providerDisplayName": "Workspace",
|
||||
"requiredPermissions": {
|
||||
"delete": true,
|
||||
"read": true,
|
||||
"write": true
|
||||
},
|
||||
"scope": "Workspace"
|
||||
}
|
||||
],
|
||||
"customs": [
|
||||
{
|
||||
"name": "Microsoft.Web/sites permissions",
|
||||
"description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)."
|
||||
}
|
||||
]
|
||||
},
|
||||
"instructionSteps": [
|
||||
{
|
||||
"title": "",
|
||||
"description": ">**NOTE:** This connector uses Azure Functions to connect to Varonis DatAlert service to pull alerts into Azure Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**For Azure function and related services installation use:**\n\n [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fvkorenkov-varonis%2Fsentinel%2Fmaster%2Fazuredeploy.json)"
|
||||
},
|
||||
|
||||
{
|
||||
"title": "",
|
||||
"description": ">**NOTE:** This data connector depends on a parser based on a Kusto Function to work as expected [**CiscoSecureEndpoint**](https://aka.ms/sentinel-ciscosecureendpoint-parser) which is deployed with the Microsoft Sentinel Solution."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "STEP 1 - Obtaining Varonis DatAlert Endpoint API credentials.\n\n1. Follow the instructions in the documentation to generate Client ID and API Key."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function.",
|
||||
"instructions": [
|
||||
{
|
||||
"parameters": {
|
||||
"fillWith": [
|
||||
"WorkspaceName"
|
||||
],
|
||||
"label": "Workspace Name"
|
||||
},
|
||||
"type": "CopyableLabel"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Option 1 - Azure Resource Manager (ARM) Template",
|
||||
"description": "Use this method for automated deployment of the data connector using an ARM Template.\n\n1. Click the Deploy to Azure button below. \n\n\t[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fvkorenkov-varonis%2Fsentinel%2Fmaster%2Fazuredeploy.json)\n2. Select the preferred Subscription, Resource Group and Location, Function App plan SKU.\n3. Enter Log Analytics Workspace Name, DatAlert Host Name, Dat Alert Api Key.\n4. Click Review + Create, Create."
|
||||
},
|
||||
{
|
||||
"title": "Option 2 - Manual Deployment of Azure Functions",
|
||||
"description": "Use the following step-by-step instructions to deploy the data connector manually with Azure Functions (Deployment via Visual Studio Code)."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "1. Deploy a Function App\n\n >NOTE: You will need to prepare VS code for Azure function development.\n1. Download the [Azure Function App file](https://raw.githubusercontent.com/vkorenkov-varonis/sentinel/master/Varonis.Sentinel.Functions.zip). Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the Azure: Functions area, choose the Deploy to function app button. If you aren't already signed in, choose the Azure icon in the Activity bar, then in the Azure: Functions area, choose Sign in to Azure If you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\t- Select folder: Choose a folder from your workspace or browse to one that contains your function app.\n\t- Select Subscription: Choose the subscription to use.\n\t- Select Create new Function App in Azure (Don't choose the Advanced option)\n\t- Enter a globally unique name for the function app: Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions.\n\t- Select a runtime: Choose DOTNET 6.0.\n\t- Select a location for new resources. For better performance and lower costs choose the same region where Microsoft Sentinel is located.\n\t6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n\t7. Go to Azure Portal for the Function App configuration."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "2. Configure the Function App\n\n1. In the Function App, select the Function App Name and select Configuration.\n2. In the Application settings tab, select + New application setting.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): DatAlertHostName, DatAlertApiKey, LogAnalyticsKey, LogAnalyticsWorkspace, FirstFetchTime, Severities, ThreatModelNameList, Statuses\n4. Once all application settings have been entered, click Save."
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"metadata": {
|
||||
"_generator": {
|
||||
"name": "bicep",
|
||||
"version": "0.24.24.22086",
|
||||
"templateHash": "1576663599375367683"
|
||||
}
|
||||
},
|
||||
"parameters": {
|
||||
"functionAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "[format('VaronisSaaS-{0}', uniqueString(resourceGroup().id))]",
|
||||
"metadata": {
|
||||
"description": "The name of the Azure Function app."
|
||||
}
|
||||
},
|
||||
"storageAccountType": {
|
||||
"type": "string",
|
||||
"defaultValue": "Standard_LRS",
|
||||
"allowedValues": [
|
||||
"Standard_LRS",
|
||||
"Standard_GRS",
|
||||
"Standard_RAGRS"
|
||||
],
|
||||
"metadata": {
|
||||
"description": "Storage Account type"
|
||||
}
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"defaultValue": "[resourceGroup().location]",
|
||||
"metadata": {
|
||||
"description": "Location for all resources."
|
||||
}
|
||||
},
|
||||
"appInsightsLocation": {
|
||||
"type": "string",
|
||||
"defaultValue": "[resourceGroup().location]",
|
||||
"metadata": {
|
||||
"description": "Location for Application Insights"
|
||||
}
|
||||
},
|
||||
"functionWorkerRuntime": {
|
||||
"type": "string",
|
||||
"defaultValue": "dotnet",
|
||||
"allowedValues": [
|
||||
"dotnet"
|
||||
],
|
||||
"metadata": {
|
||||
"description": "The language worker runtime to load in the function app."
|
||||
}
|
||||
},
|
||||
"functionPlanOS": {
|
||||
"type": "string",
|
||||
"defaultValue": "Linux",
|
||||
"allowedValues": [
|
||||
"Windows",
|
||||
"Linux"
|
||||
],
|
||||
"metadata": {
|
||||
"description": "Specifies the OS used for the Azure Function hosting plan."
|
||||
}
|
||||
},
|
||||
"functionAppPlanSku": {
|
||||
"type": "string",
|
||||
"defaultValue": "Y1",
|
||||
"allowedValues": [
|
||||
"Y1",
|
||||
"EP1",
|
||||
"EP2",
|
||||
"EP3"
|
||||
],
|
||||
"metadata": {
|
||||
"description": "Specifies the Azure Function hosting plan SKU."
|
||||
}
|
||||
},
|
||||
"packageUri": {
|
||||
"type": "string",
|
||||
"defaultValue": "https://github.com/vkorenkov-varonis/sentinel/raw/master/Varonis.Sentinel.Functions.zip",
|
||||
"metadata": {
|
||||
"description": "The zip content url."
|
||||
}
|
||||
},
|
||||
"linuxFxVersion": {
|
||||
"type": "string",
|
||||
"defaultValue": "DOTNET|6.0",
|
||||
"metadata": {
|
||||
"description": "Only required for Linux app to represent runtime stack in the format of 'runtime|runtimeVersion'. For example: 'python|3.9'"
|
||||
}
|
||||
},
|
||||
"logAnalyticsWorkspaceName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Name of the Log Analytics workspace used by Microsoft Sentinel."
|
||||
}
|
||||
},
|
||||
"datAlertHostName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Varonis DatAlert host name."
|
||||
}
|
||||
},
|
||||
"datAlertApiKey": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Varonis DatAlert API key."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"hostingPlanName": "[parameters('functionAppName')]",
|
||||
"applicationInsightsName": "[parameters('functionAppName')]",
|
||||
"storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
|
||||
"isReserved": "[if(equals(parameters('functionPlanOS'), 'Linux'), true(), false())]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"apiVersion": "2022-05-01",
|
||||
"name": "[variables('storageAccountName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"sku": {
|
||||
"name": "[parameters('storageAccountType')]"
|
||||
},
|
||||
"kind": "Storage"
|
||||
},
|
||||
{
|
||||
"condition": "[contains(parameters('functionAppPlanSku'), 'EP')]",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2022-03-01",
|
||||
"name": "[variables('hostingPlanName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"sku": {
|
||||
"tier": "ElasticPremium",
|
||||
"name": "[parameters('functionAppPlanSku')]",
|
||||
"family": "EP"
|
||||
},
|
||||
"properties": {
|
||||
"maximumElasticWorkerCount": 20,
|
||||
"reserved": "[variables('isReserved')]"
|
||||
},
|
||||
"kind": "elastic"
|
||||
},
|
||||
{
|
||||
"condition": "[contains(parameters('functionAppPlanSku'), 'Y')]",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2022-03-01",
|
||||
"name": "[variables('hostingPlanName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"sku": {
|
||||
"tier": "Dynamic",
|
||||
"name": "[parameters('functionAppPlanSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"reserved": "[variables('isReserved')]"
|
||||
},
|
||||
"kind": "[if(variables('isReserved'), 'linux', 'windows')]"
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Insights/components",
|
||||
"apiVersion": "2020-02-02",
|
||||
"name": "[variables('applicationInsightsName')]",
|
||||
"location": "[parameters('appInsightsLocation')]",
|
||||
"tags": {
|
||||
"[format('hidden-link:{0}', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
|
||||
},
|
||||
"properties": {
|
||||
"Application_Type": "web"
|
||||
},
|
||||
"kind": "web"
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2022-03-01",
|
||||
"name": "[parameters('functionAppName')]",
|
||||
"location": "[parameters('location')]",
|
||||
"kind": "[if(variables('isReserved'), 'functionapp,linux', 'functionapp')]",
|
||||
"properties": {
|
||||
"reserved": "[variables('isReserved')]",
|
||||
"serverFarmId": "[if(contains(parameters('functionAppPlanSku'), 'EP'), resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')), resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]",
|
||||
"siteConfig": {
|
||||
"linuxFxVersion": "[if(variables('isReserved'), parameters('linuxFxVersion'), null())]",
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
|
||||
"value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName')), '2020-02-02').InstrumentationKey]"
|
||||
},
|
||||
{
|
||||
"name": "AzureWebJobsStorage",
|
||||
"value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
|
||||
},
|
||||
{
|
||||
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
|
||||
"value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-05-01').keys[0].value)]"
|
||||
},
|
||||
{
|
||||
"name": "WEBSITE_CONTENTSHARE",
|
||||
"value": "[toLower(parameters('functionAppName'))]"
|
||||
},
|
||||
{
|
||||
"name": "FUNCTIONS_EXTENSION_VERSION",
|
||||
"value": "~4"
|
||||
},
|
||||
{
|
||||
"name": "FUNCTIONS_WORKER_RUNTIME",
|
||||
"value": "[parameters('functionWorkerRuntime')]"
|
||||
},
|
||||
{
|
||||
"name": "WEBSITE_RUN_FROM_PACKAGE",
|
||||
"value": "[parameters('packageUri')]"
|
||||
},
|
||||
{
|
||||
"name": "DatAlertHostName",
|
||||
"value": "[parameters('datAlertHostName')]"
|
||||
},
|
||||
{
|
||||
"name": "DatAlertApiKey",
|
||||
"value": "[parameters('datAlertApiKey')]"
|
||||
},
|
||||
{
|
||||
"name": "LogAnalyticsKey",
|
||||
"value": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').primarySharedKey]"
|
||||
},
|
||||
{
|
||||
"name": "LogAnalyticsWorkspace",
|
||||
"value": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').customerId]"
|
||||
},
|
||||
{
|
||||
"name": "FirstFetchTime",
|
||||
"value": "2 weeks"
|
||||
},
|
||||
{
|
||||
"name": "Severities",
|
||||
"value": "Low, Medium, High"
|
||||
},
|
||||
{
|
||||
"name": "ThreatModelNameList",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"name": "Statuses",
|
||||
"value": "New, Under Investigation"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"Name": "VaronisSaaS",
|
||||
"Author": "Varonis",
|
||||
"Logo": "<img src=\"\" width=\"75px\" height=\"75px\">",
|
||||
"Description": "The Varonis SaaS integration allows you to retrieve Varonis DatAlert alerts, create incident and pull activities related to the alerts for conducting investigations.",
|
||||
"Workbooks": ["Workbooks/VaronisSaaS.json"],
|
||||
"Data Connectors": ["Data Connectors/VaronisSaaS_API_FunctionApp.json"],
|
||||
"BasePath": "C:\\Projects\\DataIntegration\\Azure-Sentinel\\Solutions\\VaronisSaaS",
|
||||
"Version": "1.0.1",
|
||||
"Metadata": "SolutionMetadata.json",
|
||||
"TemplateSpec": true,
|
||||
"Is1PConnector": false
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
# Varonis SaaS
|
||||
|
||||
### In this article
|
||||
[Connector attributes](#connector-attributes)\
|
||||
[Query samples](#query-samples)\
|
||||
[Prerequisites](#prerequisites)\
|
||||
[Vendor installation instructions](#vendor-installation-instructions)\
|
||||
[Next steps](#next-steps)
|
||||
|
||||
Varonis SaaS provides the capability to ingest [Varonis Alerts](https://varonisdatalertservicemockwebapi20230907161659.azurewebsites.net/Alert/alerts) into Microsoft Sentinel.
|
||||
|
||||
## Connector Attributes
|
||||
| Connector attribute | Description |
|
||||
| ----------------------------- | --------------------------------------------- |
|
||||
| Azure function app code | https://github.com/vkorenkov-varonis/sentinel |
|
||||
| Log Analytics table(s) | VaronisAlerts_CL |
|
||||
| Data collection rules support | Not currently supported |
|
||||
| Supported by | Varonis Corporation |
|
||||
|
||||
## Query samples
|
||||
#### All Varonis Data Alerts logs
|
||||
|
||||
```kusto
|
||||
VaronisAlerts_CL
|
||||
| sort by TimeGenerated desc
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
To integrate with Varonis SaaS (using Azure Functions) make sure you have:
|
||||
- Microsoft.Web/sites permissions: Read and write permissions to Azure Functions to create a Function App is required. See the [documentation](https://learn.microsoft.com/en-us/azure/azure-functions/) to learn more about Azure Functions.
|
||||
- Varonis API credentials: Varonis API credentials with permission read log is required for Varonis DatAlert API. See the [documentation]() to learn more about creating Varonis DatAlert API credentials.
|
||||
|
||||
## Vendor installation instructions
|
||||
>This connector uses Azure Functions to connect to the Varonis DatAlert Endpoint API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/en-us/pricing/details/functions/) for details.
|
||||
|
||||
STEP 1 - Obtaining Varonis DatAlert Endpoint API credentials.
|
||||
|
||||
1. Follow the instructions in the documentation to generate Client ID and API Key. _**TODO**: NEED INSTRUCTION HERE_
|
||||
|
||||
STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function.
|
||||
|
||||
Option 1 - Azure Resource Manager (ARM) Template
|
||||
|
||||
Use this method for automated deployment of the data connector using an ARM Template.
|
||||
|
||||
1. Click the Deploy to Azure button below.\
|
||||
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fvkorenkov-varonis%2Fsentinel%2Fmaster%2Fazuredeploy.json)
|
||||
2. Select the preferred Subscription, Resource Group and Location, Function App plan SKU.
|
||||
3. Enter Log Analytics Workspace Name, DatAlert Host Name, Dat Alert Api Key.
|
||||
4. Click Review + Create, Create.
|
||||
|
||||
Option 2 - Manual Deployment of Azure Functions
|
||||
|
||||
Use the following step-by-step instructions to deploy the data connector manually with Azure Functions (Deployment via Visual Studio Code).
|
||||
|
||||
1. Deploy a Function App
|
||||
|
||||
>NOTE: You will need to prepare VS code for Azure function development.
|
||||
|
||||
1. Download the [Azure Function App file](https://raw.githubusercontent.com/vkorenkov-varonis/sentinel/master/Varonis.Sentinel.Functions.zip). Extract archive to your local development computer.
|
||||
2. Start VS Code. Choose File in the main menu and select Open Folder.
|
||||
3. Select the top level folder from extracted files.
|
||||
4. Choose the Azure icon in the Activity bar, then in the Azure: Functions area, choose the Deploy to function app button. If you aren't already signed in, choose the Azure icon in the Activity bar, then in the Azure: Functions area, choose Sign in to Azure If you're already signed in, go to the next step.
|
||||
5. Provide the following information at the prompts:
|
||||
- Select folder: Choose a folder from your workspace or browse to one that contains your function app.
|
||||
- Select Subscription: Choose the subscription to use.
|
||||
- Select Create new Function App in Azure (Don't choose the Advanced option)
|
||||
- Enter a globally unique name for the function app: Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions.
|
||||
- Select a runtime: Choose DOTNET 6.0.
|
||||
- Select a location for new resources. For better performance and lower costs choose the same region where Microsoft Sentinel is located.
|
||||
6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.
|
||||
7. Go to Azure Portal for the Function App configuration.
|
||||
|
||||
2. Configure the Function App
|
||||
|
||||
1. In the Function App, select the Function App Name and select Configuration.
|
||||
2. In the Application settings tab, select + New application setting.
|
||||
3. Add each of the following application settings individually, with their respective string values (case-sensitive): DatAlertHostName, DatAlertApiKey, LogAnalyticsKey, LogAnalyticsWorkspace, FirstFetchTime, Severities, ThreatModelNameList, Statuses
|
||||
4. Once all application settings have been entered, click Save.
|
||||
|
||||
## Next
|
||||
For more information, go to the related solution in the Azure Marketplace.
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"publisherId": "varonis",
|
||||
"offerId": "azure-sentinel-solution-varonis",
|
||||
"firstPublishDate": "2023-11-10",
|
||||
"lastPublishDate": "2023-11-10",
|
||||
"providers": ["Varonis"],
|
||||
"categories": {
|
||||
"domains" : ["Security - Network"],
|
||||
"verticals": []
|
||||
},
|
||||
"support": {
|
||||
"name": "Microsoft Corporation",
|
||||
"email": "support@microsoft.com",
|
||||
"tier": "Microsoft",
|
||||
"link": "https://support.microsoft.com"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
{
|
||||
"version": "Notebook/1.0",
|
||||
"items": [
|
||||
{
|
||||
"type": 9,
|
||||
"content": {
|
||||
"version": "KqlParameterItem/1.0",
|
||||
"parameters": [
|
||||
{
|
||||
"id": "388e603c-a5b1-40db-808f-d5dc5301793e",
|
||||
"version": "KqlParameterItem/1.0",
|
||||
"name": "time_range",
|
||||
"label": "TimeRange",
|
||||
"type": 4,
|
||||
"isRequired": true,
|
||||
"typeSettings": {
|
||||
"selectableValues": [
|
||||
{
|
||||
"durationMs": 300000
|
||||
},
|
||||
{
|
||||
"durationMs": 900000
|
||||
},
|
||||
{
|
||||
"durationMs": 1800000
|
||||
},
|
||||
{
|
||||
"durationMs": 3600000
|
||||
},
|
||||
{
|
||||
"durationMs": 14400000
|
||||
},
|
||||
{
|
||||
"durationMs": 43200000
|
||||
},
|
||||
{
|
||||
"durationMs": 86400000
|
||||
},
|
||||
{
|
||||
"durationMs": 172800000
|
||||
},
|
||||
{
|
||||
"durationMs": 259200000
|
||||
},
|
||||
{
|
||||
"durationMs": 604800000
|
||||
},
|
||||
{
|
||||
"durationMs": 1209600000
|
||||
},
|
||||
{
|
||||
"durationMs": 2419200000
|
||||
},
|
||||
{
|
||||
"durationMs": 2592000000
|
||||
},
|
||||
{
|
||||
"durationMs": 5184000000
|
||||
},
|
||||
{
|
||||
"durationMs": 7776000000
|
||||
}
|
||||
],
|
||||
"allowCustom": true
|
||||
},
|
||||
"timeContext": {
|
||||
"durationMs": 86400000
|
||||
},
|
||||
"value": {
|
||||
"durationMs": 259200000
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e1c3e667-d431-419e-ae03-2da2f7f2d42f",
|
||||
"version": "KqlParameterItem/1.0",
|
||||
"name": "page",
|
||||
"label": "Page",
|
||||
"type": 1,
|
||||
"isGlobal": true,
|
||||
"isHiddenWhenLocked": true,
|
||||
"value": "main"
|
||||
}
|
||||
],
|
||||
"style": "pills",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces"
|
||||
},
|
||||
"name": "global-parameters"
|
||||
},
|
||||
{
|
||||
"type": 11,
|
||||
"content": {
|
||||
"version": "LinkItem/1.0",
|
||||
"style": "tabs",
|
||||
"links": [
|
||||
{
|
||||
"id": "30fe89ad-9eba-419e-8d9e-53c6805870db",
|
||||
"cellValue": "page",
|
||||
"linkTarget": "parameter",
|
||||
"linkLabel": "Main",
|
||||
"subTarget": "main",
|
||||
"preText": "Main",
|
||||
"style": "link"
|
||||
},
|
||||
{
|
||||
"id": "5822aaf8-5ad8-49c6-acf8-491b439fbc1a",
|
||||
"cellValue": "page",
|
||||
"linkTarget": "parameter",
|
||||
"linkLabel": "Threats",
|
||||
"subTarget": "threats",
|
||||
"style": "link"
|
||||
},
|
||||
{
|
||||
"id": "7266371b-70ac-4b79-abda-43169b26d760",
|
||||
"cellValue": "page",
|
||||
"linkTarget": "parameter",
|
||||
"linkLabel": "Users",
|
||||
"subTarget": "users",
|
||||
"style": "link"
|
||||
},
|
||||
{
|
||||
"id": "3e007191-772f-48aa-8ac0-dfc9947f85c7",
|
||||
"cellValue": "page",
|
||||
"linkTarget": "parameter",
|
||||
"linkLabel": "Assets",
|
||||
"subTarget": "assets",
|
||||
"style": "link"
|
||||
},
|
||||
{
|
||||
"id": "e585bed2-37bc-4bfe-8269-cd12726a8fe9",
|
||||
"cellValue": "page",
|
||||
"linkTarget": "parameter",
|
||||
"linkLabel": "Devices",
|
||||
"subTarget": "devices",
|
||||
"style": "link"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "links - 6"
|
||||
},
|
||||
{
|
||||
"type": 12,
|
||||
"content": {
|
||||
"version": "NotebookGroup/1.0",
|
||||
"groupType": "editable",
|
||||
"items": [
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "let days = dynamic([\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]);\nlet months = dynamic([\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]);\nVaronisAlerts_CL\n| extend day_of_week = days[toint(dayofweek(EventUTC_t)/1d)]\n| extend month_of_year = months[getmonth(EventUTC_t)]\n| extend day_of_month = dayofmonth(EventUTC_t)\n| extend day_str = strcat(day_of_week, \" \", month_of_year, \" \", day_of_month)\n| extend day = todatetime(format_datetime(EventUTC_t, 'yyyy-MM-dd'))\n| summarize alert_count = count() by day, day_str, Severity_s\n| order by day asc, Severity_s\n| project Day = day_str, Alerts = alert_count, Severity = Severity_s",
|
||||
"size": 1,
|
||||
"showAnalytics": true,
|
||||
"title": "ALERTS OVER TIME",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "barchart",
|
||||
"chartSettings": {
|
||||
"xAxis": "Day",
|
||||
"yAxis": [
|
||||
"Alerts"
|
||||
],
|
||||
"group": "Severity",
|
||||
"createOtherGroup": null,
|
||||
"showLegend": true
|
||||
}
|
||||
},
|
||||
"name": "ALERTS OVER TIME - Copy"
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "VaronisAlerts_CL\r\n| where isnotempty( Name_s)\r\n| summarize alerts_count = count() by Name_s\r\n| project Threat = Name_s, Alerts = alerts_count\r\n| take 4",
|
||||
"size": 0,
|
||||
"showAnalytics": true,
|
||||
"title": "TOP ALERTED THREAT MODELS",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "tiles",
|
||||
"tileSettings": {
|
||||
"titleContent": {
|
||||
"columnMatch": "Threat",
|
||||
"formatter": 1
|
||||
},
|
||||
"leftContent": {
|
||||
"columnMatch": "Alerts",
|
||||
"formatter": 12,
|
||||
"formatOptions": {
|
||||
"palette": "auto"
|
||||
},
|
||||
"numberFormat": {
|
||||
"unit": 17,
|
||||
"options": {
|
||||
"maximumSignificantDigits": 3,
|
||||
"maximumFractionDigits": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBorder": false,
|
||||
"sortCriteriaField": "Alerts",
|
||||
"sortOrderField": 2,
|
||||
"size": "full"
|
||||
},
|
||||
"mapSettings": {
|
||||
"locInfo": "LatLong",
|
||||
"sizeSettings": "Alerts",
|
||||
"sizeAggregation": "Sum",
|
||||
"legendMetric": "Alerts",
|
||||
"legendAggregation": "Sum",
|
||||
"itemColorSettings": {
|
||||
"type": "heatmap",
|
||||
"colorAggregation": "Sum",
|
||||
"nodeColorField": "Alerts",
|
||||
"heatmapPalette": "greenRed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"customWidth": "50",
|
||||
"name": "TOP ALERTED THREAT MODELS"
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "VaronisAlerts_CL\r\n| extend json_arr = parse_json(UserName_s)\r\n| where isnotempty(json_arr)\r\n| mv-expand json_arr\r\n| summarize alerts_count = count() by tostring(json_arr)\r\n| project User = json_arr, Alerts = alerts_count\r\n| take 4",
|
||||
"size": 0,
|
||||
"showAnalytics": true,
|
||||
"title": " TOP ALERTED USERS",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "tiles",
|
||||
"tileSettings": {
|
||||
"titleContent": {
|
||||
"columnMatch": "User",
|
||||
"formatter": 1
|
||||
},
|
||||
"leftContent": {
|
||||
"columnMatch": "Alerts",
|
||||
"formatter": 12,
|
||||
"formatOptions": {
|
||||
"palette": "auto"
|
||||
},
|
||||
"numberFormat": {
|
||||
"unit": 17,
|
||||
"options": {
|
||||
"maximumSignificantDigits": 3,
|
||||
"maximumFractionDigits": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBorder": false,
|
||||
"sortCriteriaField": "Alerts",
|
||||
"sortOrderField": 2,
|
||||
"size": "full"
|
||||
},
|
||||
"mapSettings": {
|
||||
"locInfo": "LatLong",
|
||||
"sizeSettings": "Alerts",
|
||||
"sizeAggregation": "Sum",
|
||||
"legendMetric": "Alerts",
|
||||
"legendAggregation": "Sum",
|
||||
"itemColorSettings": {
|
||||
"type": "heatmap",
|
||||
"colorAggregation": "Sum",
|
||||
"nodeColorField": "Alerts",
|
||||
"heatmapPalette": "greenRed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"customWidth": "50",
|
||||
"name": " TOP ALERTED USERS"
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "VaronisAlerts_CL\r\n| extend json_arr = parse_json(Asset_s)\r\n| where isnotempty(json_arr)\r\n| mv-expand json_arr\r\n| summarize alerts_count = count() by tostring(json_arr)\r\n| project Asset = json_arr, Alerts = alerts_count\r\n| take 4",
|
||||
"size": 0,
|
||||
"showAnalytics": true,
|
||||
"title": "TOP ALERTED ASSETS",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "tiles",
|
||||
"tileSettings": {
|
||||
"titleContent": {
|
||||
"columnMatch": "Asset",
|
||||
"formatter": 1
|
||||
},
|
||||
"leftContent": {
|
||||
"columnMatch": "Alerts",
|
||||
"formatter": 12,
|
||||
"formatOptions": {
|
||||
"palette": "auto"
|
||||
},
|
||||
"numberFormat": {
|
||||
"unit": 17,
|
||||
"options": {
|
||||
"maximumSignificantDigits": 3,
|
||||
"maximumFractionDigits": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBorder": false,
|
||||
"sortCriteriaField": "Alerts",
|
||||
"sortOrderField": 2,
|
||||
"size": "full"
|
||||
},
|
||||
"mapSettings": {
|
||||
"locInfo": "LatLong",
|
||||
"sizeSettings": "Alerts",
|
||||
"sizeAggregation": "Sum",
|
||||
"legendMetric": "Alerts",
|
||||
"legendAggregation": "Sum",
|
||||
"itemColorSettings": {
|
||||
"type": "heatmap",
|
||||
"colorAggregation": "Sum",
|
||||
"nodeColorField": "Alerts",
|
||||
"heatmapPalette": "greenRed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"customWidth": "50",
|
||||
"name": "TOP ALERTED ASSETS"
|
||||
},
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "VaronisAlerts_CL\r\n| extend json_arr = parse_json(DeviceName_s)\r\n| where isnotempty(json_arr)\r\n| mv-expand json_arr\r\n| summarize alerts_count = count() by tostring(json_arr)\r\n| project Device = json_arr, Alerts = alerts_count\r\n| take 4",
|
||||
"size": 0,
|
||||
"showAnalytics": true,
|
||||
"title": "TOP ALERTED DEVICES",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "tiles",
|
||||
"tileSettings": {
|
||||
"titleContent": {
|
||||
"columnMatch": "Device",
|
||||
"formatter": 1
|
||||
},
|
||||
"leftContent": {
|
||||
"columnMatch": "Alerts",
|
||||
"formatter": 12,
|
||||
"formatOptions": {
|
||||
"palette": "auto"
|
||||
},
|
||||
"numberFormat": {
|
||||
"unit": 17,
|
||||
"options": {
|
||||
"maximumSignificantDigits": 3,
|
||||
"maximumFractionDigits": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBorder": false,
|
||||
"sortCriteriaField": "Alerts",
|
||||
"sortOrderField": 2,
|
||||
"size": "full"
|
||||
},
|
||||
"mapSettings": {
|
||||
"locInfo": "LatLong",
|
||||
"sizeSettings": "Alerts",
|
||||
"sizeAggregation": "Sum",
|
||||
"legendMetric": "Alerts",
|
||||
"legendAggregation": "Sum",
|
||||
"itemColorSettings": {
|
||||
"type": "heatmap",
|
||||
"colorAggregation": "Sum",
|
||||
"nodeColorField": "Alerts",
|
||||
"heatmapPalette": "greenRed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"customWidth": "50",
|
||||
"name": "TOP ALERTED DEVICES"
|
||||
}
|
||||
]
|
||||
},
|
||||
"conditionalVisibility": {
|
||||
"parameterName": "page",
|
||||
"comparison": "isEqualTo",
|
||||
"value": "main"
|
||||
},
|
||||
"name": "main-page"
|
||||
},
|
||||
{
|
||||
"type": 12,
|
||||
"content": {
|
||||
"version": "NotebookGroup/1.0",
|
||||
"groupType": "editable",
|
||||
"items": [
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "let days = dynamic([\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]);\nlet months = dynamic([\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]);\nVaronisAlerts_CL\n| extend day_of_week = days[toint(dayofweek(EventUTC_t)/1d)]\n| extend month_of_year = months[getmonth(EventUTC_t)]\n| extend day_of_month = dayofmonth(EventUTC_t)\n| extend day_str = strcat(day_of_week, \" \", month_of_year, \" \", day_of_month)\n| extend day = todatetime(format_datetime(EventUTC_t, 'yyyy-MM-dd'))\n| extend group_var = Name_s\n| summarize alert_count = count() by day, day_str, group_var\n| order by day asc, group_var\n| project Day = day_str, Alerts = alert_count, Threat = group_var\n",
|
||||
"size": 1,
|
||||
"showAnalytics": true,
|
||||
"title": "THREAT MODEL NAMES",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "barchart",
|
||||
"chartSettings": {
|
||||
"xAxis": "Day",
|
||||
"yAxis": [
|
||||
"Alerts"
|
||||
],
|
||||
"group": "Threat",
|
||||
"createOtherGroup": null,
|
||||
"showLegend": true
|
||||
}
|
||||
},
|
||||
"name": "alerts-threats-day"
|
||||
}
|
||||
]
|
||||
},
|
||||
"conditionalVisibility": {
|
||||
"parameterName": "page",
|
||||
"comparison": "isEqualTo",
|
||||
"value": "threats"
|
||||
},
|
||||
"name": "threats-page"
|
||||
},
|
||||
{
|
||||
"type": 12,
|
||||
"content": {
|
||||
"version": "NotebookGroup/1.0",
|
||||
"groupType": "editable",
|
||||
"items": [
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "let days = dynamic([\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]);\nlet months = dynamic([\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]);\nVaronisAlerts_CL\n| extend day_of_week = days[toint(dayofweek(EventUTC_t)/1d)]\n| extend month_of_year = months[getmonth(EventUTC_t)]\n| extend day_of_month = dayofmonth(EventUTC_t)\n| extend day_str = strcat(day_of_week, \" \", month_of_year, \" \", day_of_month)\n| extend day = todatetime(format_datetime(EventUTC_t, 'yyyy-MM-dd'))\n| extend json_arr = parse_json(UserName_s)\n| where isnotempty(json_arr)\n| mv-expand json_arr\n| extend group_var = tostring(json_arr)\n| summarize alert_count = count() by day, day_str, group_var\n| order by day asc, group_var\n| project Day = day_str, Alerts = alert_count, User = group_var",
|
||||
"size": 1,
|
||||
"showAnalytics": true,
|
||||
"title": "USERS",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "barchart",
|
||||
"chartSettings": {
|
||||
"xAxis": "Day",
|
||||
"yAxis": [
|
||||
"Alerts"
|
||||
],
|
||||
"group": "User",
|
||||
"createOtherGroup": null,
|
||||
"showLegend": true
|
||||
}
|
||||
},
|
||||
"name": "alerts-users-day"
|
||||
}
|
||||
]
|
||||
},
|
||||
"conditionalVisibility": {
|
||||
"parameterName": "page",
|
||||
"comparison": "isEqualTo",
|
||||
"value": "users"
|
||||
},
|
||||
"name": "users-page"
|
||||
},
|
||||
{
|
||||
"type": 12,
|
||||
"content": {
|
||||
"version": "NotebookGroup/1.0",
|
||||
"groupType": "editable",
|
||||
"items": [
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "let days = dynamic([\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]);\nlet months = dynamic([\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]);\nVaronisAlerts_CL\n| extend day_of_week = days[toint(dayofweek(EventUTC_t)/1d)]\n| extend month_of_year = months[getmonth(EventUTC_t)]\n| extend day_of_month = dayofmonth(EventUTC_t)\n| extend day_str = strcat(day_of_week, \" \", month_of_year, \" \", day_of_month)\n| extend day = todatetime(format_datetime(EventUTC_t, 'yyyy-MM-dd'))\n| extend json_arr = parse_json(Asset_s)\n| where isnotempty(json_arr)\n| mv-expand json_arr\n| extend group_var = tostring(json_arr)\n| summarize alert_count = count() by day, day_str, group_var\n| order by day asc, group_var\n| project Day = day_str, Alerts = alert_count, Asset = group_var",
|
||||
"size": 1,
|
||||
"showAnalytics": true,
|
||||
"title": "ASSETS",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "barchart",
|
||||
"chartSettings": {
|
||||
"xAxis": "Day",
|
||||
"yAxis": [
|
||||
"Alerts"
|
||||
],
|
||||
"group": "Asset",
|
||||
"createOtherGroup": null,
|
||||
"showLegend": true
|
||||
}
|
||||
},
|
||||
"name": "alerts-assets"
|
||||
}
|
||||
]
|
||||
},
|
||||
"conditionalVisibility": {
|
||||
"parameterName": "page",
|
||||
"comparison": "isEqualTo",
|
||||
"value": "assets"
|
||||
},
|
||||
"name": "assets-page"
|
||||
},
|
||||
{
|
||||
"type": 12,
|
||||
"content": {
|
||||
"version": "NotebookGroup/1.0",
|
||||
"groupType": "editable",
|
||||
"items": [
|
||||
{
|
||||
"type": 3,
|
||||
"content": {
|
||||
"version": "KqlItem/1.0",
|
||||
"query": "let days = dynamic([\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]);\nlet months = dynamic([\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]);\nVaronisAlerts_CL\n| extend day_of_week = days[toint(dayofweek(EventUTC_t)/1d)]\n| extend month_of_year = months[getmonth(EventUTC_t)]\n| extend day_of_month = dayofmonth(EventUTC_t)\n| extend day_str = strcat(day_of_week, \" \", month_of_year, \" \", day_of_month)\n| extend day = todatetime(format_datetime(EventUTC_t, 'yyyy-MM-dd'))\n| extend json_arr = parse_json(DeviceName_s)\n| where isnotempty(json_arr)\n| mv-expand json_arr\n| extend group_var = tostring(json_arr)\n| summarize alert_count = count() by day, day_str, group_var\n| order by day asc, group_var\n| project Day = day_str, Alerts = alert_count, Device = group_var\n",
|
||||
"size": 1,
|
||||
"showAnalytics": true,
|
||||
"title": "DEVICES",
|
||||
"timeContextFromParameter": "time_range",
|
||||
"queryType": 0,
|
||||
"resourceType": "microsoft.operationalinsights/workspaces",
|
||||
"visualization": "barchart",
|
||||
"chartSettings": {
|
||||
"xAxis": "Day",
|
||||
"yAxis": [
|
||||
"Alerts"
|
||||
],
|
||||
"group": "Device",
|
||||
"createOtherGroup": null,
|
||||
"showLegend": true
|
||||
}
|
||||
},
|
||||
"name": "alerts-devices-day"
|
||||
}
|
||||
]
|
||||
},
|
||||
"conditionalVisibility": {
|
||||
"parameterName": "page",
|
||||
"comparison": "isEqualTo",
|
||||
"value": "devices"
|
||||
},
|
||||
"name": "devices-page"
|
||||
}
|
||||
],
|
||||
"fallbackResourceIds": [
|
||||
"/subscriptions/4aef56e4-24c5-49ca-9ce1-b6123134b874/resourcegroups/vrns_sentinel_data_alert_rg/providers/microsoft.operationalinsights/workspaces/vrns-log-analytics-api-ws"
|
||||
],
|
||||
"fromTemplateId": "https://sentinelus.hosting.portal.azure.net/sentinelus/Content/1.0.02484.3403-231021-003920/Scenarios/Ecosystem/Content/Workbooks/CustomWorkbook.json",
|
||||
"$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
|
||||
}
|
Загрузка…
Ссылка в новой задаче