diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptions.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptions.cs index f5caf1f..f3d41e5 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptions.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptions.cs @@ -43,4 +43,7 @@ public sealed class GatewayOptions /// behaviour (alarms disabled). /// public AlarmsOptions Alarms { get; init; } = new(); + + /// Gets self-signed TLS certificate auto-generation options. + public TlsOptions Tls { get; init; } = new(); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/TlsOptions.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/TlsOptions.cs new file mode 100644 index 0000000..9be84a3 --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/TlsOptions.cs @@ -0,0 +1,22 @@ +namespace ZB.MOM.WW.MxGateway.Server.Configuration; + +/// +/// Options controlling the gateway's self-signed certificate auto-generation. +/// Only consulted when a Kestrel HTTPS endpoint is configured without its own +/// certificate; plaintext deployments never trigger generation. +/// +public sealed class TlsOptions +{ + /// Path to the persisted self-signed PFX. Reused across restarts. + public string SelfSignedCertPath { get; init; } = + @"C:\ProgramData\MxGateway\certs\gateway-selfsigned.pfx"; + + /// Lifetime in years of a freshly generated certificate. + public int ValidityYears { get; init; } = 10; + + /// Extra DNS SANs to embed (e.g. a load-balancer name). + public IReadOnlyList AdditionalDnsNames { get; init; } = []; + + /// Regenerate the persisted certificate when it has expired. + public bool RegenerateIfExpired { get; init; } = true; +} diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs new file mode 100644 index 0000000..796a4ca --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Configuration; +using ZB.MOM.WW.MxGateway.Server.Configuration; +using Xunit; + +namespace ZB.MOM.WW.MxGateway.Tests.Configuration; + +public sealed class TlsOptionsBindingTests +{ + [Fact] + public void Defaults_AreApplied_WhenSectionAbsent() + { + TlsOptions options = new(); + Assert.Equal(10, options.ValidityYears); + Assert.True(options.RegenerateIfExpired); + Assert.Empty(options.AdditionalDnsNames); + Assert.False(string.IsNullOrWhiteSpace(options.SelfSignedCertPath)); + } + + [Fact] + public void Binds_FromMxGatewayTlsSection() + { + IConfiguration config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["MxGateway:Tls:ValidityYears"] = "5", + ["MxGateway:Tls:SelfSignedCertPath"] = @"C:\tmp\gw.pfx", + ["MxGateway:Tls:RegenerateIfExpired"] = "false", + ["MxGateway:Tls:AdditionalDnsNames:0"] = "gw.internal", + }) + .Build(); + + GatewayOptions options = config.GetSection(GatewayOptions.SectionName).Get()!; + + Assert.Equal(5, options.Tls.ValidityYears); + Assert.Equal(@"C:\tmp\gw.pfx", options.Tls.SelfSignedCertPath); + Assert.False(options.Tls.RegenerateIfExpired); + Assert.Equal("gw.internal", Assert.Single(options.Tls.AdditionalDnsNames)); + } +}