Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
console-runner-image / build-runner-image (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
184 lines
8.1 KiB
C#
184 lines
8.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Cryptography.DependencyInjection;
|
|
using StellaOps.Scanner.Surface.Env;
|
|
using StellaOps.Scanner.Surface.FS;
|
|
using StellaOps.Scanner.Surface.Secrets;
|
|
using StellaOps.Scanner.Surface.Validation;
|
|
using StellaOps.Zastava.Core.Configuration;
|
|
using StellaOps.Zastava.Observer.Backend;
|
|
using StellaOps.Zastava.Observer.Configuration;
|
|
using StellaOps.Zastava.Observer.ContainerRuntime;
|
|
using StellaOps.Zastava.Observer.ContainerRuntime.Cri;
|
|
using StellaOps.Zastava.Observer.Posture;
|
|
using StellaOps.Zastava.Observer.Runtime;
|
|
using StellaOps.Zastava.Observer.Runtime.ProcSnapshot;
|
|
using StellaOps.Zastava.Observer.Secrets;
|
|
using StellaOps.Zastava.Observer.Surface;
|
|
using StellaOps.Zastava.Observer.Worker;
|
|
|
|
namespace Microsoft.Extensions.DependencyInjection;
|
|
|
|
public static class ObserverServiceCollectionExtensions
|
|
{
|
|
public static IServiceCollection AddZastavaObserver(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(services);
|
|
ArgumentNullException.ThrowIfNull(configuration);
|
|
|
|
services.AddZastavaRuntimeCore(configuration, componentName: "observer");
|
|
services.AddStellaOpsCrypto();
|
|
|
|
services.AddOptions<ZastavaObserverOptions>()
|
|
.Bind(configuration.GetSection(ZastavaObserverOptions.SectionName))
|
|
.ValidateDataAnnotations()
|
|
.PostConfigure(options =>
|
|
{
|
|
if (options.Backoff.Initial <= TimeSpan.Zero)
|
|
{
|
|
options.Backoff.Initial = TimeSpan.FromSeconds(1);
|
|
}
|
|
|
|
if (options.Backoff.Max < options.Backoff.Initial)
|
|
{
|
|
options.Backoff.Max = options.Backoff.Initial;
|
|
}
|
|
|
|
if (!options.Backend.AllowInsecureHttp && !string.Equals(options.Backend.BaseAddress.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new InvalidOperationException("Observer backend baseAddress must use HTTPS unless allowInsecureHttp is explicitly enabled.");
|
|
}
|
|
|
|
if (!options.Backend.PolicyPath.StartsWith("/", StringComparison.Ordinal))
|
|
{
|
|
throw new InvalidOperationException("Observer backend policyPath must be absolute (start with '/').");
|
|
}
|
|
|
|
if (!options.Backend.EventsPath.StartsWith("/", StringComparison.Ordinal))
|
|
{
|
|
throw new InvalidOperationException("Observer backend eventsPath must be absolute (start with '/').");
|
|
}
|
|
})
|
|
.ValidateOnStart();
|
|
|
|
services.TryAddSingleton(TimeProvider.System);
|
|
services.TryAddSingleton<ICriRuntimeClientFactory, CriRuntimeClientFactory>();
|
|
services.TryAddSingleton<IRuntimeEventBuffer, RuntimeEventBuffer>();
|
|
services.TryAddSingleton<IRuntimeProcessCollector, RuntimeProcessCollector>();
|
|
services.TryAddSingleton<IProcSnapshotCollector, ProcSnapshotCollector>();
|
|
services.TryAddSingleton<IRuntimePostureCache, RuntimePostureCache>();
|
|
services.TryAddSingleton<IRuntimePostureEvaluator, RuntimePostureEvaluator>();
|
|
services.TryAddSingleton<ContainerStateTrackerFactory>();
|
|
services.TryAddSingleton<ContainerRuntimePoller>();
|
|
|
|
services.AddHttpClient<IRuntimePolicyClient, RuntimePolicyClient>()
|
|
.ConfigureHttpClient((provider, client) =>
|
|
{
|
|
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<ZastavaObserverOptions>>();
|
|
var backend = optionsMonitor.CurrentValue.Backend;
|
|
client.BaseAddress = backend.BaseAddress;
|
|
client.Timeout = TimeSpan.FromSeconds(Math.Clamp(backend.RequestTimeoutSeconds, 1, 120));
|
|
});
|
|
|
|
services.AddHttpClient<IRuntimeEventsClient, RuntimeEventsClient>()
|
|
.ConfigureHttpClient((provider, client) =>
|
|
{
|
|
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<ZastavaObserverOptions>>();
|
|
var backend = optionsMonitor.CurrentValue.Backend;
|
|
client.BaseAddress = backend.BaseAddress;
|
|
client.Timeout = TimeSpan.FromSeconds(Math.Clamp(backend.RequestTimeoutSeconds, 1, 120));
|
|
});
|
|
|
|
services.AddHttpClient<IRuntimeFactsClient, RuntimeFactsClient>()
|
|
.ConfigureHttpClient((provider, client) =>
|
|
{
|
|
var optionsMonitor = provider.GetRequiredService<IOptionsMonitor<ZastavaObserverOptions>>();
|
|
var observer = optionsMonitor.CurrentValue;
|
|
client.Timeout = TimeSpan.FromSeconds(Math.Clamp(observer.Backend.RequestTimeoutSeconds, 1, 120));
|
|
});
|
|
|
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<ZastavaRuntimeOptions>, ObserverRuntimeOptionsPostConfigure>());
|
|
|
|
// Surface environment + cache/manifest/secrets wiring
|
|
services.AddSurfaceEnvironment(options =>
|
|
{
|
|
options.ComponentName = "Zastava.Observer";
|
|
options.AddPrefix("ZASTAVA_OBSERVER");
|
|
options.AddPrefix("ZASTAVA");
|
|
options.KnownFeatureFlags.Add("drift");
|
|
options.KnownFeatureFlags.Add("prefetch");
|
|
options.TenantResolver = sp => sp.GetRequiredService<IOptions<ZastavaRuntimeOptions>>().Value.Tenant;
|
|
});
|
|
|
|
services.AddSurfaceFileCache();
|
|
services.AddSurfaceManifestStore();
|
|
services.AddSurfaceSecrets(options =>
|
|
{
|
|
options.ComponentName = "Zastava.Observer";
|
|
options.RequiredSecretTypes.Add("cas-access");
|
|
options.RequiredSecretTypes.Add("attestation");
|
|
});
|
|
|
|
// Surface validation for preflight checks
|
|
services.AddSurfaceValidation();
|
|
|
|
services.TryAddSingleton(sp => sp.GetRequiredService<ISurfaceEnvironment>().Settings);
|
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<SurfaceCacheOptions>, SurfaceCacheOptionsConfigurator>());
|
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<SurfaceManifestStoreOptions>, SurfaceManifestStoreOptionsConfigurator>());
|
|
|
|
services.TryAddSingleton<IObserverSurfaceSecrets, ObserverSurfaceSecrets>();
|
|
services.TryAddSingleton<IRuntimeSurfaceFsClient, RuntimeSurfaceFsClient>();
|
|
|
|
services.AddHostedService<ObserverBootstrapService>();
|
|
services.AddHostedService<ContainerLifecycleHostedService>();
|
|
services.AddHostedService<RuntimeEventDispatchService>();
|
|
|
|
return services;
|
|
}
|
|
}
|
|
|
|
internal sealed class ObserverRuntimeOptionsPostConfigure : IPostConfigureOptions<ZastavaRuntimeOptions>
|
|
{
|
|
public void PostConfigure(string? name, ZastavaRuntimeOptions options)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(options.Component))
|
|
{
|
|
options.Component = "observer";
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class SurfaceCacheOptionsConfigurator : IConfigureOptions<SurfaceCacheOptions>
|
|
{
|
|
private readonly SurfaceEnvironmentSettings settings;
|
|
|
|
public SurfaceCacheOptionsConfigurator(SurfaceEnvironmentSettings settings)
|
|
{
|
|
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
public void Configure(SurfaceCacheOptions options)
|
|
{
|
|
options.RootDirectory ??= settings.CacheRoot.FullName;
|
|
}
|
|
}
|
|
|
|
internal sealed class SurfaceManifestStoreOptionsConfigurator : IConfigureOptions<SurfaceManifestStoreOptions>
|
|
{
|
|
private readonly SurfaceEnvironmentSettings settings;
|
|
|
|
public SurfaceManifestStoreOptionsConfigurator(SurfaceEnvironmentSettings settings)
|
|
{
|
|
this.settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
|
}
|
|
|
|
public void Configure(SurfaceManifestStoreOptions options)
|
|
{
|
|
options.Bucket = string.IsNullOrWhiteSpace(settings.SurfaceFsBucket) ? options.Bucket : settings.SurfaceFsBucket;
|
|
options.RootDirectory ??= Path.Combine(settings.CacheRoot.FullName, "manifests");
|
|
}
|
|
}
|