Skip to content
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9b7091a
Initial plan
Copilot Nov 7, 2025
155b2d4
Fix build errors in PowerShellSecureBootCertificateEnumerator for .NE…
Copilot Nov 7, 2025
a88e424
Add Linux client support (.NET 8) with platform-specific implementations
Copilot Nov 7, 2025
5477b82
Update documentation for Linux client support
Copilot Nov 7, 2025
0ed2c87
Add test project for Linux client with basic unit tests
Copilot Nov 7, 2025
0f4133b
Add comprehensive implementation summary for Linux client support
Copilot Nov 7, 2025
f0da4f7
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
0682f03
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
5a85d40
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
287150e
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
da5366c
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
e7b7aba
Update AzureQueueReportSink.cs
robgrame Nov 7, 2025
aec34bc
Update ISecureBootCertificateEnumerator.cs
robgrame Nov 7, 2025
34af9eb
Update SinkCoordinator.cs
robgrame Nov 7, 2025
e0bd782
Update LinuxRegistrySnapshotProvider.cs
robgrame Nov 7, 2025
0c2c59c
Update LinuxEventLogReader.cs
robgrame Nov 7, 2025
9e6bf8d
Update LinuxEventLogReader.cs
robgrame Nov 7, 2025
54f0407
Update ReportBuilder.cs
robgrame Nov 7, 2025
6d83e20
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 7, 2025
971ef9b
Update SecureBootWatcherService.cs
robgrame Nov 7, 2025
9e7b263
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 7, 2025
ff6002f
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 7, 2025
c9f12c0
Update README.md
robgrame Nov 7, 2025
8cf363c
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
85264f2
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
9753fae
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
6a330fc
Update ISecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
f86fd42
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
2c5421d
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
8e8b9d9
Update ReportBuilder.cs
robgrame Nov 8, 2025
33cf81a
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
3e1a8ab
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
163fabe
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
04bc708
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
1669144
Update FileEventCheckpointStore.cs
robgrame Nov 8, 2025
62df7a2
Update FileShareReportSink.cs
robgrame Nov 8, 2025
70f6a8b
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
c521108
Update LinuxEventLogReader.cs
robgrame Nov 8, 2025
51101da
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
d4e6da9
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
63b05b1
Update LinuxRegistrySnapshotProviderTests.cs
robgrame Nov 8, 2025
1fa9e0f
Update ReportBuilder.cs
robgrame Nov 8, 2025
90e1a4d
Update Program.cs
robgrame Nov 8, 2025
cefb506
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
588e258
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
01c6acc
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
27cb227
Update WebApiReportSink.cs
robgrame Nov 8, 2025
459ecf0
Update LinuxEventLogReader.cs
robgrame Nov 8, 2025
97f00c9
Update LinuxSecureBootCertificateEnumerator.cs
robgrame Nov 8, 2025
ddbad92
Update AzureQueueReportSink.cs
robgrame Nov 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 41 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,22 @@ Interactive Chart.js visualizations showing compliance trends and deployment sta
- **Web API**: HTTP POST directly to the dashboard ingestion endpoint
- Configured via `appsettings.json` with fleet-specific settings

### 2. **SecureBootWatcher.Shared** (netstandard2.0)
### 2. **SecureBootWatcher.LinuxClient** (.NET 8) **NEW**
- Runs on Linux systems with UEFI firmware support
- Cross-platform client for monitoring Secure Boot certificates on Linux
- Reads EFI variables from `/sys/firmware/efi/efivars`
- Queries systemd journal (journald) for boot-related events
- Enumerates UEFI certificates using direct EFI variable access
- Supports same three reporting sinks as Windows client
- Targets `linux-x64` and `linux-arm64` architectures
- Configured via `appsettings.json` with fleet-specific settings

### 3. **SecureBootWatcher.Shared** (netstandard2.0)
- Shared models, configuration, validation, and storage contracts
- Used by client, API, and web projects for consistent DTOs
- Includes certificate enumeration models and validation logic

