Skip to content

Commit

Permalink
Doc updates and fixes for .368 preview, ref server improvements (#1695)
Browse files Browse the repository at this point in the history
- Doc, docs, docs updates for new 368 support
- fix a missing null pointer check when ConditionRefresh is run
- Ref server: move NodeManager factory API to StandardServer, decoupling the NodeManager implementation from Ref server
- Ref server: add shadow mode flag, to allow to map a docker configuration in a mapped folder.
- Ref server: sample batch files how to run Github CR and local docker images
- Improve some misleading 'delete application certificate' log messages.
  • Loading branch information
mregen committed Feb 11, 2022
1 parent a7b5611 commit 7d9be41
Show file tree
Hide file tree
Showing 33 changed files with 595 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Description>.NET Console Reference Server</Description>
<Copyright>Copyright © 2004-2022 OPC Foundation, Inc</Copyright>
<RootNamespace>Quickstarts</RootNamespace>
<UserSecretsId>46345736-a30f-4466-b3bb-42548ecfaacc</UserSecretsId>
</PropertyGroup>

<ItemGroup>
Expand Down
21 changes: 21 additions & 0 deletions Applications/ConsoleReferenceServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static async Task<int> Main(string[] args)
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
bool shadowConfig = false;
string password = null;
int timeout = -1;

Expand All @@ -68,6 +69,7 @@ public static async Task<int> Main(string[] args)
{ "p|password=", "optional password for private key", (string p) => password = p },
{ "r|renew", "renew application certificate", r => renewCertificate = r != null },
{ "t|timeout=", "timeout in seconds to exit application", (int t) => timeout = t * 1000 },
{ "s|shadowconfig", "create configuration in pki root", s => shadowConfig = s != null },
};

try
Expand All @@ -90,13 +92,32 @@ public static async Task<int> Main(string[] args)
output.WriteLine("Loading configuration from {0}.", configSectionName);
await server.LoadAsync(applicationName, configSectionName).ConfigureAwait(false);

// use the shadow config to map the config to an externally accessible location
if (shadowConfig)
{
output.WriteLine("Using shadow configuration.");
var shadowPath = Directory.GetParent(Path.GetDirectoryName(
Utils.ReplaceSpecialFolderNames(server.Configuration.TraceConfiguration.OutputFilePath))).FullName;
var shadowFilePath = Path.Combine(shadowPath, Path.GetFileName(server.Configuration.SourceFilePath));
if (!File.Exists(shadowFilePath))
{
output.WriteLine("Create a copy of the config in the shadow location.");
File.Copy(server.Configuration.SourceFilePath, shadowFilePath, true);
}
output.WriteLine("Reloading configuration from {0}.", shadowFilePath);
await server.LoadAsync(applicationName, Path.Combine(shadowPath, configSectionName)).ConfigureAwait(false);
}

// setup the logging
ConsoleUtils.ConfigureLogging(server.Configuration, applicationName, logConsole, LogLevel.Information);

// check or renew the certificate
output.WriteLine("Check the certificate.");
await server.CheckCertificateAsync(renewCertificate).ConfigureAwait(false);

// Create and add the node managers
server.Create(Servers.Utils.NodeManagerFactories);

// start the server
output.WriteLine("Start the server.");
await server.StartAsync().ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,17 @@
<!-- Allows username/password -->
<ua:UserTokenPolicy>
<ua:TokenType>UserName_1</ua:TokenType>
<!-- passwords must be encrypted - this specifies what algorithm to use, unspecified uses the session profile -->
<!-- passwords must be encrypted - this specifies what algorithm to use -->
<!-- if no algorithm is specified, the active security policy is used -->
<!-- <ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri> -->
</ua:UserTokenPolicy>

<!-- Allows user certificates -->
<ua:UserTokenPolicy>
<ua:TokenType>Certificate_2</ua:TokenType>
<!-- passwords must be encrypted - this specifies what algorithm to use, unspecified uses the session profile -->
<ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri>
<!-- certificate possession must be proven with a digital signature - this specifies what algorithm to use -->
<!-- if no algorithm is specified, the active security policy is used -->
<!-- <ua:SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</ua:SecurityPolicyUri> -->
</ua:UserTokenPolicy>
</UserTokenPolicies>
<DiagnosticsEnabled>true</DiagnosticsEnabled>
Expand Down Expand Up @@ -237,6 +239,7 @@
</SupportedPrivateKeyFormats>
<MaxTrustListSize>0</MaxTrustListSize>
<MultiCastDnsEnabled>false</MultiCastDnsEnabled>

