Introduces CGS determinism test runs to CI workflows for Windows, macOS, Linux, Alpine, and Debian, fulfilling CGS-008 cross-platform requirements. Updates local-ci scripts to support new smoke steps, test timeouts, progress intervals, and project slicing for improved test isolation and diagnostics.
176 lines
6.6 KiB
C#
176 lines
6.6 KiB
C#
// -----------------------------------------------------------------------------
|
|
// VerdictServiceCollectionExtensions.cs
|
|
// Sprint: SPRINT_20251229_001_001_BE_cgs_infrastructure (CGS-007)
|
|
// Task: Wire Fulcio keyless signing for VerdictBuilder
|
|
// Description: DI extensions for VerdictBuilder with optional keyless signing
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Signer.Core;
|
|
using StellaOps.Signer.Keyless;
|
|
|
|
namespace StellaOps.Verdict;
|
|
|
|
/// <summary>
|
|
/// Dependency injection extensions for the Verdict Builder service.
|
|
/// </summary>
|
|
public static class VerdictServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Adds the Verdict Builder service to the DI container.
|
|
/// Signing mode is determined from configuration (VerdictBuilder:SigningMode).
|
|
/// </summary>
|
|
/// <param name="services">Service collection</param>
|
|
/// <param name="configuration">Configuration root</param>
|
|
/// <returns>Service collection for chaining</returns>
|
|
public static IServiceCollection AddVerdictBuilder(
|
|
this IServiceCollection services,
|
|
IConfiguration configuration)
|
|
{
|
|
// Bind configuration
|
|
services.Configure<VerdictBuilderOptions>(configuration.GetSection("VerdictBuilder"));
|
|
|
|
// Validate options on startup
|
|
services.AddSingleton<IValidateOptions<VerdictBuilderOptions>, VerdictBuilderOptionsValidator>();
|
|
|
|
// Register VerdictBuilder with conditional signer based on mode
|
|
services.AddSingleton<IVerdictBuilder>(sp =>
|
|
{
|
|
var options = sp.GetRequiredService<IOptions<VerdictBuilderOptions>>().Value;
|
|
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VerdictBuilderService>>();
|
|
|
|
IDsseSigner? signer = null;
|
|
|
|
if (options.SigningMode == VerdictSigningMode.Keyless)
|
|
{
|
|
logger.LogInformation(
|
|
"VerdictBuilder configured for Keyless signing (Fulcio: {FulcioUrl}, OIDC: {OidcIssuer})",
|
|
options.FulcioUrl,
|
|
options.OidcIssuerUrl);
|
|
|
|
signer = sp.GetRequiredService<KeylessDsseSigner>();
|
|
}
|
|
else if (options.SigningMode == VerdictSigningMode.AirGap)
|
|
{
|
|
logger.LogInformation("VerdictBuilder configured for AirGap mode (unsigned verdicts)");
|
|
}
|
|
else
|
|
{
|
|
logger.LogWarning("VerdictBuilder SigningMode={Mode} not supported, falling back to AirGap", options.SigningMode);
|
|
}
|
|
|
|
return new VerdictBuilderService(logger, signer);
|
|
});
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the Verdict Builder service with keyless signing support.
|
|
/// Requires Fulcio and OIDC configuration.
|
|
/// </summary>
|
|
/// <param name="services">Service collection</param>
|
|
/// <param name="configuration">Configuration root</param>
|
|
/// <returns>Service collection for chaining</returns>
|
|
public static IServiceCollection AddVerdictBuilderWithKeylessSigning(
|
|
this IServiceCollection services,
|
|
IConfiguration configuration)
|
|
{
|
|
// Add keyless signing infrastructure
|
|
services.AddKeylessSigning(configuration);
|
|
|
|
// Add verdict builder
|
|
services.AddVerdictBuilder(configuration);
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the Verdict Builder service for air-gapped deployments (no signing).
|
|
/// </summary>
|
|
/// <param name="services">Service collection</param>
|
|
/// <returns>Service collection for chaining</returns>
|
|
public static IServiceCollection AddVerdictBuilderAirGap(this IServiceCollection services)
|
|
{
|
|
// Configure for air-gap mode
|
|
services.Configure<VerdictBuilderOptions>(options =>
|
|
{
|
|
options.SigningMode = VerdictSigningMode.AirGap;
|
|
});
|
|
|
|
// Register VerdictBuilder without signer
|
|
services.AddSingleton<IVerdictBuilder>(sp =>
|
|
{
|
|
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VerdictBuilderService>>();
|
|
return new VerdictBuilderService(logger, signer: null);
|
|
});
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds keyless signing infrastructure (Fulcio client, OIDC token provider).
|
|
/// </summary>
|
|
private static IServiceCollection AddKeylessSigning(
|
|
this IServiceCollection services,
|
|
IConfiguration configuration)
|
|
{
|
|
// Use existing Signer.Keyless infrastructure
|
|
services.AddKeylessSigning(configuration.GetSection("VerdictBuilder:Keyless"));
|
|
|
|
// Register OIDC token provider for ambient tokens (CI runners, workload identity)
|
|
// This is environment-specific and needs to be configured based on deployment
|
|
services.AddSingleton<IOidcTokenProvider>(sp =>
|
|
{
|
|
var logger = sp.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AmbientOidcTokenProvider>>();
|
|
|
|
// Check for Gitea/GitHub Actions token
|
|
var giteaToken = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN");
|
|
var githubToken = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN");
|
|
|
|
// Check for Google Cloud workload identity
|
|
var googleToken = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
|
|
|
|
// Default to file-based ambient token
|
|
var tokenPath = Environment.GetEnvironmentVariable("OIDC_TOKEN_FILE")
|
|
?? "/var/run/sigstore/cosign/oidc-token";
|
|
|
|
var config = new OidcAmbientConfig
|
|
{
|
|
Issuer = configuration["VerdictBuilder:OidcIssuerUrl"] ?? "https://token.actions.githubusercontent.com",
|
|
TokenPath = tokenPath,
|
|
WatchForChanges = true
|
|
};
|
|
|
|
return new AmbientOidcTokenProvider(config, logger);
|
|
});
|
|
|
|
// Register KeylessDsseSigner
|
|
services.AddSingleton<KeylessDsseSigner>();
|
|
|
|
return services;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates VerdictBuilderOptions on startup.
|
|
/// </summary>
|
|
internal sealed class VerdictBuilderOptionsValidator : IValidateOptions<VerdictBuilderOptions>
|
|
{
|
|
public ValidateOptionsResult Validate(string? name, VerdictBuilderOptions options)
|
|
{
|
|
try
|
|
{
|
|
options.Validate();
|
|
return ValidateOptionsResult.Success;
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return ValidateOptionsResult.Fail(ex.Message);
|
|
}
|
|
}
|
|
}
|