feat(batch9): implement f3 nats server ocsp wiring
This commit is contained in:
189
dotnet/tests/ZB.MOM.NatsNet.Server.Tests/NatsServerOcspTests.cs
Normal file
189
dotnet/tests/ZB.MOM.NatsNet.Server.Tests/NatsServerOcspTests.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using Shouldly;
|
||||
using ZB.MOM.NatsNet.Server;
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.Tests;
|
||||
|
||||
public sealed class NatsServerOcspTests : IDisposable
|
||||
{
|
||||
private readonly List<string> _tempDirs = [];
|
||||
private readonly List<X509Certificate2> _certs = [];
|
||||
|
||||
[Fact]
|
||||
public void SetupOCSPStapleStoreDir_WithStoreDir_CreatesDirectory()
|
||||
{
|
||||
var dir = MakeTempDir();
|
||||
var server = NewServer(new ServerOptions
|
||||
{
|
||||
StoreDir = dir,
|
||||
});
|
||||
|
||||
var err = server.SetupOCSPStapleStoreDir();
|
||||
|
||||
err.ShouldBeNull();
|
||||
Directory.Exists(Path.Combine(dir, "ocsp")).ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureOCSP_WithTlsConfig_ReturnsClientEntry()
|
||||
{
|
||||
var cert = CreateSelfSignedCertificate("CN=configure-ocsp");
|
||||
var server = NewServer(new ServerOptions
|
||||
{
|
||||
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
||||
});
|
||||
|
||||
var configs = server.ConfigureOCSP();
|
||||
|
||||
configs.Count.ShouldBe(1);
|
||||
configs[0].Kind.ShouldBe("client");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewOCSPMonitor_OcspNever_ReturnsNoMonitor()
|
||||
{
|
||||
var cert = CreateSelfSignedCertificate("CN=monitor-never");
|
||||
var server = NewServer(new ServerOptions
|
||||
{
|
||||
StoreDir = MakeTempDir(),
|
||||
OcspConfig = new OcspConfig { Mode = ZB.MOM.NatsNet.Server.OcspMode.Never },
|
||||
});
|
||||
|
||||
var config = new OcspTlsConfig
|
||||
{
|
||||
Kind = "client",
|
||||
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
||||
TlsOptions = null,
|
||||
Apply = _ => { },
|
||||
};
|
||||
|
||||
var (_, monitor, err) = server.NewOCSPMonitor(config);
|
||||
|
||||
err.ShouldBeNull();
|
||||
monitor.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnableOCSP_WithAlwaysMode_AddsMonitor()
|
||||
{
|
||||
var cert = CreateSelfSignedCertificate("CN=enable-ocsp");
|
||||
var storeDir = MakeTempDir();
|
||||
WriteLocalOcspStatus(storeDir, cert);
|
||||
var server = NewServer(new ServerOptions
|
||||
{
|
||||
StoreDir = storeDir,
|
||||
OcspConfig = new OcspConfig
|
||||
{
|
||||
Mode = ZB.MOM.NatsNet.Server.OcspMode.Always,
|
||||
OverrideUrls = ["https://ocsp.example.test"],
|
||||
},
|
||||
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
||||
});
|
||||
|
||||
var err = server.EnableOCSP();
|
||||
|
||||
err.ShouldBeNull();
|
||||
server.GetOcspMonitors().Length.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartOCSPMonitoring_NoMonitors_DoesNotThrow()
|
||||
{
|
||||
var server = NewServer(new ServerOptions());
|
||||
|
||||
Should.NotThrow(() => server.StartOCSPMonitoring());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReloadOCSP_WithConfiguredTls_ReplacesMonitors()
|
||||
{
|
||||
var cert = CreateSelfSignedCertificate("CN=reload-ocsp");
|
||||
var storeDir = MakeTempDir();
|
||||
WriteLocalOcspStatus(storeDir, cert);
|
||||
var server = NewServer(new ServerOptions
|
||||
{
|
||||
StoreDir = storeDir,
|
||||
OcspConfig = new OcspConfig
|
||||
{
|
||||
Mode = ZB.MOM.NatsNet.Server.OcspMode.Always,
|
||||
OverrideUrls = ["https://ocsp.example.test"],
|
||||
},
|
||||
TlsConfig = new SslServerAuthenticationOptions { ServerCertificate = cert },
|
||||
});
|
||||
server.EnableOCSP().ShouldBeNull();
|
||||
server.GetOcspMonitors().Length.ShouldBe(1);
|
||||
|
||||
var err = server.ReloadOCSP();
|
||||
|
||||
err.ShouldBeNull();
|
||||
server.GetOcspMonitors().Length.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasOCSPStatusRequest_CertificateWithoutExtension_ReturnsFalse()
|
||||
{
|
||||
var cert = CreateSelfSignedCertificate("CN=no-status-request");
|
||||
|
||||
OcspHandler.HasOCSPStatusRequest(cert).ShouldBeFalse();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cert in _certs)
|
||||
{
|
||||
cert.Dispose();
|
||||
}
|
||||
|
||||
foreach (var dir in _tempDirs)
|
||||
{
|
||||
try { Directory.Delete(dir, recursive: true); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private NatsServer NewServer(ServerOptions options)
|
||||
{
|
||||
var (server, err) = NatsServer.NewServer(options);
|
||||
err.ShouldBeNull();
|
||||
return server!;
|
||||
}
|
||||
|
||||
private X509Certificate2 CreateSelfSignedCertificate(string subject)
|
||||
{
|
||||
var req = new CertificateRequest(
|
||||
subject,
|
||||
RSA.Create(2048),
|
||||
HashAlgorithmName.SHA256,
|
||||
RSASignaturePadding.Pkcs1);
|
||||
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true));
|
||||
req.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(req.PublicKey, false));
|
||||
|
||||
var cert = req.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(90));
|
||||
_certs.Add(cert);
|
||||
return cert;
|
||||
}
|
||||
|
||||
private string MakeTempDir()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), "nats-ocsp-" + Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(path);
|
||||
_tempDirs.Add(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private static void WriteLocalOcspStatus(string storeDir, X509Certificate2 cert)
|
||||
{
|
||||
var key = Convert.ToHexString(SHA256.HashData(cert.RawData)).ToLowerInvariant();
|
||||
var ocspDir = Path.Combine(storeDir, "ocsp");
|
||||
Directory.CreateDirectory(ocspDir);
|
||||
var payload = JsonSerializer.SerializeToUtf8Bytes(new
|
||||
{
|
||||
Status = 0,
|
||||
ThisUpdate = DateTime.UtcNow.AddMinutes(-5),
|
||||
NextUpdate = DateTime.UtcNow.AddHours(6),
|
||||
});
|
||||
File.WriteAllBytes(Path.Combine(ocspDir, key), payload);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user