Files
git.stella-ops.org/src/Signals/__Libraries/StellaOps.Signals.Ebpf/ServiceCollectionExtensions.cs
2026-02-01 21:37:40 +02:00

191 lines
6.6 KiB
C#

// <copyright file="ServiceCollectionExtensions.cs" company="StellaOps">
// SPDX-License-Identifier: BUSL-1.1
// </copyright>
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using StellaOps.Signals.Ebpf.Cgroup;
using StellaOps.Signals.Ebpf.Output;
using StellaOps.Signals.Ebpf.Parsers;
using StellaOps.Signals.Ebpf.Probes;
using StellaOps.Signals.Ebpf.Services;
using StellaOps.Signals.Ebpf.Symbols;
namespace StellaOps.Signals.Ebpf;
/// <summary>
/// DI registration extensions for eBPF runtime evidence collection.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds eBPF runtime evidence collection services.
/// </summary>
/// <param name="services">The service collection.</param>
/// <param name="configureOptions">Optional configuration callback.</param>
/// <returns>The service collection for chaining.</returns>
public static IServiceCollection AddEbpfRuntimeEvidence(
this IServiceCollection services,
Action<EbpfEvidenceOptions>? configureOptions = null)
{
var options = new EbpfEvidenceOptions();
configureOptions?.Invoke(options);
// Register options
services.AddSingleton(options);
services.AddSingleton(options.WriterOptions);
services.AddSingleton(options.CollectorOptions);
// Register memory cache if not already registered
services.TryAddSingleton<IMemoryCache>(sp =>
new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.SymbolCacheSizeLimit,
}));
// Register symbol resolver
services.AddSingleton<ISymbolResolver>(sp =>
{
var logger = sp.GetRequiredService<ILogger<EnhancedSymbolResolver>>();
var cache = sp.GetRequiredService<IMemoryCache>();
return new EnhancedSymbolResolver(logger, cache, options.ProcRoot);
});
// Register cgroup resolver
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService<ILogger<CgroupContainerResolver>>();
return new CgroupContainerResolver(logger, options.ProcRoot, options.CgroupRoot);
});
// Register event parser
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService<ILogger<EventParser>>();
var symbolResolver = sp.GetRequiredService<ISymbolResolver>();
return new EventParser(logger, symbolResolver);
});
// Register probe loader
services.AddSingleton<IEbpfProbeLoader>(sp =>
{
var logger = sp.GetRequiredService<ILogger<CoreProbeLoader>>();
var symbolResolver = sp.GetRequiredService<ISymbolResolver>();
return new CoreProbeLoader(logger, symbolResolver, options.ProbeDirectory);
});
// Register air-gap probe loader separately (different interface)
if (options.UseAirGapMode)
{
services.AddSingleton<IAirGapProbeLoader>(sp =>
{
var airGapLogger = sp.GetRequiredService<ILogger<AirGapProbeLoader>>();
return new AirGapProbeLoader(airGapLogger);
});
}
// Register NDJSON writer
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService<ILogger<RuntimeEvidenceNdjsonWriter>>();
return new RuntimeEvidenceNdjsonWriter(
logger,
options.OutputDirectory,
options.WriterOptions);
});
// Register the unified evidence collector
services.AddSingleton(sp =>
{
var logger = sp.GetRequiredService<ILogger<RuntimeEvidenceCollector>>();
var probeLoader = sp.GetRequiredService<IEbpfProbeLoader>();
var eventParser = sp.GetRequiredService<EventParser>();
var cgroupResolver = sp.GetRequiredService<CgroupContainerResolver>();
var writer = sp.GetRequiredService<RuntimeEvidenceNdjsonWriter>();
return new RuntimeEvidenceCollector(
logger,
probeLoader,
eventParser,
cgroupResolver,
writer,
options.CollectorOptions);
});
// Register the legacy IRuntimeSignalCollector adapter
services.AddSingleton<IRuntimeSignalCollector>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RuntimeSignalCollector>>();
var probeLoader = sp.GetRequiredService<IEbpfProbeLoader>();
return new RuntimeSignalCollector(logger, probeLoader);
});
return services;
}
/// <summary>
/// Adds eBPF runtime evidence collection with air-gap mode enabled.
/// </summary>
/// <remarks>
/// Air-gap mode uses offline probe loading without network dependencies.
/// </remarks>
public static IServiceCollection AddEbpfRuntimeEvidenceAirGap(
this IServiceCollection services,
Action<EbpfEvidenceOptions>? configureOptions = null)
{
return services.AddEbpfRuntimeEvidence(options =>
{
options.UseAirGapMode = true;
configureOptions?.Invoke(options);
});
}
}
/// <summary>
/// Options for eBPF evidence collection services.
/// </summary>
public sealed class EbpfEvidenceOptions
{
/// <summary>
/// Path to the /proc filesystem (default: /proc).
/// </summary>
public string ProcRoot { get; set; } = "/proc";
/// <summary>
/// Path to the cgroup filesystem (default: /sys/fs/cgroup).
/// </summary>
public string CgroupRoot { get; set; } = "/sys/fs/cgroup";
/// <summary>
/// Directory containing compiled BPF probe objects.
/// </summary>
public string? ProbeDirectory { get; set; }
/// <summary>
/// Directory for NDJSON evidence output.
/// </summary>
public string OutputDirectory { get; set; } = "/var/lib/stellaops/evidence";
/// <summary>
/// Whether to use air-gap mode (offline probe loading).
/// </summary>
public bool UseAirGapMode { get; set; }
/// <summary>
/// Maximum size of the symbol resolution cache.
/// </summary>
public long SymbolCacheSizeLimit { get; set; } = 100000;
/// <summary>
/// NDJSON writer options.
/// </summary>
public NdjsonWriterOptions WriterOptions { get; set; } = new();
/// <summary>
/// Collector options.
/// </summary>
public RuntimeEvidenceCollectorOptions CollectorOptions { get; set; } = new();
}