<!-- Reverse connection parameters for aggregation server sample -->
<!--
<ReverseConnect>
Expand All @@ -254,18 +257,18 @@
-->

<OperationLimits>
<MaxNodesPerRead>10000</MaxNodesPerRead>
<MaxNodesPerHistoryReadData>10000</MaxNodesPerHistoryReadData>
<MaxNodesPerHistoryReadEvents>10000</MaxNodesPerHistoryReadEvents>
<MaxNodesPerWrite>10000</MaxNodesPerWrite>
<MaxNodesPerHistoryUpdateData>10000</MaxNodesPerHistoryUpdateData>
<MaxNodesPerHistoryUpdateEvents>10000</MaxNodesPerHistoryUpdateEvents>
<MaxNodesPerRead>2500</MaxNodesPerRead>
<MaxNodesPerHistoryReadData>1000</MaxNodesPerHistoryReadData>
<MaxNodesPerHistoryReadEvents>1000</MaxNodesPerHistoryReadEvents>
<MaxNodesPerWrite>2500</MaxNodesPerWrite>
<MaxNodesPerHistoryUpdateData>1000</MaxNodesPerHistoryUpdateData>
<MaxNodesPerHistoryUpdateEvents>1000</MaxNodesPerHistoryUpdateEvents>
<MaxNodesPerMethodCall>2500</MaxNodesPerMethodCall>
<MaxNodesPerBrowse>2500</MaxNodesPerBrowse>
<MaxNodesPerRegisterNodes>10000</MaxNodesPerRegisterNodes>
<MaxNodesPerTranslateBrowsePathsToNodeIds>10000</MaxNodesPerTranslateBrowsePathsToNodeIds>
<MaxNodesPerNodeManagement>10000</MaxNodesPerNodeManagement>
<MaxMonitoredItemsPerCall>10000</MaxMonitoredItemsPerCall>
<MaxNodesPerRegisterNodes>2500</MaxNodesPerRegisterNodes>
<MaxNodesPerTranslateBrowsePathsToNodeIds>2500</MaxNodesPerTranslateBrowsePathsToNodeIds>
<MaxNodesPerNodeManagement>2500</MaxNodesPerNodeManagement>
<MaxMonitoredItemsPerCall>2500</MaxMonitoredItemsPerCall>
</OperationLimits>

</ServerConfiguration>
Expand Down Expand Up @@ -303,7 +306,7 @@
<!-- Show Only Security, Service Calls, Errors and Trace -->
<!-- <TraceMasks>523</TraceMasks> -->
<!-- Show Only Security, ServiceResultExceptions, Errors and Trace -->
<!-- <TraceMasks>519</TraceMasks> -->
<TraceMasks>519</TraceMasks>
</TraceConfiguration>

</ApplicationConfiguration>
27 changes: 25 additions & 2 deletions Applications/ConsoleReferenceServer/UAServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public async Task CheckCertificateAsync(bool renewCertificate)
bool haveAppCertificate = await m_application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new Exception("Application instance certificate invalid!");
throw new ErrorExitException("Application instance certificate invalid!");
}

if (!config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
Expand All @@ -117,6 +117,29 @@ public async Task CheckCertificateAsync(bool renewCertificate)
}
}

/// <summary>
/// Create server instance and add node managers.
/// </summary>
public void Create(IList<INodeManagerFactory> nodeManagerFactories)
{
try
{
// create the server.
m_server = new T();
if (nodeManagerFactories != null)
{
foreach (var factory in nodeManagerFactories)
{
m_server.AddNodeManager(factory);
}
}
}
catch (Exception ex)
{
throw new ErrorExitException(ex.Message, ExitCode);
}
}

