Add configurable transport security profiles and bind address
Adds Security section to appsettings.json with configurable OPC UA transport profiles (None, Basic256Sha256-Sign, Basic256Sha256-SignAndEncrypt), certificate policy settings, and a configurable BindAddress for the OPC UA endpoint. Defaults preserve backward compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,5 +34,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
/// Gets or sets the authentication and role-based access control settings.
|
||||
/// </summary>
|
||||
public AuthenticationConfiguration Authentication { get; set; } = new AuthenticationConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transport security settings that control which OPC UA security profiles are exposed.
|
||||
/// </summary>
|
||||
public SecurityProfileConfiguration Security { get; set; } = new SecurityProfileConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
using ZB.MOM.WW.LmxOpcUa.Host.OpcUa;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
{
|
||||
@@ -21,8 +23,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
Log.Information("=== Effective Configuration ===");
|
||||
|
||||
// OPC UA
|
||||
Log.Information("OpcUa.Port={Port}, EndpointPath={EndpointPath}, ServerName={ServerName}, GalaxyName={GalaxyName}",
|
||||
config.OpcUa.Port, config.OpcUa.EndpointPath, config.OpcUa.ServerName, config.OpcUa.GalaxyName);
|
||||
Log.Information("OpcUa.BindAddress={BindAddress}, Port={Port}, EndpointPath={EndpointPath}, ServerName={ServerName}, GalaxyName={GalaxyName}",
|
||||
config.OpcUa.BindAddress, config.OpcUa.Port, config.OpcUa.EndpointPath, config.OpcUa.ServerName, config.OpcUa.GalaxyName);
|
||||
Log.Information("OpcUa.MaxSessions={MaxSessions}, SessionTimeoutMinutes={SessionTimeout}",
|
||||
config.OpcUa.MaxSessions, config.OpcUa.SessionTimeoutMinutes);
|
||||
|
||||
@@ -67,6 +69,41 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
Log.Information("Dashboard.Enabled={Enabled}, Port={Port}, RefreshInterval={Refresh}s",
|
||||
config.Dashboard.Enabled, config.Dashboard.Port, config.Dashboard.RefreshIntervalSeconds);
|
||||
|
||||
// Security
|
||||
Log.Information("Security.Profiles=[{Profiles}], AutoAcceptClientCertificates={AutoAccept}, RejectSHA1={RejectSHA1}, MinKeySize={MinKeySize}",
|
||||
string.Join(", ", config.Security.Profiles), config.Security.AutoAcceptClientCertificates,
|
||||
config.Security.RejectSHA1Certificates, config.Security.MinimumCertificateKeySize);
|
||||
|
||||
if (config.Security.PkiRootPath != null)
|
||||
Log.Information("Security.PkiRootPath={PkiRootPath}", config.Security.PkiRootPath);
|
||||
if (config.Security.CertificateSubject != null)
|
||||
Log.Information("Security.CertificateSubject={CertificateSubject}", config.Security.CertificateSubject);
|
||||
|
||||
var unknownProfiles = config.Security.Profiles
|
||||
.Where(p => !SecurityProfileResolver.ValidProfileNames.Contains(p, System.StringComparer.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
if (unknownProfiles.Count > 0)
|
||||
{
|
||||
Log.Warning("Unknown security profile(s): {Profiles}. Valid values: {ValidProfiles}",
|
||||
string.Join(", ", unknownProfiles), string.Join(", ", SecurityProfileResolver.ValidProfileNames));
|
||||
}
|
||||
|
||||
if (config.Security.MinimumCertificateKeySize < 2048)
|
||||
{
|
||||
Log.Error("Security.MinimumCertificateKeySize must be at least 2048");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (config.Security.AutoAcceptClientCertificates)
|
||||
{
|
||||
Log.Warning("Security.AutoAcceptClientCertificates is enabled — client certificate trust is not enforced. Set to false in production");
|
||||
}
|
||||
|
||||
if (config.Security.Profiles.Count == 1 && config.Security.Profiles[0].Equals("None", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Log.Warning("Only the 'None' security profile is configured — transport security is disabled");
|
||||
}
|
||||
|
||||
Log.Information("=== Configuration {Status} ===", valid ? "Valid" : "INVALID");
|
||||
return valid;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,12 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
/// </summary>
|
||||
public class OpcUaConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the IP address or hostname the OPC UA server binds to.
|
||||
/// Defaults to <c>0.0.0.0</c> (all interfaces). Set to a specific IP or hostname to restrict listening.
|
||||
/// </summary>
|
||||
public string BindAddress { get; set; } = "0.0.0.0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the TCP port on which the OPC UA server listens for client sessions.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Host.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Transport security settings that control which OPC UA security profiles the server exposes and how client certificates are handled.
|
||||
/// </summary>
|
||||
public class SecurityProfileConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the list of security profile names to expose as server endpoints.
|
||||
/// Valid values: "None", "Basic256Sha256-Sign", "Basic256Sha256-SignAndEncrypt".
|
||||
/// Defaults to ["None"] for backward compatibility.
|
||||
/// </summary>
|
||||
public List<string> Profiles { get; set; } = new List<string> { "None" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the server automatically accepts client certificates
|
||||
/// that are not in the trusted store. Should be <see langword="false"/> in production.
|
||||
/// </summary>
|
||||
public bool AutoAcceptClientCertificates { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether client certificates signed with SHA-1 are rejected.
|
||||
/// </summary>
|
||||
public bool RejectSHA1Certificates { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum RSA key size required for client certificates.
|
||||
/// </summary>
|
||||
public int MinimumCertificateKeySize { get; set; } = 2048;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional override for the PKI root directory.
|
||||
/// When <see langword="null"/>, defaults to <c>%LOCALAPPDATA%\OPC Foundation\pki</c>.
|
||||
/// </summary>
|
||||
public string? PkiRootPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional override for the server certificate subject name.
|
||||
/// When <see langword="null"/>, defaults to <c>CN={ServerName}, O=ZB MOM, DC=localhost</c>.
|
||||
/// </summary>
|
||||
public string? CertificateSubject { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
private readonly HistorianDataSource? _historianDataSource;
|
||||
private readonly AuthenticationConfiguration _authConfig;
|
||||
private readonly IUserAuthenticationProvider? _authProvider;
|
||||
private readonly SecurityProfileConfiguration _securityConfig;
|
||||
private ApplicationInstance? _application;
|
||||
private LmxOpcUaServer? _server;
|
||||
|
||||
@@ -52,7 +53,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
public OpcUaServerHost(OpcUaConfiguration config, IMxAccessClient mxAccessClient, PerformanceMetrics metrics,
|
||||
HistorianDataSource? historianDataSource = null,
|
||||
AuthenticationConfiguration? authConfig = null,
|
||||
IUserAuthenticationProvider? authProvider = null)
|
||||
IUserAuthenticationProvider? authProvider = null,
|
||||
SecurityProfileConfiguration? securityConfig = null)
|
||||
{
|
||||
_config = config;
|
||||
_mxAccessClient = mxAccessClient;
|
||||
@@ -60,6 +62,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
_historianDataSource = historianDataSource;
|
||||
_authConfig = authConfig ?? new AuthenticationConfiguration();
|
||||
_authProvider = authProvider;
|
||||
_securityConfig = securityConfig ?? new SecurityProfileConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,63 +72,66 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
{
|
||||
var namespaceUri = $"urn:{_config.GalaxyName}:LmxOpcUa";
|
||||
|
||||
// Resolve configured security profiles
|
||||
var securityPolicies = SecurityProfileResolver.Resolve(_securityConfig.Profiles);
|
||||
foreach (var sp in securityPolicies)
|
||||
{
|
||||
Log.Information("Security profile active: {PolicyUri} / {Mode}", sp.SecurityPolicyUri, sp.SecurityMode);
|
||||
}
|
||||
|
||||
// Build PKI paths
|
||||
var pkiRoot = _securityConfig.PkiRootPath ?? System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"OPC Foundation", "pki");
|
||||
var certSubject = _securityConfig.CertificateSubject ?? $"CN={_config.ServerName}, O=ZB MOM, DC=localhost";
|
||||
|
||||
var serverConfig = new ServerConfiguration
|
||||
{
|
||||
BaseAddresses = { $"opc.tcp://{_config.BindAddress}:{_config.Port}{_config.EndpointPath}" },
|
||||
MaxSessionCount = _config.MaxSessions,
|
||||
MaxSessionTimeout = _config.SessionTimeoutMinutes * 60 * 1000, // ms
|
||||
MinSessionTimeout = 10000,
|
||||
UserTokenPolicies = BuildUserTokenPolicies()
|
||||
};
|
||||
foreach (var policy in securityPolicies)
|
||||
serverConfig.SecurityPolicies.Add(policy);
|
||||
|
||||
var secConfig = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(pkiRoot, "own"),
|
||||
SubjectName = certSubject
|
||||
},
|
||||
TrustedIssuerCertificates = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(pkiRoot, "issuer")
|
||||
},
|
||||
TrustedPeerCertificates = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(pkiRoot, "trusted")
|
||||
},
|
||||
RejectedCertificateStore = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(pkiRoot, "rejected")
|
||||
},
|
||||
AutoAcceptUntrustedCertificates = _securityConfig.AutoAcceptClientCertificates,
|
||||
RejectSHA1SignedCertificates = _securityConfig.RejectSHA1Certificates,
|
||||
MinimumCertificateKeySize = (ushort)_securityConfig.MinimumCertificateKeySize
|
||||
};
|
||||
|
||||
var appConfig = new ApplicationConfiguration
|
||||
{
|
||||
ApplicationName = _config.ServerName,
|
||||
ApplicationUri = namespaceUri,
|
||||
ApplicationType = ApplicationType.Server,
|
||||
ProductUri = namespaceUri,
|
||||
|
||||
ServerConfiguration = new ServerConfiguration
|
||||
{
|
||||
BaseAddresses = { $"opc.tcp://0.0.0.0:{_config.Port}{_config.EndpointPath}" },
|
||||
MaxSessionCount = _config.MaxSessions,
|
||||
MaxSessionTimeout = _config.SessionTimeoutMinutes * 60 * 1000, // ms
|
||||
MinSessionTimeout = 10000,
|
||||
SecurityPolicies =
|
||||
{
|
||||
new ServerSecurityPolicy
|
||||
{
|
||||
SecurityMode = MessageSecurityMode.None,
|
||||
SecurityPolicyUri = SecurityPolicies.None
|
||||
}
|
||||
},
|
||||
UserTokenPolicies = BuildUserTokenPolicies()
|
||||
},
|
||||
|
||||
SecurityConfiguration = new SecurityConfiguration
|
||||
{
|
||||
ApplicationCertificate = new CertificateIdentifier
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"OPC Foundation", "pki", "own"),
|
||||
SubjectName = $"CN={_config.ServerName}, O=ZB MOM, DC=localhost"
|
||||
},
|
||||
TrustedIssuerCertificates = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"OPC Foundation", "pki", "issuer")
|
||||
},
|
||||
TrustedPeerCertificates = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"OPC Foundation", "pki", "trusted")
|
||||
},
|
||||
RejectedCertificateStore = new CertificateTrustList
|
||||
{
|
||||
StoreType = CertificateStoreType.Directory,
|
||||
StorePath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"OPC Foundation", "pki", "rejected")
|
||||
},
|
||||
AutoAcceptUntrustedCertificates = true
|
||||
},
|
||||
ServerConfiguration = serverConfig,
|
||||
SecurityConfiguration = secConfig,
|
||||
|
||||
TransportQuotas = new TransportQuotas
|
||||
{
|
||||
@@ -148,6 +154,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
|
||||
await appConfig.Validate(ApplicationType.Server);
|
||||
|
||||
// Hook certificate validation logging
|
||||
appConfig.CertificateValidator.CertificateValidation += OnCertificateValidation;
|
||||
|
||||
_application = new ApplicationInstance
|
||||
{
|
||||
ApplicationName = _config.ServerName,
|
||||
@@ -156,19 +165,34 @@ namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
};
|
||||
|
||||
// Check/create application certificate
|
||||
bool certOk = await _application.CheckApplicationInstanceCertificate(false, 2048);
|
||||
var minKeySize = (ushort)_securityConfig.MinimumCertificateKeySize;
|
||||
bool certOk = await _application.CheckApplicationInstanceCertificate(false, minKeySize);
|
||||
if (!certOk)
|
||||
{
|
||||
Log.Warning("Application certificate check failed, attempting to create...");
|
||||
certOk = await _application.CheckApplicationInstanceCertificate(false, 2048);
|
||||
certOk = await _application.CheckApplicationInstanceCertificate(false, minKeySize);
|
||||
}
|
||||
|
||||
_server = new LmxOpcUaServer(_config.GalaxyName, _mxAccessClient, _metrics, _historianDataSource,
|
||||
_config.AlarmTrackingEnabled, _authConfig, _authProvider);
|
||||
await _application.Start(_server);
|
||||
|
||||
Log.Information("OPC UA server started on opc.tcp://localhost:{Port}{EndpointPath} (namespace={Namespace})",
|
||||
_config.Port, _config.EndpointPath, namespaceUri);
|
||||
Log.Information("OPC UA server started on opc.tcp://{BindAddress}:{Port}{EndpointPath} (namespace={Namespace})",
|
||||
_config.BindAddress, _config.Port, _config.EndpointPath, namespaceUri);
|
||||
}
|
||||
|
||||
private void OnCertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
|
||||
{
|
||||
if (_securityConfig.AutoAcceptClientCertificates)
|
||||
{
|
||||
e.Accept = true;
|
||||
Log.Debug("Client certificate auto-accepted: {Subject}", e.Certificate?.Subject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Client certificate validation: {Error} for {Subject} — Accepted={Accepted}",
|
||||
e.Error?.StatusCode, e.Certificate?.Subject, e.Accept);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
86
src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/SecurityProfileResolver.cs
Normal file
86
src/ZB.MOM.WW.LmxOpcUa.Host/OpcUa/SecurityProfileResolver.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Server;
|
||||
using Serilog;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Host.OpcUa
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps configured security profile names to OPC UA <see cref="ServerSecurityPolicy"/> instances.
|
||||
/// </summary>
|
||||
public static class SecurityProfileResolver
|
||||
{
|
||||
private static readonly ILogger Log = Serilog.Log.ForContext(typeof(SecurityProfileResolver));
|
||||
|
||||
private static readonly Dictionary<string, ServerSecurityPolicy> KnownProfiles =
|
||||
new Dictionary<string, ServerSecurityPolicy>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["None"] = new ServerSecurityPolicy
|
||||
{
|
||||
SecurityMode = MessageSecurityMode.None,
|
||||
SecurityPolicyUri = SecurityPolicies.None
|
||||
},
|
||||
["Basic256Sha256-Sign"] = new ServerSecurityPolicy
|
||||
{
|
||||
SecurityMode = MessageSecurityMode.Sign,
|
||||
SecurityPolicyUri = SecurityPolicies.Basic256Sha256
|
||||
},
|
||||
["Basic256Sha256-SignAndEncrypt"] = new ServerSecurityPolicy
|
||||
{
|
||||
SecurityMode = MessageSecurityMode.SignAndEncrypt,
|
||||
SecurityPolicyUri = SecurityPolicies.Basic256Sha256
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the configured profile names to <see cref="ServerSecurityPolicy"/> entries.
|
||||
/// Unknown names are skipped with a warning. An empty or fully-invalid list falls back to <c>None</c>.
|
||||
/// </summary>
|
||||
/// <param name="profileNames">The profile names from configuration.</param>
|
||||
/// <returns>A deduplicated list of server security policies.</returns>
|
||||
public static List<ServerSecurityPolicy> Resolve(IReadOnlyCollection<string> profileNames)
|
||||
{
|
||||
var resolved = new List<ServerSecurityPolicy>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var name in profileNames ?? Array.Empty<string>())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
continue;
|
||||
|
||||
var trimmed = name.Trim();
|
||||
|
||||
if (!seen.Add(trimmed))
|
||||
{
|
||||
Log.Debug("Skipping duplicate security profile: {Profile}", trimmed);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (KnownProfiles.TryGetValue(trimmed, out var policy))
|
||||
{
|
||||
resolved.Add(policy);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Unknown security profile '{Profile}' — skipping. Valid profiles: {ValidProfiles}",
|
||||
trimmed, string.Join(", ", KnownProfiles.Keys));
|
||||
}
|
||||
}
|
||||
|
||||
if (resolved.Count == 0)
|
||||
{
|
||||
Log.Warning("No valid security profiles configured — falling back to None");
|
||||
resolved.Add(KnownProfiles["None"]);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of valid profile names for validation and documentation.
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<string> ValidProfileNames => KnownProfiles.Keys.ToList().AsReadOnly();
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
configuration.GetSection("Dashboard").Bind(_config.Dashboard);
|
||||
configuration.GetSection("Historian").Bind(_config.Historian);
|
||||
configuration.GetSection("Authentication").Bind(_config.Authentication);
|
||||
// Clear the default Profiles list before binding so JSON values replace rather than append
|
||||
_config.Security.Profiles.Clear();
|
||||
configuration.GetSection("Security").Bind(_config.Security);
|
||||
|
||||
_mxProxy = new MxProxyAdapter();
|
||||
_galaxyRepository = new GalaxyRepositoryService(_config.GalaxyRepository);
|
||||
@@ -161,7 +164,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
? new Domain.ConfigUserAuthenticationProvider(_config.Authentication.Users)
|
||||
: (Domain.IUserAuthenticationProvider?)null;
|
||||
_serverHost = new OpcUaServerHost(_config.OpcUa, effectiveMxClient, _metrics, historianDataSource,
|
||||
_config.Authentication, authProvider);
|
||||
_config.Authentication, authProvider, _config.Security);
|
||||
|
||||
// Step 9-10: Query hierarchy, start server, build address space
|
||||
DateTime? initialDeployTime = null;
|
||||
|
||||
@@ -122,6 +122,17 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the security profile configuration for the test host.
|
||||
/// </summary>
|
||||
/// <param name="security">The security profile configuration to inject.</param>
|
||||
/// <returns>The current builder so additional overrides can be chained.</returns>
|
||||
public OpcUaServiceBuilder WithSecurity(SecurityProfileConfiguration security)
|
||||
{
|
||||
_config.Security = security;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effectively disables Galaxy change detection by pushing the polling interval beyond realistic test durations.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"OpcUa": {
|
||||
"BindAddress": "0.0.0.0",
|
||||
"Port": 4840,
|
||||
"EndpointPath": "/LmxOpcUa",
|
||||
"ServerName": "LmxOpcUa",
|
||||
@@ -36,6 +37,14 @@
|
||||
"AnonymousCanWrite": true,
|
||||
"Users": []
|
||||
},
|
||||
"Security": {
|
||||
"Profiles": ["None"],
|
||||
"AutoAcceptClientCertificates": true,
|
||||
"RejectSHA1Certificates": true,
|
||||
"MinimumCertificateKeySize": 2048,
|
||||
"PkiRootPath": null,
|
||||
"CertificateSubject": null
|
||||
},
|
||||
"Historian": {
|
||||
"Enabled": false,
|
||||
"ConnectionString": "Server=localhost;Database=Runtime;Integrated Security=true;",
|
||||
|
||||
Reference in New Issue
Block a user