### 3. **SecureBootDashboard.Api** (ASP.NET Core 8)
### 4. **SecureBootDashboard.Api** (ASP.NET Core 8)
- REST API for ingesting reports and querying aggregated data
- Two storage backends:
- **EF Core + SQL Server**: full relational persistence with migrations
Expand All @@ -145,7 +155,7 @@ Interactive Chart.js visualizations showing compliance trends and deployment sta
- `GET /api/SecureBootReports/{id}` – individual report details
- `GET /health` – health checks

### 4. **SecureBootDashboard.Web** (ASP.NET Core 8 Razor Pages)
### 5. **SecureBootDashboard.Web** (ASP.NET Core 8 Razor Pages)
- Modern, responsive dashboard UI for viewing devices, reports, and compliance
- **Features**:
- Splash screen with smooth animations
Expand All @@ -161,19 +171,27 @@ Interactive Chart.js visualizations showing compliance trends and deployment sta
## Prerequisites

### Development
- **.NET SDK 8.0+** (for API & Web projects)
- **.NET Framework 4.8 Developer Pack** (for client)
- **.NET SDK 8.0+** (for API, Web, and Linux client projects)
- **.NET Framework 4.8 Developer Pack** (for Windows client)
- **SQL Server** (LocalDB, Express, or full instance) *or* configure file storage
- **Visual Studio 2022** or **VS Code** with C# extensions
- **PowerShell 5.0+** (for certificate enumeration and deployment scripts)
- **PowerShell 5.0+** (for Windows certificate enumeration and deployment scripts)

### Runtime (Client)
### Runtime (Windows Client)
- **Windows 10/11** or **Windows Server 2016+** with UEFI firmware
- **.NET Framework 4.8 runtime**
- **PowerShell 5.0+** with SecureBoot module
- Administrator/SYSTEM privileges (for registry and certificate access)
- Network or Azure connectivity for sinks

### Runtime (Linux Client) **NEW**
- **Linux distribution** with UEFI firmware support (Ubuntu 20.04+, RHEL 8+, Debian 11+, etc.)
- **.NET 8 runtime** (`dotnet-runtime-8.0`)
- **systemd** with journald (for event logging)
- Root/sudo privileges (for EFI variable access at `/sys/firmware/efi/efivars`)
- Network or Azure connectivity for sinks
- Optional: `mokutil` for Secure Boot status checking

### Runtime (Dashboard API/Web)
- **Azure App Service** (Linux or Windows) *or* on-premises IIS/Kestrel
- **SQL Server** (Azure SQL Database, SQL Managed Instance, or on-prem) *or* file storage mount
Expand Down Expand Up @@ -271,13 +289,27 @@ Navigate to:
- **Web Dashboard**: `https://localhost:7001`
- **API Swagger**: `https://localhost:5001/swagger`

### 7. Run the Client
### 7. Run the Windows Client
```powershell
cd SecureBootWatcher.Client\bin\Debug\net48
.\SecureBootWatcher.Client.exe
```
Watch logs for successful registry snapshot, certificate enumeration, event capture, and confirm payloads reach your API.

### 8. Run the Linux Client (Optional)
```bash
cd SecureBootWatcher.LinuxClient
dotnet run

# Or publish and run as self-contained:
dotnet publish -c Release -r linux-x64 --self-contained
cd bin/Release/net8.0/linux-x64/publish
sudo ./SecureBootWatcher.LinuxClient
```
**Note**: Root/sudo privileges are required to access EFI variables at `/sys/firmware/efi/efivars`.

Watch logs for successful EFI variable access, certificate enumeration, journal event capture, and confirm payloads reach your API.

---

## 📚 Documentation
Expand Down Expand Up @@ -491,7 +523,7 @@ For questions, issues, or support:
- [ ] Enhanced analytics (30/60/90 day trends)

