84 lines
2.8 KiB
C#
84 lines
2.8 KiB
C#
|
|
using Microsoft.Extensions.Logging;
|
|
using StellaOps.Excititor.Connectors.Abstractions.Trust;
|
|
using StellaOps.Excititor.WebService.Contracts;
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace StellaOps.Excititor.WebService.Services;
|
|
|
|
internal sealed class AirgapSignerTrustService
|
|
{
|
|
private readonly ILogger<AirgapSignerTrustService> _logger;
|
|
private readonly string? _metadataPath;
|
|
private ConnectorSignerMetadataSet? _metadata;
|
|
|
|
public AirgapSignerTrustService(ILogger<AirgapSignerTrustService> logger)
|
|
{
|
|
_logger = logger;
|
|
_metadataPath = Environment.GetEnvironmentVariable("STELLAOPS_CONNECTOR_SIGNER_METADATA_PATH");
|
|
}
|
|
|
|
public bool Validate(AirgapImportRequest request, out string? errorCode, out string? message)
|
|
{
|
|
errorCode = null;
|
|
message = null;
|
|
|
|
if (string.IsNullOrWhiteSpace(_metadataPath) || !File.Exists(_metadataPath))
|
|
{
|
|
_logger.LogDebug("Airgap signer metadata not configured; skipping trust enforcement.");
|
|
return true;
|
|
}
|
|
|
|
_metadata ??= ConnectorSignerMetadataLoader.TryLoad(_metadataPath);
|
|
if (_metadata is null)
|
|
{
|
|
_logger.LogWarning("Failed to load airgap signer metadata from {Path}; allowing import.", _metadataPath);
|
|
return true;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(request.Publisher))
|
|
{
|
|
errorCode = "AIRGAP_SOURCE_UNTRUSTED";
|
|
message = "publisher is required for trust enforcement.";
|
|
return false;
|
|
}
|
|
|
|
if (!_metadata.TryGet(request.Publisher, out var connector))
|
|
{
|
|
errorCode = "AIRGAP_SOURCE_UNTRUSTED";
|
|
message = $"Publisher '{request.Publisher}' is not present in trusted signer metadata.";
|
|
return false;
|
|
}
|
|
|
|
if (connector.Revoked)
|
|
{
|
|
errorCode = "AIRGAP_SOURCE_UNTRUSTED";
|
|
message = $"Publisher '{request.Publisher}' is revoked.";
|
|
return false;
|
|
}
|
|
|
|
if (connector.Bundle?.Digest is { } digest && !string.IsNullOrWhiteSpace(digest))
|
|
{
|
|
if (!string.Equals(digest.Trim(), request.PayloadHash?.Trim(), StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
errorCode = "AIRGAP_PAYLOAD_MISMATCH";
|
|
message = "Payload hash does not match trusted bundle digest.";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Basic sanity: ensure at least one signer entry exists.
|
|
if (connector.Signers.IsDefaultOrEmpty || connector.Signers.Sum(s => s.Fingerprints.Length) == 0)
|
|
{
|
|
errorCode = "AIRGAP_SOURCE_UNTRUSTED";
|
|
message = $"Publisher '{request.Publisher}' has no trusted signers configured.";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|