This commit is contained in:
StellaOps Bot
2025-11-23 23:40:10 +02:00
parent c13355923f
commit 029002ad05
93 changed files with 2160 additions and 285 deletions

View File

@@ -29,38 +29,45 @@ public static class ServiceCollectionExtensions
var env = sp.GetRequiredService<ISurfaceEnvironment>();
var options = sp.GetRequiredService<IOptions<SurfaceSecretsOptions>>().Value;
var logger = sp.GetRequiredService<ILoggerFactory>().CreateLogger("SurfaceSecrets");
return CreateProvider(env.Settings.Secrets, logger);
return CreateProviderChain(env.Settings.Secrets, logger);
});
return services;
}
private static ISurfaceSecretProvider CreateProvider(SurfaceSecretsConfiguration configuration, ILogger logger)
private static ISurfaceSecretProvider CreateProviderChain(SurfaceSecretsConfiguration configuration, ILogger logger)
{
var providers = new List<ISurfaceSecretProvider>();
switch (configuration.Provider.ToLowerInvariant())
var providers = new List<ISurfaceSecretProvider>
{
case "kubernetes":
providers.Add(new KubernetesSurfaceSecretProvider(configuration, logger));
break;
case "file":
providers.Add(new FileSurfaceSecretProvider(configuration.Root ?? throw new ArgumentException("Secrets root is required for file provider.")));
break;
case "inline":
providers.Add(new InlineSurfaceSecretProvider(configuration));
break;
default:
logger.LogWarning("Unknown surface secret provider '{Provider}'. Falling back to inline provider.", configuration.Provider);
providers.Add(new InlineSurfaceSecretProvider(configuration));
break;
}
CreateProvider(configuration.Provider, configuration, logger)
};
if (!string.IsNullOrWhiteSpace(configuration.FallbackProvider))
if (configuration.HasFallback)
{
providers.Add(new InlineSurfaceSecretProvider(configuration with { Provider = configuration.FallbackProvider }));
providers.Add(CreateProvider(configuration.FallbackProvider!, configuration, logger));
}
return providers.Count == 1 ? providers[0] : new CompositeSurfaceSecretProvider(providers);
}
private static ISurfaceSecretProvider CreateProvider(string providerId, SurfaceSecretsConfiguration configuration, ILogger logger)
{
if (string.IsNullOrWhiteSpace(providerId))
{
throw new ArgumentException("Provider id is required", nameof(providerId));
}
switch (providerId.Trim().ToLowerInvariant())
{
case "kubernetes":
return new KubernetesSurfaceSecretProvider(configuration, logger);
case "file":
return new FileSurfaceSecretProvider(configuration.Root ?? throw new ArgumentException("Secrets root is required for file provider."));
case "inline":
return new InlineSurfaceSecretProvider(configuration);
default:
logger.LogWarning("Unknown surface secret provider '{Provider}'. Falling back to inline provider if allowed; otherwise requests will fail.", providerId);
return new InlineSurfaceSecretProvider(configuration);
}
}
}

View File

@@ -8,6 +8,7 @@ public static class SurfaceValidationIssueCodes
public const string CacheQuotaInvalid = "SURFACE_ENV_CACHE_QUOTA_INVALID";
public const string SecretsProviderUnknown = "SURFACE_SECRET_PROVIDER_UNKNOWN";
public const string SecretsConfigurationMissing = "SURFACE_SECRET_CONFIGURATION_MISSING";
public const string SecretsConfigurationInvalid = "SURFACE_SECRET_FORMAT_INVALID";
public const string TenantMissing = "SURFACE_ENV_TENANT_MISSING";
public const string BucketMissing = "SURFACE_FS_BUCKET_MISSING";
public const string FeatureUnknown = "SURFACE_FEATURE_UNKNOWN";

View File

@@ -35,6 +35,14 @@ internal sealed class SurfaceSecretsValidator : ISurfaceValidator
"Set SCANNER_SURFACE_SECRETS_PROVIDER to 'kubernetes', 'file', or another supported provider."));
}
if (secrets.HasFallback && !KnownProviders.Contains(secrets.FallbackProvider!))
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsProviderUnknown,
$"Fallback secrets provider '{secrets.FallbackProvider}' is not recognised.",
"Choose a supported fallback provider (kubernetes | file | inline) or clear SCANNER_SURFACE_SECRETS_FALLBACK_PROVIDER."));
}
if (string.Equals(secrets.Provider, "kubernetes", StringComparison.OrdinalIgnoreCase) &&
string.IsNullOrWhiteSpace(secrets.Namespace))
{
@@ -53,6 +61,24 @@ internal sealed class SurfaceSecretsValidator : ISurfaceValidator
"Set SCANNER_SURFACE_SECRETS_ROOT to a directory path."));
}
if (string.Equals(secrets.Provider, "file", StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrWhiteSpace(secrets.Root) &&
!Directory.Exists(secrets.Root))
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsConfigurationInvalid,
$"File secrets root '{secrets.Root}' does not exist.",
"Ensure SCANNER_SURFACE_SECRETS_ROOT points to an existing directory with 0600-style permissions."));
}
if (string.Equals(secrets.Provider, "inline", StringComparison.OrdinalIgnoreCase) && !secrets.AllowInline)
{
issues.Add(SurfaceValidationIssue.Error(
SurfaceValidationIssueCodes.SecretsConfigurationInvalid,
"Inline secrets provider is selected but AllowInline=false.",
"Either enable SCANNER_SURFACE_SECRETS_ALLOW_INLINE for dev/test or switch provider."));
}
if (string.IsNullOrWhiteSpace(secrets.Tenant))
{
issues.Add(SurfaceValidationIssue.Error(