feat: Initialize Zastava Webhook service with TLS and Authority authentication

- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint.
- Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately.
- Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly.
- Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
This commit is contained in:
2025-10-19 18:36:22 +03:00
parent 7e2fa0a42a
commit 5ce40d2eeb
966 changed files with 91038 additions and 1850 deletions

View File

@@ -0,0 +1,80 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Zastava.Webhook.Certificates;
using StellaOps.Zastava.Webhook.Configuration;
using Xunit;
namespace StellaOps.Zastava.Webhook.Tests.Certificates;
public sealed class SecretFileCertificateSourceTests
{
[Fact]
public void LoadCertificate_FromPemPair_Succeeds()
{
using var rsa = RSA.Create(2048);
var request = new CertificateRequest("CN=zastava-webhook", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
using var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddMinutes(-5), DateTimeOffset.UtcNow.AddHours(1));
using var certificateWithKey = certificate.CopyWithPrivateKey(rsa);
var certificatePath = Path.GetTempFileName();
var privateKeyPath = Path.GetTempFileName();
try
{
File.WriteAllText(certificatePath, certificateWithKey.ExportCertificatePem());
using var exportRsa = certificateWithKey.GetRSAPrivateKey() ?? throw new InvalidOperationException("Missing RSA private key");
var privateKeyPem = PemEncoding.Write("PRIVATE KEY", exportRsa.ExportPkcs8PrivateKey());
File.WriteAllText(privateKeyPath, privateKeyPem);
var source = new SecretFileCertificateSource(NullLogger<SecretFileCertificateSource>.Instance);
var options = new ZastavaWebhookTlsOptions
{
Mode = ZastavaWebhookTlsMode.Secret,
CertificatePath = certificatePath,
PrivateKeyPath = privateKeyPath
};
using var loaded = source.LoadCertificate(options);
Assert.Equal(certificateWithKey.Thumbprint, loaded.Thumbprint);
Assert.NotNull(loaded.GetRSAPrivateKey());
}
finally
{
File.Delete(certificatePath);
File.Delete(privateKeyPath);
}
}
[Fact]
public void LoadCertificate_FromPfx_Succeeds()
{
using var rsa = RSA.Create(2048);
var request = new CertificateRequest("CN=zastava-webhook", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
using var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddMinutes(-5), DateTimeOffset.UtcNow.AddHours(1));
using var certificateWithKey = certificate.CopyWithPrivateKey(rsa);
var pfxPath = Path.GetTempFileName();
try
{
var pfxBytes = certificateWithKey.Export(X509ContentType.Pfx, "test");
File.WriteAllBytes(pfxPath, pfxBytes);
var source = new SecretFileCertificateSource(NullLogger<SecretFileCertificateSource>.Instance);
var options = new ZastavaWebhookTlsOptions
{
Mode = ZastavaWebhookTlsMode.Secret,
PfxPath = pfxPath,
PfxPassword = "test"
};
using var loaded = source.LoadCertificate(options);
Assert.Equal(certificateWithKey.Thumbprint, loaded.Thumbprint);
}
finally
{
File.Delete(pfxPath);
}
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Zastava.Webhook.Certificates;
using StellaOps.Zastava.Webhook.Configuration;
using Xunit;
namespace StellaOps.Zastava.Webhook.Tests.Certificates;
public sealed class WebhookCertificateProviderTests
{
[Fact]
public void Provider_UsesMatchingSource()
{
var options = Options.Create(new ZastavaWebhookOptions
{
Tls = new ZastavaWebhookTlsOptions
{
Mode = ZastavaWebhookTlsMode.Secret,
CertificatePath = "/tmp/cert.pem",
PrivateKeyPath = "/tmp/key.pem"
}
});
var source = new ThrowingCertificateSource();
var provider = new WebhookCertificateProvider(options, new[] { source }, NullLogger<WebhookCertificateProvider>.Instance);
Assert.Throws<InvalidOperationException>(() => provider.GetCertificate());
Assert.True(source.Requested);
}
private sealed class ThrowingCertificateSource : IWebhookCertificateSource
{
public bool Requested { get; private set; }
public bool CanHandle(ZastavaWebhookTlsMode mode) => true;
public System.Security.Cryptography.X509Certificates.X509Certificate2 LoadCertificate(ZastavaWebhookTlsOptions options)
{
Requested = true;
throw new InvalidOperationException("test");
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.Zastava.Webhook\StellaOps.Zastava.Webhook.csproj" />
</ItemGroup>
</Project>