### v2.0 (Q3 2025)
- [ ] Linux client support (.NET 8)
- [x] Linux client support (.NET 8) **COMPLETED**
- [ ] API v2 with GraphQL
- [ ] Machine learning anomaly detection
- [ ] Integration with ServiceNow/Jira
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
is_global = true
build_property.IsMSTestTestAdapterReferenced = true
build_property.RootNamespace = SecureBootWatcher.Client.Tests
build_property.ProjectDir = C:\Users\nefario\source\repos\robgrame\Nimbus.BootCertWatcher\SecureBootWatcher.Client.Tests\
build_property.ProjectDir = /home/runner/work/Nimbus.BootCertWatcher/Nimbus.BootCertWatcher/SecureBootWatcher.Client.Tests/
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ public async Task<SecureBootCertificateCollection> EnumerateAsync(CancellationTo
var script = "Confirm-SecureBootUEFI";
var result = await ExecutePowerShellAsync(script, cancellationToken);

if (result.Contains("True", StringComparison.OrdinalIgnoreCase))
if (result.IndexOf("True", StringComparison.OrdinalIgnoreCase) >= 0)
{
return true;
}
else if (result.Contains("False", StringComparison.OrdinalIgnoreCase))
else if (result.IndexOf("False", StringComparison.OrdinalIgnoreCase) >= 0)
{
return false;
}
Expand Down Expand Up @@ -128,7 +128,7 @@ private async Task EnumerateDatabaseAsync(

var base64Data = await ExecutePowerShellAsync(script, cancellationToken);

if (string.IsNullOrWhiteSpace(base64Data) || base64Data.Contains("Error", StringComparison.OrdinalIgnoreCase))
if (string.IsNullOrWhiteSpace(base64Data) || base64Data.IndexOf("Error", StringComparison.OrdinalIgnoreCase) >= 0)
{
_logger.LogDebug("No data returned for {Database}", databaseName);
return;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.10" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SecureBootWatcher.LinuxClient\SecureBootWatcher.LinuxClient.csproj" />
<ProjectReference Include="..\SecureBootWatcher.Shared\SecureBootWatcher.Shared.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SecureBootWatcher.LinuxClient.Services;
using SecureBootWatcher.Shared.Models;

namespace SecureBootWatcher.LinuxClient.Tests.Services;

public class LinuxRegistrySnapshotProviderTests
{
[Fact]
public async Task CaptureAsync_ShouldReturnSnapshot()
{
// Arrange
var logger = NullLogger<LinuxRegistrySnapshotProvider>.Instance;
var provider = new LinuxRegistrySnapshotProvider(logger);

// Act
var result = await provider.CaptureAsync(CancellationToken.None);

// Assert
Assert.NotNull(result);
Assert.True(result.CollectedAtUtc <= DateTimeOffset.UtcNow);
Assert.True(result.CollectedAtUtc > DateTimeOffset.UtcNow.AddMinutes(-1));
}

[Fact]
public async Task CaptureAsync_ShouldSetDeploymentState()
{
// Arrange
var logger = NullLogger<LinuxRegistrySnapshotProvider>.Instance;
var provider = new LinuxRegistrySnapshotProvider(logger);

// Act
var result = await provider.CaptureAsync(CancellationToken.None);

// Assert
Assert.NotEqual(SecureBootDeploymentState.NotStarted, result.DeploymentState);
// Linux systems will return Unknown (no EFI vars path) or Unknown (EFI vars exist)
// since UEFI CA 2023 tracking is Windows-specific
}

[Fact]
public async Task CaptureAsync_WithCancellationToken_ShouldComplete()
{
// Arrange
var logger = NullLogger<LinuxRegistrySnapshotProvider>.Instance;
var provider = new LinuxRegistrySnapshotProvider(logger);
using var cts = new CancellationTokenSource();

// Act
var result = await provider.CaptureAsync(cts.Token);

// Assert
Assert.NotNull(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SecureBootWatcher.Shared.Configuration;

namespace SecureBootWatcher.LinuxClient.Configuration
{
internal static class ConfigurationExtensions
{
public static IServiceCollection AddSecureBootWatcherOptions(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<SecureBootWatcherOptions>(configuration.GetSection("SecureBootWatcher"));
return services;
}
}
}
Loading
Loading