Compare commits

..

6 Commits

Author SHA1 Message Date
bob
d183716f95 Update README.md 2024-10-15 15:20:49 -05:00
robbwilcox
a099b77bf4 revert 2024-10-14 15:57:55 -05:00
cd9682379d v3 2024-10-13 20:49:50 -05:00
1e9c269f07 Add proper creds 2024-10-13 16:35:33 -05:00
8efe02cc86 Update run freq 2024-10-11 23:17:09 -05:00
62a4a47373 Init 2024-10-11 23:10:01 -05:00
12 changed files with 275 additions and 1 deletions

4
.gitignore vendored
View File

@@ -1,10 +1,14 @@
# ---> VisualStudioCode # ---> VisualStudioCode
.vscode
.vscode/* .vscode/*
!.vscode/settings.json !.vscode/settings.json
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
!.vscode/*.code-snippets !.vscode/*.code-snippets
.idea/*
launchsettings.json
# Local History for Visual Studio Code # Local History for Visual Studio Code
.history/ .history/

View File

@@ -0,0 +1,75 @@
using Azure;
using Azure.Monitor.Query;
using Azure.Monitor.Query.Models;
using Azure.ResourceManager;
using Azure.ResourceManager.Resources;
using Microsoft.Extensions.Logging;
using ProperDI.Azure.Endpoints.ResourceGroup.Models;
namespace ProperDI.Azure.Endpoints.ResourceGroup;
public interface IActivityLogReader
{
Task ScanAppAsync(string appName, CancellationToken cancellationToken, QueryTimeRange? givenTimeRange);
Task ScanAllAsync(CancellationToken cancellationToken);
}
public class ActivityLogReader : IActivityLogReader
{
private readonly ILogger<ActivityLogReader> _logger;
private readonly ArmClient _armClient;
private readonly LogsQueryClient _logsQueryClient;
public ActivityLogReader(ILogger<ActivityLogReader> logger, ArmClient armClient, LogsQueryClient logsQueryClient)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_armClient = armClient ?? throw new ArgumentNullException(nameof(armClient));
_logsQueryClient = logsQueryClient ?? throw new ArgumentNullException(nameof(logsQueryClient));
}
public async Task ScanAppAsync(string appName, CancellationToken cancellationToken, QueryTimeRange? givenTimeRange = default)
{
var timeRange = givenTimeRange ?? new QueryTimeRange(DateTime.UtcNow.AddDays(-7), DateTime.UtcNow);
try
{
_logger.LogInformation("Scanning for resources");
await ScanLogsForAppActivityAsync();
}
catch (TaskCanceledException)
{
_logger.LogWarning("Task canceled");
}
catch (Exception ex)
{
_logger.LogError(ex, "Http request failed");
throw;
}
}
public async Task ScanAllAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public async Task ScanLogsForAppActivityAsync()
{
}
private async Task<SubscriptionResource> GetDefaultSubscriptionAsync(CancellationToken cancellationToken)
{
try
{
var sub = await _armClient.GetDefaultSubscriptionAsync(cancellationToken);
return sub;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve default subscription");
throw;
}
}
}

View File

@@ -0,0 +1,3 @@
namespace ProperDI.Azure.Endpoints.ResourceGroup.Models;
public record AppActivitySummary(string AppName, string PermissionsExercised);

View File

@@ -0,0 +1,3 @@
namespace ProperDI.Azure.Endpoints.ResourceGroup.Models;
public record TimeRange(DateTime Start, DateTime End);

View File

@@ -0,0 +1,87 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ProperDI.Azure.Endpoints.ResourceGroup;
public class RoleAssesorBackgroundService : IHostedService, IDisposable
{
private readonly IActivityLogReader _activityLogReader;
private readonly ILogger<RoleAssesorBackgroundService> _logger;
private readonly IOptionsMonitor<RoleAssesorBackgroundServiceOptions> _options;
private Timer? _timer;
private int _runFrequencyInMinutes;
public RoleAssesorBackgroundService(
IActivityLogReader activityLogReader,
ILogger<RoleAssesorBackgroundService> logger,
IOptionsMonitor<RoleAssesorBackgroundServiceOptions> options)
{
_activityLogReader = activityLogReader;
_logger = logger;
_options = options;
// This sets our initial run frequency
_runFrequencyInMinutes = _options.CurrentValue.RunFrequencyInMinutes;
// Subscribe to any changes in the config file
_options.OnChange(UpdateTimerInterval);
}
private void UpdateTimerInterval(RoleAssesorBackgroundServiceOptions options)
{
var newFreq = options.RunFrequencyInMinutes;
if (newFreq == _runFrequencyInMinutes)
return;
_runFrequencyInMinutes = newFreq;
_logger.LogInformation("Run frequency updated to {RunFreq} minutes", _runFrequencyInMinutes);
_timer?.Change(TimeSpan.Zero, TimeSpan.FromMinutes(_runFrequencyInMinutes));
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Role Assessor background service is starting.");
_timer = new Timer(async _ => await ProcessAsync(cancellationToken), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
private async Task ProcessAsync(CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Role Assessor background service is running at: {time}", DateTimeOffset.Now);
if (cancellationToken.IsCancellationRequested)
return;
await _activityLogReader.ScanAppAsync("Test", cancellationToken, null);
}
catch (OperationCanceledException)
{
_logger.LogInformation("Operation was canceled");
}
catch (System.Exception ex)
{
_logger.LogError(ex, "An error occurred while reading the logs");
throw;
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping the Role Assessor background service.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
GC.SuppressFinalize(this);
_timer?.Dispose();
}
}

View File

@@ -0,0 +1,4 @@
public class RoleAssesorBackgroundServiceOptions
{
public int RunFrequencyInMinutes { get; set; } = 5; // Defaults to 5 minute.
}

30
Program.cs Normal file
View File

@@ -0,0 +1,30 @@
using Azure.Identity;
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ProperDI.Azure.Endpoints.ResourceGroup;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddTransient<IActivityLogReader, ActivityLogReader>();
builder.Services.AddAzureClients(clientBuilder =>
{
// var creds = new ClientSecretCredential(
// "",
// "",
// "");
clientBuilder.UseCredential(new DefaultAzureCredential());
clientBuilder.AddArmClient(builder.Configuration.GetSection("Am95DevSubscriptionId"));
clientBuilder.ConfigureDefaults(client =>
{
client.Retry.MaxRetries = 3;
});
});
builder.Services.Configure<RoleAssesorBackgroundServiceOptions>(builder.Configuration.GetSection("RoleAssessorBackgroundService"));
builder.Services.AddHostedService<RoleAssesorBackgroundService>();
var host = builder.Build();
await host.RunAsync();

View File

@@ -1,3 +1,3 @@
# Sentinel # Sentinel
Watches over the land and smites and rogue roles! Watches over the land and smites any rogue roles!

21
Sentinel.csproj Normal file
View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.1" />
<PackageReference Include="Azure.Monitor.Query" Version="1.5.0" />
<PackageReference Include="Azure.ResourceManager" Version="1.13.0" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.9.0" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.7.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
</ItemGroup>
</Project>

25
Sentinel.sln Normal file
View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sentinel", "Sentinel.csproj", "{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA937A3C-5191-4DAC-AC62-EBF7EE90BD1F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {79EE9B52-D46E-4FBB-8310-32FD5835CF1D}
EndGlobalSection
EndGlobal

View File

22
appsettings.json Normal file
View File

@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"System.Net": "Warning",
"Azure.Core": "Warning",
"Azure.Identity": "Warning"
}
},
"RoleAssessorBackgroundService": {
"RunFrequencyInMinutes": 12
},
"Am95DevSubscriptionId": "040698c2-a013-45be-b1fa-e975f46b9d63",
"DefaultSubscriptionId": "2690a5f1-155b-4fa8-896f-92c6bcb62bee",
"ASPNETCORE_ENVIRONMENT": "Development",
"AzureSubscriptionId": "2690a5f1-155b-4fa8-896f-92c6bcb62bee",
"AzureTenantId": "11ffe5d3-5e72-4414-8dfd-1eb209497679",
"AzureClientId": "d0870778-24dd-4c87-a449-2c36674035d1",
"AzureClientSecret": "EWX8Q~jpfKab8hB1TzujorzKIeJEV5NKEfCuOaFC"
}