/// <summary>
/// Start the server.
/// </summary>
Expand All @@ -125,7 +148,7 @@ public async Task StartAsync()
try
{
// create the server.
m_server = new T();
m_server = m_server ?? new T();

// start the server
await m_application.Start(m_server).ConfigureAwait(false);
Expand Down
2 changes: 1 addition & 1 deletion Applications/ConsoleReferenceServer/dockerbuild.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ REM build a docker container of the console reference server
set buildoptions=--configuration Release -p:NoHttps=true --framework net6.0
dotnet build %buildoptions% ConsoleReferenceServer.csproj
dotnet publish %buildoptions% ConsoleReferenceServer.csproj -o ./publish
docker build -t consolerefserver .
docker build -f Dockerfile -t consolerefserver .
4 changes: 2 additions & 2 deletions Applications/ConsoleReferenceServer/dockerbuild.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
echo build a docker container of the .NET Core reference server
echo build a docker container of the .NET Core reference server, without https support
buildoptions="--configuration Release -p:NoHttps=true --framework net6.0"
dotnet build $buildoptions ConsoleReferenceServer.csproj
dotnet publish $buildoptions ConsoleReferenceServer.csproj -o ./publish
sudo docker build -t consolerefserver .
sudo docker build -f Dockerfile -t consolerefserver .
9 changes: 6 additions & 3 deletions Applications/ConsoleReferenceServer/dockerrun.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
echo Run a docker container of the console reference server
echo The certificate store of the ref server is mapped to './OPC Foundation'
docker run -it -p 62541:62541 -h refserver -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest -c -a
@echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to '.\OPC Foundation\pki'
echo A log file is created at '.\OPC Foundation\Logs\Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in '.\OPC Foundation\Quickstarts.ReferenceServer.Config.xml'
docker run -it -p 62541:62541 -h %COMPUTERNAME% -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest -c -s
9 changes: 6 additions & 3 deletions Applications/ConsoleReferenceServer/dockerrun.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/bin/bash
echo Run a docker container of the console reference server
echo The certificate store of the ref server is mapped to './OPC Foundation'
sudo docker run -it -p 62541:62541 -h refserver -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest
set echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to './OPC Foundation/pki'
echo A log file is created at './OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in './OPC Foundation/Quickstarts.ReferenceServer.Config.xml'
sudo docker run -it -p 62541:62541 -h $HOSTNAME -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" consolerefserver:latest -c -s
7 changes: 7 additions & 0 deletions Applications/ConsoleReferenceServer/dockerrunghcr.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to '.\OPC Foundation\pki'
echo A log file is created at '.\OPC Foundation\Logs\Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in '.\OPC Foundation\Quickstarts.ReferenceServer.Config.xml'
docker pull ghcr.io/opcfoundation/uanetstandard/refserver:latest
docker run -it -p 62541:62541 -h %COMPUTERNAME% -v "%CD%/OPC Foundation:/root/.local/share/OPC Foundation" ghcr.io/opcfoundation/uanetstandard/refserver:latest -c -s
8 changes: 8 additions & 0 deletions Applications/ConsoleReferenceServer/dockerrunghcr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
set echo off
echo Run the local docker container of the console reference server
echo By default, the certificate store of the ref server is mapped to './OPC Foundation/pki'
echo A log file is created at './OPC Foundation/Logs/Quickstarts.ReferenceServer.log.txt'
echo A shadow configuration file for customization is created in './OPC Foundation/Quickstarts.ReferenceServer.Config.xml'
sudo docker pull ghcr.io/opcfoundation/uanetstandard/refserver:latest
sudo docker run -it -p 62541:62541 -h $HOSTNAME -v "$(pwd)/OPC Foundation:/root/.local/share/OPC Foundation" ghcr.io/opcfoundation/uanetstandard/refserver:latest -c -s
16 changes: 15 additions & 1 deletion Applications/Quickstarts.Servers/Boiler/BoilerNodeManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* ========================================================================
* Copyright (c) 2005-2019 The OPC Foundation, Inc. All rights reserved.
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
Expand Down Expand Up @@ -40,10 +40,24 @@ namespace Boiler
/// </summary>
public class BoilerNodeManagerFactory : INodeManagerFactory
{
/// <inheritdoc/>
public INodeManager Create(IServerInternal server, ApplicationConfiguration configuration)
{
return new BoilerNodeManager(server, configuration);
}

/// <inheritdoc/>
public StringCollection NamespacesUris
{
get
{
var nameSpaces = new StringCollection {
Namespaces.Boiler,
Namespaces.Boiler + "Instance"
};
return nameSpaces;
}
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,24 @@ namespace MemoryBuffer
/// </summary>
public class MemoryBufferNodeManagerFactory : INodeManagerFactory
{
/// <inheritdoc/>
public INodeManager Create(IServerInternal server, ApplicationConfiguration configuration)
{
return new MemoryBufferNodeManager(server, configuration);
}

/// <inheritdoc/>
public StringCollection NamespacesUris
{
get
{
var nameSpaces = new StringCollection {
Namespaces.MemoryBuffer,
Namespaces.MemoryBuffer + "/Instance"
};
return nameSpaces;
}
}
}

/// <summary>
Expand Down
82 changes: 41 additions & 41 deletions Applications/Quickstarts.Servers/ReferenceServer/ReferenceServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,14 @@ public partial class ReferenceServer : ReverseConnectServer
/// </remarks>
protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration)
{
Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Managers.");
Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Manager.");

List<INodeManager> nodeManagers = new List<INodeManager>();
IList<INodeManager> nodeManagers = new List<INodeManager>();

// create the custom node managers.
// create the custom node manager.
nodeManagers.Add(new ReferenceNodeManager(server, configuration));

if (m_nodeManagerFactory == null || m_nodeManagerFactory.Count == 0)
{
AddDefaultFactories();
}

foreach (var nodeManagerFactory in m_nodeManagerFactory)
foreach (var nodeManagerFactory in NodeManagerFactories)
{
nodeManagers.Add(nodeManagerFactory.Create(server, configuration));
}
Expand All @@ -90,14 +85,14 @@ protected override MasterNodeManager CreateMasterNodeManager(IServerInternal ser
/// </remarks>
protected override ServerProperties LoadServerProperties()
{
ServerProperties properties = new ServerProperties();

properties.ManufacturerName = "OPC Foundation";
properties.ProductName = "Quickstart Reference Server";
properties.ProductUri = "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04";
properties.SoftwareVersion = Utils.GetAssemblySoftwareVersion();
properties.BuildNumber = Utils.GetAssemblyBuildNumber();
properties.BuildDate = Utils.GetAssemblyTimestamp();
ServerProperties properties = new ServerProperties {
ManufacturerName = "OPC Foundation",
ProductName = "Quickstart Reference Server",
ProductUri = "http://opcfoundation.org/Quickstart/ReferenceServer/v1.04",
SoftwareVersion = Utils.GetAssemblySoftwareVersion(),
BuildNumber = Utils.GetAssemblyBuildNumber(),
BuildDate = Utils.GetAssemblyTimestamp()
};

return properties;
}
Expand Down Expand Up @@ -164,6 +159,35 @@ protected override void OnServerStarted(IServerInternal server)
{ }

}

/// <summary>
/// Override some of the default user token policies for some endpoints.
/// </summary>
/// <remarks>
/// Sample to show how to override default user token policies.
/// </remarks>
public override UserTokenPolicyCollection GetUserTokenPolicies(ApplicationConfiguration configuration, EndpointDescription description)
{
var policies = base.GetUserTokenPolicies(configuration, description);

// sample how to modify default user token policies
if (description.SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss &&
description.SecurityMode == MessageSecurityMode.SignAndEncrypt)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.Certificate));
}
else if (description.SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep &&
description.SecurityMode == MessageSecurityMode.Sign)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.Anonymous));
}
else if (description.SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep &&
description.SecurityMode == MessageSecurityMode.SignAndEncrypt)
{
policies = new UserTokenPolicyCollection(policies.Where(u => u.TokenType != UserTokenType.UserName));
}
return policies;
}
#endregion

#region User Validation Functions
Expand Down Expand Up @@ -351,33 +375,9 @@ private void VerifyUserTokenCertificate(X509Certificate2 certificate)
new LocalizedText(info)));
}
}

private static INodeManagerFactory IsINodeManagerFactoryType(Type type)
{
var nodeManagerTypeInfo = type.GetTypeInfo();
if (nodeManagerTypeInfo.IsAbstract ||
!typeof(INodeManagerFactory).IsAssignableFrom(type))
{
return null;
}

return Activator.CreateInstance(type) as INodeManagerFactory;
}

private void AddDefaultFactories()
{
var assembly = GetType().Assembly;
var factories = assembly.GetExportedTypes().Select(type => IsINodeManagerFactoryType(type)).Where(type => type != null);
m_nodeManagerFactory = new List<INodeManagerFactory>();
foreach (var nodeManagerFactory in factories)
{
m_nodeManagerFactory.Add(nodeManagerFactory);
}
}
#endregion

#region Private Fields
private IList<INodeManagerFactory> m_nodeManagerFactory;
private ICertificateValidator m_userCertificateValidator;
#endregion
}
Expand Down

0 comments on commit 7d9be41

Please sign in to comment.