feat(secrets): add ZB.MOM.WW.Secrets refs + pre-host ${secret:} expander (Tasks 1-2)
Adopt the ZB.MOM.WW.Secrets library (0.1.2, Gitea feed) into OtOpcUa under CPM:
- Directory.Packages.props + NuGet.config source mapping for the three packages
- Host/AdminUI/Driver.Galaxy.Contracts/Driver.OpcUaClient package references
- Layer-A pre-host ${secret:} config expander in Host/Program.cs, placed after all
config providers assemble and before the first config read (mechanism only,
behavior-neutral no-op until Task 4 introduces tokens)
- Secrets section in appsettings.json (env-var KEK, SQLite store, 30s cache TTL)
Mechanism mirrors the proven HistorianGateway adoption. KEK never committed.
This commit is contained in:
@@ -132,6 +132,9 @@
|
|||||||
<PackageVersion Include="ZB.MOM.WW.Auth.Abstractions" Version="0.1.1" />
|
<PackageVersion Include="ZB.MOM.WW.Auth.Abstractions" Version="0.1.1" />
|
||||||
<PackageVersion Include="ZB.MOM.WW.Auth.Ldap" Version="0.1.1" />
|
<PackageVersion Include="ZB.MOM.WW.Auth.Ldap" Version="0.1.1" />
|
||||||
<PackageVersion Include="ZB.MOM.WW.Auth.AspNetCore" Version="0.1.1" />
|
<PackageVersion Include="ZB.MOM.WW.Auth.AspNetCore" Version="0.1.1" />
|
||||||
|
<PackageVersion Include="ZB.MOM.WW.Secrets" Version="0.1.2" />
|
||||||
|
<PackageVersion Include="ZB.MOM.WW.Secrets.Abstractions" Version="0.1.2" />
|
||||||
|
<PackageVersion Include="ZB.MOM.WW.Secrets.Ui" Version="0.1.2" />
|
||||||
<PackageVersion Include="ZB.MOM.WW.Audit" Version="0.1.0" />
|
<PackageVersion Include="ZB.MOM.WW.Audit" Version="0.1.0" />
|
||||||
<PackageVersion Include="ZB.MOM.WW.Theme" Version="0.3.1" />
|
<PackageVersion Include="ZB.MOM.WW.Theme" Version="0.3.1" />
|
||||||
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="0.3.0" />
|
<PackageVersion Include="ZB.MOM.WW.HistorianGateway.Client" Version="0.3.0" />
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
<package pattern="ZB.MOM.WW.Theme" />
|
<package pattern="ZB.MOM.WW.Theme" />
|
||||||
<package pattern="ZB.MOM.WW.HistorianGateway.Contracts" />
|
<package pattern="ZB.MOM.WW.HistorianGateway.Contracts" />
|
||||||
<package pattern="ZB.MOM.WW.HistorianGateway.Client" />
|
<package pattern="ZB.MOM.WW.HistorianGateway.Client" />
|
||||||
|
<package pattern="ZB.MOM.WW.Secrets" />
|
||||||
|
<package pattern="ZB.MOM.WW.Secrets.*" />
|
||||||
</packageSource>
|
</packageSource>
|
||||||
</packageSourceMapping>
|
</packageSourceMapping>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
+1
@@ -13,5 +13,6 @@
|
|||||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Abstractions.csproj" />
|
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Abstractions.csproj" />
|
||||||
<!-- Logging abstraction needed by GalaxySecretRef.ResolveApiKey's optional warning logger. -->
|
<!-- Logging abstraction needed by GalaxySecretRef.ResolveApiKey's optional warning logger. -->
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
|
<PackageReference Include="ZB.MOM.WW.Secrets.Abstractions" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+1
@@ -21,6 +21,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client"/>
|
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client"/>
|
||||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration"/>
|
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration"/>
|
||||||
|
<PackageReference Include="ZB.MOM.WW.Secrets.Abstractions"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client"/>
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client"/>
|
||||||
<PackageReference Include="ZB.MOM.WW.Theme"/>
|
<PackageReference Include="ZB.MOM.WW.Theme"/>
|
||||||
|
<PackageReference Include="ZB.MOM.WW.Secrets.Ui"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ using ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
|||||||
using ZB.MOM.WW.Configuration;
|
using ZB.MOM.WW.Configuration;
|
||||||
using ZB.MOM.WW.Telemetry.Serilog;
|
using ZB.MOM.WW.Telemetry.Serilog;
|
||||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Api;
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Api;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using ZB.MOM.WW.Secrets.Abstractions;
|
||||||
|
using ZB.MOM.WW.Secrets.Configuration;
|
||||||
|
using ZB.MOM.WW.Secrets.DependencyInjection;
|
||||||
|
using ZB.MOM.WW.Secrets.Sqlite;
|
||||||
|
|
||||||
// Roles drive the entire conditional wiring below — see ZB.MOM.WW.OtOpcUa.Cluster.RoleParser.
|
// Roles drive the entire conditional wiring below — see ZB.MOM.WW.OtOpcUa.Cluster.RoleParser.
|
||||||
var roles = RoleParser.Parse(Environment.GetEnvironmentVariable("OTOPCUA_ROLES"));
|
var roles = RoleParser.Parse(Environment.GetEnvironmentVariable("OTOPCUA_ROLES"));
|
||||||
@@ -72,6 +77,27 @@ if (roleSuffix is not null)
|
|||||||
builder.Configuration.AddCommandLine(args);
|
builder.Configuration.AddCommandLine(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-host ${secret:} expansion: rewrites ${secret:name} tokens in the assembled configuration
|
||||||
|
// BEFORE the host is built, so every downstream binder/validator sees resolved plaintext.
|
||||||
|
// Placement matters: this runs AFTER all config providers are assembled (base appsettings, the
|
||||||
|
// role overlay, and the env-vars + command-line re-append above) but BEFORE anything READS a
|
||||||
|
// config value (AddZbSerilog's ReadFrom.Configuration, AddOtOpcUaConfigDb, and the first
|
||||||
|
// ValidateOnStart bind all follow). With no ${secret:} tokens present this is a no-op pass;
|
||||||
|
// it also migrates the secrets SQLite store on startup.
|
||||||
|
// ASP0000: DELIBERATE throwaway container — expansion must run before builder.Build(), so it
|
||||||
|
// cannot use the app's DI. Fully disposed here; shares no singletons with the host container.
|
||||||
|
#pragma warning disable ASP0000
|
||||||
|
await using (var secretsProvider = new ServiceCollection()
|
||||||
|
.AddZbSecrets(builder.Configuration, "Secrets")
|
||||||
|
.BuildServiceProvider())
|
||||||
|
#pragma warning restore ASP0000
|
||||||
|
{
|
||||||
|
await secretsProvider.GetRequiredService<SqliteSecretsStoreMigrator>().MigrateAsync(default);
|
||||||
|
var resolver = secretsProvider.GetRequiredService<ISecretResolver>();
|
||||||
|
await new SecretReferenceExpander(resolver)
|
||||||
|
.ExpandConfigurationAsync((IConfigurationRoot)builder.Configuration, default);
|
||||||
|
}
|
||||||
|
|
||||||
// Anchor the process working directory to the install directory (AppContext.BaseDirectory) so every
|
// Anchor the process working directory to the install directory (AppContext.BaseDirectory) so every
|
||||||
// relative runtime path resolves under the install dir rather than the service's startup CWD. A Windows
|
// relative runtime path resolves under the install dir rather than the service's startup CWD. A Windows
|
||||||
// service starts with CWD=C:\Windows\System32, which otherwise scattered the Serilog rolling-file sink
|
// service starts with CWD=C:\Windows\System32, which otherwise scattered the Serilog rolling-file sink
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
<PackageReference Include="ZB.MOM.WW.Telemetry" />
|
<PackageReference Include="ZB.MOM.WW.Telemetry" />
|
||||||
<PackageReference Include="ZB.MOM.WW.Telemetry.Serilog" />
|
<PackageReference Include="ZB.MOM.WW.Telemetry.Serilog" />
|
||||||
<PackageReference Include="ZB.MOM.WW.Configuration" />
|
<PackageReference Include="ZB.MOM.WW.Configuration" />
|
||||||
|
<PackageReference Include="ZB.MOM.WW.Secrets" />
|
||||||
|
<PackageReference Include="ZB.MOM.WW.Secrets.Abstractions" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -11,6 +11,15 @@
|
|||||||
"DisableLogin": false
|
"DisableLogin": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Secrets": {
|
||||||
|
"SqlitePath": "otopcua-secrets.db",
|
||||||
|
"MasterKey": {
|
||||||
|
"Source": "Environment",
|
||||||
|
"EnvVarName": "ZB_SECRETS_MASTER_KEY"
|
||||||
|
},
|
||||||
|
"RunMigrationsOnStartup": true,
|
||||||
|
"ResolveCacheTtl": "00:00:30"
|
||||||
|
},
|
||||||
"ServerHistorian": {
|
"ServerHistorian": {
|
||||||
"_comment": "Server-side HistoryRead backend (the ZB.MOM.WW.HistorianGateway gRPC client). Disabled => NullHistorianDataSource (historized nodes return GoodNoData). The gateway must run RuntimeDb:EventReadsEnabled=true for alarm-history ReadEvents, and the API key must carry historian:read + historian:write + historian:tags:write scopes.",
|
"_comment": "Server-side HistoryRead backend (the ZB.MOM.WW.HistorianGateway gRPC client). Disabled => NullHistorianDataSource (historized nodes return GoodNoData). The gateway must run RuntimeDb:EventReadsEnabled=true for alarm-history ReadEvents, and the API key must carry historian:read + historian:write + historian:tags:write scopes.",
|
||||||
"Enabled": false,
|
"Enabled": false,
|
||||||
|
|||||||
Reference in New Issue
Block a user